结构体浅析
C语言提供了两种聚合数据类型:数组和结构。结构变量属于标量类型(例如:int,char等),所以可以像对待标量那样子对待结构变量,
1. 结构变量可以作为传递给函数的参数
2. 可以作为返回值
3. 相同类型的结构变量还可以互相赋值
结构体声明和定义变量的几种模式
1. 声明:struct tag{member_list};
定义:struct tag variable1;
2. 声明:struct tag{member_list}variable_list;
定义:已在variable_list定义,亦可使用 1 中定义方法
3. 声明:typedef struct tag{member_list};
定义:tag variable1;
4. (使用频率最高的方式,可以少写很多东西)
声明:typedef struct tag{member_list}variable_list;
定义:已在variable_list定义,亦可使用 3 中定义方法
5. (几乎不这样子使用,只有在声明时能够定义变量,很不方便)
声明+定义:struct {member_list}variable_list;
注:
member_list可以包括:普通标量(int 、char、标量指针、结构体指针),其他结构体 。
variable_list可以是普通变量,数组,指针等
结构的自引用
上面已经说过了member_list的成员了,那么现在我们再来讨论下成员是否可以有自身结构,首先看看下面的这两个声明结构
【错】
struct test {
int a;
struct test1 b;
};
【对】
struct test {
int a;
struct test1 *b;
};
显然区别就在‘*’,前者的成员是结构b,而后者的成员是结构指针b,前者的成员就是自己,成员里面还有成员,这样子就陷入了死循环,编译器绝对不允许这样的事情出现,而后者的结构指针就不一样了,因为指针所占的空间大小是一定的,所以编译器可以很轻易的就给该结构变量分配内存空间
注:struct{
Int a;
Variable b;
}*Variable;
它是错误的,因为在member_list中,变量Variable还没有声明,编译器还不知道Variable是个什么东西,所以是上面的声明是错误的,正确的应该是下面的:
struct test{
Int a;
struct test *b;
}*Variable;
结构体的互相引用
结构体的互相引用,必然存在这样的问题:无论先声明哪个结构体
,它里面总包含另一个结构体,但是我们又没有声明另一个结构体,反之亦然。因而我们可以利用不完整的声明解决这个问题
例如:我们需要结构体A中包含结构体B,结构体B中包含结构体A。
struct B;
struct A{
struct B;
};
struct B{
struct A;
};
这样子就完美解决结构体互相引用的问题了。
结构的存储
大家先看看下面这两个结构体:
typedef struct first{
int a;
char b;
char c;
};
typedef struct second{
char b;
int a;
char c;
};
他们两个有什么区别呢?
只是member_list的顺序不同罢了,但是由于边界对齐技术(CPU在工作时只能按照某长度的整倍数为边界进行内存操作)的存在,因而产生了不同的效率。
first:
█
█
█
█
█
b
█c
前面的四个为a占用的空间,紧接着的是b,c占用的,最后的两个由于对齐而保留,下次cpu将从空白后面的地址开始访问内存。空间利用率:75%
Second:
█
b
█
█
█
█
█
c
前面的四个空间存储的是b,中间的是a,下来的是c,空间利用率:50%
下面是测试我的测试程序,仅供大家参考:
注:函数offsetof(type,member) type:结构体类型; member:结构体成员; 功能:求得member开始存储的位置距离存储的位置偏移的字节数
运行结果截图如下:
<img src="https://img-blog.youkuaiyun.com/20140620233055156?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvY29sZW9wdGlsZQ==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" alt="" />
经过上面的许多测试,我们可以得出这样的结论:尽量把占用空间最大的类型放在member_list的最上面,这样子可以使空间利用率最大,甚至达到100%,节约的这点内存空间在程序创建成百上千个结构上体现的尤为明显。
作为函数参数的结构
例:typedef struct {
int stu_id;
char stu_name[20];
}stu;
void print( stu student)
{
Printf(“id:%d\n”,student.stu_id;);
Printf(“name:%s\n”,student.stu_name);
}
这样子传结构体参数是可以的,可是由于在c语言中传递传递参数就是把参数拷贝一份,然后供函数使用,这样子拷贝就有点多了,远不如传递结构体指针简单,但是传递指针可能改变结构内容,因而我们可以加上关键字const,实现如下:
void print( stu const *student)
{
Printf(“id:%d\n”,student->stu_id;);
Printf(“name:%s\n”,student->stu_name);
}
传递指针的效率就很高了,只需要拷贝一个指针标量就行了,是不是很能提高效率,尤其是在大量调用一个函数的情况下。
#include<stdio.h>
#include<stddef.h>
#include<windows.h>
typedef struct test1{
char c;
double a;
char b;
};
typedef struct test2{
char b;
char c;
double a;
};
typedef struct test3{
double a;
char b;
char c;
};
typedef struct test4{
double a;
int b;
int c;
};
int main()
{
/*测试编译器给予不同类型标量的大小*/
printf("测试编译器给予不同类型标量的大小!\n");
printf("sizeof(char):%d\n",sizeof(char));
printf("sizeof(int):%d\n",sizeof(int));
printf("sizeof(double):%d\n\n",sizeof(double));
/*测试不同排序下结构体的空间利用率*/
printf("sizeof(struct test1):%d\n",sizeof(struct test1));
printf("offsetof(struct test1,c):%d\n",offsetof(struct test1,c));
printf("test1空间利用率:%.2lf\n\n",(double)(sizeof(double)+2*sizeof(char))/(double)sizeof(struct test1));
printf("sizeof(struct test2):%d\n",sizeof(struct test2));
printf("offsetof(struct test2,c):%d \n",offsetof(struct test2,c));
printf("test2空间利用率:%.2lf\n\n",(double)(sizeof(double)+2*sizeof(char))/(double)sizeof(struct test2));
printf("sizeof(struct test3):%d\n",sizeof(struct test3));
printf("offsetof(struct test3,c):%d\n",offsetof(struct test3,c));
printf("test3空间利用率:%.2lf\n\n",(double)(sizeof(double)+2*sizeof(char))/(double)sizeof(struct test3));
/*验证结论猜想是否正确*/
printf("sizeof(struct test4):%d\n",sizeof(struct test4));
printf("offsetof(struct test4,c):%d\n",offsetof(struct test4,c));
printf("test4空利用率:%.2lf\n\n",(double)(sizeof(double)+2*sizeof(int))/(double)sizeof(struct test4));
system("PAUSE");
return 0;
}