编程化境
- 操作系统:windows 64位
- 具体环境:windows64位 + msys2(UCRT64,gcc)+ clion + cmake
- c++标准的配置: c++2026
常用附加库
- boost
- fmt
头文件:ayzw.top/init.h
持续更新
//
// Created by ayzw.top on 2026/6/10.
// 自定义cpp文件
//
#include <iostream> // 尖括号 < > —— 优先搜索系统路径
#include <limits>
#include <iomanip>
#include <cstdint>
#include <quadmath.h>
#include <fmt/core.h>
#include <fmt/ranges.h>
#include <fmt/os.h>
#include <format>
// using 是现代 C++ 的类型安全系统(诞生于 C++11),完全遵循 C++ 的作用域规则。
// 它可以被限制在某个函数内部、某个类(class)内部,或者某个命名空间(namespace)中
// 不要用 #define ,它处于预处理阶段,在代码编译之前,预处理器会像文本编辑器的“全部替换”功能一样,
// 粗暴地把宏名换成后面的文本。它完全不懂 C++ 语法
// 整数类型简写
using int8 = std::int8_t;
using int16 = std::int16_t;
using int32 = std::int32_t;
using int64 = std::int64_t;
using uint8 = std::uint8_t;
using uint16 = std::uint16_t;
using uint32 = std::uint32_t;
using uint64 = std::uint64_t;
using f128 = __float128;
using namespace std;
using namespace fmt;
整数类型
使用 <cstdint> 库,该库提供了跨平台、大小完全固定的整数类型。使用这些类型可以彻底解决 int 或 long 在不同系统(如 32位 vs 64位)上字节长度缩水的问题。
| 字节数 | 类型名称 (有符号) | 有符号取值范围 | 类型名称 (无符号) | 无符号取值范围 |
|---|---|---|---|---|
| 1 字节 | int8_t | -128 到 127 | uint8_t | 0 到 255 |
| 2 字节 | int16_t | -32,768 到 32,767 | uint16_t | 0 到 65,535 |
| 4 字节 | int32_t | 约 -21.4 亿 到 21.4 亿 | uint32_t | 0 到 约 42.9 亿 |
| 8 字节 | int64_t | ≈ ± 9.22 × 10¹⁸ (百亿亿) | uint64_t | 0 到 ≈ 1.84 × 10¹⁹ |
打印示例:
#include <cstdint>
#include <fmt/core.h>
int main() {
// 1. 有符号整数 (Signed Integers)
int8_t a_8 = -12;
int16_t a_16 = -1234;
int32_t a_32 = -1234567;
int64_t a_64 = -1234567890123LL;
fmt::print("--- 有符号整数 ---\n");
fmt::print("int8_t: {}\n", a_8);
fmt::print("int16_t: {}\n", a_16);
fmt::print("int32_t: {}\n", a_32);
fmt::print("int64_t: {}\n", a_64);
// 2. 无符号整数 (Unsigned Integers)
uint8_t b_8 = 250;
uint16_t b_16 = 65000;
uint32_t b_32 = 4294967295U;
uint64_t b_64 = 18446744073709551615ULL;
fmt::print("\n--- 无符号整数 ---\n");
// 注意:uint8_t 可能会被某些旧版或特殊配置当作 char 打印。
// fmt 默认会将其当作数字打印。
fmt::print("uint8_t: {}\n", b_8);
fmt::print("uint16_t: {}\n", b_16);
fmt::print("uint32_t: {}\n", b_32);
fmt::print("uint64_t: {}\n", b_64);
// 3. 高级格式化技巧
fmt::print("\n--- 高级格式化 ---\n");
fmt::print("十六进制显示 (小写): {:x}\n", b_32);
fmt::print("十六进制显示 (大写且带前缀): {:#X}\n", b_64);
fmt::print("二进制显示: {:b}\n", b_8);
fmt::print("靠右对齐并补零 (宽度为10): {:010}\n", a_32);
return 0;
}
输出:
--- 有符号整数 ---
int8_t: -12
int16_t: -1234
int32_t: -1234567
int64_t: -1234567890123
--- 无符号整数 ---
uint8_t: 250
uint16_t: 65000
uint32_t: 4294967295
uint64_t: 18446744073709551615
--- 高级格式化 ---
十六进制显示 (小写): ffffffff
十六进制显示 (大写且带前缀): 0XFFFFFFFFFFFFFFFF
二进制显示: 11111010
靠右对齐并补零 (宽度为10): -001234567
整数字面量,建议大写后缀U,LL,ULL:
#include "ayzw.top/init.h"
int main() {
// 1. 显式指定 8位 和 16位 整型变量(GCC 原生最正统写法)
int8 val_i8 = 120; // 8位 有符号整型
uint8 val_u8 = 250; // 8位 无符号整型
int16 val_i16 = 32000; // 16位 有符号整型
uint16 val_u16 = 64000; // 16位 无符号整型
// 2. GCC 原生最欢迎的大写纯后缀(32位与64位)
int32 val_i32 = 2147483647; // 默认就是标准的 32位 signed int
uint32 val_u32 = 4294967295U; // 大写 U 表示 32位 unsigned int
int64 val_i64 = 9'223'372'036'854'775'807LL; // 大写 LL 强行确保 64位 signed
uint64 val_u64 = 18'446'744'073'709'551'615ULL; // 大写 ULL 强行确保 64位 unsigned
print("=== 8位与16位整型验证 ===\n");
// 注意:8位整型底层是 char,必须强转 int 才能用 fmt 正确打印出数字
print("val_i8 物理大小: {} 字节, 数值: {}\n", sizeof(val_i8), static_cast<int>(val_i8));
print("val_u8 物理大小: {} 字节, 数值: {}\n", sizeof(val_u8), static_cast<int>(val_u8));
print("val_i16 物理大小: {} 字节, 数值: {}\n", sizeof(val_i16), val_i16);
print("val_u16 物理大小: {} 字节, 数值: {}\n\n", sizeof(val_u16), val_u16);
print("=== 32位与64位大写后缀验证 ===\n");
print("val_i32 物理大小: {} 字节, 数值: {}\n", sizeof(val_i32), val_i32);
print("val_u32 物理大小: {} 字节, 数值: {}\n", sizeof(val_u32), val_u32);
print("val_i64 物理大小: {} 字节, 数值: {}\n", sizeof(val_i64), val_i64);
print("val_u64 物理大小: {} 字节, 数值: {}\n", sizeof(val_u64), val_u64);
return 0;
}
输出:
=== 8位与16位整型验证 ===
val_i8 物理大小: 1 字节, 数值: 120
val_u8 物理大小: 1 字节, 数值: 250
val_i16 物理大小: 2 字节, 数值: 32000
val_u16 物理大小: 2 字节, 数值: 64000
=== 32位与64位大写后缀验证 ===
val_i32 物理大小: 4 字节, 数值: 2147483647
val_u32 物理大小: 4 字节, 数值: 4294967295
val_i64 物理大小: 8 字节, 数值: 9223372036854775807
val_u64 物理大小: 8 字节, 数值: 18446744073709551615
浮点数
在 64 位操作系统下,float 和 double 的取值范围是绝对的定值:
- float(单精度浮点数):永远占用 4 个字节(32 位)
- double(双精度浮点数):永远占用 8 个字节(64 位)
| 类型 | 占用字节 | 有效数字精度 |
|---|---|---|
| float | 4 字节 | 约 6 ~ 7 位 |
| double | 8 字节 | 约 15 ~ 17 位 |
| f128 | 16 字节 | 约 33 ~ 36 位 |
- float只能保证6位有效数字
- double只能保证15位有效数字
__float128(对应 IEEE 754 标准中的 binary128 四精度格式)拥有 113 位二进制尾数。转换为十进制后,它保证具有 33 位完全准确的有效数字(digits10),最大可完整显示 36 位有效数字
#include "ayzw.top/init.h"
#include <iostream>
#include <iomanip> // 用于控制 float 和 double 的精度
#include <quadmath.h> // GCC 原生 128 位浮点数头文件
int main() {
// 1. 字面量声明(注意:__float128 必须加 q 后缀)
float f_val = 1.123456789012345678901234567890123456789f;
double d_val = 1.123456789012345678901234567890123456789;
__float128 q_val = 1.123456789012345678901234567890123456789q;
// 2. 打印 float 和 double(使用最常用的 std::setprecision)
std::cout << "--- 强行打印 40 位小数对比 ---" << std::endl;
std::cout << "float: " << std::fixed << std::setprecision(40) << f_val << std::endl;
std::cout << "double: " << std::fixed << std::setprecision(40) << d_val << std::endl;
// 3. 打印 __float128(最原始、最直接的单行缓冲区法)
char buf[128];
// %.36Qf 表示:打印 36 位小数,Q 是 __float128 的专属占位符
quadmath_snprintf(buf, sizeof(buf), "%.36Qf", q_val);
std::cout << "__float128: " << buf << std::endl;
return 0;
}
输出:
--- 强行打印 40 位小数对比 ---
float: 1.1234568357467651367187500000000000000000
double: 1.1234567890123456912476740399142727255821
__float128: 1.123456789012345678901234567890123475
一般情况下,double的精度足够了
不用boost库的float128位小数,速度相对较慢,不过boost通用性更强–支持clang编译器
浮点数字面量后缀:建议大写F、Q
#include "ayzw.top/init.h"
int main() {
print("=== 64位系统 浮点数字面量后缀极限体检 ===\n\n");
// 1. 使用标准大写后缀声明字面量(故意写一个 40 位的超长小数)
auto f_val = 1.123456789012345678901234567890123456789F; // 大写 F 强制 float
auto d_val = 1.123456789012345678901234567890123456789; // 无后缀 默认 double
auto q_val = 1.123456789012345678901234567890123456789Q; // 大写 Q 强制 __float128
// 2. 强行要求打印 45 位小数,肉眼观察内存截断点
print("[float (F后缀)]\n");
cout << fixed << setprecision(45) << f_val << "\n";
print("-> 结论: 仅前 7 位正确,后面全部崩塌。\n\n");
print("[double (无后缀)]\n");
cout << fixed << setprecision(45) << d_val << "\n";
print("-> 结论: 仅前 15~17 位正确,后面开始失真。\n\n");
print("[__float128 (Q后缀)]\n");
char buf[128];
quadmath_snprintf(buf, sizeof(buf), "%.45Qf", q_val); // 使用专属的 Q 占位符
print("{}\n", buf);
print("-> 结论: 完美保持精准到第 34 位小数!之后才出现二进制舍入误差。\n");
return 0;
}
输出:
=== 64位系统 浮点数字面量后缀极限体检 ===
[float (F后缀)]
1.123456835746765136718750000000000000000000000
-> 结论: 仅前 7 位正确,后面全部崩塌。
[double (无后缀)]
1.123456789012345691247674039914272725582122803
-> 结论: 仅前 15~17 位正确,后面开始失真。
[__float128 (Q后缀)]
1.123456789012345678901234567890123474525951569
-> 结论: 完美保持精准到第 34 位小数!之后才出现二进制舍入误差。
求模 %
- 结果如果不为零,的符号和被除数一致,所以可能为负数
- 判断奇数、偶数比较好的方式是:求模结果是否为零,而不是为1
int main() {
print("=== C++ 求模运算符号测试 ===\n\n");
print(" 10 % 3 = {}\n", 10 % 3);
print(" 10 % -3 = {}\n", 10 % -3);
print("-10 % 3 = {}\n", -10 % 3);
print("-10 % -3 = {}\n", -10 % -3);
return 0;
}
结果
=== C++ 求模运算符号测试 ===
10 % 3 = 1
10 % -3 = 1
-10 % 3 = -1
-10 % -3 = -1
强制类型转换
函数风格:
typeName (value)
强制类型转换运算符:
static_cat
#include "ayzw.top/init.h"
int main() {
print("=== C++ 强制类型转换全面验证 ===\n\n");
// ====================================================
// 1. 验证“向零截断”行为(用 3.678 证明不是四舍五入)
// ====================================================
double val_double = 3.678;
// 无论是函数风格还是 static_cast,3.678 转换后都会变成 3,而不是 4
int32_t int_func = int32_t(val_double);
int32_t int_static = static_cast<int32_t>(val_double);
print("[1. 浮点数转整数:截断行为验证]\n");
print("原始 double 数值: {}\n", val_double);
print("函数风格转换结果: {}\n", int_func);
print("static_cast 转换结果: {}\n\n", int_static);
// ====================================================
// 2. 新增:双精度 double 转换为 单精度 float
// ====================================================
// 声明一个拥有 16 位长尾数的典型 double 变量
double precise_double = 3.123456789012345;
// 转换为只有 32 位位宽的 float(有效数字只有约 7 位)
float float_func = float(precise_double);
float float_static = static_cast<float>(precise_double);
print("[2. double 转 float:精度缩水验证]\n");
print("原始精确 double: {:.15f}\n", precise_double);
// 强行要求打印 15 位小数,观察 float 内存截断
print("函数风格转 float: {:.15f}\n", float_func);
print("static_cast 转 float: {:.15f}\n", float_static);
print("-> 结论: 转换后从第 8 位数字开始就发生了精度扭曲丢失。\n\n");
// ====================================================
// 3. 真实重现:字符 'A' 的打印困境
// ====================================================
char char_val = 'A'; // 底层对应的 ASCII 码就是整数 65
print("[3. 真实应用:解决字符与数字的打印困境]\n");
print("直接打印 char 变量: {}\n", char_val);
print("使用 static_cast 强转后: {}\n\n", static_cast<int>(char_val));
// ====================================================
// 4. 补充:C++ 标准流 cout 中的困境重现
// ====================================================
uint8_t byte_val = 65;
cout << "[4. 标准 cout 流中的相同表现]\n";
cout << "直接用 cout 打印 uint8_t: " << byte_val << " (屏幕打印出的是字符 A!)\n";
cout << "用 static_cast 强转后打印: " << static_cast<int>(byte_val) << " (顺利打印出数字 65)\n";
return 0;
}
输出:
=== C++ 强制类型转换全面验证 ===
[1. 浮点数转整数:截断行为验证]
原始 double 数值: 3.678
函数风格转换结果: 3
static_cast 转换结果: 3
[2. double 转 float:精度缩水验证]
原始精确 double: 3.123456789012345
函数风格转 float: 3.123456716537476
static_cast 转 float: 3.123456716537476
-> 结论: 转换后从第 8 位数字开始就发生了精度扭曲丢失。
[3. 真实应用:解决字符与数字的打印困境]
直接打印 char 变量: A
使用 static_cast 强转后: 65
[4. 标准 cout 流中的相同表现]
直接用 cout 打印 uint8_t: A (屏幕打印出的是字符 A!)
用 static_cast 强转后打印: 65 (顺利打印出数字 65)
数组
数组定义:
typeName arrayName[arraySize];
arraySize不能为变量。
字符串
- c语言风格的字符串是以空字符
\0(ASCII码为0)结尾的字符串数组。C 语言风格的字符串在内存中本质上就是一个 char 数组。它最核心的灵魂在于 必须以 \0(空字符)作为结束标记。如果不含空字符\0,就不是字符串,只是一个字符数组。 - 双引号z
char str1[5]={'a','b','c','d','e'};
char str2[5]={'a','b','c','d','\0'};
类
#include "ayzw.top/init.h"
int main() {
string s1="hello",s2=" world";
string s3=s1+s2;
cout<<s3<<endl;
cout<<s1.size()<<" "<<s2.size()<<" "<<s3.size()<<endl;
getline(cin,s3);
cout<<s3<<endl;
return 0;
}
输出:
hello world
5 6 11
你好
你好
结构
结构比数组更灵活,可以存储多种类型的数据。结构也是C++OOP编程的基石。
#include "ayzw.top/init.h"
struct person {
string name;
float heightCM;
float weightKG;
};
int main() {
person myFriend1{
"小明",
180,
80
};
person myFriend2 = myFriend1;
myFriend2.name = "大刚";
myFriend2.heightCM = 190;
myFriend2.weightKG = 90;
cout << "我有一位好朋友名字叫" << myFriend1.name << ",他身高" << myFriend1.heightCM << "cm,体重"
<< myFriend1.weightKG
<< "kg." << endl;
cout << "我有一位好朋友名字叫" << myFriend2.name << ",他身高" << myFriend2.heightCM << "cm,体重"
<< myFriend2.weightKG
<< "kg." << endl;
return 0;
}
输出:
我有一位好朋友名字叫小明,他身高180cm,体重80kg.
我有一位好朋友名字叫大刚,他身高190cm,体重90kg.
正文完