【C语言】结构体的创建和使用与结构体内存对齐

article/2025/10/15 2:22:58

结构体创建

  • 前言
  • 结构体的声明
    • 全局声明
    • 特殊声明
  • 结构体变量定义与成员初始化
    • 成员类型
    • 变量定义
    • 匿名结构体变量定义
  • 结构体的自引用
  • 结构体成员的访问
    • 成员初始化
    • 操作符
    • 结构体传参
  • 结构体内存对齐
    • 对齐数
    • 对齐规则

前言

在生活中我们要描述一个人时是不是要知道他的名字、性别、年龄啊,同理在c语言中要描述一个人也要他相关的信息,而我们在c语言中储存一个人的信息时可以发现一个人的信息需要多种数据类型来储存我们可以把这类对象称之为复杂对象,因此我们来介绍自定义类型中的结构体,结构体就可以很好的储存一个复杂对象(如一个人或一本书)。

结构体的声明

全局声明

结构体声明的语法是这样的

struct 结构体名
{成员变量;
};/*注意分号不能丢*/

举例当我要描述一个人的信息时我们可以这样写。

#include <stdio.h>struct Jk
{char name[10];/*名字*/char gender[5];/*性别*/int age;/*年龄*/
};int main() 
{return 0;
}

此时写的Jk这个结构体是全局的,如果想写成局部只需这样写

#include <stdio.h>int main() 
{struct Jk{char name[10];/*名字*/char gender[5];/*性别*/int age;/*年龄*/};return 0;
}

特殊声明

还有一种特殊声明的声明叫不完全声明语法如下。

struct
{成员变量;
};

这种声明省略了结构体名此时的结构体叫匿名结构体。
此时的匿名结构体是无法使用的,他的使用我们在结构体变量定义与成员初始化里讲。

结构体变量定义与成员初始化

成员类型

结构的成员可以是标量、数组、指针,甚至是其他结构体。

变量定义

声明好结构体后,如何定义变量其实很简单他有三种定义的方法:

  1. 声明时顺带定义一个全局结构体变量
#include <stdio.h>struct Jk
{char name[10];/*名字*/char gender[5];/*性别*/int age;/*年龄*/
}jk;/*<--声明时顺带定义一个全局结构体变量*/int main() 
{return 0;
}
  1. 定义一个全局结构体变量
#include <stdio.h>struct Jk
{char name[10];/*名字*/char gender[5];/*性别*/int age;/*年龄*/
};struct Jk n;/*<--定义一个全局结构体变量*/int main() 
{return 0;
}
  1. 定义一个局部结构体变量
#include <stdio.h>struct Jk
{char name[10];/*名字*/char gender[5];/*性别*/int age;/*年龄*/
};int main() 
{struct Jk n;/*<--定义一个局部结构体变量*/return 0;
}

除了结构体变量,结构体指针,结构体数组这些也是可以定义的如

#include <stdio.h>struct Jk
{char name[10];/*名字*/char gender[5];/*性别*/int age;/*年龄*/
}*p, arr[10];/*<--定义了一个结构体指针和结构体数组*/int main() 
{return 0;
}

匿名结构体变量定义

匿名结构体变量定义只能在声明时顺带定义如下

#include <stdio.h>struct
{int a;
}n;/*<--只能在声明时顺带定义一个全局结构体变量*/int main() 
{return 0;
}

当我们的程序中声明了两个匿名结构体时不管他两的成员是否相同,编译器都会把他俩看成两个不同的类型

结构体的自引用

在结构体成员中除了上面说的成员类型里说的成员类型,还可以包含一个类型为该结构体本身的指针成员的,写法如下

#include <stdio.h>struct KK
{char a;struct KK* p;/*<--包含了一个类型为 struct KK 的指针p*/
};int main() 
{return 0;
}

此时的kk类型的结构体,就可以指向下一个同类型的结构体了

结构体成员的访问

成员初始化

只需要在{}中输入对应的数据就行了

#include<stdio.h>struct kk
{int a;char b;float c;int arr1[2];char arr2[];
}i3 = {1, 'a', 3.5f, {1,2}, "abc"};struct kk i2 = {1, 'a', 3.5f, {1,2}, "abc"};int main()
{struct kk i1 = {1, 'a', 3.5f, {1,2}, "abc"};return 0;
}

