C语言结构体

一、什么是结构体?

我们知道C语言有内置类型,比如char、short、int、long、float、double等,但在生活中我们如果去描述一个人,一本书该怎么描述呢?人有许多属性比如身高,体重,年龄等单一的内置类型是不能完全表示出来的,C语言为了解决这种问题就增加了结构体这种自定义的数据类型,让程序员可以创造合适的类型去表述它们。

struct tag
{
    member-list;
}variable-list;

这就是结构体的表示形式,结构体的关键字是struct,结构体是一些值的集合,这些值称为成员变量,结构体的每个成员的类型可以是任意的,如指针、数组、内置类型,甚至是结构体类型。

如描述一本书,代码可以写成如下形式:

struct Book
{
    char name[20];//书名
    float price;//价格
    char author[20];//作者
};

二、结构体变量的定义和初始化

通过上面的代码,我们已经知道了一个结构体的形式,接下来我们具体说一说结构体变量怎么定义。

struct Book
{
    char name[20];//书名
    float price;//价格
    char author[20];//作者
}s1;

struct Book s2;

上述代码在结构体后有一个是s1,s1是声明结构体的同时定义结构体变量,s2是类型名定义的结构体变量,两种方式都可以。

那么单单定义是不行的,我们要对结构体变量进行初始化。

struct Book s2 = {"C程序与设计",19.9,"xxx"}

这就是简单的初始化,按结构体成员变量顺序初始化,当然,我们也可以指定顺序,代码如下:

struct Book s2 = {.price = 19.9,.name = "C程序与设计",.author = "xxx"}

如果对s1初始化则可以直接在其后面初始化,代码如下:

struct Book
{
    char name[20];//书名
    float price;//价格
    char author[20];//作者
}s1={"C陷阱与缺陷",29.9,"xxxxx"};

如果结构体中包含结构体,像下面情况:

struct Student
{
    char name[20];
    struct Book b;
};

struct Student s={"zhangsan",{"C设计",39.9,"xx"}};

结构体变量就可以像这样初始化。

三、结构体成员如何访问?

通过上述,我们已经知道结构体变量的定义和初始化,那么我们如何去访问它们呢?结构体成员的直接访问是通过点操作符(.)访问的,有的地方叫句点操作符。点操作符接受两个操作数。

struct Point
{
    int x;
    int y;
};

int main()
{
    struct Point p={1,2};
    printf("%d %d\n",p.x,p.y);//打印结果为:1 2
    return 0;
}

代码中,p.x就是通过点操作符进行访问的,p和x是两个操作数。

那还有没有其他访问方式呢?答案是有的,下面请看代码:

struct Point
{
    int x;
    int y;
};

int main()
{
    struct Point p={1,2};
    struct Point* po=&p;
    printf("%d %d\n",po->x,po->y);//打印结果为:1 2
    return 0;
}

我们知道指针是非常"强大"的,当然也有结构体指针,C语言给出了箭头操作符(->),po是结构体指针通过箭头操作符可以访问到结构体的成员变量。

四、结构体内存对齐

在vs2019环境下,short类型变量占2个字节,int类型变量占4个字节,那么一个结构体变量在内存中会占多少个字节呢?这就涉及到结构体内存对齐,下面先介绍结构体内存对齐规则:1.结构体的第⼀个成员对齐到和结构体变量起始位置偏移量为0的地址处。2.其他成员变量要对齐到某个数(对齐数)的整数倍的地址处。

对齐数=编译器默认的⼀个对齐数与该成员变量大小的较小值。

在vs2019环境下,编译器的默认对齐数是8

在Linux和gcc中没有默认对齐数,对齐数就是成员自身大小

3.结构体总大小为最⼤对⻬数(结构体中每个成员变量都有⼀个对⻬数,所有对齐数中最⼤的)的整数倍。4.如果嵌套了结构体的情况,嵌套的结构体成员对⻬到⾃⼰的成员中最⼤对齐数的整数倍处,结构体的整体大小就是所有最⼤对⻬数(含嵌套结构体中成员的对⻬数)的整数倍。
我们现在只需要记住怎么求对齐数即可,其他不用担心,接下来我会一一介绍。

请看下面代码:

struct S1
{
    char c1;//对齐数为1
    int a;//对齐数为4
    char c2;//对齐数为1
};
int main()
{
    printf("%zd",sizeof(struct S1));
    return 0;
}

我们根据代码来具体分析,c1的大小为1字节,在vs2019下默认对齐数为8,1小于8,所以c1的对齐数为1,依次类推a的对齐数为4,c2的对齐数为1,我们第一步工作已经完成了。

在vs2019环境下,根据规则1、2我们知道c1处的偏移量为0,存放完c1后偏移量加1,此时偏移量为1,接着到变量a了,变量a的对齐数为4,偏移量为1不是4的倍数,偏移量为4可以满足,因此变量a要在偏移量为4的位置开始存放4字节,存放完后,偏移量为8,最后存放变量c2,c2的对齐数为1,8是1的倍数所以紧接着存放c2,然后偏移量加1,此时偏移量为9,至此结构体中3个成员变量已经存放完毕,我们再看规则3,变量c1,a,c2中最大对齐数为4,9不是4的倍数,所以偏移量要到12,最后结构体大小就是从偏移量为0的位置于偏移量为12的位置之间的字节数,即12字节。

我们也可以利用#pragma这个预处理指令修改编译器的默认对齐数,例如:

#pragma pack(1)

//代码


