<<代码阅读>> 读书笔记

来源:互联网 发布:oracle数据库性能优化 编辑:程序博客网 时间:2024/04/30 07:59

最近在看一本叫做 代码阅读的书籍, 阅读别人的代码也是一种提高自身能力的好的途径。参见文章

我为何爱读代码?你为何也应当爱?  以及Read Great Programs

 第三章:高级C数据类型

 1,在 c/c++ 中 指针类型(其实就是一个内存地址)的参数常用来避免函数调用过程中大型数据元素的复制开销。常常以const

       声明符来标示 为了高效而采用引用方式传递参数。

2, 在c/c++ 中 ,当数组作为参数传入函数或作为结果从中返回时都会隐式使用到指针

3,  注意字符指针 字符数组的区别:字符指针是一个指针变量,他是要占用空间的,这个空间的大小取决于机器是多少位的,

        字符数组虽然在使用上类似上像是在使用字符指针,然而实际上,本质上他只不过是编译器在内存中做的标记,字符数组

        本身不占用任何空间。

         

struct test{    int a;    char* buff;    char buf[];}; 


上面的struct在32位机器上的大小是8个字节。int 、char * 分别占用4个字节。char buf[]; 这个东西一个空间也不占用,他就像是一个隐形的字符指针。

所以 指针是单独变量,有自己的地址空间,其值是字符串常量的地址,而数组名就代表这个数组,没有单独地址空间;

另外例子:

char *s = "haha";char line[] = "haha";

在我的机器上变量 sizeof(s) 大小为4字节,它能指向其它的字符串,但是不能改变所指向字符串的内容,因为这样会产生一些无法预知的行为。

而 sizeof(line) 大小为5. line指向的内存地址不能改变,但可以自由改变它所包含的元素。

总结为:
char ch[]="abc";表示ch 是一个足以存放字符串初值和空字符'/0'的一维数组,可以更改数组中的字符,但是char本身是不可改变的常量。char *pch = "abc";那么pch 是一个指针,其初值指向一个字符串常量,之后它可以指向其他位置,但如果试图修改字符串的内容,结果将不确定。     ______           ______      ______ch: |abc\0 |    pch: | ◎----->  |abc\0 |     ______           ______      ______


4,c中利用struct 实现面向对象编程。有时候通过结构体将相关数据和函数指针封装在一起,来模拟类的字段和方法,从而创建

    类似对象的实体。如下:

 

structdomain {intdom_family;/* AF_xxx */char*dom_name;void(*dom_init)/* initialize domain data structures */__P((void));int(*dom_externalize)/* externalize access rights */__P((struct mbuf *));void(*dom_dispose)/* dispose of internalized rights */__P((struct mbuf *));structprotosw *dom_protosw, *dom_protoswNPROTOSW;structdomain *dom_next;int(*dom_rtattach)/* initialize routing table */__P((void **, int));intdom_rtoffset;/* an arg to rtattach, in bits */intdom_maxrtkey;/* for routing layer */};

按照这样的声明,该结构体类型的变量或指针在完成初始化以后,就可以像C++对象那样使用了。

 

for (dom = domains; dom; dom = dom->dom_next)if (dom->dom_family == i && dom->dom_rtattach) {dom->dom_rtattach((void **)&nep->ne_rtable[i],dom->dom_rtoffset);break;}

对象可以使用不同的方法(函数指针)初始化,并且利用相同的接口调用(通过使用同一结构体成员名),这其实就是面向对象语言中实现虚函数和多态的

做法。事实上,因为同一类型的对象存储了相同的方法(指向相同的函数地址)。

5, 联合体(union, 又叫共用体)中的所有元素占用同一存储空间,因此在任一时刻只能存储或访问其中的一个元素。C中联合体实现下面的功能:

    有效利用内存空间,实现多态,使用内部表征方式访问数据。

  到目前为止,联合体最常见的用法是实现多态, 多态在这里是指用同一个对象(通常用c结构体表示)表示多种类型。将不同数据类型的存储在联合体

  不同的成员变量中,可以节省一些内存。但节省内存不是主要的目的,真正的目的是存储不同的类型的数据。实现多态用途的联合体通常包含在结构体

  中,同时该结构体还拥有一个枚举类型的字段,用来指明联合体中存储的数据类型,该字段的名称也非常的简单明了,例如type。 下面举例 Sun RPC库

  中的一段代码,其中包含了RPC 消息的结构体。 这个结构体可以表示两种类型的消息:调用型消息和应答型消息。 枚举类型的变量msg_type 用来指明

  消息的具体类型,而联合体中则包含了每种类型数据元素的结构体。

enum msg_type {CALL=0,REPLY=1};[...]/* * The rpc message */struct rpc_msg {u_int32_trm_xid;enum msg_typerm_direction;union {struct call_body RM_cmb;struct reply_body RM_rmb;} ru;
6,什么时候应该使用volatile修饰符?

 volatile 修饰符告诉编译程序不要对该变量所参与的操作进行某些优化。在两种特殊的情况下需要使用volatile修饰符:第一种情况涉及到内存映射硬件 (memory-mapped hardware,如图形适配器,这类设备对计算机来说就好象是内存的一部分一样),第二种情况涉及到共享内存(shared memory,即被两个以上同时运行的程序所使用的内存)。
    大多数计算机拥有一系列寄存器,其存取速度比计算机主存更快。好的编译程序能进行一种被称为“冗余装入和存储的删去”(redundant load and store removal)的优化,即编译程序会·在程序中寻找并删去这样两类代码:一类是可以删去的从内存装入数据的指令,因为相应的数据已经被存放在寄存器中;另一种是可以删去的将数据存入内存的指令,因为相应的数据在再次被改变之前可以一直保留在寄存器中。
    如果一个指针变量指向普通内存以外的位置,如指向一个外围设备的内存映射端口,那么冗余装入和存储的优化对它来说可能是有害的。例如,为了调整某个操作的时间,可能会用到下述函数:   
time_t time_addition(volatile const struct timer * t, int a),{         int    n         int    x         time_t  then         x=0;         then= t->value         for (n=0; n<1000; n++)         {               x=x+a ;         }      return t->value - then;}


    在上述函数中,变量t->value实际上是一个硬件计数器,其值随时间增加。该函数执行1000次把a值加到x上的操作,然后返回t->value在这1000次加法的执行期间所增加的值。
    如果不使用volatile修饰符,一个聪明的编译程序可能就会认为t->value在该函数执行期间不会改变,因为该函数内没有明确地改变t- >value的语句。这样,编译程序就会认为没有必要再次从内存中读入t->value并将其减去then,因为答案永远是0。因此,编译程序可能会对该函数进行“优化”,结果使得该函数的返回值永远是0。
    如果一个指针变量指向共享内存中的数据,那么冗余装入和存储的优化对它来说可能也是有害的,共享内存通常用来实现两个程序之间的互相通讯,即让一个程序把数据存到共享的那块内存中,而让另一个程序从这块内存中读数据。如果从共享内存装入数据或把数据存入共享内存的代码被编译程序优化掉了,程序之间的通讯就会受到影响。    


7,一个变量可以同时被说明为const和volatile吗?

可以。const修饰符的含义是变量的值不能被使用了const修饰符的那段代码修改,但这并不意味着它不能被这段代码以外的其它手段修改。例如,6 的例子中,通过一个volatile const指针t来存取timer结构。函数time_addition()本身并不修改t->value的值,因此t->value被说明为const。不过,计算机的硬件会修改这个值,因此t->value又被说明为volatile。如果同时用const和volatile来说明一个变量,那么这两个修饰符随便哪个在先都行,