在C语言程序开发中,有经验的程序员有时会定义只有一个数组成员的结构体,虽然语法简单,但是却常常让初学者感到迷惑:这么做有什么好处吗?
structABC{
unsignedlongarray[MAX];
}abc;
这么做有什么好处吗?答案是肯定的,这样做主要有两个好处:一是便于值传递,二是便于后期扩展。
方便的数组值传递
看过我之前文章的读者应该明白,调用C语言函数时,如果将数组作为参数传递给函数,那么在被调用函数内部,数组常常会退化成指针。下面是一段C语言代码示例:
#includestdio.h
voidfun(chararr[16]){
printf(sizeofarris%d\n,(int)sizeof(arr));
}
intmain(){
chara[16]={0};
fun(a);
return0;
}
C语言代码示例编译并执行上述C语言代码,得到如下输出:
#gcct.c
#./a.out
sizeofarris8
可见,在函数fun()内部,sizeof(arr)并不等于数组长度16,而是等于8(指针长度,我的机器是64位的,指针占用内存空间为8个字节),这说明即使函数fun()的参数C语言代码明确指定为fun(chararr[16]),在函数内部,arr还是退化成指针了。
而如果将数组封装在结构体内部,将结构体作为参数,那么在函数内部,我们依然可以获得完整的数组,请看下面的C语言代码示例:
#includestdio.h
typedefstruct{
chararr[16];
}String;
voidfun(String*str){
printf(sizeofarris%d\n,(int)sizeof(str-arr));
}
intmain(){
Strings;
fun(s);
return0;
}
将结构体作为参数编译并执行上述C语言代码,得到如下输出:
#gcct.c
#./a.out
sizeofarris16
一切与预期一致。上面的C语言代码有意将结构体typedef为String,因为它的行为很像用于描述字符串的“新类型”。读者应该明白,C语言中的数组是不支持直接赋值的:
chara[16],b[16];
...
a=b;//非法
但是借助于C语言的结构体语法,String类型是可以直接赋值的,这使得编写代码方便不少:
Stringa,b={1,2,3,4,5};
a=b;//合法
printf(%d%d%d%d%d\n,
a.arr[0],a.arr[1],
a.arr[2],a.arr[3],a.arr[4]);
//输出
方便后续扩展方便后续扩展
C语言中的结构体可用于将一些基本类型的数据封装成一个具有内在联系的数据结构,而且结构体并不限制自身成员的数目和占用内存空间的大小,这样的特性使得在C语言项目后续开发中添加数据方便不少。
例如,可能刚开始fun()方法需要完成的需求比较简单,可能它只需要接收一个数组就可以:
voidfun(chararr[]);
chara[16];
...
fun(a);
随着项目的继续推进,我们发现fun()函数不仅需要数组arr的地址,还需要知道arr中究竟有多少有用的数据,因此需要对fun()函数做如下修改:
voidfun(chararr[],intsize);
chara[16];
...
fun(a,size);
显然,如果一开始fun()函数接收到的参数是裸数组,那么在后续开发中,要扩展参数就必须修改fun()函数原型,相应的调用处也需要修改,如果fun()函数的调用处特别多,那么扩展工作将会无比痛苦,并且很容易出错。
另外一个问题是,voidfun(chararr[],intsize);中的arr和size是对应关系,但是却没有语法规则约束这种对应关系,因此在复杂的项目开发中,将数组b的size误认为是数组a的size使用的情况是极有可能存在的,这样的错误发现和排查都相当困难。
如果,我们一开始就将数组封装在结构体里,在创建fun()函数时,其实并未增加多少工作量:
typedefstruct{
chararr[16];
}String;
voidfun(String*a);
Stringa;
...
fun(a);
现在需要对fun()方法扩展,增加size参数:
typedefstruct{
chararr[16];
intsize;
}String;
voidfun(String*a);
Stringa;
...
fun(a);
显然,唯一要做的就是为结构体String新增了一个size成员以及相应的方法实现,fun()函数原型不动,因此所有调用fun()函数的代码也就不需要修改了,这样的扩展无疑是方便的。另外,size和arr的对应关系由C语言结构体语法约束,这也在最大程度上避免了程序员错用size。
小结
C语言语法本身是非常简单的,代码本身是易懂的,但是简单的组合背后常常隐藏着程序员匠心的一面,这些组合常常能为项目开发带来意想不到的好处。
点个