在这里插入图片描述

嵌套结构体初始化只需要在嵌套结构体成员对应的位置用{}来初始就可以了

#include<stdio.h>struct SS
{int a;char arr[];
};struct kk
{int a;struct SS n;
}i3 = {1, {1, "abc"} };struct kk i2 = {1, {1, "abc"} };int main()
{struct kk i1 = {1, {1, "abc"} };return 0;
}

在这里插入图片描述

操作符

结构体的操作符有两个

操作符1:.
使用方法:结构体 . 成员名
操作符2:->
使用方法:结构体指针 -> 成员名

#include <stdio.h>struct SS
{int a;char arr[];
}a1 = { 1, "abc" };/*<--声明时顺带定义一个全局结构体变量*/int main()
{int a = a1.a;char* arr1 = a1.arr;char arr2 = a1.arr[1];struct SS* p = &a1;int b = p->a;char* arr3 = p->arr;char arr4 = p->arr[1];return 0;
}

在这里插入图片描述

结构体传参

函数形参接收时只需要保持类型一致即可。

#include<stdio.h>struct kk
{int a;char b;float c;int arr1[2];char arr2[];
};void s1(struct kk n)
{}void s2(struct kk *p)
{}
int main()
{struct kk i1 = { 1, 'a', 3.5f, {1,2}, "abc" };s1(i1);s2(&i1);return 0;
}

以上两种方法虽然都可以接收但是s2的方法是比s1的方法好的,因为在函数传参中的形参是实参的一份临时拷贝,所以s1的参数在传输的过程中是要压栈的,如果传输的结构体很大那在他压栈时会花费很多的内存和时间,而s2的接收结构体地址的方法就可以很好的节约时间跟内存的开销。

结构体内存对齐

对齐数

在不同的编译器中对齐数也是不同的,如vs中默认的对齐数是8而linux 32默认是4,所以下面我们以vs的对齐数来介绍对齐规则。

对齐规则

我们先看结构体的对齐规则:

  1. 第一个成员在与结构体变量偏移量为0的地址处。
  2. 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。
  3. 结构体总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍。
    如int是4、char是1、float是4、double是8
  4. 如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。

知道规则后我们来计算一下以下结构体

struct Jk{char a;char b;int c;char d;};

我们先把各个成员的对齐数求出来

char a 变量自带对齐数是1 默认对齐数是8 取较小值当对齐数也就是 1
char b 变量自带对齐数是1 默认对齐数是8 取较小值当对齐数也就是 1
int c 变量自带对齐数是4 默认对齐数是8 取较小值当对齐数也就是 4
char d 变量自带对齐数是1 默认对齐数是8 取较小值当对齐数也就是 1

先看规则一:第一个成员在与结构体变量偏移量为0的地址处。
所以我们的第一个成员a需要在偏移量为0的地址处开始往下用占一个字节
再看规则二: 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。
此时0偏移量的地址被占用了所以我们从偏移量1的地址开始看,偏移量1是成员b的对齐数1的倍数我们就在偏移量为1的地址处开始往下占用1个字节。
此时1偏移量的地址被占用了所以我们从偏移量2的地址开始看,偏移量2、3都不是成员c的对齐数4的倍数,继续往下可以看到偏移量4是成员c的对齐数4的倍数我们就在偏移量为4的地址处开始往下占用4个字节。
此时没被占用的偏移量地址是8,偏移量8是成员d的对齐数1的倍数我们就在偏移量为8的地址处开始往下占用1个字节。
这时a、b、c、d就排好了但结束了吗?,并没有我们来看规则三:
结构体总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍。
我们知道c是最大的对齐数4,通过上面的排序我们排到第9个字节显然9个字节不是偏移量4的倍数,所以我们要给还是9个字节的结构体多占用3个字节让他的大小变成12个字节,此时就满足了结构体大小是偏移量4的倍数这一条件了。
最终我没得出的结构体大小是12个字节,效果如下图
ss
在这里插入图片描述
搞定是无嵌套的后,我们来计算嵌套的结构体

struct Jk1
{int b;char c;
};
struct Jk
{char a;struct Jk1 n;char d;
};

