c专家编程

------------------------------pag13  ( 关于替代库函数
标准规定编译器只有在违反 语法规则约束条件的情况下才能产生错误信息。
所有C语言标准头文件中声明的标识符均保留,所以不能声明一个叫做malloc()的函数,因为在标准头文件里已经有一个函数以此为名。 但由于这个规定不是约束条件,因此可以违反他。(所以可以用自己声明的函数来替换标准头文件,如:串口通信putchar替换)

------------------------------pag19 ( 关于const修饰指针
每个实参都应该具有自己的类型,这样他的值就可以赋值给他所对应的形参类型的对象(该对象的类型不能含有限定符)。
要使上面赋值合法,必须满足下列条件之一:
两个操作数都是指向有限定符或无限定符的相容类型的指针,左边指针所指向的类型必须具有右边指针所指向类型的全部限定符。
char      *cp;
const    char    *ccp;
ccp = cp;
上例是可以的,因为char与char相容,且左操作数具有右操作数限定符(无),再加上自身限定符。
char      **cp;
const    char    **ccp;
ccp = cp;
上例是不行的,因为:
cp  -》 (char*)  -》char
ccp-》(const char*) -》 const char  ( 括号是一个整体的意思,没有分离出来的限定符!)
cp指向的是字符指针,ccp指向的是常字符指针,两者指向对象不同,(这 里和上例不一样,因为char和const char是有无限定符的问题,而(char *)和(const char*)已经分别是一个整体了(都是指针,无限定符),所以不存在有无限定符问题,是类型不一样!)所以不相容。虽然他们的最终指向char和char相容,但是相容无法传递。
总之为了防止误用,应尽量避免用含有const修饰两层深度以上的指针。

------------------------------pag23 ( 类型转换
数据类型一般朝着浮点精度更高,长度更长的方向转换,整形数如果转换为signed不会丢失信息,就转换为signed,否则转换为unsigned。

------------------------------pag 24 ( 有符号数和无符号数
有符号数和无符号数是无法进行直接相比的,因为有符号数会被提升为无符号数,可能产生错误结果。(sizeof返回的是无符号数)

------------------------------pag28 ( ‘\0’字符占空间
无论在什么时候,如果遇见了这样一条语句malloc(strlen(str));,几乎可以肯定他时错误的,而 malloc(strlen(str) + 1);才是正确的,因为其他字符串处理库函数几乎都包含一个额外空间,用于容纳字符串结尾的'\0'字符。strlen是个特殊情况,他不将'\0'计入。
一个'L'的NUL用于结束一个ASCLL字符串。
两个'L'的NULL用于表示什么也不指向(空指针)。

------------------------------pag31 ( const不是常量
const关键字并不真正表示常量。(在case和数组声明中可以体现出来,编译无法通过)

------------------------------pag39 ( 运算符优先级
算术运算符高于移位运算符:msb《《4+lsb和msb《《(4+lsb)等价!(我过去犯过,百思不得其解)
逗号运算符在所有运算符中优先级最低。

------------------------------pag41 ( 结合性
结合性只用于表达式中出现两个以上相同优先级的操作符的情况。所有优先级相同的操作符,他们的结合性也相同。

------------------------------pag59 ( c语言声明限制
非法:
  • 函数的返回值不能是一个函数,所以像foo()()这样是非法的。
  • 函数的返回值不能是一个数组,所以像foo()[]这样是非法的。
  • 数组里面不能有函数,所以像foo[]()这样是非法的。

合法:

  • 函数的返回值允许是一个函数指针,如:int(* fun())();
  • 函数的返回值允许是一个指向数组的指针,如:int(* foo())[];
  • 数组里面允许有函数指针,如:int(* foo[])();
  • 数字里面允许有其他数组,如:int foo[][];

pag60 (段位

struct pid_tag

{

unsigned int inactive : 1;

unsigned int              : 1;  (一个位的填充)

unsigned int refcount : 6;

unsigned int              : 0; (填充到下一字边界)

short pid_id;

struct pid_tag *link;

}

位段的类型必须是,int,unsigned int, signed int(或加上限定符)。


------------------------------pag61 (参数传递

参数传递首先尽可能地存放在寄存器中,不够了再存在堆栈中。

一个int型参数一般会被传递到寄存器中,而结构参数则很可能被传递到堆栈中。


------------------------------pag62 (联合

在联合中所有的成员都从偏移地址0开始存储。这样每个成员的位置重叠在一起:在某一时刻,只有一个成员真正存储于该地址。(联合大小为最大的那个元素大小)。

union bits32_tag

{

int whole;

struct {char c0, c1, c2, c3;} byte;

}  value;

这个联合允许程序员提取整个32位值(作为int),也可以提取单独的字节段如value.byte.c0。


------------------------------pag64 (c声明优先级

A    声明从他的名字开始读取,然后按照优先级顺序依次读取。

B    优先级从高到低依次是:

     B1    声明中被括号括起来的那部分

     B2    后缀操作符:

             括号()表示这个一个函数,而放括号[]表示这是一个数组

     B3    前缀操作符:星号*表示“指向。。。的指针”

C    如果const和volatile关键字的后面紧跟着类型说明符(如int,long等),他作用于类型说明符。在在其他情况下,const和volatile关键字作用域他左边紧邻的指针星号。

例:

char * const *(* next)();

A    首先,看变量名“next”,并注意到他直接被括号所括住

B1  所以先把括号里的东西作为一个整体,得出“next是一个指向...的指针”

B   然后考虑括号外面的东西,在星号前缀和括号后缀之间作出选择

B2   B2规则告诉我们优先级较高的是右边的函数括号,所以得出“next是一个函数指针,指向一个返回...的函数”

B3   然后,处理前缀“*”,得出指针所指的内容

C     最后,把“char *const”解释为指向字符的常量指针

结果:

next是一个指针,他指向一个函数,该函数返回另一个指针,该指针指向一个类型为char的常量指针。


------------------------------pag68 (typedef和#define

1、可以用其他类型说明符对宏类型名进行扩展,但对typedef所定义的类型名却不能这样做

#define peach int

unsigned peach i; //合法

typedef int banana;

unsigned banana i;//非法

2、在连续几个变量的声明中,用typedef定义的类型能够保证声明中所有变量为同一类型,#define只有第一个

------------------------------pag71(typedef应该用在:

1、数组、结构、指针以及函数的组合类型

2、可移植类型。如 typedef unsigned int int16_t;移植时只要在typedef中修改就可以

------------------------------pag83(声明数组时的长度

由于并未在声明中为数组分配内存,所以并不需要提供关于数组长度的信息。对于多维数组,需要提供最左边一维之外其他维的长度---这就给编译器足够的信息产生相应的代码。

------------------------------pag85(数组指针差异

extern char *p;

p[3];

编译器:

1、取得符号表(.symtab)(存放定义和引用的函数和全局变量信息)中指针p的地址,提取存储于此处的值(地址)

2、把下标表示的偏移量与地址值相加,产生另一个地址

3、访问上面这个地址,取得字符

之所以会如此,是因为我们告诉编译器我们拥有的是一个指针。

但是只有当p原来定义为指针时这个方法才是正确的。

文件a.c

char p[10];  (p中是字符)

文件b.c

extern char *p; (p中是地址)

p[i];

当用p[i]这种形式提取这个声明的内容时,实际上得到的是一个字符。但编译器按照上面的3个步骤,却把他当成是一个指针!

加上点我自己的理解:

第二个例子和常见的函数中实参传递给形参的拷贝值并不一样,上面这样相当于把*p和p[10]等同了,类似给文件a.c中的数组p加上了这样一个操作:(char *)p[0~9]。像上面第二中情况,因为编译器在编译阶段已经确认了p的类型,于是后面无法改变,链接时就出现了相应的问题。如果在像char *p = "abcd";p[3];这样出现在同一文件中,编译器编译时就知道p是一个指针,并按照指针的方式处理,不会把p当成数组名那样看成指针常量(就是地址值)直接加上3个元素的大小,然后取地址中的值,而是提取指针变量p中的值作为地址做相应操作。定义为指针时要比定义为数组时多一层提取!

所以跨文件时不要这样做,数组就声明成数组,指针就声明成指针!

你可能感兴趣的:(c专家编程)