数据类型
- 修饰符 long 在 C99 以后可以修饰 long int 和 double 类型(整型修饰后 int 可不写)
- 布尔类型在 C99 及以后的标准才支持,使用时添加 <stdbool.h> 支持 bool 等别名
- sizeof 关键字(一般编译时确定)的返回结果是 size_t 类型(本质是类型别名)
- 在 <limits.h> 和 <float.h> 头文件中定义了整数和浮点数类型的范围与最值
数据类型 内置类型 自定义类型 指针类型 字符型
char 整型
short, int, long, long long 浮点型
float, double, long double 布尔类型
_Bool 空类型
void 数组 结构体-struct 枚举体-enum 联合体-union
内存划分
- 常见平台栈区地址是从高到低增长,其他分区是从低到高增长(数组元素遵循地址递增存储)
- 局部变量的作用域和生命周期是在该局部范围,全局变量的作用域和生命周期是在全局范围
- 作用域就近原则:程序在查找变量时,优先从当前作用域的局部变量开始并逐层向外查找
- static 修饰的全局变量和函数,只能在本文件内部使用(修饰局部变量会将其生命周期变长)
- 数据段又称静态区,下图的地址划分总体是从高到低进行分配的(代码段通常是只读的)
Memory 内核空间
用户代码不可读写 栈区
局部变量/函数参数 内存映射段
文件映射/动态库/匿名映射 堆区
动态分配内存 数据段
全局数据/静态数据 代码段
可执行代码/只读常量 内存布局说明 内存区域从高地址到低地址分布 内核空间与用户空间严格分离 栈区自动管理
堆区需手动分配释放 代码段和数据段在程序加载时确定 内存映射段用于高效文件IO和共享内存
操作符
- 通过 (a % p + p) % p 操作让取模结果不是负数
- 位操作符涉及数据在计算机的存储(只能操作整型)
| 类别 | 操作符 | 作用 | 注意 |
|---|---|---|---|
| 算术 | + | 相加 | 溢出 |
| - | 相减 负号 | 溢出 | |
| * | 相乘 | 溢出 | |
| / | 相除 | 有浮点数才为浮点数 除数不能为零 | |
| % | 取模 | 结果正负取决于左操作数 操作数必须为整数 除数不能为零 | |
| 关系 | == | 相等 | 注意区分 == 和 = |
| != | 不相等 | ||
| > | 大于 | ||
| < | 小于 | ||
| >= | 大于等于 | ||
| <= | 小于等于 | ||
| 逻辑 | && | 逻辑与 | 短路特性 |
| || | 逻辑或 | ||
| ! | 逻辑非 | ||
| 位 | & | 按位与 | |
| | | 按位或 | ||
| ^ | 按位异或 | 任何数异或零都得自己 相同的数异或为零 满足交换与结合律 | |
| ~ | 按位取反 | ||
| << | 左移 | 移动位数为非负整数 左移有乘的效果 | |
| >> | 右移 | 移动位数为非负整数 算术右移高位补符号位 逻辑右移高位固定补零 右移有除的效果 | |
| 赋值 | = | 赋值 | 左操作数必是可变的左值 |
| += | a = a + b | ||
| -= | a = a - b | ||
| *= | a = a * b | ||
| /= | a = a / b | ||
| %= | a = a % b | ||
| &= | a = a & b | ||
| |= | a = a | b | ||
| ^= | a = a ^ b | ||
| <<= | a = a << b | ||
| >>= | a = a >> b | ||
| 自增自减 | ++ | 自增 | 前置先加再用 后置先用再加 |
| -- | 自减 | 前置先减再用 后置先用再减 | |
| 条件 | ? : | 三目操作 | 判断 ? 真 : 假 |
| 逗号 | , | 逗号表达式 | 值取最后一表达式值 |
| 指针 | * | 解引用 | 指针指向必须有效地址 |
| & | 取地址 | 必须有分配内存空间 | |
| 成员访问 | . | 通过变量访问 | 操作数必须是结构体或联合体 |
| -> | 通过指针访问 | 操作数必须是指向其的指针 | |
| sizeof | sizeof | 计算占用 | 编译期完成 类型大小计算必加括号 |
| 强转 | (type) | 强制类型转换 | 可能导致数据丢失 |
| 其他 | () | 括号运算符 | 优先级不明确加括号 |
| [] | 下标运算符 | 数组越界 |
输入输出
占位符
- 占位符一般是既可以用于输入(过滤起首空白字符)也可以用于输出的
- 对于 %c 在输入时不过滤起首空白字符,输出时 %f 会转成双精度浮点数
- 对于输入输出有特殊情况的占位符,具有额外的说明(以下是常见占位符)
| 占位符 | 作用 |
|---|---|
| %c | 字符 |
| %hd | 短整型 |
| %d | 整型 |
| %u | 无符号整型 |
| %ld | 长整型 |
| %lld | 长长整型 |
| %f | 单精度浮点数 |
| %lf | 双精度浮点数 |
| %s | 字符串(字符数组) |
| %p | 指针的值(不可格式输入) |
| %zd | ssize_t 类型数据(可能负值) |
| %zu | size_t 类型数据(非负值) |
| %o 或 %O | %o 小写八进制形式无符号整数(%O 则是大写) |
| %x 或 %X | %x 小写十六进制形式无符号整数(%X 则是大写) |
| %e 或 %E | %e 指数部分用 e 表示的科学计数法浮点数(%E 是 E) |
| %g 或 %G | 根据数值的大小,自动选择以普通或科学计数法模式 |
格式说明
- 编译器会自动拼接相邻的两个字符串(无论在何处)
- 语言标准规定:long 所占空间大于等于 int 所占空间即可
- 序列点是左边操作完成与右边操作开始的安全分隔点(顺序无关)
- 序列点之间对同一变量进行多次修改或既修改又读取,其结果是不确定的
c
#include <stdio.h>
int main()
{
// 输出修饰
printf("%+d\n", 123);
printf("%5d\n", 123);
printf("%-5d\n", 123);
printf("%.2lf\n", 3.145);
printf("%.5s\n", "Hello World");
printf("%*d\n", 5, 123);
// 输入修饰
int a = 0, b = 0;
scanf("%d%*c%d", &a, &b);
scanf(" %c", &a);
// 两个序列点之间对同一变量的多次修改是未定义的(UB)
int i = 1, ret = 0;
ret = (++i) + (++i) + (++i);
printf("%d %d\n", i, ret);
// sizeof 是操作符
printf("%zu %zu\n", sizeof a, sizeof(long));
// 输出八进制与十六进制
printf("%d %d\n", 0xf, 017);
return 0;
}