嵌套结构体只需加上规则四:
如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。
嵌套结构体最大对齐数是4,所以只需要在4的倍数的偏移量那里开始向下占用嵌套结构体大小的字节即可。
当d占用完后此时的结构体为13个字节,我们知道这个结构体最大的对齐数是嵌套结构体中的b的对齐数4,显然此时的结构体并不满足规则四中的:
结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。
所以要给结构体多占用3个字节,此时的结构体是16个字节就满足上面的条件了。
最终我们得出的结果是16个字节,效果如下图

在这里插入图片描述
在这里插入图片描述
以上就是全部内容了,如有错误欢迎在评论区指出,我会积极修正。


http://chatgpt.dhexx.cn/article/aAgghqTq.shtml

相关文章

C语言struct结构体内存

对齐规则&#xff1a; &#xff08;i&#xff09;结构体内 成员存储位置 的起始地址为成员自身长度与默认对齐值 中的较小者的整倍数。 &#xff08;ii&#xff09;结构体A嵌套在结构体B内&#xff0c;则A在B内存储位置起始地址为 A内成员最长长度 的整数倍。 &#xff08;iii&…

嵌入式系统开发:C语言中的位结构体

在嵌入式开发中&#xff0c;经常需要表示各种系统状态&#xff0c;位结构体的出现大大方便了我们&#xff0c;尤其是在进行一些硬件层操作和数据通信时。但是在使用位结构体的过程中&#xff0c;是否深入思考一下它的相关属性&#xff1f;是否真正用到它的便利性&#xff0c;来…

keil中,编写结构体成员运算符(.)后不能自动弹出结构体成员

keil中&#xff0c;编写结构体成员运算符&#xff08;.&#xff09;后不能自动弹出结构体成员 解决办法&#xff1a; 1、确保源文件里面包含sys.h/stm32f10x.h文件&#xff08;或者源文件里面的头文件也行&#xff09; 2、把源文件路径加载到keil里面&#xff08;魔术棒->C/…

c++中的结构体案例

结构体案例一 案例描述&#xff1a;学生做毕设项目&#xff0c;每名老师有5个学生&#xff0c;总共有3个老师&#xff0c;需求如下&#xff1a; 设计学生和老师的结构体&#xff0c;在老师的结构体中&#xff0c;有老师的姓名和一个存放了5名学生的数组作为成员学生的成员有姓…

C语言:位结构体

位结构体是一种特殊的结构, 在需按位访问一个字节或字的多个位时, 位结构体比按位运算符更加方便。 一、位结构—简介 有些信息在存储时&#xff0c;并不需要占用一个完整的字节&#xff0c; 而只需占几个或一个二进制位。 例如在存放一个开关量时&#xff0c;只有0和1 两种状…

C 语言编程 — 构造数据类型 — 结构体(struct)

目录 文章目录 目录结构体声明一个结构体类型定义一个结构体类型的变量定义一个结构体类型的指针变量结构体的成员结构体的内存分布 结构体 结构体&#xff08;Structure&#xff09;是一种由不同类型的数据成员组成的数据类型。通过定义结构体&#xff0c;我们可以将多个不同…

C语言结构体和其他构造数据类型详解

结构体和其他构造数据类型 结构体 “结构”是一种构造类型&#xff0c;它是由若干“成员”组成的。 每一个成员可以是一个基本数据类型或者又是一个构造类型。 结构既然是一种“构造”而成的数据类型&#xff0c;那么说明和使用之前必须先定义它&#xff0c;也就是构造它。 结…

C的实用笔记38——结构体的引入和定义

1.为什么要用结构体&#xff1f; 0、原因&#xff1a;实际开发中&#xff0c;我们需要将不同类型的数据组合成一个整体&#xff0c;这就是结构体。结构体是自创的类型。 1、查找一名学生信息的两种方案&#xff1a;在EXCEL表格和数据库中经常有这种情况&#xff0c;比方说一个…

7.IDA-创建结构体

结构体分类 结构体的一个显著特点在于&#xff0c;结构体中的数据字段是通过名称访问&#xff0c;而不是像数组那样通过索引访问。不好的是&#xff0c;字段名称被编译器转换成了数字偏移量。结果&#xff0c;在反汇编代码清单中&#xff0c;访问结构体字段的方式看起来与使用…

结构体的初步认识以及其内存的计算

