C专家编程学习笔记(2)

第4章:令人震惊的事实:数组和指针并不相同
1.区别定义和声明
声明相当于普通的声明:它所说明的并非自身,而是描述其他地方的创建的对象.
定义相当于特殊的声明:它为对象分配内存

2.当书写了extern char *p,然后用p[3]来引用其中的元素时,编译器将会:
(1) 取得符号表中p的地址,提取存储与此处的指针
(2) 把下标所表示的偏移量与指针相加,产生一个地址
(3) 访问上面的地址,取得字符
既然把p声明为指针,那么不管p原先是定义为指针还是数组,都会按照上面所示的三个步骤进行操作,但是只有当p原来定义为指针时这个方法才是正确的.
p在这里声明为extern char *p;而它原先定义的却是char p[10];这种情形.当用p[i]这种形式提取这个声明的内容时,实际上得到的是一个字符.但按照上面的方法,编译器却把他当成一个指针,把ACSII字符解释为地址显然是牛头不对马嘴.

3.数组和指针的区别

指针

数组

保存数据的地址

保存数据

间接访问数据,首先取得指针的内容,把它作为地址,然后从这个地址提取数据.

如果指针有一个下标[I],就把指针的内容加上I作为地址,从中提取数据

直接访问数据,a[I]只是简单的以a+I为地址取得数据

通常用于动态数据结构

通常用于存储固定数目且数据类型相同的元素

相关的函数为malloc(),free()

隐式分配和删除

通常指向匿名数据

自身即为数据名


4.定义指针时,编译器并不为指针所指向的对象分配空间,它只分配指针本身的空间,除非在定义时同时赋给指针一个字符串常量进行初始化.例如,下面的定义创建了一个字符串常量(为其分配了内存):
char *p = "breadfruit";
注意只有对字符串常量才是如此.不能期望为浮点数之类的常量分配空间,如:
float *pip = 3.141; /* 错误!无法通过编译. */
在ANSI C中,初始化指针时所创建的字符串常量被定义为只读.如果试图通过指针修改这个字符串的值,程序就会出现未定义的行为.在有些编译器中,字符串常量被存放在只允许读取的文本段中,以防止它被修改.
数组也可以用字符串常量进行初始化:
char a[] = "gooseberry";
与指针相反,由字符串常量初始化的数组是可以修改的.其中的单个字符在以后可以改变.比如下面的语句:
strncpy(a,"black",5);

第5章:函数库选项应置于何处
这章和C语言本身关系相对小一些,还是罗列一些有用的信息
1.函数库选项应置于何处
始终将-l函数库选项放在编译命令行的最右边.

2.Interpositioning
Interpositioning(有些人称它为"interposing")就是通过编写与库函数同名的函数来取代该库函数的行为.

第6章:运动的诗章:运行时数据结构
1.Unix中段的概念
.text:文本段,可执行文件的指令
.data:数据段,初始化后的全局和静态变量
.bss:BSS段,未初始化的全局变量
数据段保存在目标文件中.
BSS段不保存在目标文件中(除了记录BSS段在运行时所需要的大小).
文本段是最容易受优化措施影响的段.
a.out文件的大小受调试状态下编译的影响,但段不受影响.

2.堆栈(这里指的是栈,stack)
(1)堆栈为函数内部声明的局部变量提供存储空间.按照C语言的术语,这些变量被称为"自动变量".
(2)进行函数调用时,堆栈存储与此相关的一些维护性信息.这些信息被称为堆栈结构(stack frame),另外一个更常用的名字是过程活动记录(precedure activation record).包括函数调用地址(即当所调用的函数结束后跳回的地方),然和不适合装入寄存器的参数以及一些寄存器值的保存.
(3)堆栈也可以被用作暂时存储区.

3.setjump和longjump
setjump(jump_buf j)必须首先被调用.它表示"使用变量j记录现在的位置.函数返回0"
longjump(jmp_buf j,int i)可以接着被调用.它表示"回到j所记录的位置,让它看上去是从原来的setjump()函数返回一样.但是函数返回i,使代码能够知道它是实际上通过longjump()返回的"
当使用于longjump()时,j的内容被销毁.

第7章:对内存的思考
1.Unix和Intel 80x86段的概念
在Unix中,段就是一块以二进制形式出现的相关内容
在Intel 80x86内存模型中,段是内存模型设计的结果,在80x86的内存模型中,各处理器的地址空间并不一致(因为要保持兼容性),但它们都被分割成以64KB为单位的区域,每个这样的区域便称为段.

2.内存泄露
释放或改写仍在使用的内存(称为"内存损坏")
未释放不再使用的内存(称为"内存泄露")

3.通常导致段错误的几个直接原因:
(1)解除引用一个包含非法值的指针.
(2)解除引用一个空指针(常常由于从系统程序中返回空指针,并未检查就使用).
(3)在未得到正确的权限进行访问.例如,试图往一个只读的文本段存储值就会引起段错误.
(4)用完了堆栈或堆空间(虚拟内存虽然大但绝非无限).

4.如何在链表中释放元素
在遍历链表时正确的释放元素的方法是使用临时变量存储下一个元素的地址.这样就可以安全的在任何时候释放当前元素,不必担心在取下一个元素的地址时还引用它.代码如下:
struct node *p, *start, *tmp;
for(p = start; p; p = tmp)
{
    tmp = p -> next;
    free(p);
}

第8章:为什么程序员无法分清万圣节和圣诞节
1.类型转换
printf(" %d ", sizeof 'A');
打印出来的结果是4(或int长度),而不是1. 这时因为在表达式中,每个char都被转换为int
具体地说,char和short转换为int,而float转换为double.

2.散列表
散列(Hash)表是一种提高访问表中数据元素的方法.它不是线性的搜索表中的元素,而是一下子跳到最相像的元素来存储值.

3.复杂的强制类型转换
(1).一个对象的声明,它的类型就是想要转换的结果类型.
(2).删去标识符(以及任何如extern之类的存储限定符),并把剩余的内容放到一对括号里.
(3).把第二步产生的内容放在需要进行类型转换的对象的左边.
如:
void qsort(void base, size_t nel, size_t width, int(*compar)(const void*, const void *));
要出入的函数指针是:
int incompare(const int *i, const int  *j)

{
    return(*i - *j);
}
参数不匹配,因促需要强制转换.
qsort(a, 10, sizeof(int), (int (*)(const void*, const void*)) intcompare);

你可能感兴趣的:(数据结构,编程,c,存储,float,编译器)