C: 变量与数据类型
变量就像一个个贴了标签的盒子——每个盒子有名字、有类型,只能装对应种类的东西,装错了就会出问题。
1. 什么是变量
变量是程序中用来存储数据的命名内存空间。你可以把它想象成一个盒子:
- 变量名:盒子上贴的标签,方便你找到它
- 数据类型:盒子能装什么种类的东西
- 变量值:盒子里当前装的东西
- 内存地址:盒子在仓库中的位置
2. 变量声明与初始化
(1) 声明变量
声明变量就是告诉编译器:我要用一个盒子,它叫什么名字、装什么类型的东西。
int age;
float weight;
char blood_type;
声明后,变量的值是未定义的(垃圾值),里面装的是内存中残留的随机数据。千万别直接使用未初始化的变量!
(2) 初始化变量
初始化就是在声明的同时给变量赋一个初始值:
int age = 20;
float weight = 65.5f;
char blood_type = 'A';
💡 最佳实践:始终在声明变量时就初始化,避免使用未定义的垃圾值。这是一个好习惯,能帮你避免大量难以排查的bug。
(3) 先声明后赋值
你也可以先声明,后赋值:
int age;
age = 20;
效果和声明时初始化一样,但两步写法更分散,容易在声明和赋值之间误用未初始化的变量。
(4) 同时声明多个同类型变量
int a = 1, b = 2, c = 3;
⚠️ 注意:不建议在同一行声明多个变量并初始化,可读性差。如果每个变量都有重要的初始值,建议分行写。
3. 变量命名规则
C语言对变量名有严格的规则:
必须遵守的:
- 只能由字母(a-z, A-Z)、数字(0-9)和下划线(_)组成
- 必须以字母或下划线开头,不能以数字开头
- 不能是C语言的关键字(如
int、return、if等) - 区分大小写(
Age和age是不同的变量)
建议遵守的:
- 变量名应有意义,用
student_count而非sc - 使用 snake_case 风格(小写字母+下划线)
- 避免以双下划线开头(如
__count),编译器内部使用这种命名 - 避免过长的名字(超过31个字符可能不被所有编译器支持)
| 命名 | 是否合法 | 说明 |
|---|---|---|
age |
合法 | 简洁有意义 |
_count |
合法 | 下划线开头 |
student_name |
合法 | snake_case风格 |
2nd_place |
非法 | 数字开头 |
my-score |
非法 | 含减号(会被当成减法) |
int |
非法 | 关键字 |
Age |
合法但不建议 | 大写开头通常用于类型名 |
4. 基本数据类型
C语言的数据类型决定了变量占多少内存、能存多大范围的值、数据如何解读。C99的基本数据类型如下:
(1) 整数类型
| 类型 | 占用字节 | 取值范围(典型) | 格式占位符 |
|---|---|---|---|
char |
1 | -128 ~ 127 | %c 或 %d |
unsigned char |
1 | 0 ~ 255 | %u |
short |
2 | -32768 ~ 32767 | %hd |
unsigned short |
2 | 0 ~ 65535 | %hu |
int |
4 | -2147483648 ~ 2147483647 | %d |
unsigned int |
4 | 0 ~ 4294967295 | %u |
long |
4或8 | 平台相关 | %ld |
unsigned long |
4或8 | 平台相关 | %lu |
long long |
8 | -9223372036854775808 ~ 9223372036854775807 | %lld |
💡 实际建议:日常编程用 int 就够了,需要更大范围用 long long,处理字符用 char。short 在现代平台上很少使用,因为 int 的效率通常更高。
⚠️ 注意:上表中 int 占4字节是大多数现代平台的情况,但C标准只要求 int 至少占2字节。实际大小取决于平台,用 sizeof 来确认。
(2) 浮点类型
| 类型 | 占用字节 | 有效数字 | 取值范围(绝对值) | 格式占位符 |
|---|---|---|---|---|
float |
4 | 6-7位 | 1.2e-38 ~ 3.4e38 | %f |
double |
8 | 15-16位 | 2.2e-308 ~ 1.8e308 | %lf |
long double |
8或更大 | 18+位 | 平台相关 | %Lf |
浮点数的精度有限,不能精确表示所有小数。例如:
float f = 0.1f;
printf("%.20f\n", f);
0.10000000149011611938
0.1 在二进制中是无限循环小数,float无法精确存储。这是浮点数的固有特性,不是bug。
🔥 实用建议:需要小数时默认用 double,精度更高且现代CPU上运算速度不比float慢。只有在内存受限的嵌入式场景才优先用 float。
(3) 字符类型
char 类型本质上是一个1字节的整数,只不过通常用来存储字符。字符在内存中以ASCII码存储:
char ch = 'A';
printf("%c\n", ch);
printf("%d\n", ch);
A
65
'A' 的ASCII码是65,用 %c 输出字符,用 %d 输出整数。这说明字符和整数在C中是相通的。
⚠️ 字符 vs 字符串:'A' 是字符(单引号),"A" 是字符串(双引号,末尾有 \0)。两者完全不同,不能混用。
(4) _Bool类型
C99引入了 _Bool 类型,只能存储0和1。包含 <stdbool.h> 后可以用 bool、true、false:
#include <stdbool.h>
bool is_valid = true;
bool is_empty = false;
5. sizeof运算符
sizeof 是C语言的关键字(不是函数),用来获取数据类型或变量占用的字节数:
▶ 示例
#include <stdio.h>
int main(void) {
printf("char: %zu bytes\n", sizeof(char));
printf("short: %zu bytes\n", sizeof(short));
printf("int: %zu bytes\n", sizeof(int));
printf("long: %zu bytes\n", sizeof(long));
printf("long long: %zu bytes\n", sizeof(long long));
printf("float: %zu bytes\n", sizeof(float));
printf("double: %zu bytes\n", sizeof(double));
return 0;
}
char: 1 bytes
short: 2 bytes
int: 4 bytes
long: 4 bytes
long long: 8 bytes
float: 4 bytes
double: 8 bytes
💡 %zu:sizeof 返回 size_t 类型,对应的格式占位符是 %zu。如果编译器不支持 %zu(某些旧版本),可以用 %lu 代替。
sizeof 也可以用于变量:
int age = 20;
printf("%zu\n", sizeof(age));
6. 格式化输出详解
(1) 整数格式化
int n = 42;
printf("十进制: %d\n", n);
printf("八进制: %o\n", n);
printf("十六进制: %x\n", n);
printf("大写十六进制: %X\n", n);
printf("带前缀八进制: %#o\n", n);
printf("带前缀十六进制: %#x\n", n);
十进制: 42
八进制: 52
十六进制: 2a
大写十六进制: 2A
带前缀八进制: 052
带前缀十六进制: 0x2a
(2) 浮点数格式化
double pi = 3.141592653589793;
printf("默认: %f\n", pi);
printf("2位小数: %.2f\n", pi);
printf("8位小数: %.8f\n", pi);
printf("科学计数法: %e\n", pi);
printf("宽度12.2f: [%12.2f]\n", pi);
默认: 3.141593
2位小数: 3.14
8位小数: 3.14159265
科学计数法: 3.141593e+00
宽度12.2f: [ 3.14]
7. 类型取值范围
C标准库提供了头文件来查询各类型的取值范围:
(1) limits.h(整数类型)
#include <limits.h>
printf("int最小值: %d\n", INT_MIN);
printf("int最大值: %d\n", INT_MAX);
printf("char最大值: %d\n", CHAR_MAX);
常用宏:
| 宏 | 含义 |
|---|---|
INT_MIN |
int的最小值 |
INT_MAX |
int的最大值 |
CHAR_MIN |
char的最小值 |
CHAR_MAX |
char的最大值 |
LONG_MAX |
long的最大值 |
LLONG_MAX |
long long的最大值 |
(2) float.h(浮点类型)
#include <float.h>
printf("float有效数字: %d\n", FLT_DIG);
printf("double有效数字: %d\n", DBL_DIG);
printf("float最大值: %e\n", FLT_MAX);
▶ 示例
▶ 示例
#include <stdio.h>
#include <limits.h>
#include <float.h>
int main(void) {
char ch = 'Z';
int num = 100;
float f = 3.14f;
double d = 2.718281828;
printf("char '%c' = ASCII %d, size = %zu\n", ch, ch, sizeof(ch));
printf("int %d, size = %zu, range = [%d, %d]\n", num, sizeof(num), INT_MIN, INT_MAX);
printf("float %.6f, size = %zu, digits = %d\n", f, sizeof(f), FLT_DIG);
printf("double %.9f, size = %zu, digits = %d\n", d, sizeof(d), DBL_DIG);
return 0;
}
char 'Z' = ASCII 90, size = 1
int 100, size = 4, range = [-2147483648, 2147483647]
float 3.140000, size = 4, digits = 6
double 2.718281828, size = 8, digits = 15
▶ 示例
#include <stdio.h>
int main(void) {
char lower = 'a';
char upper = lower - 32;
printf("小写: %c (ASCII %d)\n", lower, lower);
printf("大写: %c (ASCII %d)\n", upper, upper);
printf("\n数字字符:\n");
char digit = '0';
printf("字符 '%c' 的ASCII码 = %d\n", digit, digit);
printf("字符 '%c' 的数值 = %d\n", digit, digit - '0');
return 0;
}
小写: a (ASCII 97)
大写: A (ASCII 65)
数字字符:
字符 '0' 的ASCII码 = 48
字符 '0' 的数值 = 0
💡 技巧:大小写字母的ASCII码相差32,小写减32得大写,大写加32得小写。数字字符 '0' 减去 '0' 可以得到它的整数值,这是字符转数字的基础。
❓ 常见问题
📖 小节
- 变量是命名内存空间,声明时指定类型和名称,建议同时初始化
- 命名必须以字母或下划线开头,区分大小写,不能用关键字
- 整数类型:char(1字节)、short(2)、int(4)、long(4或8)、long long(8)
- 浮点类型:float(4字节,6-7位精度)、double(8字节,15-16位精度)
- sizeof获取类型或变量的字节大小,limits.h和float.h提供取值范围
📝 作业
- 编写程序,用sizeof输出你电脑上所有整数类型和浮点类型的字节大小,与你同学的结果对比,看看是否相同
- 编写程序,输出字母'a'到'z'的ASCII码值,每行5个,用%-5d格式对齐
- 计算并输出int类型的最大值+1的结果(整数溢出),观察输出并与INT_MIN对比,思考为什么