目录 前言 结构体的定义 结构体的自引用 结构体的初始化 结构体内存的计算 前言 我们知道&#xff0c;通过不同的数据类型我们可以表达不同意义的数据&#xff0c;如长度宽度面积只需要定义一个 int 类型的数据就可以符合大部分的需求。但是&#xff0c;有些东西自存在就是一…

IDA创建结构体方法

1. ida是最好用的逆向分析工具 2.问题 如果你在使用IDA分析时遇到大量的结构体指针偏移形式的变量&#xff0c;肯定想根据自己的分析重命名结构体各项。 3.定义结构体 这是需要先找到结构体初始化的地方 这里记录了每一项的偏移量&#xff0c;记录了结构体总大小Ox110uLL。 …

手把手教你写单片机的结构体

手把手教你写单片机的结构体 摘要&#xff1a;听说还有好多学单片机的小伙伴不会用结构体&#xff1f;指针和结构体是学单片机必须要掌握的&#xff0c;如果你C语言掌握的不牢&#xff0c;单片机根本学不到精髓&#xff0c;只能完成一些低级的项目。看得懂结构体并且能够灵活运…

STM32基于Flash对结构体读写暨再认识结构体内存分配

目录 前言 认识结构体内存分布 模拟结构体搬运 实现FLASH的结构体读写 前言 记录本篇主要是记录我进行stm32学习中利用stm32的flash&#xff08;闪存&#xff09;读写结构体的总结。 认识结构体内存分布 结构体&#xff0c;主要要来存放我们的自定义类型的数据&#xff0c;结…

单片机的结构体

手把手教你写单片机的结构体 FROM8号线攻城狮 公众号 电子信息工程硕士在读&#xff0c;分享单片机、嵌入式linux、物联网等知识&#xff0c;致力于打造最硬核的嵌入式技术公众号。 半碗鸡汤&#xff0c;半碗杂粮。 摘要&#xff1a;听说还有好多学单片机的小伙伴不会用结构…

windows下mysql初始密码设置

转载自&#xff1a;http://blog.csdn.net/ofreelander/article/details/50802780 1.my-default.ini 改名my.ini 在解压的目录下面复制my-default.ini一份改名字为 my.ini。 2.打开 Windows 环境变量设置, 新建变量名 MYSQL_HOME , 变量值为 MySQL 安装目录路径, 在 环境变量 …

MYSQL修改初始化密码的方法

解决方法之一&#xff1a; “ ALTER USER rootlocalhost identified by 123456 ; ” 最近安装了mysql&#xff0c;在使用“ set password for rootlocalhostpassword(123456);”时会遇到 “ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that…

Centos下安装mysql查看初始密码并修改密码

题记&#xff1a;当我们在centos成功安装了mysql后&#xff0c;需要查看初始密码&#xff0c;大概率也需要自己修改密码。 一&#xff0c;查看mysql历时密码 cat var/log/mysqld.log二&#xff0c;进入到mysql mysql -u root -p按回车&#xff0c;然后再password:输入刚才所…

MySQL设置初始密码—注意版本mysql-8.0.30

MySQL设置初始密码—注意版本mysql-8.0.30 第一步&#xff1a;使用管理员模式下的命令行&#xff0c;进入mysql的所在文件下的bin目录; 第二步&#xff1a;输入命令mysql -u root -p; 第三步&#xff1a;这里第一次不用输入密码; 第四步&#xff1a; ALTER USER rootlocalhost…

安装mysql的初始密码在哪里

mysql初始密码是多少 介绍怎么找到mysql安装的root的初始密码 mysql初始密码是多少 介绍怎么找到mysql安装的root的初始密码 怎么给mysql设置用户名和密码 登录数据库 “命令提示字符”窗口录入&#xff0c; 录入cd C:\mysql\bin 并按下回车键&#xff0c;将目录切换为 cd C:\m…

关于MySQL初始化密码忘记的问题

本文章可以解决MySQL密码忘记的问题&#xff01;&#xff01;&#xff01; 1、到mysql安装的bin目录下运行PowerShell 2、在命令行输入 mysql -u root -p 登录 mysql&#xff0c;可以随意输入一个密码&#xff0c;返回”ERROR 1045 (28000): Access denied for user rootlocal…