第02章 数据类型与变量
本章介绍C语言的数据类型体系、IEEE754浮点数标准、变量的存储类型以及宏定义
2.1 新的C语言新增bool类型
#include <stdbool.h>
bool a = false;
printf("a = %d\n", a);
2.2 C语言数据类型体系
C语言数据类型体系
C语言数据类型
├── 基本数据类型
│ ├── 数值类型
│ │ ├── 整数(整型)
│ │ │ ├── 短整型short
│ │ │ ├── 整型int
│ │ │ └── 长整型long
│ │ └── 浮点数
│ │ ├── 单精度float
│ │ └── 双精度double
│ └── 字符类型char
├── 构造类型
│ ├── 数组
│ ├── 结构体struct
│ ├── 共用体union
│ └── 枚举enum
├── 指针类型
└── 空类型void
2.3 变量的定义
变量定义语法
[存储类型] 数据类型 标识符 = 值;
存储类型:
auto- 自动变量(默认,存储在栈中)static- 静态变量register- 寄存器变量(建议型)extern- 外部变量(说明型)
示例
#include <stdio.h>
#include <stdlib.h>
int main()
{
auto int a = 10; // auto可省略,默认就是auto
static int b = 20; // 静态变量
register int c = 30; // 建议存储在寄存器
printf("a = %d\n", a);
printf("b = %d\n", b);
printf("c = %d\n", c);
exit(0);
}
2.4 存储类型详解
2.4.1 auto - 自动变量
- 默认的存储类型,可以省略
- 存储在栈中
- 函数结束后自动释放
int main()
{
auto int a = 10; // 等同于: int a = 10;
// 函数结束,a被自动释放
return 0;
}
2.4.2 static - 静态变量
特点:
- 自动初始化为0值或空值
- 值具有继承性(保持上次的值)
- 存储在静态存储区
- 具有全局寿命,局部可见性
修饰变量:
#include <stdio.h>
void func()
{
static int count = 0; // 只初始化一次
count++;
printf("count = %d\n", count);
}
int main()
{
func(); // 输出: count = 1
func(); // 输出: count = 2
func(); // 输出: count = 3
return 0;
}
修饰函数:
// file1.c
static void helper() // static函数,外部文件不可访问
{
printf("Helper function\n");
}
// file2.c
void helper(); // 编译错误:找不到helper函数
2.4.3 register - 寄存器变量
特点:
- 建议编译器将变量存储在寄存器中(仅建议)
- 只能定义局部变量
- 不能定义全局变量
- 只能定义4字节数据类型
- 没有地址,无法使用&取址
示例:
int main()
{
register int i;
for (i = 0; i < 1000000; i++)
{
// 频繁使用的循环变量适合用register
}
// printf("%p\n", &i); // 错误:寄存器变量没有地址
return 0;
}
⚠️ 注意: 现代编译器已经很智能,会自动优化,register关键字基本不再使用。
2.4.4 extern - 外部变量
特点:
- 说明型,不能改变被说明变量的值或类型
- 用于声明定义在其他文件中的全局变量
示例:
// file1.c
int global_var = 100; // 定义全局变量
// file2.c
extern int global_var; // 声明外部变量
int main()
{
printf("%d\n", global_var); // 可以使用file1.c中的变量
return 0;
}
2.5 IEEE754浮点数存储
2.5.1 浮点数表示法
根据IEEE754标准,任意一个二进制浮点数V可以表示为:
V = (-1)^S × M × 2^E
- S: 符号位 (0表示正数,1表示负数)
- M: 尾数 (有效数字,大于等于1,小于2)
- E: 指数
2.5.2 32位单精度浮点数存储
32位浮点数的内存分配:
| S | E (8位) | M (23位) |
| 1位 | 指数位 | 尾数位 |
31 30 23 22 0
示例: 5.25的二进制存储
- 5.25的二进制: 101.01
- 规范化: 1.0101 × 2²
- S = 0 (正数)
- M = 0101 (隐藏高位1)
- E = 2 + 127 = 129 = 10000001 (偏移127)
2.5.3 隐藏高位1
尾数部分的最高位始终为1,因此可以省略,节约1位存储空间。
示例:
- 实际尾数: 1.0101
- 存储尾数: 0101 (省略"1.")
2.5.4 浮点数精度问题
#include <stdio.h>
int main()
{
float f = 3.14159265358979323846;
double d = 3.14159265358979323846;
printf("float: %.20f\n", f); // 精度丢失
printf("double: %.20f\n", d); // 精度更高
return 0;
}
输出:
float: 3.14159274101257324219
double: 3.14159265358979311600
⚠️ 注意: 浮点数存在精度问题,不能用于精确计算(如金融)。
2.6 基本数据类型
2.6.1 整数类型
#include <stdio.h>
int main()
{
short s = 10; // 短整型,2字节
int i = 100; // 整型,4字节
long l = 1000L; // 长整型,4或8字节
long long ll = 10000LL; // 长长整型,8字节
printf("short: %d bytes\n", sizeof(s));
printf("int: %d bytes\n", sizeof(i));
printf("long: %d bytes\n", sizeof(l));
printf("long long: %d bytes\n", sizeof(ll));
return 0;
}
有符号与无符号:
int main()
{
signed int a = -10; // 有符号(默认)
unsigned int b = 10; // 无符号
printf("signed: %d\n", a);
printf("unsigned: %u\n", b);
return 0;
}
2.6.2 浮点类型
int main()
{
float f = 3.14f; // 单精度,4字节
double d = 3.14; // 双精度,8字节
long double ld = 3.14L; // 扩展精度,10/12/16字节
printf("float: %d bytes\n", sizeof(f));
printf("double: %d bytes\n", sizeof(d));
printf("long double: %d bytes\n", sizeof(ld));
return 0;
}
2.6.3 字符类型
int main()
{
char c = 'A'; // 字符,1字节
printf("char: %c\n", c); // 输出字符: A
printf("ASCII: %d\n", c); // 输出ASCII码: 65
printf("size: %d bytes\n", sizeof(c)); // 1字节
return 0;
}
2.6.4 布尔类型
C99标准新增布尔类型:
#include <stdio.h>
#include <stdbool.h> // 需要包含此头文件
int main()
{
bool flag = false;
printf("flag = %d\n", flag); // 输出: 0
flag = true;
printf("flag = %d\n", flag); // 输出: 1
return 0;
}
2.7 宏定义与常量
2.7.1 宏定义 #define
特点:
- 在预处理阶段进行文本替换
- 不检查语法
- 不占用内存
对象宏:
#include <stdio.h>
#define PI 3.14asd // 语法错误,但预处理阶段不检查
int main()
{
printf("%d\n", PI); // 编译阶段才报错
return 0;
}
预处理后:
int main()
{
printf("%d\n", 3.14asd); // 此时才发现语法错误
return 0;
}
函数宏:
#define ADD 2 + 3
#define MAX(a, b) (a > b ? a : b)
int main()
{
printf("%d\n", ADD * ADD); // 2 + 3 * 2 + 3 = 11(不是25!)
printf("%d\n", MAX(1, 2)); // (1 > 2 ? 1 : 2) = 2
return 0;
}
⚠️ 宏定义的危险性:
- 容易出现运算优先级问题
- 不进行类型检查
- 调试困难
2.7.2 const常量
特点:
- 编译时检查类型
- 占用内存
- 不能修改值
int main()
{
const float pi = 3.14; // 常量
printf("%f\n", pi);
// pi = 3.14159; // 错误:不能修改const变量
return 0;
}
2.7.3 const vs #define
| 特性 | #define | const |
|---|---|---|
| 类型检查 | 否 | 是 |
| 占用内存 | 否 | 是 |
| 调试 | 困难 | 容易 |
| 作用域 | 全局 | 可限定 |
| 推荐度 | 低 | 高 |
推荐使用const:
const float PI = 3.14159; // 推荐
// #define PI 3.14159 // 不推荐
2.8 类型转换
2.8.1 隐式转换
int main()
{
int a = 10;
float b = 3.14;
float c = a + b; // int自动转换为float
printf("%f\n", c); // 13.140000
return 0;
}
2.8.2 显式转换(强制转换)
int main()
{
float f = 3.14;
int i = (int)f; // 强制转换,小数部分被截断
printf("f = %f\n", f); // 3.140000
printf("i = %d\n", i); // 3
return 0;
}
2.9 sizeof运算符
sizeof是编译时运算符,返回类型或变量的字节数:
#include <stdio.h>
int main()
{
printf("char: %lu bytes\n", sizeof(char));
printf("short: %lu bytes\n", sizeof(short));
printf("int: %lu bytes\n", sizeof(int));
printf("long: %lu bytes\n", sizeof(long));
printf("float: %lu bytes\n", sizeof(float));
printf("double: %lu bytes\n", sizeof(double));
printf("pointer:%lu bytes\n", sizeof(void*));
int arr[10];
printf("array: %lu bytes\n", sizeof(arr)); // 40字节
return 0;
}
2.10 实践练习
练习1: 测试存储类型
编写程序测试static变量的特性:
#include <stdio.h>
void test()
{
static int count = 0;
count++;
printf("count = %d\n", count);
}
int main()
{
test();
test();
test();
return 0;
}
练习2: 浮点数精度
比较float和double的精度差异:
#include <stdio.h>
int main()
{
float f = 3.141592653589793;
double d = 3.141592653589793;
printf("float: %.15f\n", f);
printf("double: %.15f\n", d);
return 0;
}
练习3: 宏定义陷阱
理解宏定义的运算优先级问题:
#include <stdio.h>
#define SQUARE(x) x * x
#define SQUARE_FIX(x) ((x) * (x))
int main()
{
int a = 5;
printf("%d\n", SQUARE(a + 1)); // 5 + 1 * 5 + 1 = 11
printf("%d\n", SQUARE_FIX(a + 1)); // (5+1) * (5+1) = 36
return 0;
}
2.11 常见错误
错误1: 混淆宏定义和函数
#define MAX(a, b) a > b ? a : b // 缺少括号
int x = MAX(1, 2) + 3; // 展开为: 1 > 2 ? 1 : 2 + 3 = 5 (错误!)
正确写法:
#define MAX(a, b) ((a) > (b) ? (a) : (b))
错误2: 修改const变量
const int a = 10;
a = 20; // 错误:不能修改const变量
错误3: register变量取地址
register int a = 10;
printf("%p", &a); // 错误:register变量没有地址
2.12 小结
本章学习了:
C语言的数据类型体系 变量的定义和存储类型(auto, static, register, extern) IEEE754浮点数存储原理 基本数据类型(整型、浮点型、字符型、布尔型) 宏定义与const常量的区别 类型转换(隐式和显式) sizeof运算符
重点提示:
- 优先使用const而非#define定义常量
- 理解static的全局寿命、局部可见性特性
- 浮点数存在精度问题,避免用于精确计算
- 宏定义要注意括号,避免运算优先级错误
上一章: 第01章 C语言基础与编译下一章: 第03章 运算符