基本类型
C语言有5种基本数据类型:char、int、float、double、void(不能被修饰)
通过2个长度修饰符:
short:只能修饰intlong:可修饰1~2次int或1次double
以及2个符号修饰符(只能修饰整型类型,可以和长度修饰符组合):
signed:可以缺省,不写符号修饰符等同于signed。有符号则表示正数和负数,绝对值范围减半。unsigned:不可以缺省。无符号则只表示正数,不表示负数。
可以组合出14种基本数据类型。
| 一级分类 | 二级分类 | 字节数 | 类型 | 完整形式 | 简化形式 | 取值范围 | 描述 |
|---|---|---|---|---|---|---|---|
| 整型类型 | 字符型 | 1 | char | signed char | char | -2⁷ ~ 2⁷-1 | 字符类型(8位,不支持中文) |
| 整型类型 | 字符型 | 1 | unsigned char | unsigned char | unsigned char | 0 ~ 2⁸-1 | 无符号字符型(8位,不支持中文) |
| 整型类型 | 短整型 | 2 | short | signed short int | short/short int | -2¹⁵ ~ 2¹⁵-1 | 有符号短整型(16位) |
| 整型类型 | 短整型 | 2 | unsigned short | unsigned short int | unsigned short | 0 ~ 2¹⁶-1 | 无符号短整型(16位) |
| 整型类型 | 标准整型 | 4 | int | signed int | int/signed | -2³¹ ~ 2³¹-1 | 有符号整型(32位,默认) |
| 整型类型 | 标准整型 | 4 | unsigned int | unsigned int | unsigned int/unsigned | 0 ~ 2³²-1 | 无符号整型(32位) |
| 整型类型 | 长整型 | 4/8 | long | signed long int | long | -2³¹ ~ 2³¹-1 或 -2⁶³ ~ 2⁶³-1 | 有符号长整型(32位或64位) |
| 整型类型 | 长整型 | 4/8 | unsigned long | unsigned long int | unsigned long | 0 ~ 2³²-1 或 0 ~ 2⁶⁴-1 | 无符号长整型(32位或64位) |
| 整型类型 | 长长整型 | 8 | long long | signed long long int | long long int/signed long long/long long | -2⁶³ ~ 2⁶³-1 | 有符号长长整型(64位) |
| 整型类型 | 长长整型 | 8 | unsigned long long | unsigned long long int | unsigned long long | 0 ~ 2⁶⁴-1 | 无符号长长整型(64位) |
| 浮点型类型 | 单精度 | 4 | float | float | float | ±1.18×2⁻¹²⁶ ~ ±3.40×2¹²⁷ | 单精度浮点型(32位IEEE754) |
| 浮点型类型 | 双精度 | 8 | double | double | double | ±2.23×2⁻¹⁰²² ~ ±1.80×2¹⁰²³ | 小数默认类型,双精度浮点型(64位IEEE754) |
| 浮点型类型 | 扩展精度 | 8/12/16 | long double | long double | long double | 平台相关 | 扩展精度浮点型(64/80/128位) |
| 特殊类型 | - | 0 | void | void | void | - | 空类型,不占存储空间 |
char
char 用于存储一个基本的 ASCII 字符(不可以存储中文)。
ASCII 码表很小,只包含大小写字母、数字、标点、打印机用的换行、换页等符号。
- 用单引号括起来的单个ASCII 码表字符:
'A','1','$','\n'
C语言严格区分单引号和双引号,单引号才表示单个字符,双引号表示字符串?
#include <stdio.h>
int main(){
const char letter = 'A';
const char newline = '\n';
const char tab = '\t';
printf("字符: %c\n", letter);
printf("换行前%c换行后\n", newline);
printf("制表符前%c制表符后\n", tab);
return 0;
}
char和int类型转换时,int转为char时会是对应的ASCII码表中字符。char转为int时会是对应的ASCII码表中对应的数字。
ASCII 表完整内容可以查看:https://www.asciitable.com/
想要存储中文可以用字符串数组。
- 用双引号括起来:例如
"Hello","C语言" - 系统会自动在字符串末尾添加 '\0' 作为结束标志
#include <stdio.h>
#include <string.h>
int main(){
const char greeting[] = "Hello World";
const char empty[] = "";
printf("问候语: %s\n", greeting);
printf("空字符串长度: %lu\n", strlen(empty));
return 0;
}
int
定义时可以带正负号,默认是正数。支持四种进制格式:
- 十进制整数:例如
666,-120,0 - 二进制整数:以
0b开头,例如0b1010(十进制的10) - 八进制整数:以
0开头,例如0123( 十进制的83) - 十六进制整数:以
0x开头,例如0x123(十进制的291)
#include <stdio.h>
int main(){
printf("十进制: %d\n", 123); // 123
printf("八进制: %d\n", 0123); // 83
printf("十六进制: %d\n", 0x123); // 291
printf("二进制: %d\n", 0b1010); // 10
return 0;
}
判断下列数字是否合理
+178
0b325 // 二进制只能是0-1
0b0010
0986 // 八进制只能是0-7
00011
0x001
0x7h4 // 十六进制只能是0-9,a-f,A-F
0xffdc
signed 和 unsigned
符号修饰符(仅适用于整数类型):
signed- 有符号位unsigned- 无符号位
signed 和 unsigned 的区别就是它们的最高位是否要当做符号位 ,并不会像 short 和 long 那样改变数据的长度,即所占的字节数。
浮点数类型(float、double、long double)天然支持正负值,不能使用符号修饰符
signed 表示有符号的,也就是说最高位要当做符号位。刚好 int 的最高位本来就是符号位。
因此 signed 等价 int 等价 signed int 。
short 和 long
short:只能修饰intlong:可修饰1~2次int或1次double
🤔 为什么 long 不一定比 int 大?有时是4字节,有时是8字节?
核心原因:C标准只规定了相对大小关系,没有规定绝对大小!
// C标准只保证这些相对关系:
sizeof(char) <= sizeof(short) <= sizeof(int) <= sizeof(long) <= sizeof(long long)
// 1字节 ≥2字节 ≥2字节 ≥4字节 ≥8字节
1980年代-1990年代(32位时代):
// 当时的"标准"配置
char = 1字节 (8位)
short = 2字节 (16位)
int = 4字节 (32位) ← 与CPU字长匹配
long = 4字节 (32位) ← 也是32位,够用了
2000年代后(64位时代):
// 两种不同的选择
// Linux/macOS (LP64模型)
int = 4字节 (32位) ← 保持兼容性
long = 8字节 (64位) ← 升级到64位
// Windows (LLP64模型)
int = 4字节 (32位) ← 保持兼容性
long = 4字节 (32位) ← 与32位系统保持一致,向前兼容
long long = 8字节 (64位) ← 用long long做64位
如果你需要特定大小:
// ✅ 推荐:使用标准库的固定大小类型
#include <stdint.h>
int32_t x; // 明确32位
int64_t y; // 明确64位
uint32_t z; // 明确32位无符号
// ⚠️ 不推荐:依赖long的大小
long maybe_big; // 大小不确定
float与double
实型常量
- 单精度小数?:以字母
f或F结尾,例如3.14f - 双精度小数?:默认的小数形式,例如
3.14159 - 指数形式:使用
e或E表示科学计数法,例如1.23e5(表示123000)
#include <stdio.h>
int main(){
const float pi_f = 3.14f; // 单精度
const double pi_d = 3.14159; // 双精度(默认的小数形式就是双精度)
const double big_num = 1.23e5; // 科学计数法
const double small_num = 1.23e-3; // 0.00123
printf("单精度PI: %.7f\n", pi_f);
printf("双精度PI: %.15f\n", pi_d);
printf("大数: %.0f\n", big_num);
printf("小数: %.5f\n", small_num);
return 0;
}
判断下列数字是否合理
10.98 // ✅ 合理:标准的十进制小数
.089 // ✅ 合理:浮点数,前面的0可以省略,等同于0.089
-.003 // ✅ 合理:负数浮点常量
96.0f // ✅ 合理:单精度浮点常量
3.14E-2 // ✅ 合理:科学计数法,等同于0.0314
96f // ❌ 错误:缺少小数点
96.oF // ❌ 错误:用了字母'o'而不是数字'0',应该是96.0F
3.14.15 // ❌ 错误:多个小数点
long double 精度差异的技术演进
如果说long int字节不一致是处理器字长升级导致的兼容性问题,那么long double字节差异则反映了不同厂商在"精度与效率"之间的技术权衡。
C标准只规定了相对大小关系,没有规定绝对大小,因此long double的精度取决于各大硬件厂商的具体实现。
1980年代初期
Intel开发了x87浮点协处理器(8087),采用80位(10字节)扩展精度格式,提供了比标准64位double更高的计算精度。这在当时的科学计算领域具有重要意义。
Linux和GCC 选择充分利用Intel的80位精度能力,为需要高精度计算的应用提供支持。
Microsoft 则采用了更为保守的策略,在Visual Studio中将long double实现为64位,与double相同。这样做的考虑是:
- 简化编译器实现和调试
- 保持与早期Windows系统的兼容性
- 64位精度已能满足大多数应用需求
2000年代后期
随着移动设备兴起,ARM架构 开始在嵌入式和移动领域占主导地位。ARM处理器专注于功耗优化:
- 80位运算增加功耗和复杂度
- 移动设备电池容量有限
- 64位精度足够满足移动应用需求
苹果、Google、高通、三星 等移动平台厂商都选择了ARM的64位实现方案。
同时期:高性能计算的野心
就在移动厂商追求效率的同时,高性能计算领域却在追求更极致的精度:
IBM AIX/PowerPC 拿出了"双倍双精度"方案:"既然单个64位不够,那就用两个64位组合!" 这种Double-Double格式提供了约31位十进制精度。
GCC社区 更进一步,推出了__float128:"我们要真正的128位IEEE标准实现!" 34位十进制精度,满足最苛刻的科学计算需求。
现在的状况:
| 平台/编译器 | long double大小 | 设计考量 |
|---|---|---|
| Windows + Visual Studio | 8字节(64位) |