阅读完《C专家编程》,总结出如下
1、参数传递限定符
两个操作数都是指向【有限定符】或者【无限定符】的相容类型的指针,左边指针所指向的类型必须具有右边指针所指向类型的全部限定符。
例如:
char *cp;
const char *ccp;
ccp = cp; //ok,ccp有const限定符,cp无限定符,所以是满足上述条例
cp=ccp;//error
2、const限定符含义
const并不表示常量,而是表示所修饰的符号不能被赋值。对于这个符号来说是只读的,但是通过其他方法可以改变这个值。
3、寻常算数转换
在“双目运算符”运算时,若左右两边数据类型不一致,则会发生“寻常算数转换”
数据精度:低->高
占用字节:小->大
整形数据:signed->unsigned
例如:
int d = -1;
if( d<= sizeof(int) -2)
printf("ok!\n");
if语句是不会为true的;sizeof运算后,为unsigned类型,当与d比较时,d会被转换为unsigned类型,造成if为false。
4、声明优先级
A 声明从最左边的名字开始读取,然后按照优先级顺序依次读取。
B 优先级高低顺序为:
B.1 声明中被()括起来的部分
B.2 后缀操作符:“()”:表示一个函数;“[ ]”:表示一个数组
B.3 前缀操作符:“*”:表示指向...的指针
C const和volatile修饰其左边关键字,如果左边不存在,则修饰其右边关键字。
例如1:
char * const * (*next)();
next为一函数指针,该函数形参为空,返回一个指向char的常量指针。
例如2:
5、typedef与define的区别char *(* c[10])(int **p);
c为一个含有10个元素的数组,该数组成员为一个函数指针,参数为int **m,返回char *。
常量指针:指向的数据为常量。
指针常量:指针为常量。
6、声明与定义
声明:可以出现多次,不分配存储空间。
定义:只能出现一次,分配存储空间。
7、数组与指针的区别
a. 首先理解左值与右值
左值:出现在赋值符左边的符号。编译时确定地址。
右值:出现在赋值符右边的符号。运行时才知其值。
编译器为每一个变量分配一个地址(左值),这个地址在编译时可知,且在系统运行时一直保存该值,存储在变量中的值(右值),只有在运行时可知,取值操作。
X = Y;
对于编译器而言,符号X代表X的地址,符号Y为其地址内容。mov [X],Y 把Y的值存储到X地址上去。
b. 数组为不可修改的左值。
c. char *p = “abcdefg” 与 char a[] = "abcdefg"
假设P的地址为0x400,a的内容0x0xff800。
访问p[3]:
步骤一:取得地址0x400的内容0xff400。
步骤二:取得0xff400+3的值。
步骤三:取地址[0xff400+3]的内容。
a[3]:
步骤一:取得0xff800+3的值。
步骤二:取得[0xff800+3]的内容。
从上述可以看出,指针比数组多一次取地址操作。
8、运行时数据结构
9、悬垂指针ELF:Executable and Linking Format,可执行和链接格式。ELF由段构成,段由section组成。
BSS段:Block Started by Symbol,bss段只保存没有值的变量,bss段的大小记录在目标文件中,不占据目标文件的任何空间。未初始化的全局和静态变量。
DATA段:经过初始化的全局和静态变量。
TEXT段:程序指令。
堆:动态分配内存。
栈:系统调用,局部变量存储区。
指针所指向的地址空间失去的有效性。
例如:
char * func()
{
char p[ ] = "apple";
return p;
进入函数func时,自动变量p在堆栈中分配,在函数结束后,变量p的空间被回收,在任何时候可能被覆盖。}
10、页错误与段错误、总线错误
11、内存泄露页错误:page error,访问分配给堆栈之外的空间时。
段错误:segmentation fault,访问不属于“你”的地址空间的地址,例如野指针。
总线错误:bus error,未对齐的读或写。
12、未释放不再使用的内存。
避免内存泄露:分配与释放要对应。
检测内存泄露:查看剩余内存空间,在一段时间内是否一直在减少。