C语言程序设计:现代方法阅读笔记

第二十六章

  1. atexit函数允许用户“注册”在程序终止时要调用的函数:atexit(func); 在程序终止后,func函数会被自动调用
  2. clock()函数可以计算程序运行时间
  3. time函数返回当前的日历时间,返回类型是time_t

第二十四章

  1. void assert(expression); 如果返回非0,则正常运行,如果返回0,则会向stderr返回一条消息,并且调用abort函数终止程序
  2. 用多了影响程序的运行时间
  3. #define NDEBUG 禁用assert
  4. errno用来检测库函数调用中的错误,如果发生了错误,errno返回非0,但是注意在函数调用前将errno置零
  5. perror和strerror输出errno的错误具体信息
  6. signal(SIGINT, handler); handler为信号处理函数
  7. raise触发信号函数
  8. setjmp设置跳转标志位的位置,longjmp函数跳转到设定位置处。与goto语句不同的是,可以实现函数之间的跳转。标志位类型为jmp_buf
  • 为什么p451 tsignal.c的输出有多种

第二十三章

  1. <math.h>的cbrt函数计算参数的立方根
  2. <ctype.h>是字符处理库,判断是否是数字,是否大写等

第二十二章

  1. 输入重定向: demo <in.dat 输出重定向: demo > out.dat
  2. 使用fopen函数时要检测其返回值是不是空指针
  3. fopen打开二进制文件是,需要在模式字符串中包含字母b
  4. freopen可以实现重定向
  5. sprintf()函数将输出写入字符数组中,可以用来把数转换成字符格式
  • SEEK_CUR,文件的当前位置怎么确定

第二十一章

  1. ptrdiff_t 当进行指针相减运算时,其结果的类型,这个必须时有符号整型
  2. <stddef.h>中的offsetof函数可以计算结构体的起点到指定成员间的字节数,即偏移字节量
  3. 结构体的第一个成员的地址与结构体自身的地址相同
  • 两个指向整型的指针相加减的返回值,如果用的这个值的话需不需要强制转换

第二十章

  1. i«j的值是将i中的位左移j位后的结果。每次从i的最左端溢出一位,在i的最右端补一个0位
  2. i»j的值是将i中的位右移j位后的结果,如果i是无符号数或非负值,则需要在i的左端补0.如果i是负值,一些情况左端补0,一些情况保留符号位补1
  3. 设置i的第4位:i|=0x0010;  设置第j位: i|=1«j;
  4. 清除i的第4位:i&=~0010;  清除第j位:i&=~(i«j);
  5. 测试i的第4位:if(i&0x0010); 测试第j位:if(i&1«j);

第十九章

  1. 将具体数据实现方式隐藏起来的数据类型称为抽象数据类型
  2. 描述了对象但缺少定义对象大小所需的信息
  • p360用void*表示的数据项为什么不能是int、double之类的基本类型

第十八章

  1. 在函数体内部声明的变量具有自动存储期限、块作用域,并且无链接
  2. 在程序最外部声明的变量具有静态存储期限,文件作用域和外部链接
  3. 内部链接:只能被用于单独一个文件,并且被函数共享;外部链接:可以被程序中的几个文件共享
  4. register 由于寄存器没有地址,所以对寄存器变量使用&取址是非法的
  5. 指针数组:int *p[10]; 数组指针int (*p)[10];
  6. 返回指针的函数:int *f(int a); 函数指针 int (*p)(int a);
  7. 具有静态存储期限的变量的初始化式必须是常量
  8. const不能用于常量表达式
  • 内联函数与define定义的区别

第十七章

  1. 内存分配函数malloc, calloc, realloc的返回类型为 void*
  2. 若p为指向结构体的指针,利用scanf时,scanf(”%d”, &p→value); 必须加&,如果没有&,则传入的是value的值
  3. 受限指针:int *restrict p; 如果指针p指向的对象在之后需要修改,那么该对象不会允许通过除指针p之外的任何方式访问
  4. NULL的定义:#define NULL (void *) 0
  • 受限指针的应用
  • qsort()

