最近在看一本叫做 代码阅读的书籍, 阅读别人的代码也是一种提高自身能力的好的途径。参见文章
我为何爱读代码?你为何也应当爱? 以及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(line) 大小为5. line指向的内存地址不能改变,但可以自由改变它所包含的元素。
总结为:
char ch[]="abc";
表示ch 是一个足以存放字符串初值和空字符'/0'的一维数组,可以更改数组中的字符,但是char本身是不可改变的常量。
char *pch = "abc";
那么pch 是一个指针,其初值指向一个字符串常量,之后它可以指向其他位置,但如果试图修改字符串的内容,结果将不确定。
______ ______ ______
ch: |abc\0 | pch: | ◎-----> |abc\0 |
______ ______ ______
4,c中利用struct 实现面向对象编程。有时候通过结构体将相关数据和函数指针封装在一起,来模拟类的字段和方法,从而创建
类似对象的实体。如下:
struct domain { int dom_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 *)); struct protosw *dom_protosw, *dom_protoswNPROTOSW; struct domain *dom_next; int (*dom_rtattach) /* initialize routing table */ __P((void **, int)); int dom_rtoffset; /* an arg to rtattach, in bits */ int dom_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_t rm_xid; enum msg_type rm_direction; union { struct call_body RM_cmb; struct reply_body RM_rmb; } ru;6,什么时候应该使用volatile修饰符?
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; }
7,一个变量可以同时被说明为const和volatile吗?
可以。const修饰符的含义是变量的值不能被使用了const修饰符的那段代码修改,但这并不意味着它不能被这段代码以外的其它手段修改。例如,6 的例子中,通过一个volatile const指针t来存取timer结构。函数time_addition()本身并不修改t->value的值,因此t->value被说明为const。不过,计算机的硬件会修改这个值,因此t->value又被说明为volatile。如果同时用const和volatile来说明一个变量,那么这两个修饰符随便哪个在先都行,