#pragma pack()

第一个#pragma就可以将编译器的默认对齐数改为1,第二个#pragma就可以使对齐数回到原来的对齐数。

为什么会存在内存对齐呢?

⼤部分的参考资料都是这样说的:

1. 平台原因(移植原因):不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常。

2. 性能原因:数据结构(尤其是栈)应该尽可能地在自然边界上对齐。原因在于,为了访问未对⻬的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要⼀次访问。假设⼀个处理器总是从内存中取8个字节,则地址必须是8的倍数。如果我们能保证将所有的double类型的数据的地址都对齐成8的倍数,那么就可以用一个内存操作来读或者写值了。否则,我们可能需要执⾏两次内存访问,因为对象可能被分放在两个8字节内存块中。

总体来说:结构体的内存对齐是拿空间来换取时间的做法。

以上就是全部内容了,祝大家天天开心!

  • 21
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
【项目资源】:包含前端、后端、移动开发、操作系统、人工智能、物联网、信息化管理、数据库、硬件开发、大数据、课程资源、音视频、网站开发等各种技术项目的源码。包括STM32、ESP8266、PHP、QT、Linux、iOS、C++、Java、MATLAB、python、web、C#、EDA、proteus、RTOS等项目的源码。 【项目质量】:所有源码都经过严格测试,可以直接运行。功能在确认正常工作后才上传。 【适用人群】:适用于希望学习不同技术领域的小白或进阶学习者。可作为毕设项目、课程设计、大作业、工程实训或初期项目立项。 【附加价值】:项目具有较高的学习借鉴价值,也可直接拿来修改复刻。对于有一定基础或热衷于研究的人来说,可以在这些基础代码上进行修改和扩展,实现其他功能。 【沟通交流】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。鼓励下载和使用,并欢迎大家互相学习,共同进步。【项目资源】:包含前端、后端、移动开发、操作系统、人工智能、物联网、信息化管理、数据库、硬件开发、大数据、课程资源、音视频、网站开发等各种技术项目的源码。包括STM32、ESP8266、PHP、QT、Linux、iOS、C++、Java、MATLAB、python、web、C#、EDA、proteus、RTOS等项目的源码。 【项目质量】:所有源码都经过严格测试,可以直接运行。功能在确认正常工作后才上传。 【适用人群】:适用于希望学习不同技术领域的小白或进阶学习者。可作为毕设项目、课程设计、大作业、工程实训或初期项目立项。 【附加价值】:项目具有较高的学习借鉴价值,也可直接拿来修改复刻。对于有一定基础或热衷于研究的人来说,可以在这些基础代码上进行修改和扩展,实现其他功能。 【沟通交流】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。鼓励下载和使用,并欢迎大家互相学习,共同进步。【项目资源】:包含前端、后端、移动开发、操作系统、人工智能、物联网、信息化管理、数据库、硬件开发、大数据、课程资源、音视频、网站开发等各种技术项目的源码。包括STM32、ESP8266、PHP、QT、Linux、iOS、C++、Java、MATLAB、python、web、C#、EDA、proteus、RTOS等项目的源码。 【项目质量】:所有源码都经过严格测试,可以直接运行。功能在确认正常工作后才上传。 【适用人群】:适用于希望学习不同技术领域的小白或进阶学习者。可作为毕设项目、课程设计、大作业、工程实训或初期项目立项。 【附加价值】:项目具有较高的学习借鉴价值,也可直接拿来修改复刻。对于有一定基础或热衷于研究的人来说,可以在这些基础代码上进行修改和扩展,实现其他功能。 【沟通交流】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。鼓励下载和使用,并欢迎大家互相学习,共同进步。【项目资源】:包含前端、后端、移动开发、操作系统、人工智能、物联网、信息化管理、数据库、硬件开发、大数据、课程资源、音视频、网站开发等各种技术项目的源码。包括STM32、ESP8266、PHP、QT、Linux、iOS、C++、Java、MATLAB、python、web、C#、EDA、proteus、RTOS等项目的源码。 【项目质量】:所有源码都经过严格测试,可以直接运行。功能在确认正常工作后才上传。 【适用人群】:适用于希望学习不同技术领域的小白或进阶学习者。可作为毕设项目、课程设计、大作业、工程实训或初期项目立项。 【附加价值】:项目具有较高的学习借鉴价值,也可直接拿来修改复刻。对于有一定基础或热衷于研究的人来说,可以在这些基础代码上进行修改和扩展,实现其他功能。 【沟通交流】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。鼓励下载和使用,并欢迎大家互相学习,共同进步。【项目资源】:包含前端、后端、移动开发、操作系统、人工智能、物联网、信息化管理、数据库、硬件开发、大数据、课程资源、音视频、网站开发等各种技术项目的源码。包括STM32、ESP8266、PHP、QT、Linux、iOS、C++、Java、MATLAB、python、web、C#、EDA、proteus、RTOS等项目的源码。 【项目质量】:所有源码都经过严格测试,可以直接运行。功能在确认正常工作后才上传。 【适用人群】:适用于希望学习不同技术领域的小白或进阶学习者。可作为毕设项目、课程设计、大作业、工程实训或初期项目立项。 【附加价值】:项目具有较高的学习借鉴价值,也可直接拿来修改复刻。对于有一定基础或热衷于研究的人来说,可以在这些基础代码上进行修改和扩展,实现其他功能。 【沟通交流】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。鼓励下载和使用,并欢迎大家互相学习,共同进步。【项目资源
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值