第十六章

  1. 两个结构体之间可以直接赋值:struct1 = struct 2; 但是不能进行其他操作,例如判断==或者!=
  2. 声明了struct part {…}; 后面必须加分号,如果想声明结构体变量,则struct part part1; 不能省略struct字段
  3. typedef struct {…} part; 进行了类型声明,之后可以直接 part part1;来声明结构体变量
  4. 结构变量存储在不同的内存地址中,而联合变量的成员存储在同一内存地址中
  5. 如果把一个值存入联合体u的u.d中,那么先前存储在u.i的值将会丢失
  • 枚举类型的存储方式

第十五章

  1. extern int i; extern表示变量i是在程序中的其他位置定义的,而不是在这个位置
  2. 在数组的声明中使用extern时,可以省略数组的长度。extern int a[]; 因为此刻编译器不需要为数组a分配空间
  3. #ifndef 头文件名 #define 头文件名 #endif
  • makefile的使用方法需要熟悉

第十四章

  1. 预处理器将每一行的注释都替换为一个空格字符
  2. #运算符将宏的一个参数转换为字符串字面量
  3. ##运算符可以将两个记号“粘合”在一起
  4. 宏定义的作用范围通常到出现这个宏的文件末尾
  5. #indef和#ifndef 测试标识符是否没有被定义为宏
  • p231如果宏循环替换会怎么样。答:如果在扩展宏的过程中原先宏名重复出现的话,宏名不会再次被替换

第十三章

  1. char ch; ch = “abc”[1]; 返回ch = b,在字符串后面加下标为取字符串的对应字符
  2. 当声明用于存放字符串的字符数组时,要始终保证数组的长度比字符串的长度多一个字符
  3. 当指针指向字符串时,不能用指针修改字符串的内容:char *p = “abc”; *p = 'b';
  4. char a[] = “hello”; a有6个字节空间
  5. scanf(”%s ”, str); 不用再str前加&
  6. scanf会在开始跳过空白字符,并且在遇到空白字符后停止输入,而gets函数不会
  7. if(str1 == str2) 那两个字符串作为指针来比较,因为他们都有不同的地址,所以比较结果一定为0
  8. while (*s) s++; 和 while(*s++); 搜索字符串结尾的空字符
  9. argc是命令行参数的数量,argv是指向命令行参数的指针数组,argv[0]指向程序名,从argv[1]到arfv[argc-1]指向余下的命令行参数,argv[argc]始终是一个空指针
  10. 根据getchar()返回是否为EOF来检测读入字符是否失败
  • p205 如果输入的字符个数正好为n,那么会不会存储空白字符

第十二章

  1. p = &a[5]; q = &a[1]; i = p - q;(i=4) i = q - p;(i=-4)这里的计算不用考虑数组元素字节的大小
  2. int *p = (int []){3, 0, 3, 4, 1}; 指向复合常量的指针
  3. 对数组元素个数为N的数组a[N]取地址运算符是合理的
  4. *(p++)的值是*p,即p当前指向的对象,之后再自增p,等同于*p++;(*p)++返回的是p指向的对象的值,之后在自增对象
  5. *++p或*(++p) 先自增p,自增后表达式的值是*p;++*p或++(*p) 先自增*p,自增后表达式的值是*p
  6. 可以同数组的名字作为指向数组第一个元素的指针,但是不能给数组名赋新的值。例如: while(*a != 0) a++;
  7. int *p; p = a[i]; 访问二维数组第i行。int (*p)[N]表示数组指针,(*p)[i]中,*p代表a的一整行,(*p)[i]选中了该行的第i列的元素
  8. 如果p的类型是int *,那么p+j表示给p加上4j,因为int的类型需要4个字节存储
  • p192页处理多维数组的列的代码中 p = &a[0];能不能改成 p = a;

