同一种类型的数据的集合是数组,和数组不同,结构体是多种类型的数据的集合。结构数据类型可以把基本数据类型和派生类型结合起来,以描述复杂的事物。结构类型也是派生类型。下面我们就来学习结构体的相关内容。
结构体的声明
结构类型的声明从关键字struct开始,大括号内包含成员声明列表:
struct [结构体类型名] {成员声明列表} [变量名列表 ];
当我们描写一个图形的时候,需要描写屏幕上的点,我们需要用x,y描写,当只有一个点的时候还好,我们可以直接定义,如果多个点的时候,我们就需要使用结构体来定义。
struct coord
{
int x;
int y;
};
这样我们就声明了一个结构变量。它现在就和其他的数据类型一样,我们声明就行了,比如int a,double b, 现在就是 struct coord c了,前面的struct coord 是一个整体。
结构体的声明方式有两种 :
struct coord
{
int x;
int y;
}first,second;
struct coord
{
int x;
int y;
};
struct coord first ,second;
第一种是直接声明在结构体的末尾,第二种是声明和变量定义放在源代码的不同区域。因为结构体的声明一般都在mian()函数的前面,而变量定义一般都在mian()函数的内部。
利用typedef为结构体类型定义一个名称,例如:
typedef struct coord Coord
这个时候Coord就是struct coord 的同义词,我们定义的时候只需要像这样就可以了 Coord first。
结构体变量的初始化也和前面的其他基本数据类型一样,Coord={0,0};
注意:结构体类型声明语句必须以分号结尾,可以放在函数内部,可以以放在函数外部,其作用域和变量的作用域类似。
访问结构体成员
在C语言中,使用结构成员运算符(.)来访问结构体成员。结构成员运算符也称为点运算符。因此通过first表示屏幕位置(20,50),可以这样写。
first.x=20;
first.y=50;
也可以和其他变量有一样,通过简单赋值表达式语句在相同类型的结构间相互赋值。
second=first;
这个时候,second表示的值和first一样。
我们尝试着用结构体的变量计算两点间的距离
struct coord
{
int x;
int y;
};
#include<stdio.h>
#include <math.h>
int mian()
{
struct coord first,second;
double distance;
printf("请输入第一个坐标:");
scanf("%d %d", &first.x, &first.y);
printf("请输入第二个坐标:");
scanf("%d %d", &second.x, &second.y);
int xDiff = first.x - second.x;
int yDiff= first.y - second.y;
distance = sqrt(xDiff * xDiff - yDiff * yDiff);
printf("两点间的距离是%.2f", distance);
return 0;
}
结构体成员的类型可以相同,也可以不同,结构体成员类型可以是基本数据类型,还可以是数组,指针和其他结构体。例如,当我们需要描述一个人的时候,需要说它的名字,性别,年龄。
可以给出如下声明:
struct person
{
char name[20];
char sex[5];
int age;
};
可以定义结构体指针,通过指针访问结构体类型变量成员,使用 “->” 运算符,例如:
struct person me,*ptme;
ptme=&me;
下面是通过指针变量ptme用->对me进行访问的操作。
strcpy(ptme->name,"zhangsan");
ptme->age=18;
结构体变量的内存分配
结构变量的内存空间大小为所有成员空间大小之和,但需要考虑内存对齐问题。
内存地址对齐是计算机语言自动进行的,也是编译器所做的工作。但不意味着程序猿不需要做任何事情,因为如果能遵循某些规则,可以让编译器做的更好,并且节约内存。等下我们了解了结构体变量的内存是怎样储存的时候你就能明白。
对齐规则
1.结构体的第一个成员对齐到和结构体变量起始位置偏移量0的地址处。
2.其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。
对齐数=编译器默认的一个对齐数与该成员变量大小的较小值。
vs中默认值为8
Linux中gcc没有默认对齐数,对齐数就是成员自身的大小。
3.结构体总大小为最大对齐数(结构体中每个成员变量都有一个对齐数,所有对齐数中最大的)的整数倍。
4.如果嵌套了结构体的情况,嵌套的结构体成员对齐到自己 的成员中最大对齐数的整数倍处,结构体的整体大小就是所有最大对字数(含嵌套结构体中成员的对齐数)的整数倍。
struct s1
{
char c1;
int i;
char c2;
};
你可以拿着笔计算以下 s1 的大小。
不知道你有没有算对,下面我们来讲解一下,结构体是如何在内存中储存的吧。
首先,第一条规则说了,结构体的第一个成员对齐到和结构体变量起始位置偏移量0的地址处,我们直接从0开始写下来,所以第一个char类型 占据的是0这个位置的一个字节。对于int 如果你跟着从1开始向后计算的话就错了。我们再上去看看第二条,其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处,因为int是4 个字节,比vs默认的8的对齐数要小,所以我们要从4的整数倍开始计算,也就是从4开始算,前面的3个字节就浪费了。对于最后一个char ,由于它也比vs默认的8的对齐数要小,所以取小的,还是1,因为它是任何函数的整数倍,所以它可以直接跟再7的后面,好了,现在计算完了,从0~8,你是否会认为是9个字节,那又错了,我们需要去看看第3条:结构体总大小为最大对齐数(结构体中每个成员变量都有一个对齐数,所有对齐数中最大的)的整数倍,再char 和int 中,int的字节数为4,所以最大对齐数要是4的整数倍,但还要比9大,就是12。所以说最终的内存大小就是12。
同样的再来看看下一个。
struct s2
{
char c1;
char c2;
int i;
};
int main()
{
printf("%d", sizeof(struct s2));
return 0;
}
这一次是8了,不知道你有没有计算对。
首先还是从0开始,按照上面的规则,一步一步向下,因为char类型为1 所以它可以再任何内存的地方,至于int 只能从4开始向后数,然后再根据第三条,就可以计算出为8了。从上面这两个例子可以看到,同样是两个char的类型,一个int的类型,第二个结构体的内存大小比第一个内存大小小了4个字节,我们可以思考一下是什么原因。因为每个类型在计算的时候,都需要和自己的整数倍的地址数对齐,所以会导致有一部分的内存被浪费。所以当我们在声明结构体的时候,我们既要满足对齐,又要节省空间,如何做到呢:让占用空间小的成员尽量集中在一起。
注意:如果说有嵌套的结构体,那就可以把嵌套的结构体看作一个数据类型,用同样的方式计算即可。
在必要的时候我们可以修改默认对齐数。#pragma这个预处理指令,可以改变编译器的默认对齐数。
#pragma pack(1)
括号内是我们想要得到的对齐数,在main()函数之前声明即可。
总结
以上就是关于一些简单的结构体的初始知识和结构体如何求它的大小的一些方法和技巧。如果有什么将错的地方希望大家在评论区指出,希望以上内容能够帮到大家,大家可以收藏,关注,点赞支持一下博主哦!!谢谢大家的观看!