《C陷阱与缺陷》学习笔记

不要把==误用成=,同样也不要把=误用成==

&和|不同于&&和||

区分‘ ’和“ ”

贪心算法:

编译器将程序分解成符号的方法是,从左到右一个字符一个字符地读入,如果该字符可能组成一个符号,那么再读入下一个字符,判断已经读入的两个字符组成的字符串是否可能是一个符号的组成部分;如果可能,继续读入下一个字符,重复上述判断,直到读入的字符组成的字符串已不再可能组成一个有意义的符号.

a---b a-- -b

整型常量:

避免为了对其,无意将十进制写成八进制,如果一个整型常数的第一个字符是数字0,那么该常量将被视作八进制数。如0195相当于十进制数141

struct{ int num; char* str; }tab[]={ 123,"cccccc", 069,"yyyyyy" }

字符和字符串:

用单引号括起的一个字符代表一个整数,而用双引号括起的一个字符代表一个指针,如果两者混用,那么编译器的类型检查功能将会检测到这样的错误。

第一章练习题:

问:为什么n-->0的含义是n-- >0而不是n- ->0?

答:根据贪心算法,即每一个符号应该包含尽可能多的字符规则,在编译器读入>之前,就已经将--作为单个符号了。

问:a+++++b的含义是什么?

答:上式唯一有意义的解析方式是:a ++ + ++ b

可是,我们也注意到,根据”贪心算法“规则,上式应该被分解为:a ++ ++ + b

这个式子从语法上来说是不正确的,它等价于:((a++)++) +b,

但是,a++的结果不能作为左值,因此编译器不会接收a++作为后面的++运算符的操作数。所以只能被理解为第一种。

函数声明:

从0地址开始执行

(*(void(*)())0)();的含义

void(*)()是一个函数指针,该函数返回void型数据

(void(*)())0就是将常数0转换为指向返回值为void的函数的指针

(*(void(*)())0)();就是一个表达式,调用0地址的函数

该表达式相当:

typedef void (*funcptr)();

(*(funcptr)0)();

赋值:

所有的赋值运算符的优先级是一样的,而且它们的结合方式是从右到左.因此

home_score = visitor_score = 0;

与下面两条语句表达意思相同:

visitor_score = 0;

home_score = visitor_score;

指针和数组:

数组的大小必须在编译期就作为一个常数确定下来

char *a="KING-ERIC" 与 char a[]="KING-ERIC"的区别:

字符串存放的内存区域不同:前者存放在常量区,不可修改,后则存放在栈中,可以修改

变量 a 存放的内容不同:前者存放的是一个地址,而后者存放的是字符串 "abcdef" ,因此使用 sizeof 它们的结果是不同的,分别是 4 和 10

char *arr[4] = {"hello", "world", "shannxi", "xian"};

char **arrr=arr;

char arr[4][6] = {"hello", "world", "shann", "xian"};

char (*arrr)[6]=arr;

static:

被 static 修饰符修饰的外部变量、函数,其作用域都仅限于当前源文件内部,如果一个变量或函数仅供同一个源文件中的其他函数调用,我们就应该将其声明为 static ,避免同名冲突.

第五章练习题:

问:当一个程序异常终止时,程序输出的最后几行常常会丢失,原因是什么?我们能够采用怎样的措施来解决这个问题?

答:一个异常终止的程序可能没有机会来清空输出其缓冲区,因此,该程序生成的输出可能位于内存中的某个位置,但却永远不会被写出了。解决方案就是在调试时强制不允许对输出进行缓冲。setbuf(stdout,(char*)0);这个语句必须在任何输出被写入stdout(包括任何对printf函数的调用)之前执行。该语句最恰当的位置就是作为main函数的第一个语句。

问:下面程序的作用是把它的输入复制到输出:

#include<stdio.h> int main() { register int c; while((c = getchar())!=EOF) putchar(c); return 0; } //把代码改为下面的代码,程序依然能够正确运行,但是慢了许多,这是为什么? #define EOF -1 int main() { register int c; while((c = getchar())!=EOF) putchar(); return 0; }

答:函数调用需要花费较长的程序执行时间,因此getchar常常被实现为宏。这个在stdio.h中定义,因此一个程序没有包含stdio.h头文件,在所有fgetchar宏出现的地方,都用getchar函数调用来替换getchar宏。这个程序之所以变慢,就是因为函数调用所导致的开销增多。同样的依据也适用于putchar。

宏定义:

不能忽视宏定义中的空格

宏并不是函数,最好将宏定义中的每个参数都用括号括起来

宏并不是语句,宏定义后不能加分号

宏并不是类型定义

第六章练习题:

问:请使用宏来实现max的一个版本,其中max的参数都是整数,要求在宏max的定义中这些整型参数只被求值一次。

答:

static int max_temp1,max_temp2; #define max(p,q) (max_temp1 = (p),max_temp2 = (q),\ max_temp1>max_temp2? max_temp1:max_temp2)

atol()函数一个可移植的版本:

long atol(char *s) { long ret = 0; int sign = 0; switch(*s) { case '-': sign = 1; //do not break here! case '+': s++; break; } while(*s>='0' && *s<='9') { int n = *s++ - '0'; if(sign) n = -n; ret = ret*10 + n; } return ret; }

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值