第十一章

  1. 指针就是地址,而指针变量就是存储地址的变量。如果*p是指针,那么*p表示p当前指向的对象。
  2. j = *&i 等价于 j = i
  3. int *p, *q; p = q表示把q的内容(即q指向的变量的地址)复制给p; *p = *q表示把q指向的值(q指向的变量的值)复制到p指向的对象
  4. int i, *p; p = &i; scanf(”%d”, p); p前不需要加&
  5. 指针作为返回值时,不能返回函数内一个局部变量的地址,因为函数结束后局部变量被释放
  • 针对(3),如果 int *p, *q, i=3; *p = &i; *q = *p 是否合法

第十章

  1. 函数体中的静态存储变量具有永久的存储单元,一个函数中的静态存储变量对其他函数不可见,只为所在函数的再次调用保留变量值
  • 如果声明了外部静态全局变量i,在某函数内也声明静态变量i,是否冲突

第九章

  1. 如果省略返回类型,C89标准会假定函数返回值的类型是int类型,但这在C99中这是不合法的
  2. printf函数返回显示的字符个数
  3. 当形式参数是一维数组时,可以不说明数组的长度
  4. 在把数组名传递给函数时,不要在数组名的后面放置方括号
  5. 函数无法检测传入的数组长度的正确性
  6. 声明多维数组时,只能忽略第一维的长度,必须声明列的数量
  7. 声明函数 int func(int n, int a[星号]); 星号表示数组a的长度与形式参数列表中前面的参数(即n)相关
  8. int func(int a[static 3], int n); 表明数组a的长度至少是3。如果数组参数是多维的,static仅可以用于第一维(即行)
  9. 在函数调用f(a, b)中,逗号是标点符号;而在f1)中,逗号是运算符
  10. 函数原型中的形式参数的名字不需要和后面函数定义中给出的名字匹配
  11. 复合字面量: total = sum_array((int []){3, 0, 3, 4, 1},5);
  • printf中返回的字符个数,那么转移字符例如” ”是算一个字符还是两个字符
  • static在描述数组长度中的具体作用

第八章

  1. a[i] = b[i++];可能导致未定义的行为,不建议这样写
  2. int a[5]={[2]=1, [4]=7}; 结果是a[5]={0,0,1,0,7};
  3. int c[10]={5,1,9,[4]=3};前三个元素为519,元素4的值为3,其余元素默认为0
  4. sizeof(a)/sizeof(a[0]),计算数组长度,但是会有警告,因为sizeof返回size_t类型,需要强制转换
  5. C99标准中引入变长数组; int n; int a[n]; goto语句不能绕过变长数组的声明,这样会导致程序对未分配空间的数组进行访问
  6. int a[] = {1,5,9,[0]=2,7}; 结果a[3]={2,7,9};
  • 在二维数组a[3][3]中,直接操作a[0]=1的结果

第七章

  1. 查看头文件<limits.h>可以确定整数类型范围
  2. 在整数后加L表示长整型,15L;在整数后加U,表示无符号整型,15U;LU也可以结合,15UL,表示既是长整型又是无符号的
  3. 两个int型整数相加的结果必须是int型
  4. 读写短整数时,在d、o、u、x前面加上h
  5. 默认情况下,浮点常量都以双精度数的形式存储
  6. touppper函数可以检测大小写,如果是小写则转换成对应的大写,如果是大写则返回原值。该函数在<ctype.h>中
  7. getchar()返回一个int型变量
  8. 类型转换原则为向上转型,signed类型优先转换为unsigned
  9. float f; f = 15.33f; 这里如果15.33后面不加f,15.33将是double类型
  10. long i; int j = 1000; i = j * j; 在类型转换之前的j * j发生了溢出,需要改成 i = (long)j * j;如果改成i = (long)(j * j)是错误的,因为在强制转换前就发生了溢出
  11. sizeof()函数的返回类型是size_t,为无符号整型
  12. printf函数中输出一个double型浮点数,应该用f而不是lf,printf函数不接受lf类型
  13. ” %c”在%前空一个空格可以是scanf函数读入下一个非空白字符
  • long int + unsigned int的结果为什么类型(书中好像存在错误P101,P102)

