#127 《C专家编程》读书笔记

《C专家编程》是一本94年出版的讲语言的书,如今二十一世纪都已经过去十年多了,书上很多东西是不是依旧有用将是本文将要解答的问题。

尽量不要使用无符号数

当无符号数与有符号数再一起使用时,有符号数会被强转为无符号数。

1
2
3
4
5
6
7
8
9
10
11
12
13
int value_int = -1;
unsigned int value_uint = 5;
int result = (value_int < value_uint) ? 1 : 0;
printf("%d\n",result);// 结果为0
```

解释:C在计算含有不同类型表达式时,会将类型向上提升,上面代码中的int被提升为了unsigned int,使-1的补码被解析为很大的整数。

### 相邻的自字符串会被合并

``` c
char *str[] = {"hehe" "lala","woca"};
printf("%s",*str);// hehelala

优先级的问题

. 比 * 高:

1
2
*value.func -->
*(value.func)

[] 比 * 高:

1
2
int *array[] -->
int *(array[]) // array[]的元素为int*

函数() 比 * 高:

1
2
int *pfunc() -->
int* pfunc()// 返回值是int*

==和!= 比 位操作符 高:

1
2
value&mask!=0 -->
value & (mask != 0)

==和!= 比 赋值 高:

1
2
c = getchar() != EOF -->
c = (getchar() != EOF)

算数运算 比 位移运算 高:

1
2
value << 4 + constant -->
value << (4 + constant)

优先级最低:

1
2
i = 1,2 -->
(i = 1), 2

拆分声明、定义、typedef

  1. 找标识符
  2. 找被括号括起来的部分
  3. 找后缀操作符,如果是(),则表示是函数;如果是[],则表示是数组
  4. 找前缀操作符,如果是*,则表示指向某某的指针
  5. 找const和volatile,如果const,volatile紧跟类型,那么它作用于类型,其他情况下,作用于左边紧邻的项。
1
2
void (*signal(int value, void (*func)(int)))(int);
//signal是一个函数,接受一个int,一个函数指针,并返回一个函数指针。

左值和右值

左值通常表示存储结果的地址,在编译时可知右值表示地址的内容,值仅当运行时才被确定。

指针与数组名不等同的情况(定义为数组,却声明为指针,反之同)

假设定义:int array[10], *ptr; (1)使用数组名下表访问(array[1]),会直接将数组名的地址加上偏移值作为变量的地址。 (2)使用指针下表访问(ptr[1]),会先取指针指向的内容,然后将这个内容加上偏移值作为变量的地址。 原因:当定义为数组时,却声明为指针时,相当于用指针下表访问的方法来解析一个数组名下标,即先取数组第0号元素的内容,然后将这个东西加上偏移值作为变量的地址,从而访问了不该访问的东西。反之同。

数组与指针等同的情况

(1)编译器将表达式中的数组名当作指向该数组第0号元素的指针,下标当作指针的偏移量,array[i]被作为*(array+i); (2)编译器将函数参数声明中的数组名当作指向该数组第0号元素的指针,即在函数内部得到的是指针,而不是数组名。 根据(1),可知道下面这条为谣言: 用指针迭代数组比用下标迭代数组更快 根据(2),可以解释为什么在传递数组后,不能使用下方法计算数组长度:

1
2
3
4
int arr_lenth(int arr[])
{
return sizeof(arr)/sizeof(arr[0]);// 返回值必为1,因为arr为指针,不是数组名
}

值得注意的是(2)中将数组改写为指针不是递归定义: 实参char hello[10][10]被改写为char (hello)[10],这里将数组的数组改写为数组的指针 实参char hello[10]被改写为char **hello,这里将指针数组改写为指针的指针 实参char (*hello)[10]不被改变,因为这时hello是指针非数组

interposition

interposition指的是用户定义的函数取代库中声明完全相同的函数,注意这不是指重载,而是指:

1
2
char* strchr(const char *s, int c);//user defined
char* strchr(const char *s, int c);//library

出现interposition时,需要注意:

1
2
3
4
5
6
7
char* strchr(const char *s, int c);//user defined
int main()
{
strchr(...);//调用的是user defined的函数
strstr(...);//这个函数会调用库函数strchr,此时由于interposition,变成调用用户定义的strchr
return 0;
}

注意:出现interposition时,vs2012会警告:inconsistent dll linkage

堆栈段作用

(1)存储局部变量 (2)函数调用时,存储有关的维护信息 (3)用作暂时存储区: 计算一个很长的表达式时,会把部分结果先压到堆栈中。

如果我的文章对你起到了帮助,你可以选择金额不限的捐助,帮助我写出更多的文章。