C结构体——技巧
难度 3/5 技巧3/5 -适合实际工程使用
C结构体技巧有以下那么几种(需要补充的联系我)
1、结构体成员偏移计算(我目前,没怎么用)
2、结构体数组指针(初学经常会想不通)
3、结构体的文件写入与读取(常用技巧)
4、可变长结构体(高级技巧)
一、结构体成员偏移计算
无意中看到一个问题,怎么快速计算结构体成员的偏移量?
那时候我想了一想,声明一个结构体,然后取地址相减不就可以得到结果了么?
当然,我们是可以这样子做的。但是这样是不是最好的做法呢?显然还有一种写法可以更加简便就可以获取偏移量了。
//声明
Typedef struct name
{
Char a;
Int b;
Char c;
}
Sstruct;
//运算
int offset = (int)&(((Sstruct*)0)->b);
首先我们(Sstruct*)(0) 使用0号地址为基地址,->b获得b这个变量,这时候((Sstruct*)0)->b获得的是成员 int b。然后我们取成员int b的地址: &()获得b的指针,这个指针式相对于0基地址的。所以把这个指针再次转化为整型(int) 便可以得到偏移量。
这时候我们应该要想一下,这个offset的值在32位系统里面应该是多少?64位呢?这个数值又和什么相关?请编程思考,答案在下面。
32位系统 offset = 4;
二、结构体数组指针
有时候我们想结构体里面有一些数组指针来代表可变长的变量
如
Struct data{
Char * data_ptr
Char data_arr[];
}
我们通常会用到这两种方式去表达一些可变长数据
即使这两个指针都可以当作数组用,但是区别还是很大的。
我们要计算结构体大小来赋值对应的内存,auto size = sizeof(data); 这样指针方式和数组方式得到的值就很可能不同,指针永远得到的是4,数组得到的是数组长度(某些情况内存对齐,值就更加大了)。这样的差异,很难让我们检查出来。类似于这种的mencpy(....),看上去是没有问题的。但是如果是指针形式,你就只是copy了一个指针,指针说指向的内存却没有被复制。如果是数组形式,你得到的是一个完整的copy。这就是书上所说的:浅复制、深复制(浅copy,深copy)。
三、结构体的文件写入与读取
结构体用于文件读写,实在是太方便了,我们无需写什么特殊语句来获取某个值,而是直接用二进制文件读写,就可以识别他们所在的位置。例如,我想把一个的播放列表,保存在一个文件中:
Typedef Struct Slist_type
{
Char name[64];
Char is_exit;
Char type;
Char index;
Char pic_index;
}Slist;
Slist list;
这样的结构体,我们可以用字符形式存储,也可以用二进制的形式存储
字符形式:
写:
fprintf(file,“name %s\n”,list.name);
.........
fprintf(file,“pic_index %d\n”,list.pic_index);
读:
fprintf(file,“name %s\n”,list.name);
.........
fprintf(file,“pic_index %d\n”,&list.pic_index);
优点,打开文件,我们可以直观地看到内容。
二进制形式
写:fwrite(&list,,sizeof(list),1,file);
读:fread(&list,,sizeof(list),1,file);
优点,快速,代码量少
看完这两种形式,我们就很清楚可以知道,如何快速读写结构体。这时候你可以用一个语句,美丽优雅地完成一个结构体的读写。
四、可变长结构体
这个是比较有用的技巧。通常我们做工程的时候,有部分数据经常是可变的例如:
一个模拟车辆的数据包。
Struct Scar_info //一辆车的数据
{
Double speed;
Double widht;
Double height;
Doble weight;
Double lenght;
}
Struct S_All_Cars //所有车的数据
{
Int count;
}
两个结构体,分别是表示:单一、多个。
这样我们会做这样一个结构体S_All_Cars来表达多个,但是这样就出现一个问题,count并不确定啊。我们并不能写成类似这样的结构体:
Struct S_All_Cars //所有车的数据
{
Int count;
Scar_info cars[count];
}
这样是不能通过编译的。
有些人还会写成这样,一次表达所有的可能,把代码给写死了:
#define MAX_COUNT 100
Struct S_All_Cars //所有车的数据
{
Int count;
Scar_info cars[MAX_COUNT];
}
这个方案明显会消耗过大的内存,因为为了保证程序能够正常运行,MAX_COUNT总是比任何一个可能值还要大的。如果你用的是一个实体,而不是指针。这样会造成浪费。
还有一个很常用的方案,当然这个方案和上面的方案一样使用数组,只是数组长度为1。
Struct Scar_info //一辆车的数据
{
Double speed;
Double widht;
Double height;
Doble weight;
Double lenght;
}
Struct S_All_Cars //所有车的数据
{
Int count;
Scar_info cars[1];
}
其实这样的结构体,数组长度为1,看起来是没有什么用的。的确,当我们用这样的结构体来定义一个实例:S_All_Cars cars_all; 这样是不能表达什么的。但是如果我们用指针而不是一个实例的话,这样Scar_info cars[1];数组长度不再固定是1了,而是能表达任意整数。
例如:
char data[1024];
S_All_Cars* cars_all = (S_All_Cars*)data;
这样我们就可以使用 cars_all->cars[0] 或者 cars_all->cars[2] 或者cars_all->cars[8]。感受到这种指针的好处了吗?你用的数组不再是1了,而是一个可以确定的范围(局限于你申请(malloc)的内存大小)。
正常来说我们会这样用:
//获取count的值
Int count = 10;//任意整型 10只是假设
//计算需要的内存 = 总的结构体大小 - 一个小结构的大小 + N个小结构体大小
Int size = sizeof(S_All_Cars) - sizeof(Scar_info) + sizeof(Scar_info)*count;
//申请内存
S_All_Cars* cars_all = (S_All_Cars*)malloc(size);
//访问
cars_all->cars[9].speed = 10;
cars_all->cars[8].weight = 10;
这时候就相当于用了一个这样的结构体:
Struct S_All_Cars //所有车的数据
{
Int count;
Scar_info cars[10];
}
可变长结构体的魅力就在这里,你不需要写定长度,而是由指针来控制一切。
下一次会出一篇,关于C结构体 和 C++结构体的区别与使用技巧
2015年6月29日
By 郭顺铭