当前位置:C++技术网 > 资讯 > C结构体-实用技巧

C结构体-实用技巧

更新时间:2015-06-29 23:29:20浏览次数:1+次

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++结构体的区别与使用技巧

2015629

By 郭顺铭