标准规定编译器只有在违反 语法规则和 约束条件的情况下才能产生错误信息。
所有C语言标准头文件中声明的标识符均保留,所以不能声明一个叫做malloc()的函数,因为在标准头文件里已经有一个函数以此为名。 但由于这个规定不是约束条件,因此可以违反他。(所以可以用自己声明的函数来替换标准头文件,如:串口通信putchar替换)
------------------------------pag19 ( 关于const修饰指针)
每个实参都应该具有自己的类型,这样他的值就可以赋值给他所对应的形参类型的对象(该对象的类型不能含有限定符)。
要使上面赋值合法,必须满足下列条件之一:
两个操作数都是指向有限定符或无限定符的相容类型的指针,左边指针所指向的类型必须具有右边指针所指向类型的全部限定符。
char
const
ccp = cp;
上例是可以的,因为char与char相容,且左操作数具有右操作数限定符(无),再加上自身限定符。
char
const
ccp = cp;
上例是不行的,因为:
cp
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
unsigned int refcount : 6;
unsigned int
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;
}
这个联合允许程序员提取整个32位值(作为int),也可以提取单独的字节段如value.byte.c0。
------------------------------pag64 (c声明优先级)
A
B
C
例:
char * const *(* next)();
A
B1
B
B2
B3
C
结果:
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];
文件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中的值作为地址做相应操作。定义为指针时要比定义为数组时多一层提取!
所以跨文件时不要这样做,数组就声明成数组,指针就声明成指针!