Chapter 1 初识C语言
- 标准: C89(ANSI C)/C90、 C99、C11
- 目标代码文件 + 启动代码 + 库函数 -> 可执行文件
Chapter 2 C 语言概述
Chapter 3 数据和C
- 整数: int, 变式: short, long, long long, unsigned, signed.
- 后缀: l/L, ll/LL, ull/ULL.
- 打印: %d, %ld, %hd (short)
- 溢出: 当达到能表达的最大值时,重新从起点开始。unsigned 从0开始,signed 从 -xxxx开始。
- 浮点数:float, double, long double.
- 存储方式:符号位、指数部分、尾数部分。C 标准规定,float至少精确标示小数点后6位有效数字。
- 小数的二进制表示: 乘以2,整数部分作为小数点后的第一位,再将小数部分乘以2,得到的整数部分作为作为小数点后的第二位,以此类推,直到小数部分为0
- 小数点形式,指数形式 2.0e10 2.0E10。c99/c11提供了通过十六进制和2的幂来表示的指数形式: 2^5-> 0x1p+5
- 后缀: f/F, l/L(long double).
- 打印: %f,%lf; %e,%le; %a,%la(十六进制);
- 上溢(inf)、下溢(0)、NaN(not a number)、舍入错误:一个较大大浮点数,加1再减去原值,结果不等于1。缺少足够的位来保证正确的运算。2.0e20 + 1 - 2.0e20 = ?
- 字符: char, 也可表示较小的整数。
- C语言中,多个连续的字符串等价于一个组合的字符串
- 布尔: _Bool, C99提供 stdbool.h 头文件,让 bool 成为 _Bool 的别名。
- 复数、虚数 C99
- float _Complex, double _Complex, long double _Complex
- float _Imaginary, double _Imaginary, long double _Imaginary
- 包含头文件complex.h,便可用complex替代_Complex,用imaginary替代_Imaginary.用I代替-1的平方根。
- 衍生类型: 数组、指针、结构、联合
- sizeof() C 语言内置运算符,以字节为单位给出指定类型的大小。C99提供%zd用于打印sizeof()的返回值。
- 进制
- 八进制 前缀:0 printf()打印:%#o
- 十六进制 前缀:0x 0X printf()打印:%#x %#X p计数法
- 可移植类型
- 相关头文件: stdint.h, inttypes.h
- (精确宽度整数类型) int32_t: 32位有符号整数类型。在int为16位,long为32位的系统中,将int32_t作为了long的别名
- 最小宽度类型 int_least8_t: 可容纳8位有符号整数值的类型中宽度最小的类型的别名 C99 C11
- 最快最小宽度类型 int_fast8_t: 对8位有符号值而言运算最快的整数的别名 C99 C11
使用printf()参数的数量、类型一定要匹配
刷新缓冲区——从缓冲区把数据发送到屏幕或文件。刷新输出:缓冲区满、遇到换行符、需要输入、fflush()函数
Chapter 4 字符串和格式化输入输出
- C语言中,字符串被存储在char类型的数组中。空字符’\0’标志字符串的结束。
- scanf()遇到空白(空格、制表符、换行符)停止读取输入。%s
- sizeof()以字节为单位给出对象大小。strlen()给出字符串的长度。–%zd
- 定义常量
- 预处理器定义 #define 编译时替换 没有分号,通常全大写。(建议)
- const C90 限定一个变量为只读。
相关头文件:limits.h float.h
- printf()转换说明, 一定要匹配!!!
把以二进制格式存储在计算机中的值转换成一系列字符(字符串)以便于显示。并不替换原始值,只是翻译
- char: %c
- 字符串: %s
- 指针: %p
- sizeof(): %zd
- int: %d, %i
- unsigned int: 八进制-%o, 十进制-%u, 十六进制-%x, %X
- double: %e, %E, %f, %F, %g %G:(根据值得不同,自动选择%f或%e/%E)
- %a %A: 浮点数、十六进制数和p计数法
- 打印%: %%
- printf()转换说明修饰符
- 标记
- -: 左对齐
- +: 显示正负号
- 空格: 空格覆盖+标记的正号
- #:将结果转换为另一种形式。%#o: 以0开始,%#x: ox %#X: 0X
- 0: 前导0填充字段宽度。
- 数字 最小字段宽度,若不够容纳,系统会使用更宽的字段
- .数字 精度
- %e、%E、%f: 表示小数点右边数字位数
- %g、%G: 表示有效数字最大位数
- %s: 表示待打印字符的最大数量
- 整型转换: 表示待打印数字的最小位数,如有必要,使用前导0
- 只使用. 表示其后跟随一个0, %.f 同 %.0f, 即舍去小数部分
- h: 与整型转换说明一起使用。表示 (unsigned) short int. eg: %hu, %hx, %hd
- hh: 与整型转换说明一起使用。表示 (unsigned) short char. eg: %hhu, %hhx, %hhd
- j: 与整型转换说明一起使用。表示 intmax_t 或 uintmax_t 类型的值。定义在stdint.h中。
- l: 与整型转换说明一起使用。表示 (unsigned) long int
- ll: 与整型转换说明一起使用。表示 (unsigned) long long int
- L: 与浮点转换说明一起使用。表示 long double
- t: 与整型转换说明一起使用。表示 ptrdiff_t 类型的值,两个指针差值的类型。
- z:与整型转换说明一起使用。表示 size_t 类型的值 (sizeof返回的类型),可移植性考虑
- 标记
- printf()返回值:打印字符的个数,错误返回-1.
- 代码中给长字符串断行的三种方式:
- 多个printf();
- 使用’\’和 Enter 键组合。作用:光标移至下一行,且不含换行符。
- ANSI C 中引入的字符串连接。 两个双引号括起来的字符串以空白隔开,C 编译器看作是1个字符串。
- scanf() 将输入的字符(串)转换为整数、浮点数、字符或字符串。参数列表中使用的是指针。
- 修饰符
- printf()中: *代替字符段宽度, 程序中通过其他参数传递
- scanf()中: *跳过相应的输入项 抑制赋值
Chapter 5 运算符、表达式和语句
- 基本运算符: ‘=’ ‘+’ ‘-’ ‘*’ ‘/’ (没有指数运算符,标准数学库提供了pow())
- 概念
- 数据对象:用于储存值得数据存储区域
- 左值: 用于标识特定数据对象的名称或表达式。新标准术语:对象定位值
- 右值:能赋给可修改左值得量,且本身不是左值。
- 除法,整数除法结果为整数,发生截断,丢弃整个小数部分,而不是四舍五入。趋零截断
- 概念
- 其他运算符
- sizeof以及size_t类型。size_t: 无符号整数类型,(标准类型)。 %zd转换说明
- typedef: 为现有类型创建别名。
- 求模运算符:% 只能用于整数运算。负数求模,结果的符号与第一个运算符的符号相同。
- 递增递减运算符: ++ – 生成的机器语言效率更高
- 前缀模式
- 后缀模式
- 优先级高,只有圆括号的优先级高于递增运算符。
- 只能作用于可修改的左值。
- sizeof以及size_t类型。size_t: 无符号整数类型,(标准类型)。 %zd转换说明
- 术语:副作用、序列点
- 类型转换
- 需要转换时, unsigned 及 signed 的 char 和 short 都会被转换为int, 有必要时转换为unsigned int.
- 涉及两种类型的运算,两个值会被转换成比两种类型更高的级别
- 类型级别: long double > double > float > unsigned long long > long long > unsigned long > long > unsigned int > int
- 运算时,根据两个运算对象升级;赋值时,根据变量类型,会导致升级或降级。降级会截断,类型降级危险
- 函数参数传递, char和short被转换为int,float转换为double. 函数原型会覆盖自动升级
- 强制类型转换: (type)
Chapter 6 控制语句:循环
- 3种循环
- for 入口条件循环
- while 入口条件循环
- do while 出口条件循环,循环体至少循环一次
- 关系运算符 ‘>’ ‘>=’ ‘==’ ‘<=’ ‘<’ ‘!=’
- 优先级: 低于算术运算符,高于赋值运算符
- 真值
- 数值方面:非0为真,0为假
- 关系表达式为真,求值为1;关系表达式为假,求值为0
- _Bool C99 包含头文件stdbool.h, 让bool成为_Bool的别名,true和false分别定义为1和0的符号常量,可与C++兼容。C++将bool、true、false定义为关键字。
*尽量使用while(goats)而不是while(goats != 0)
如果待比较的值是常量,放在 ‘==’ 左边有助于编译器捕捉错误*
- 其他赋值运算符
- += -= *= /= %=
- 逗号运算符 ,:
char ch, date; 此处为分隔符,不是运算符
- 被分隔的表达式从左往右求值。逗号是一个序列点,左侧项的副作用在程序执行右侧项之前发生
a++, b = 5 * a; 不常用
y = 249,500; 不是语法错误,相当于y = 249;500; - 整个逗号表达式的值是右侧项的值
x = (y = 3, (z = ++y + 2) + 5); 糟糕
y = (249,500); 不是语法错误,y = 500;
- 被分隔的表达式从左往右求值。逗号是一个序列点,左侧项的副作用在程序执行右侧项之前发生
Chapter 7 控制语句:分支和跳转
- if语句
- if
- if… else…
- if… else if… else…
- else与if配对问题:与最近的没有被花括号括起来的if配对
- 字符输入输出函数: getchar(); putchar();字符处理函数头文件ctype.h
- 字符测试函数
- isalnum() 字母或数字
- isalpha() 字母
- isblank() 标准的空白字符(空格、水平制表符、换行符)
- iscntrl() 控制字符 eg: Ctrl+B
- isdigit() 数字
- isgraph() 除空格之外任意可打印字符
- islower() 小写
- isprint() 可打印字符
- ispunct() 标点符号
- isspace() 空白字符(空格、换行符、换页符、回车符、垂直制表符、水平制表符)
- isupper() 大写字母
- isxdigit() 十六进制数字符
- 字符映射函数 不修改原始参数,只返回已修改的值
- tolower()
- toupper()
- 字符测试函数
- 逻辑运算符: &&、||、!
- 备选拼写:iso646.h头文件 and、or、not
- 条件运算符: ?:
- 循环辅助
- continue
- break
- 多重选择: switch和break
- ”default: ” 可以写在任何一个case标签可以出现的位置。当switch表达式的值与所有case标签都不匹配时,default子句后面的语句会被执行。每个switch语句只能有一条default子句,但可以出现在语句列表的任何位置。
- goto语句 谨慎使用,或者根本不用
- 标签
Chapter 8 字符输入/输出和输入验证
- 输入
- 缓冲输入
- 完全缓冲I/O:缓冲区被填满才刷新缓冲区(内容被发送到目的地)
- 行缓冲I/O:出现换行符时刷新
- 无缓冲输入,立刻回显
- 缓冲输入
- 单字符I/O: getchar(), putchar()
- 流
- stdin: 键盘输入
- stdout: 屏幕输出
- 文件结尾 EOF 定义在stdio.h中
- 重定向
Chapter 9 函数
- 递归
- 尾递归:递归调用置于函数的末尾,return语句之前。
- 头文件, 存放函数原型、已定义的符号常量
- &运算符 获取变量地址。 %p
- 指针, 间接运算符 *
Chapter 10 数组和指针
- 数组初始化
- C99新增:指定初始化器 eg: int a[10] = { [5] = 6 };
- 数组、指针、函数
- ”int a[]” 只能用于函数原型和函数定义中
- 指针操作:
- 赋值
- 解引用 *
- 取址:指针自身的地址
- 指针与整数相加: 与指针指向类型的大小相乘,然后把结果与初始地址相加
- 递增指针
- 指针减去一个整数
- 递减指针
- 指针求差: 指向的两个元素的距离
- 比较,必须指向相同的类型。
注意事项:不能解引用未初始化的指针。未初始化的指针,其值是一个随机值。创建指针时,系统只分配了储存指针本身的内存,并未分配储存数据的内存。
- 通过数组名或指针传递参数,使用const保护原始数据。
- 指针和多维数组 eg: a[4][2] a = &a[0] = a[0] = &a[0][0]
- 声明一个指向多维数组的指针: int (*p)[2];
必须使用圆括号,[]的优先级高于解引用符。指向一个数组,该数组内含2个int类型值。
如何理解? 以a[4][2]为例,将指针声明为int,仅与a[0]的类型匹配,说明指向一个int类型的值,数组名a是首元素的地址,首元素是一个内含两个int类型值的一维数组。
int *p[2]; 声明了一个指针数组,含两个元素,指向int类型
- 声明一个指向多维数组的指针: int (*p)[2];
- C与C++中const的区别
- C中不允许通过const变量设置数组长度。
const int size = 10;
int a[size] = {0}; /* C中错误,C++中可以 */ - C++中指针赋值检查更严格。
const int a;
const int *p1 = &a;
int *p2;
p2 = p1; /C++不允许,C只给出警告 /
- C中不允许通过const变量设置数组长度。
- 变长数组(VLA) C99
常量
- 符号常量
- 字面量
- 复合字面量:(int [2]) {1,2} 或(int []){1,2} 必须在创建的同时使用,可以通过指针记录地址来用,可以传递给匹配的形参
创建数组的三种方法
- 声明数组时,用常量表达式表示数组维度。可用静态内存或自动内存创建。
- 声明变长数组(C99),用变量表达式表示数组维度。只能在自动内存中创建。
- 声明一个指针,调用malloc(),将返回值赋给指针。该指针可以是静态的或自动的
Chapter11 字符串和字符串函数
- 定义字符串
- 字符串常量, 静态存储类别,只会被存储一次,整个程序的生命周期内存在。用双引号括起来的内容被视为指向该字符串储存位置的指针,类似于将数组名作为指向该数组位置的指针
- char类型数组
- 指向char类型指针
- 输入/输出相关函数
- 显示字符串函数
- puts: 显示字符串,自动添加换行符
- fputs() 与fgets()配合,不自动添加换行符;若要显示在屏幕上,以标准输出stdout为参数
- printf()
- 读取字符串函数
- gets() 读取整行,直到换行符停止,丢弃换行符。unsafe,C11已废除
- fgets() 遇到换行符储存; 读入从键盘输入的数据,以标准输入stdin为参数,定义在stdio.h中
- gets_s(),C11新增,可选扩展,编译器不一定支持
- scanf() %s只能读取1个单词
- 自定义输入/输出函数: 利用getchar()和putchar()
- 显示字符串函数
- 数组和指针的区别
- 数组名是常量,指针通常为变量。
- 两者都可以使用数组表示法 []
- 两者都可以进行指针加法操作 *(数组名/指针 + 整数), 但只有指针能进行递增操作
- 不能通过指向字符串字面量的指针来修改字符串字面量
建议: const char *p = “string”;
- 数组名是常量,指针通常为变量。
- 字符串数组
- 指向字符串的指针数组 const char * str[2] = {“Hello”, “World”}; //占用字节少 但不可修改
- char类型数组的数组(二维数组) char str[2][6] = {“Hello”, “World”};
- 处理字符串函数 头文件string.h
- strlen()
- strcat() strncat() 拼接字符串
- strcmp() strncmp() 比较字符串
- strcpy() strncpy() 拷贝字符串, 第一个不必指向数组的开始,可以拷贝数组的一部分
- sprintf() 头文件stdio.h 把数据写入字符串,而不是打印在显示器上。
- 其他函数: strchr(), strpbrk(), strrchr(), strstr()
- 命令行参数, main()参数
- 没有参数 void
- 2个参数
- argc: argument count
- argv: argument value
- 字符串转数字
头文件stdlib.h
- atoi(), atol(), atoll(), atof() 字符串转int, long, long long, double.
- strtol(), strtoul(), strtod()…更智能, 检查首字符是否为数字,甚至指定进制
Chapter 12 存储类别、链接和内存管理
- 作用域,可访问标识符的区域
- 块作用域
- 函数作用域: 仅用于goto语句标签, 即使一个标签首次出现在函数的内存块中,它的作用域也延伸到整个函数。
- 函数原型作用域: 用于函数原型中的形参名,范围从形参定义处到原型声明结束。编译器只关心形参类别,形参名(如果有)无关紧要。只有在变长数组中,原型中的形参名才有用。另外,函数定义中的形参具有块作用域
- 文件作用域
- 翻译单元: 源代码文件和包含的所有头文件被看作是一个翻译单元。C预处理用头文件的内容替换#include指令。
- 链接。C变量有三种链接属性。
- 外部链接: 变量具有文件作用域,可延伸至其他翻译单元。 简称全局作用域或程序作用域
- 内部链接: 变量具有文件作用域,只能在一个翻译单元中使用。简称文件作用域。 存储类别说明符:static
- 无链接:具有块作用域、函数作用域、函数原型作用域的变量。
- 存储期
- 静态存储期: 程序执行期间一直存在。文件作用域变量(无论内部链接还是外部链接)和加关键字static的块作用域变量。对文件作用域变量,关键字static表明的是链接属性而非存储期。
- 线程存储期:用于并发程序设计。从被声明到线程结束一直存在。关键字: _Thread_local, 每个线程都会获得该变量的私有备份
- 自动存储期: 块作用域的变量
- 动态分配存储期
存储类别
存储类别 生命周期 作用域 链接 声明方式 自动 自动 块 无 块内 寄存器 自动 块 无 块内,使用关键字:register 静态外部链接 静态 文件 外部 所有函数外 静态内部链接 静态 文件 内部 所有函数外,使用关键字:static 静态无链接 静态 块 无 块内,使用关键字static - 自动变量:
- 存储类别说明符auto auto变量在C++中用法完全不同,编写C/C++兼容程序,最好不要使用auto关键字
- 自动变量不会默认初始化!,其值是随机值。
- 寄存器变量
- 访问和处理速度快
- 无法获得寄存器变量的地址
- 使用register只是发出请求,编译器可能会忽略
- 外部链接的静态变量
- 使用定义在另一个文件中的外部变量,须加关键字:extern。也可使用extern来说明使用本文件中定义的外部变量。
- 只能使用常量表达式初始化,不能通过变量初始化。如未初始化,会自动初始化为0
- 自动变量:
- C99和C11标准要求编译器识别局部标识符的前63个字符和外部标识符的前31个字符
- 声明
- 定义式声明: 定义 只能有一个
- 引用式声明
- 存储类别和函数
- 外部函数(默认)
- 静态函数 使用关键字:static
- 内联函数(C99新增)
除非使用关键字static,否则一般函数声明都默认为extern
- 相关函数
头文件stdlib.h
- 随机函数和静态变量
- rand(): 伪随机数生成器 均匀分布
- srand()
- 分配、释放内存
- malloc() 返回void*,通用指针。
- calloc()
- free()
- exit() “EXIT_FAILUE, EXIT_FAILURE”两个返回值在所有系统中都能正常工作
- 随机函数和静态变量
- 类型限定符
- const
- 在C++中,默认状态下,const对象仅在本文件内有效,若要在其他文件中使用,无论定义和声明都加extern关键字
- 若将const变量放在头文件中,必须加static声明。否则所有包含该头文件的源文件中都有一份定义式声明。优点:方便。缺点:数据重复。
- volatility
- 代理(而不是变量所在的程序)可以改变该变量的值。
- 用于硬件地址和线程中共享数据。
- 涉及编译器优化
- restrict: C99新增。
- 允许编译器优化某部分代码以更好地支持计算。
- 只能用于指针,表明该指针是访问数据对象的唯一且初始的方式。
- 可用于函数形参中的指针。编译器假定在函数体内其他标识符不会修改该指针指向的数据,编译器尝试对其优化。
- _Atomic C11 原子类型, 用于并发程序设计。
- const
- 旧关键字的新位置 C99
Chapter 13 文件输入/输出
- 文件模式
- 文本模式
- 二进制模式
- 标准文件, C程序会自动打开3个文件。
- stdin: 标准输入
- stdout: 标准输出
- stderr: 标准错误输出
相关函数
- exit()与return()区别
- 在main()中,最初的调用,两者作用相同。如果在递归中,exit()退出程序,而return()将控制权交给上一级。
- 初main()以外的其他函数中,调用exit()也能结束整个程序。
- fopen() fclose()
- 打开文件的模式: “r”, “w”, “a”, “r+’, “w+”, “a+”, “rb”, “wb”, “ab”… unix/linux只有一种文件类型,带b字母的模式与不带b字母的模式相同。 C11提供带x字母的模式。
- 成功返回指向FILE类型的指针。并不指向实际的文件,而是指向一个包含文件信息的数据对象。
- getc() putc()
- fprintf() fscanf()
- rewind() 回到文件开始处。
- fgets() fputs()
fseek() ftell() 随机访问
- 文件起始点
模式 偏移量起始点 SEEK_SET 文件开始处 SEEK_CUR 当前位置 SEEK_END 文件末尾 - ftell()返回当前位置
- fgetpos() fsetpos()
- ungetc() 把指定的字符放回输入流中。
- fflush() 刷新缓冲区
- setvbuf() 创建供标准I/O函数替换使用的缓冲区
int setvbuf(FILE *fp, char *buf, int mode, size_t size);
mode取值
- _IOFBF: 完全缓冲,在缓冲区满时刷新
- _IOLBF: 行缓冲,缓冲区满或写入一个换行符时
- _IONBF: 无缓冲
- 二进制I/O fread() fwrite()
- feof() ferror() 收到EOF时,区分是否真正到达文件尾而不是发生错误。
- exit()与return()区别
Chapter 14 结构和其他数据形式
- 结构 struct
- 结构的标记名可选。
- 初始化
- 按顺序用逗号分隔成员的初始化项(声明时用分号分隔各项)。
- 初始化器 C99/C11 点运算符,顺序任意
- 结构数组
- 嵌套结构
- 指向结构的指针
- 与数组不同,结构名并不是结构的地址,初始化结构指针时要用&
- 访问时用 -> 或 (*指针名).成员名
- 允许把一个结构赋值给另一个结构,即使成员是数组,也可以
- 结构中的字符数组,指向char的指针(谨慎使用,初始化指针,利用malloc()申请合适的存储空间,利用free()释放)
- 复合字面量和结构
- (struct 结构名){ 初始化列表 }
- 伸缩型数组成员 C99
- 匿名结构C11
- 链式结构
- 联合 union
同一个存储空间中存放不同的数据类型(不是同时存储)。
- 编译器分配足够的空间以便存储联合声明中占用最大字节的类型。
- 初始化联合
- 用同类型的联合初始化
- 初始化联合的第一个元素
- C99 使用指定初始化器
- C11 匿名联合
- 枚举类型 enum 声明符号名称来表示整型常量
- C允许枚举变量使用++运算符,C++不允许
- 默认情况下,枚举列表中的值为 0, 1, 2…
- 声明时,可以为枚举常量指定整数值
- typedef 为某一类型自定义名称
可看作定义变量时,在前加typedef关键字,变量名成为新类型名。
- 与#define的区别
- typedef创建的符号名仅限于类型,不能用于值。
- typedef由编译器解释而不是预处理器。
- 在受限范围内,typedef比#define更灵活
- 与#define的区别
- 其它复杂声明
- ’*’ ‘()’ ‘[]’ 的优先级
- 数组名后的[]和函数名后的()优先级相同,二者比 ‘*(解引用运算符)’的优先级高。
- ()和[]从左往右结合
- ’*’ ‘()’ ‘[]’ 的优先级
- 函数和指针
- 函数的类型: 返回值、形参
- 声明一个指向函数的指针, 可将函数声明中的函数名替换为(*指针名)
另:函数名是指针,可以互换使用。 - 函数名的几种用法
- 函数原型
- 函数定义
- 函数调用
- 用作指针
- 用作指针参数
Chapter 15 位操作
- 有符号整数的表示
- 用1位存储符号 缺点:有两个0: +0 -0 8位可表示 -127~127
- 二进制反码: 反转每一位形成负数 。
- 二进制补码:
1位表示符号,确定负值时,用9位100000000减去负数的位组合,结果为该负值的量。
- 二进制补码表示,获得相反数:反转每一位,再加1.
- 二进制浮点数
分两部分存储:二进制小数和二进制指数。
- 二进制小数: 以2的幂作分母
- 按位运算符:
- 按位取反: ~
- 按位与: &, &=
- 按位或: |, |=
- 按位异或: ^, ^=
- 用法:掩码、打开位(设置位)、关闭位(清空位)、切换位、检查位
- 移位运算符
产生一个新的位值,但不改变其运算对象。
- 左移 <<
- 右移 >>
- 用法:针对2的幂进行快速乘法和除法; 提取某些位
- 位字段
- singed int 或 unsigned int 类型变量中的一组相邻的位。
- 通过结构声明来建立,为每个字段提供标签,并确定该字段的宽度。
- C以unsigned int作为位字段结构的基本布局单元。即便结构中唯一的成员是1位字段,该结构的大小(所占位数)也是unsigned int类型的大小。
- 对齐特性
- _Alignof: 给出一个类型的对齐要求。
- _Alignas: 指定一个变量或类型的对齐值。不应该要求小于该类型的基本对齐值。
- aligned_alloc() 内存分配,动态对齐 C11
添加stdalign.h头文件,可将alignas和alignof分别作为别名使用。
Chapter 16 C预处理器和C库
- 预处理指令
- #define 宏 替换体
- 类函数宏 区别于函数调用
- 替换体,记号型字符串而非字符型字符串。字符型字符串,把空格视为替换体的一部分;记号型字符串,把空格视为替换体中各记号的分隔符。
- 双引号字符串中的字符被视为普通文本,不是一个可被替换的记号。若要在字符串中包含宏参数,使用#号。
- ##: 预处理器粘合剂。把两个记号组合成一个记号。
- 变参宏: 宏参数列表中最后的参数写成省略号 … _ VA_ARGS _ 用在替换部分
- #include 文件包含
- #undef: 取消已定义的#define指令
- 条件编译
- #ifdef或#ifndef, #else, #endif;
- #ifndef 还可用于防止多次包含一个文件。
- #if, #elif
- #if defined VAX 可替代 #ifdef VAX
- #ifdef或#ifndef, #else, #endif;
- #line : 重置 _ _ LINE _ _ 和 _ _ FILE _ _ 宏报告的行号和文件名。
- #error: 让预处理器发出一条错误信息。
- #pragma _Pragma
- _Generic: C11关键字。泛型表达式不是预处理指令, 但常用作#define宏定义的一部分。
- #define 宏 替换体
- 预定义宏
- _ _ DATE _ _
- _ _ FILE _ _
- _ _ LINE _ _
- _ _ STDC _ _
- _ _ STDC _ _ HOSTED _ _
- _ _ STDC _ _ VERSION _ _
- _ _ TIME _ _
- _ _ func _ _ :函数名
- 函数说明符
- inline: 内联函数
- _Noreturn: 调用完成后函数不返回主调函数。 exit()是一个示例。
- C库
- 数学库 头文件math.h
- 通用工具库
- 随机数生成器
- 查找和排序函数
- 转换函数
- 内存管理函数
几个函数:
exit()和atexit()
qsort()
memcpy()和memmove()
- 断言库 头文件 assert.h
- assert() 运行时检查,若为假,向标准错误写信息并调用abort()终止程序。
- _Static_assert() 编译时检查。
- 关闭assert(): #define NDEBUG
- 可变参数 stdarg.h
用法较为复杂。
Chapter 17 高级数据表示
- 链表
- 结构不能含有与本身类型相同的结构,但是可以含有指向同类型结构的指针。–>定义链表的基础
- 头指针 head: 指向链表的第一项
- next 指针: 指向下一下。最后一项的next为NULL.
- 队列 FIFO
使用链表实现时
- 新项只能添加到链表的末尾。
- 只能从链表的开头移除项。
- 二叉查找树