周立功教你学程序设计结构体:内存对齐和基本数据类型
间的前面,存放的都是char型数据,由于char型数据只占用一个字节,往往使得其紧接着的空间不能被其它长度更长的数据使用。
为了降低内存浪费的概率,应该在char型数据之后,存放长度最小的成员。即在定义结构体时,应按照长度递增的顺序依次定义各个成员。优化示例结构体的定义如下:
类似地,依次存放各个成员,详见表2.2。
表2.2 依次存放各个成员
所有成员实际存放位置为[0,19],中间的地址为5的内存空间被弃用。由于结构体占用的大小为20个字节,已经是4的整数倍,因此无需再做额外的处理。结构体只浪费了1个字节空间,使用率达到95%。显然,通过优化结构体成员的定义顺序,在同样满足内存对齐的要求下,可以大大地减少内存的浪费。
>>> 2.2.2 基本数据类型
1. 范围值校验
如果有min≤value≤max,则check()范围值校验函数需要3个int型参数value、min和max。如果value合法,则返回true,否则返回false,详见程序清单 2.10。
程序清单 2.10 rangeCheck()范围值校验函数的实现(1)
-
代码整洁之道
rangeCheck是一个非常具有描述性的名字,因为它较好地描述了函数要做的事,所以好名字的价值怎么评价都不过分。如果每个示例都让你感到深合己意,那就是整洁代码。函数越短小,功能越集中,就越容易取一个好名字。名字长一些并不可怕,长而具有描述性的名字,比短而令人费解的名字更好。选择具有描述性的名字能帮助程序员理清模块的设计思路,追索好名字往往会使代码重构得更好。
从代码整洁之道的角度来看,最理想的函数参数个数是0(零参数函数),其次是单参数函数,再次是双参数函数,因尽量避免三参数函数。如果需要三个以上的参数,需要有足够的理由,否则无论如何也不要这样做,因为参数带有太多的概念性。
从测试的角度来看,参数甚至更叫人感到为难,因为编写确保参数的各种组合运行正常的测试用例,且测试覆盖所有可能值的组合是令人生畏的事情。输出参数比输入参数还要难以理解,因为人们习惯性地认为,信息通过参数输入函数,通过返回值从函数中输出,输出参数往往让人苦思之后才会觉得恍然大悟。如果函数看起来需要两个、三个或三个以上的参数,说明其中的一些参数就应该封装为结构体类。比如:
由此可见,减少函数参数的最佳方法是一个函数只做一件事,"函数要么做什么事,要么回答什么事!"两者不可兼得。函数应该修改某个对象的状态,或返回该对象的有关信息,两样都干常常会出现混乱。
2. 类型与变量
由于有了结构体,因此可以将rangeCheck()的形参min和max转移到结构体中,不仅减少了一个形参,而且处理起来更方便。比如:
该声明描述了一个由两个int类型变量组成的结构体,不仅创建了实际数据的对象range,而且描述了该对象是由什么组成的,因为它勾勒出了结构体是如何存储数据的。显然,range是struct _Range类型的结构体变量,如果在该结构体定义前添加typedef:
此时,range就变成了该结构体的类型,即range等同于struct _Range。习惯的写法是将类型名的首字符大写,将变量名的首字符小写。有了Range类型,即可同时定义一个Range类型的变量range和一个指向Range *类型的指针变量pRange,当然也可以省略类型名_Range。比如:
注意,结构体有两层含义,一层含义是"结构体布局",结构体布局告诉编译器是如何表示数据的,但它并未让编译器为数据分配空间。下一步是创建一个结构体变量,即结构体的另一层含义,其定义如下:
编译器执行这行代码便创建了一个结构体变量range,编译器使用Range为该变量分配空间:一个int类型的变量min和一个int类型的变量max,这些存储空间都与一个名称range结合在一起。
3. 初始化
假设value值的有效范围为0~9,在这里可以使用名为newRangeCheck的宏方便地将结构体初始化。比如:
使用方法如下:
宏展开后如下:
其相当于:
从本质上来看,.min和.max的作用相当于Range结构体的下标。虽然Range是一个结构体,但range.min和range.max都是int类型的变量,因此可以象使用其它int类型变量那样使用它,比如,&(range.min)。
由此可见,如果初始化一个静态存储期的结构体,初始化列表中的值必须是常量表达式。如果是自动存储期,初始化列表中的值可以不是常量。
4. 接口与实现
(1)传递结构体成员
只要结构体成员是一个具有单个值的数据类型,比如,int、char、float
- 电源软启动的实用设计技巧(07-16)
- 周立功:动态分布内存——malloc()函数与calloc()函数(07-22)
- 周立功“程序设计与数据结构”:深度解剖动态分布内存的free()函数与realloc()函数(07-25)
- 周立功教你学程序设计技术:做好软件模块的分层设计,回调函数要这样写(07-30)
- 周立功教你学C语言编程:教你数组是如何保存指针的(07-31)
- 算法的泛化问题,这些坑你可能都经历过!|周立功教你学软件设计(08-01)