头文件
#include<stdio.h>
#include<string.h>
#include<limits.h>
#include<float.h>
#include<math.h>
#include<stdbool.h>
#include<ctype.h> //字符函数:字符测试函数、字符映射函数,P156
#include<stdlib.h>
#include<unistd.h> //许多在Linux下开发的C程序都需要头文件unistd.h,但VC中没有个头文件,需要自行保存
#include <windows.h> //Windows独有,非标准库
返回值
printf()返回打印字符的数目
scanf()返回成功读入的项目的个数,没有读到返回0,检测到“文件结尾”返回EOF
sizeof()返回size_t类型的值,这是一个无符号整数类型,以字节为单位返回运算对象的大小
time()返回time_t类型
fabs()返回一个浮点数的绝对值
getchar()针对字符类型的输入,返回值是读取的字符的整数表示。它从标准输入(通常是键盘)获取一个字符,并将其作为整数返回。如果成功读取字符,返回的值将是该字符的ASCII码值(0-127)。如果到达文件结束(EOF),则返回特殊值EOF
(通常表示为-1)。
putchar()针对字符类型的输出,返回值是成功写入的字符的整数表示。它将一个字符以整数形式写入标准输出(通常是屏幕)。如果成功写入字符,返回的值将是写入的字符的ASCII码值。如果发生错误,putchar()
函数返回EOF
的整数表示。
gets()返回一个指向char类型的指针值。用于接收字符串。如果get()识别到文件结尾或者输入出错,则返回一个空指针,用常量NULL表示。
fgets()返回一个指向char类型的指针值。用于接收字符串。如果fget()识别到文件结尾或者输入出错,则返回一个空指针,用常量NULL表示。
puts()如果成功,该函数返回一个非负值为字符串长度(包括末尾的 \0),如果发生错误则返回 EOF。
fputs()如果成功,返回非负值;如果发生错误,返回 EOF。
字符串函数
strlen()返回字符串 str 的长度。
strcat() 是 char*(指向 char 的指针)类型。返回它的第一个参数的值,即其后添加了第个字符串为那个字符串中第一个字符的地址。
strncat() 是 char*(指向 char 的指针)类型。返回它的第一个参数的值,即其后添加了第个字符串为那个字符串中第一个字符的地址。
strcmp()比较两个字符串是否相等,相等返回0,否则返回非零值。
strncmp()比较两个字符串在指定长度内是否相等,相等返回0,否则返回非零值。
strcpy() 返回类型为char*,返回的是第一个参数的值,即一个字符的地址。
strncpy() 返回类型为char*,返回的是第一个参数的值,即一个字符的地址。
sprintf()如果成功,则返回写入的字符总数,不包括字符串追加在字符串末尾的空字符。如果失败,则返回一个负数。
rand()随机数生成函数,返回随机数,rand()% size 得到 0 —(size-1) 范围内的随机数。
malloc()分配所需的内存空间,并返回一个指向它的void类型指针。
calloc()分配所需的内存空间并初始化,并返回一个指向它的void类型指针。
free()该函数不返回任何值。
文件函数
fopen()打开成功返回文件指针(并不指向实际的文件,指向一个包含文件信息的数据对象),失败返回NULL。
getc()返回值是读取的字符的整数表示。它从标准输入(通常是键盘)获取一个字符,并将其作为整数返回。如果成功读取字符,返回的值将是该字符的ASCII码值(0-127)。如果到达文件结束(EOF),则返回特殊值EOF
(通常表示为-1)。
putc()返回值是成功写入的字符的整数表示。它将一个字符以整数形式写入文件。如果成功写入字符,返回的值将是写入的字符的ASCII码值。如果发生错误,putchar()
函数返回EOF
的整数表示。
fclose()关闭成功返回0,失败返回EOF。
fprintf() 如果成功,则返回写入的字符总数,否则返回一个负数。
fscanf() 如果成功,该函数返回成功匹配和赋值的个数。如果到达文件末尾或发生读错误,则返回 EOF。
fgets() 如果成功,该函数返回相同的 str 参数。如果到达文件末尾或者没有读取到任何字符,str 的内容保持不变,并返回一个空指针。如果发生错误,返回一个空指针。
fputs() 该函数返回一个非负值,如果发生错误则返回 EOF。
fseek()正常返回0,出现错误返回-1()试图移动的距离超出文件范围。
ftell()返回参数指向文件的当前位置据文件开始处的字节数。
转义字符
\n 换行 \r回车 \a(\007)报警 \0空字符 \b 后退一格
打印类型
P69
unsigned int %u
short int %hd
%zd 打印 sizeof()类型
地址 %p
十六进制 %x 八进制%o 科学计数%e
打印修饰符
%2d 设置字段宽度
正数为右对齐,负数为左对齐,宽度小于字符占用长度时,自动扩充至原宽度。
%2.2f .2表示小数点后位数
%05d 用0填充前导空白而不是空格
%.5s 只打印5个字符
*修饰符
printf()打印指定宽度,浮点类型精度,P82,%*d
scanf()跳过相应输入项进行储存
4 字符串和格式化输入输出
字符串
字符串以数组形式储存,\0截止,意味数组容量必须比字符串大1
sizeof()以字节为单位给出指定类型大小——占用 内存空间
strlen()给出字符串中的字符长度 ——字符数量
scanf("%s", &a);
//zhao shiting
printf("%s,&a");
//zhao
这表明scanf()只读取了第一个空格字符前(空格 制表符 换行符)的内容。
类型转换
表达式及其运算涉及类型转换,过程包括升级和降级,降级可能涉及因不匹配导致的问题(可能放不下),具体规则P107。
强制类型转换运算符
(type) eg.(int)
6 C 控制语句:循环
逻辑运算符
C保证逻辑表达式的求值顺序是从左往右的,在判断某个元素让整个表达式无效时立刻停止求值。
&&和||都是序列点,程序从一个运算对象执行到另一个运算对象之前,所有副作用都会生效。
7 分支和跳转
条件运算符?:
x=(y<0)?-y:y;
//等价于
if(y<0)
x=-y;
else
x=y;
循环辅助
continue跳出本次循环,并开始下一次循环
break终止所在循环,直接执行下一阶段
多重选择
swich(){
case 1:
case 2:break;
case 3:
default:
}
8 字符输入输出和输入验证
文件、流、和键盘输入
C程序处理的是流而不是直接处理文件,流是一个实际输入或输出映射的理想化数据流。**这意味着不同属性和不同种类的输入,由属性更加统一的流来表示。**打开文件的过程就是将流和文件相关联,而且读写都是通过流来表示。
C把输入和输出设备视为存储设备上的普通文件。尤其是把键盘和显示设备视为每个C程序自动打开的文件。
stdin 表示键盘输入
stdout 表示显示屏幕输出。
EOF 表示文件结尾,键盘输入时模拟文件结尾条件。Windows系统中将一行开始处的ctrl+Z识别为文件结尾信号。
重定向
重定向用于连接一个可执行程序和一个数据文件,更改该程序的标准输入输出。echo_eof为一个可执行程序,words和mywords为两个数据文件。
重定向输入
./echo_eof < words
重定向输出
./echo_eof > mywords
组合重定向
./echo_eof < words > mywords
输入验证
在使用getchar()获取字符时,常出现因\n等未清除导致的问题,以下提供一种清除换行符的方法:
ch=getchar();
while(getchar!='\n')
continue;
以上程序可以在读取到\n之前一直获取字符,以此清除输入缓冲区。
9.函数
定义形参时,不能像通常的变量声明那样使用变量列表来声明同一类型的变量。
声明变量时,可以省略变量名。
被调函数使用的值时从主调函数中拷贝而来,所以无论被调函数对拷贝数据进行什么操作,都不会影响主调函数中的原始数据。
递归
C允许函数自己调用自己,这调用过程被称为递归。
递归相较于循环,方案更简洁,但效率更低
基本原理:
1.每级函数调用都有自己的变量。但是代码并没有被拷贝;
2.函数每次调用都会返回一次,函数执行完毕后,控制权将被传回上一级递归;
3.递归函数中位于递归调用之前的顺序执行,位于递归调用之后的倒序执行;
4.必须包含让递归调用停止的语句。
尾递归
递归调用语句在函数结尾即return之前。
多源代码文件程序的编译
一般情况下,把自定义函数原型和常量定义放在一个头文件里。
自己编写的头文件在调用时写“ ”
#include "hotel.h"
指针
指针用于存储变量的地址。
10.数组和指针
数组 数组声明 数组索引
只读数组const
const int a[10]={};
程序在运行过程中不能修改该数组内的内容,将每个元素当作常量处理。
数组定义
1.未进行初始化——数组存储该地址原先值;
2.部分初始化——未初始化元素置为0;
3.初始化元素个数大于容量——视为错误(可以省略方括号内的值由编译器自行判断)。
数组赋值
C不允许把数组作为一个单元赋给另一个数组,除初始化外也不允许使用花括号列表的形式赋值。一般情况下使用循环的方式对数组元素依次赋值。
数组边界
数组边界小于元素个数时,会发生内存溢出,发生更改其他地址存储的内容等情况。
多维数组
二维数组在定义时有两种写法
int a[2][3]={
{1,2,3}
{4,5,6}
}
int a[2][3]={1,2,3,4,5,6}
在定义时填满元素的情况下,两种写法没有区别,但如果元素未填满,其填充规则为:
int [2][4]={
{1,2,3} // 1,2,3,0
{4,5,6} // 4,5,6,0
}
int[2][4]={
1,2,3,4,5,6 // 1,2,3,4
} // 5,6,0,0
指针和数组
指针+1指的是增加一个存储单元,对数组而言,意味着+1是下一个元素的地址,所以必须声明指针指向的对象类型。
int (* pz)[2]; //pz指向一个内含两个int类型值的数组;
int * pax[2]; pax是一个内含两个指针元素的数组,每个指针都指向int的指针
指针和函数
函数定义和声明数组时,可省略中括号内的数字,如
int sum(int ar[]);
一元运算符*和++优先级相同,但结合律从右向左,所以
*p++ //先用后加,加指针
(*p)++ //先用后加,加数值
*(++p) //先加后用,加指针
++(*p) //先加后用,加数值
指针相减
为相差内存单元,需要考虑指针类型。
指针可以和整数相加,代表下一个内存单元。
使用指针时,千万不要解引用未初始化的指针。
保护数组中的数据const
double rates[5]={1,2,3,4,5};
const double* pd=rates;//pd不可修改所指向的数值
double* const pd=rates;//pd不可指向别处
const double* const pd=rates;//pd既不可修改所指向的数值也不可指向别处
此时,指针pd指向的double类型被声明为常量,意味着不能使用pd更改数组rates内的值,但是rates并未做const声明,故而可以用rates[3]改变数组内的值。这种情况一般用于声明形参时,表明函数不会使用指针改变数值。
此外,const数值只能赋值给const指针,普通指针不能接收const数值。
指针和多维数组
11.字符串和字符串函数
字符串是以空字符(\0)结尾的char类型数组。(如果没有\0,这就不是一个字符串,而是一个字符数组)
字符串属于静态存储类别。
字符串字面量之间没有间隔或者用空白字符分割,C会将其视为串联起来的字符串字面量。
字符串用双引号括起来的内容被视为指向该字符串存储位置的指针。
字符变量占1字节,字符常量占int类型所占字节数,字符串占字符个数+1(\0)字符数。
字符串输入
创建存储空间——定义字符串时需要为字符串预设足够大的存储空间,以防填入字符串时覆盖其他空间原本的值。
gets()
读取\n,传递\n之前所有的字符,并在字符串结尾添加\0。
gets()返回一个指向char类型的指针值。用于接收字符串。如果get()识别到文件结尾或者输入出错,则返回一个空指针,用常量NULL表示。
不足:gets()不检查预留存储空间是否能容纳实际输入的数据,多出的数据简单地溢出到相邻的内存区。
fgets()
fgets(放置位置,最大字数,读取位置)
读入n-1个字符,或者读到遇到的第一个换行符为止。
会存储读到的第一个换行符,后续可能需要strchr()检查筛选剔除\n.
fgets()返回一个指向char类型的指针值。用于接收字符串。如果fget()识别到文件结尾或者输入出错,则返回一个空指针,用常量NULL表示。
P.S.处理换行符将其换成空字符 和缓冲区内的多余字符串——P285
空字符和空指针P286
gets_s()
get_s(words,STLEN)
在输入没有超过最大输入时,和gets()函数用法相同,若超过最大输入,gets_s() 将把目标数组中的首字符替换为\n,读取并丢弃随后的输入直到读到换行符或文件结尾,然后返回空指针。
字符串输出
puts()
参数为字符串的地址。遇到空字符停止输出。
fputs()
fputs(输出内容,输出位置)
stdout 标准输出
fputs()并不会在输出末尾添加换行符。
gets()丢弃输入中的换行符,puts()在输出中补全换行符;fgets()保留输入中的换行符,fputs()不会在输出末尾添加换行符。
字符串函数
strlen()
用于统计字符串的长度。(不包括\0)
strcat()
把2添加到1后面。
接受两个字符串参数。它将第二个字符串的一份拷贝添加到第一个字符串的结尾,并覆盖第一个字符串结尾的空字符。从而使第一个字符串成为一个新的组合字符串,第二个字符串并没有改变。
strcat()函数是 char*(指向 char 的指针)类型。这个函数返回它的第一个参数的值,即其后添加了第个字符串为那个字符串中第一个字符的地址。
strcat ()函数并不检查第一个数组是否能容纳第二个字符。
strncat()
函数需要另一个参数来指明最多允许添加的符的数目。例如strncat(bugs,add,13)函数把 add 字符串中的内容添加到bugs 上,直到加到13 个字符或遇到空字符为止,由二者中先符合的那一个来终止添加过程。
strcmp()
比较两个字符串是否相等,相等返回0,否则返回非零值。
strncmp()
比较两个字符串在指定长度内是否相等,相等返回0,否则返回非零值。
strcpy()
strcpy(目标字符串,源字符串) 返回类型为char*,返回的是第一个参数的值,即一个字符的地址。
第一个字符不必指向数组的开始,可拷贝数组的一部分。
strcpy ()函数并不检查第一个数组是否能容纳第二个字符。
strncpy()
strcpy(目标字符串,源字符串,最大拷贝数)
拷贝整个字符串或者或者n个字符,此时不一定会拷贝\0。因此,为保证拷贝副本是字符串,常用的处理方式为,拷贝最大容量n-1个字符,并将最后一个字符置为\0。
sprintf()
打印到字符串,第一个参数为目标地址,后面与printf相同。
sprintf指的是字符串格式化命令,函数声明为 int sprintf(char *string, char *format [,argument,…]);,主要功能是把格式化的数据写入某个字符串中,即发送格式化数据输出到 string 所指向的字符串。
返回值:
如果成功,则返回写入的字符总数,不包括字符串追加在字符串末尾的空字符。如果失败,则返回一个负数。
sprintf 返回以format为格式argument为内容组成的结果被写入string的字节数,结束字符‘\0’不计入内。即,如果“Hello”被写入空间足够大的string后,函数sprintf 返回5。
12.存储类别、链接和内存管理
对象:被存储的值占用的一块内存。
标识符:一个名称,用于指定特定对象的内容。
左值:指定内存位置上的内容的表达式。
存储期:对象在内存中保留了多长时间。
可以通过函数调用的方式显式分配和释放内存。
作用域
描述程序中可访问标识符的区域。
分为块作用域、函数作用域、函数原型作用域、文件作用域。
编译器在处理函数原型中的形参时只关心它的类别,而形参名无关紧要。但是在变长数组中,形参名有用
void fun(int n,int m,ar[n][m])
方括号中必须使用函数原型中已经声明的名称。
翻译单元和文件
编译器把源代码文件和所有的头文件都看成一个包含信息的单独文件,被称为翻译单元。文件作用域的可见范围实际上是整个翻译单元。
链接
外部链接:可以在多文件程序中调用。在主函数外定义的全局变量。
内部链接:只能在一个翻译单元中使用。文件私有。使用存储类别说明符static。
无链接:具有块作用域、函数作用域、函数原型作用域的变量都时无链接的。意味着变量为其私有。
存储期
静态存储期:在程序执行期间一直存在。(文件作用域变量,无论内部还是外部链接,都是静态存储期)
块作用域变量也能具有静态存储期。
void fun(int num){
static int ct=0;
}
//ct在程序被载入到程序结束期间都存在,但是作用域定义在fun()函数块中。只有在执行该函数时,程序才能使用ct访问它所执行的对象(但函数可以给其他函数提供该存储区的地址以便间接访问该对象,例如通过指针形参或返回值)。
线程存储期:用于并发程序设计,从声明时到线程结束一直存在。以关键字_Thread_local声明一个对象时,每个线程都获得该变量的私有备份。
自动存储期:块作用域的变量通常具有自动存储期。自动变量占用的内存视为一个可重复使用的工作区或暂存区。
动态分配存储期
作用域、链接和存储期为变量提供了多种存储方案。
存储类别
存储类别 | 存储期 | 作用域 | 链接 | 声明方式 |
---|---|---|---|---|
自动 | 自动 | 块 | 无 | 块内 |
寄存器 | 自动 | 块 | 无 | 块内,使用关键字register |
静态外部链接 | 静态 | 文件 | 外部 | 所有函数外 |
静态内部链接 | 静态 | 文件 | 内部 | 所有函数外,使用关键字static |
静态无链接 | 静态 | 块 | 无 | 块内,使用关键字static |
自动变量
只有在定义变量所在块中才能通过访问变量名访问该变量。注意就近原则。
寄存器变量
存储在CPU的寄存器中,概括的说,存储在最快的可用内存中。
寄存器变量的地址无法获取。
绝大多数方面,寄存器变量和自动变量一样。
register 关键字用于声明寄存器变量。可声明为register的变量有限,double类型可能没有足够大的空间存储。
块作用域的静态变量
静态的意思是该值在内存中的位置原地不动,而不是他的值不变。
static在编译时只会被初始化一次。如果未显式初始书静态变量,它们会被初始化为0。块作用域是告诉编译器只有当前块可以看见该变量。
外部链接的静态变量
变量的定义性声明在所有函数外面。如果一个源代码文件使用的外部变量定义在另一个源代码文件中,则必须使用extern在该文件中声明。如果是同源代码文件的函数使用,可选的用extern声明,提高代码可读性。
若在函数中再次进行定义性声明同名函数,相当于新建内存空间创建该函数可用变量。因此,extern关键字表明该声明不是定义,不会引起分配存储空间(外部变量只能初始化一次,必须在定义变量时进行)。
外部变量的作用域:从声明处到文件结尾。
内部链接的静态变量
用static在所有函数外面声明,变量当前源代码文件可见,可以用extern在函数内引用性声明。
多文件
在一个文件内定义性声明——单方面允许其他文件使用
在其他文件中引用性声明——之后可用
存储类别说明符
auto register static extern _Thread_local typedef
存储类别和函数
函数可以是外部函数(默认)或静态函数,C99新增内联函数。
随机数函数和静态变量
rand()函数用于生成随机数,但是生成的是伪随机数。
time()函数返回系统时间,一般接受参数为time_t类型对象的地址,也可以传入空指针(0)作为参数。
time()函数可以结合rand()生成真随机数。
分配内存:malloc()和free()
malloc()接受参数:所需的内存字节数,分配内存,但不会为其赋名,可以把该地址赋给一个指针变量。
一种malloc()使用场景:
pt=int* malloc(n*sizeof(int));
在程序结束时,会自动释放malloc()分配的值。但是保险起见,最好使用free()进行释放,尤其是在复杂程序中。
free(pt);
free()所用的指针变量可以与malloc()的指针变量不同没达标两个指针必须存储相同的地址。
calloc()
分配内存函数(不常用), calloc()函数在分配空间后会进行初始化,将块中的所有位都设置为0。
pt=(long*)calloc(100,sizeof(long));
free();
ANSI C类型限定符
const
volatile
告知计算机,代理可以改变该变量的值。即一个值x在程序中未被改变,被存入寄存器中进行高速缓存。但在从其他设备传入时改变,故不可从寄存器读取数据,而要从原始内存位置读取。
restrict
只能用于指针,表明该指针是访问数据对象的唯一且初始的方式。目的是允许编译器优化某部分代码以更好地支持计算。
_Atomic
多线程时标识原子类型,当一个线程对一个原子类型地对象执行原子操作时,其他线程不能访问该对象。
13.文件输入/输出
标准输入 stdin 标准输出 stdout 标准错误 stderr
fopen()
打开文件,打开成功返回文件指针(并不指向实际的文件,指向一个包含文件信息的数据对象),失败返回NULL。
r 读 w 复写 a 续写
getc()
get(fp)从fp中获取一个字符
putc()
putc(ch,fpout)把字符存入fpout文件。
fclose()
关闭成功返回0,失败返回EOF。
补充:argc , argv[]
int main(int argc, char* argv[])
(int argc, char* argv[])
: 这是main
函数的参数列表。main
函数可以接受两个参数,分别是argc
和argv
。
-
int argc
: 这是命令行参数的计数(argument count),表示程序运行时传递给程序的命令行参数的数量。具体的值取决于程序被如何调用以及调用时是否传递了参数。一般情况下:
如果只运行程序本身而没有传递额外的命令行参数,
argc
的值通常为1。这是因为程序名称本身也被视为一个参数。 如果在运行程序时传递了额外的命令行参数,
argc
的值将包括程序名称参数以及传递的其他参数的数量。例如,如果运行程序时输入了./myprogram arg1 arg2
,那么argc
的值将为3,其中包括程序名称(./myprogram
)以及两个额外的参数(arg1
和arg2
)。 -
char* argv[]
: 这是命令行参数的数组(argument vector),是一个指向字符串数组的指针,每个字符串表示一个命令行参数。
整个main
函数的目的是接受命令行输入,并执行程序的主要逻辑。例如,你可以通过argv
数组访问命令行参数,通过argc
确定参数的数量。
fprintf()
fscanf()
运行时与scanf()相似,会跳过空格。
fgets()
不舍弃’\n’
fputs()
不添加’\n’
rewind()
指针指向文件开头。
fseek()
fsttk(文件指针,偏移量:正前移负后移0不动,起始点位置:SEEK_SET SEEK_CUR SEEK_END)
ftell()
返回参数指向文件的当前位置距文件开始处的字节数。
fwrite()和fread()
二进制读写文件。
fwrite(待写入数据块地址,数据块大小sizeof,写入数据块数量,目标文件地址)
fread(目标数据块地址,数据块大小sizeof,读取数据块数量,待读取文件地址)
14.结构和其他数据形式
struct book{
char a;
int b;
float c;
};
//结构体标记 book
struct{
char a;
int b;
float c;
}library;
//组合后的结构声明和结构变量定义可省略结构标记
struct book lib{new, 3, 1.2,};
//赋值用逗号
结构体
结构体指针
struct guy* him; //结构指针声明
him = &barney[0]; //结构体名称并不是结构变量的地址,要使用&获取地址
则 him->income = barney.income = (*him).income;
则 him + 1 = &barney[1];
结构体内的字符串一般使用数组表示形式,若使用指针表示形式,常将字符串读入临时数组,并用malloc()形式为指针分配空间,拷贝字符串。
联合
同一内存空间存储不同数据类型(不是同时存储)
union hold{
int digit;
double bigfl;
char letter;
}
枚举
枚举类型声明符号名称来表示整型常量。
enum spectrum{red,orange,yellow,green,blue,violet}; //创建 red.ed 枚举符
enum spectrum color; //声明
枚举默认是从0开始的int值,自定义需要自行赋值(赋值枚举符的后续值递增)。
名称空间
理论上,相同作用域中的变量和标记的名称可以相同,因为他们使用不同的名称空间,但不建议这么做。
typedef 简介
为某一类型自定义名称。
创建的符号名只受限于类型,不能用于值。
typedef unsigned char BYTE;
BYTE x, y[10], *z;
typedef char(*FRPTC())[5];
FRPTC a;
与#define 不同,typedef创建的符号名只受限于类型,不能用于值。
用 typedef来命名一个结构类型时,可以省略该结构的标签。
符号优先级
数组[ ]和函数( )有相同优先级,且优先于指针*。
函数和指针
函数指针常用作另一个函数的参数,告诉该函数使用哪一个函数。
声明函数指针时,必须声明指针指向的函数类型(返回值和形参类型)。
把函数名替换为表达式(*pf)是创建指向函数指针最简单的方式。
15.位操作
二进制整数
有符号整数
符号量表示法
高阶位储存符号,其他7位储存值。(-127-127)
二进制补码
得到一个二进制补码的相反数,最简单的方法是反转每一位后加一。(-128-127)
二进制反码
反转每一位形成负数。(-127-127)
C按位运算符
二进制反码或按位取反 ~ :1 变0,0变1/
按位与 & :两个都为1,才为1.
按位或 |:有1,则为1.
按位异或 ^:相同为0,不同为1.
用法
打开位(设置位):按位或
关闭位(清空位):取反按位与
切换位:按位异或
检查值是否位1:按位与==检验数
移位运算符
左移<< 向左移位右侧补0 乘2的幂次方
右移>> 无符号数向又移位右侧补0, 有符号数按编译器规则向又移位右侧补0或1 除2的幂次方
位字段
位字段是一个signed int或unsigned int类型变量中的一组相邻的位。
通过一个结构体来声明,提供标签并确定宽度。
一个字段不允许跨越两个unsigned int边界,多以会自动移动跨界的字段,留下一个未命名的洞,可用未命名字段填充。
使用一个宽度为0的未命名字段迫使下一个字段和下一个整数对齐。
switch语句中可以使用位字段成员,也可以把位字段成员作为数组下标。
16.C预处理器和C库
明示常量 #define
#define 宏(类对象宏、类函数宏) 替换列表或替换体 ——过程叫做宏展开
类函数宏在使用时最好通过足够多的括号来保证优先级
避免在宏参数中使用自增变量
#undef 取消定义
#运算符
双引号里的宏被视为普通字符,若想以宏形式显示,在宏前加#
##运算符可用于类函数宏的替换部分,还可以作为对象宏的替换部分。##把两个记号组合成一个记号。“粘合剂”
变参宏
…和"_ _ VA_ARGS _ _"
#define PR(X,...) printf("message " #X ": " __VA_ARGS__)
x=12;
PR(1,"%d\n",x);
输出
message 1: 12
#include
查看#include后面的文件名并把文件的内容包含到当前文件中
#include<>在系统目录查找
#include“ ”在当前目录查找
头文件.h,包含需要放在程序顶部的信息——明示常量、宏函数、函数声明、结构模板定义、类型定义,还可以使用头文件声明外部变量供其他文件共享。
条件编译
#ifdef #else #endif #ifndef
qsort()
快速排序函数,使用时需要自己编写比较函数函数
void qsort(void *base, size_t nmemb, size_t size, int(*comper)(const void*, const void*));
断言库
assert.h assert(判断条件) 辅助调试程序,判断条件成立,程序继续运行;否则退出报错。
在#incluse(assert.h)前使用#define(NDEUBUG)可禁用调试。
memcpy()和memmove()
从s2拷贝n字节到s1
void *memcpy(void * restrict s1, const void * restrict s2, size_t n);
//要求两个内存空间没有重合
void *memmove(void * s1, const void * s2, size_t n);
//无要求
不过使用方法稍微复杂一些。必须按如下步骤进行:1.在函数原型中使用省略号。
2.在函数定义中创建一个 va_list 类型的变量。
公3.用宏将该变量初始化为一个参数列表。
4.用宏访问这个参数列表。
5.用宏完成清理工作。
#运算符
双引号里的宏被视为普通字符,若想以宏形式显示,在宏前加#
##运算符可用于类函数宏的替换部分,还可以作为对象宏的替换部分。##把两个记号组合成一个记号。“粘合剂”
变参宏
…和"_ _ VA_ARGS _ _"
#define PR(X,...) printf("message " #X ": " __VA_ARGS__)
x=12;
PR(1,"%d\n",x);
输出
message 1: 12
#include
查看#include后面的文件名并把文件的内容包含到当前文件中
#include<>在系统目录查找
#include“ ”在当前目录查找
头文件.h,包含需要放在程序顶部的信息——明示常量、宏函数、函数声明、结构模板定义、类型定义,还可以使用头文件声明外部变量供其他文件共享。
条件编译
#ifdef #else #endif #ifndef
qsort()
快速排序函数,使用时需要自己编写比较函数函数
void qsort(void *base, size_t nmemb, size_t size, int(*comper)(const void*, const void*));
断言库
assert.h assert(判断条件) 辅助调试程序,判断条件成立,程序继续运行;否则退出报错。
在#incluse(assert.h)前使用#define(NDEUBUG)可禁用调试。
memcpy()和memmove()
从s2拷贝n字节到s1
void *memcpy(void * restrict s1, const void * restrict s2, size_t n);
//要求两个内存空间没有重合
void *memmove(void * s1, const void * s2, size_t n);
//无要求
不过使用方法稍微复杂一些。必须按如下步骤进行:1.在函数原型中使用省略号。
2.在函数定义中创建一个 va_list 类型的变量。
公3.用宏将该变量初始化为一个参数列表。
4.用宏访问这个参数列表。
5.用宏完成清理工作。