第六章

  1. do while循环在while的判断值为真是继续进入循环,为假时跳出循环
  2. for(int i = 0; i > 0; i++); 如果变量i在之前已经做了声明,这个语句将创建一个新的i且该值仅用于循环内
  3. 逗号表达式:表达式1, 表达式2; 计算表达式1并且扔掉计算的值,之后计算表达式2,把这个值作为整个表达式的值
  4. 带空循环体的循环更便于读取字符数据
  • for循环里声明多个不同类型的变量的结果
  • ++i, i+j; 执行后i的值为多少,如果输入i=1,j=5,i在表达式结束后是否为2

第五章

  1. i < j < k 等同于 (i < j)< k;
  2. &&和|| 在左边操作数的值推导出来之后会考虑要不要推导右操作数的值,而&和|会把两边都推导
  3. i > 0 && ++j > 0; 如果i < 0,则不会执行后面的j自增
  4. 布尔值的三种替换方法:标志位flag;宏定义TRUE为1和FALSE为0;宏定义BOOL 为int;
  5. C99标准中定义了 _BOOL类型,同时可以引用<stdbool.h>,这样可以直接用 bool 来声明布尔类型
  6. 选择语句case后面必须是常量表达式,且后面可以跟任意数量的语句
  7. 如果没有break,控制将会从一个分支继续到下一个分支,将之后的分支语句全部执行,直到遇到break或者switch执行完
  8. 如果i是int型,f是float型,条件表达式(i > 0 ? i : f)的结果是float型,即使 i > 0 为真
  • 条件表达式冒号的左右两边可不可以写多个表达式

第四章

  1. N元运算符表示该运算符需要N个操作数
  2. 当两个操作数都是整数时,运算符/会丢掉分数部分,例如1/2的结果为0而不是0.5
  3. 如果%的操作数有一个不是整数,则程序无法编译通过
  4. 在C99标准中,除法的结果向零取整,例如-3/2的结果为-1;并且i%j的结果的符号与i相同
  5. 一元运算符(+和-)是右结合的, - + i实际是-(+ i)
  6. int i; float f; f = i = 33.1f; 结果i=33,f=33.0而不是33
  7. e = (a + b)*(c - d);无法判断是(a+b)先求出来还是(c-d)先求出来
  8. v += e只求一次v的值,而v = v + e会求两次v的值
  • 输入 int i + 1;的执行结果

第三章

  1. %m.pX转换说明:m表示显示的最少字符数,默认右对齐;p表示显示的小数位数,默认6位;X为具体的转换说明符
  2. 如果要左对齐,则在m前加“-”。p如果对整数使用,不会显示小数,而是在整数多出来的位数前补0
  3. 编写程序时无法预知数的大小或者变化范围很大的情况下,可以用“g”说明转换符
  4. 尽量避免使用scanf函数,可以采用字符格式读取所有数据,然后把他们转换成数值形式
  5. 在scanf格式中,%d只能匹配十进制数,而%i可以匹配八进制、十进制和十六进制
  6. 在printf中“%%”显示百分号%
  7. 在scanf中如果要求输入一个数,但是输入了别的字符,则会跳过对应匹配,只能匹配到数值
  • 在scanf转换说明中输入“ ”是什么结果
  • 如果scanf输入的是%c,是不是就会考虑空格,另外如果是%c%c,sacnf如何如何判断输入

第二章

  1. 进行算术运算时float比int慢,并且float所存储的数值往往只是一个近似值,可能存在误差
  2. 在把小数赋给一个float变量时,需要在小数后面加一个f例如:a = 33.24f,不加可能会引发编译器警告
  3. 计算m/n向上取整,可以(m+(n-1))/n
  4. int a, b, c = 1; 只有c被初始化为1,a和b都没有被初始化
  5. 在编写输入时,需要考虑实际输入内容的类型
  • 如果int a b=0; 会发生什么情况

第一章

  1. 在编译C语言程序检查基本语法错误的同时,也需要关注越界问题和内存泄漏等问题
原文地址:https://www.cnblogs.com/zzdbullet/p/9362179.html