C的缺陷和陷阱读书笔记

词法陷阱

1、if语句的特殊用法

1、if(x>max) max=x;

2、if(x>max?x;max)  //条件表达式,是执行第二个,否执行第三个

3、if(x>max);  //条件成立后执行——空语句

4、if((f=open(arg v[i],0))>0)  //open函数执行,成功返回后面的0(或者正数),执行失败则 返回-1

//代码通过对open的执行结果赋值然后判断是否>0,来判断open函数执行是否成功

2、符号的贪心法

C语言的运算字符分为单字字符和多字字符,当有一串运算字符相连的时候——从第一个开始尽可能多的连接字符串,看连接起来的字符是否含有意义,直到多加一个就不会有意义的时候停止

1、a---b  //(a--)-b

2、a-----b  //(a--)-(--b)

3、八进制的表示

  • 书面表示——(123)8、123o
  • 计算机表示——0123、/123

所以在计算机编程中,切记不能为了将数值对齐而把十进制123写为0123

4、字符与字符串

单引号引起一个字符的实质 —— 代表了一个整数

双引号引起的字符串的实质 —— 代表了一个指向无名数组的起始字符串的指针,该数组初始化为双引号之间的内容加 \0

指针——即内存地址,指针变量即用来存放指针的变量,有自己的内存编号和内存空间

5、条件运算符

可以直接作为一句代码写在文件里面,而不是非要与if语句组合才可以

ch = (ch > 'A' && ch < 'Z') ? (ch + 32): ch;

语法陷阱

1、首地址相关

指针指向首地址,指针变量是存储目标首地址的变量

取地址取到的是首地址,解引用得到的是首地址

2、运算符的优先级

优先级顺序:初等运算符>单目运算符>算术运算符>关系运算符>逻辑运算符>条件运算符>赋值运算符>逗号运算符

初等运算符包括圆括号()、下标运算符[ ]、结构体成员运算符->

运算符的结合性:单目运算符、条件运算符、赋值运算符及其扩展运算符,结合方向都是从右到左,其余的运算符都是从左到右

C的缺陷和陷阱读书笔记_第1张图片

常见的优先级

  • && 优先于 ||
  • & 优先于 ^ 优先于 |
  • <、> 优先级于 == 和 !=
  • * 优先于 ++、--
  • +、-、*、/  优先于 <<
  • != 优先于 &

*p() 函数指针,要调用指针变量所指向的函数要使用 (*p)()

逗号表达式在只能返回一个结果或者只能有一个参数的时候:顺序计算,但只使用最后一个表达式的值;除此外都是顺序计算

3、switch语句

switch…case语句,case的值必须是整数或者 'a' 类的字符(单引号引起来的字符实质上是一个ASCII码对应的数字),在没有循环跳出语句的情况下,只要进入某一个入口,将不会对后面的入口条件进行判别,而会直接执行对应的语句

4、else语句的悬挂

else和if语句总是配对出现且是最短配对

语义陷阱

1、指针

  • 两指针变量的赋值,赋给目标的首地址,使用任何一个指针变量修改都会影响到目标
  • 给指针加一个整数就会跳到下一个存储单元,如果存储的是int,一次跳四个字节
  • 两指针指向的是同一个数组中的元素,指针相减的话得到两个指针的距离
  • 指针可以自增p++
  • 指针变量定义之后必须要确定它具体指向哪一个存储单元,否则不能修改指针内容
  • int *p=&a 在定义的时候取地址和解引用才能共存,其他时候不能共存,如赋值的时候不能出现两个,但必须出现一个
  • 引用一个数组时,指针使用比下标引用更快
  • 数组名作形参是指针常量,不能自增,+n,作形参的时候是指针变量,可以++,也就是说作为参数传递的时候, p[ ] 就是 *p,可以自增,+n

#二位数组&指针

行地址

二维数组的数组名代表二维数组的首元素地址,即首行地址

数组名+偏移量——偏移量对应行的地址

数组一维元素取地址——对应行的地址

行列地址

二维数组名解引用——行地址降维为行列地址

数组名加偏移量解引用——偏移后的行地址降维为行列地址

数组名加偏移量解引用再偏移——偏移后的行地址降维为行列地址后再偏移为最后的行列地址

二维数组名取元素——数组名为行地址,取行内元素即为行列地址

数组二维元素取地址——对应行列地址

2、数组

把数组作为参数传给一个函数的时候本质上传递的是该数组的首地址

只出现数组名时——本质是该数组的首地址

可加偏移量解引用取得偏移后的地址————n维数组可经过n次加偏移后的解引用

数组的初始化有以下几种方法

  • 直接初始化 (完全初始化)
    int arr[5] = {1, 2, 3, 4, 5};
    char arr[5]={"hello"};  或者 char arr[5]="hello";
  • 不完全初始化——后面的内容如果为0,可在标明数组长度的情况下,省略后面的0
    int arr[] = {1, 2, 3, 4, 5};
    ​
    int arr[10] = {1, 2};   //未初始化的部分为0
  • 遍历初始化
    int array[10];
    for(int i=0;i<10;i++)
        array[i]=0;
  • 集成初始化                              C99标准中规定数组长度可以为变量
    int array[i] = { [0]=0,[5]=10,[10]=23 };
    
    const int number=10;
    int array[number]={0};
    /*C99标准中规定数组长度可以为变量,即number*/
  • 动态初始化——此处使用的常变量可以做到一改全改,很方便(符号常量也有这个好处)
    int array[i] = { [0]=0,[5]=10,[10]=23 };
    
    const int number=10;
    int array[number]={0};
    /*C99标准中规定数组长度可以为变量,即number*/

3、sizeof和strlen

sizeof用于计算字符串的大小,包括后面的\0

strlen只计算有用的字符的个数,不包括\0,使用strlen计算的存储空间应为n+1

4、内存分配

malloc函数分配内存空间会沿着空闲链表找一个没用过的内存块

  • 函数的定义下的返回值为void*,所以使用的时候,应当强制转化其返回类型
  • 如果分配的内存小于使用的内存,就会顺序占用接下来的内存空间,该内存空间的值就会被清除
  • 不是用这块内存空间的时候要使用free函数释放该内存空间——因为此时的内存空间的占用为显式的内存分配,所以要使用函数显式的释放内存——不像局部变量属于是隐式的分配内存,故而释放内存的时候也是隐式的释放

你可能感兴趣的:(c语言,开发语言)