首页 > 其他分享 >【C语言】结构体

【C语言】结构体

时间:2024-03-30 21:04:43浏览次数:12  
标签:struct int C语言 char 对齐 字节 结构

在这里插入图片描述

个人主页点这里~


结构体

一、结构体类型的声明

我们在指针终篇中提到过结构体的这一部分内容(详情请阅拙作终の指针)现在我们来整个展开叙述一下

1、结构的声明

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

花括号 { } 中放的是成员变量,结构的每个成员变量都可以是不同的类型,每一个被定义的结构体中都要有至少一个成员变量,结构是一些值的集合。
定义一个人

struct man
{
	char name[20];//名字
	int age;//年龄
	char sex[5];//性别
	char id[20];//身份证号
};

2、结构体变量的创建和初始化

#include <stdio.h>
struct Stu
{
 char name[20];//名字
 int age;//年龄
 char sex[5];//性别
 char id[20];//身份证号
 int main()
 {
     struct Stu s = { "张三", 18, "男", "111111200602023215" };
     //结构体顺序初始化
     struct Stu s2 = { .age = 19, .name = "lisi", .id = "111111200502023222", .sex = "⼥" };
     //指定顺序初始化
     return 0;
};

3、声明时的特殊情况

匿名结构体类型,如果没有对结构体进行重命名的话,仅能使用一次

struct
{
    int a;
    char b;
    float c;
}x;

形如上面代码的结构体未重命名的话,使用这一次便被回收

4、自引用

自引用的正确方法:

struct Node
{
    int data;
    struct Node* next;
};

通过结构体指针的形式来进行自引用
并且结构体自引用是不能用typedef重命名的
像这个:

typedef struct
{
 int a;
 Node* next;
}Node;

我们会在创建Node结构体之前在结构体当中使用Node,所以不可取

二、结构体内存对齐

结构体内存对齐是计算结构体大小的一个必备条件

1、对齐规则

①结构体的第一个成员对齐到结构体变量起始位置的地址
②其他成员变量要对齐到对齐数的整数倍的地址处
对齐数:编译器默认的对齐数与该成员变量大小的较小值(我所使用的vs2022默认对齐数为8)
③结构体总大小一定为对齐数的整数倍
④如果结构体中嵌套了结构体,嵌套的结构体对齐到对齐到自己成员中最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数的整数倍
我们可以将大小看作一个数组,每一个位置都是一个字节

struct S1
{
 char c1;//1字节,<8,就将1字节放在0位置处
 int i;//4字节,<8,因为1,2,3位置不是4的整数倍,所以我们直接找到4位置,将4个字节放入
 char c2;//1字节,<8,放在8位置处
};

又因为现在指向9位置处,9不是最大对齐数4的整数倍,所以要指向12处,所以结构体S1的大小为12字节
printf打印一下:
在这里插入图片描述

struct S2
{
 char c1;//1字节,放到0位置
 char c2;//1字节,放到1位置
 int i;//4字节,2不是4的整数倍,放到4位置
};

最终指向8位置,是4的整数倍,故S2的大小为8字节
在这里插入图片描述

struct S3
{
 double d;//8字节,放到0位置处
 char c;//1字节,放到8位置处
 int i;//4字节,9不是4的整数倍,放到12位置处,最终指向16
};

因为最大对齐数为8,16为8的整数倍,所以结构体S3的大小就是16个字节

struct S4
{
 char c1;//1字节,放到0位置处
 struct S3 s3;//16字节,以8为对齐数,放到8位置,最后指向24位置处
 double d;//8字节,放到24位置,最终指向32位置
};

32是最大对齐数8的整数倍,所以结构体S4的大小就是32个字节

2、存在内存对齐的原因

在数据访问时,对齐的内存只需要一次访问,而不对齐的内存需要两次访问
结构体的内存对齐是拿空间来换取时间
我们可以将占用内存小的尽量集中在一起来节省空间

struct S1
{
 char c1;
 int i;
 char c2;
};
struct S2
{
 char c1;
 char c2;
 int i;
};

3、修改默认对齐数

#pragma
#include <stdio.h>
#pragma pack(1)//设置默认对⻬数为1
struct S
{
 char c1;//1字节,存到0位置
 int i;//4字节,默认对齐数为1小于4,存到1位置
 char c2;//1字节,存到5位置,指向6
};
#pragma pack()//取消设置的对⻬数,还原为默认
int main()
{
 printf("%d\n", sizeof(struct S));
 return 0;
}

在这里插入图片描述

三、结构体传参

struct S
{
	int data[100];
	int num;
};
struct S s = { {1,2,3,4}, 1000 };
void print(struct S* ps)
{
	printf("%d\n", ps->num);
}
int main()
{
	print(&s);
	return 0;
}

结构体传参的时候最好传一个地址,因为直接传一个结构体过去的话会造成时间和空间上不必要的开销,导致性能下降

四、结构体实现位段

位段的成员可以是int , unsigned int , signed int ,char类型的
位段不跨平台,可移植程序应该避免使用位段

struct S
{
	char a : 3;
	char b : 4;
	char c : 5;
	char d : 4;
};
struct S s = { 0 };
int main()
{
	s.a = 10;
	s.b = 12;
	s.c = 3;
	s.d = 4;
	printf("%d\n", s.a);
	printf("%d\n", s.b);
	printf("%d\n", s.c);
	printf("%d\n", s.d);

}

在这里插入图片描述
在这里插入图片描述

这里的a存入了10,由于位段作用,被存入a的二进制数为010,用整数形式打印,第一位为0,为正数,以第一位补位到32位,即00000000 00000000 00000000 00000010,即为2

这里的b存入了12,由于位段作用,被存入b的二进制数为1100,用整数形式打印,第一位为1,为负数,补1到11111111 11111111 11111111 11111100,这是补码,然后取反加一为原码,即10000000 00000000 00000000 00000100,即为-4

这里的c存入了3,由于位段作用,被存入c的二进制数为00011,用整数形式打印,第一位为0,为正数,以第一位补位到32位,即00000000 00000000 00000000 00000011,即为3

这里的d存入了4,由于位段作用,被存入a的二进制数为0100,用整数形式打印,第一位为0,为正数,以第一位补位到32位,即00000000 00000000 00000000 00000100,即为4

但是它空间的开辟是这样的:
在这里插入图片描述
第一个数据:二进制的01100010,十六进制的0x62
第二个数据:二进制的00000011,十六进制的0x03
第三个数据:二进制的00000100,十六进制的0x04

在这里插入图片描述
可以看到我们的结构体中存放的数据是62 03 04 00,与上述分析相符

跟结构体相比,位段可以达到同样的效果,并且可以很好的节省空间,缺点是有跨平台的问题存在

因为地址的分配是以字节为单位的,位段下的某些数据是没有地址的,所以位段数据不能用指针来访问


今天的分享就到这了~
在这里插入图片描述

标签:struct,int,C语言,char,对齐,字节,结构
From: https://blog.csdn.net/s_little_monster/article/details/136890829

相关文章

  • 自学-C语言-基础-注释、变量、运算符、判断、循环
    运行环境DevC++DevC++官网认识C语言C语言是一种通用的、面向过程式的计算机程序设计语言。1972年,为了移植与开发UNIX操作系统,丹尼斯·里奇在贝尔电话实验室设计开发了C语言。C语言是一种广泛使用的计算机语言,它与Java编程语言一样普及,二者在现代软件程序员......
  • C语言学习笔记day17
    1.结构体类型得定义      struct结构体名{         数据类型1成员变量1;         数据类型2成员变量2;         数据类型3成员变量3;         ...      };2.结构体变量得定义      存......
  • 使用C语言在VS 环境下基本实现贪吃蛇游戏
    使用C语言在VS环境下基本实现贪吃蛇游戏一丶实现前的准备工作1.设置vs运行环境为window控制台而非window终端1.正确的运行环境页面2.设置正确的运行环境2.了解句柄(下面代码能看明白会照葫芦画瓢用就行)3.利用system函数丶cmd命令设置window控制台窗口的尺寸4.了......
  • C语言中char字符型数据的存取形式:ASCII码之间的转换
    unsignedcharchannelNum=49;则编译器会将ASCII码49存入变量channelNum,实际channelNum表示字符1,所以下次如果以%c形式打印出来,则输出1。e.g:查看代码unsignedcharchannelNum=49;#include"bsp_seg.h"#include"bsp_Init.h"//------------------------------------//将s......
  • CHC5223数据结构和算法
    CHC5223数据结构和算法2023-2024第2学期课业1价值40%的课程个人工作学习成果学生将能够理解:1.1数据结构1.2数据结构的应用1.3面向对象编程概念1.4程序测试方法学生将掌握以下方面的技能:2.1数据抽象2.2数据结构的使用2.3使用高级面向对象语言进行更高级的编程2.4程序测试......
  • 椋鸟数据结构笔记#4:栈与队列
    萌新的学习笔记,写错了恳请斧正。目录栈栈的实现队列队列的实现循环队列栈栈是一种特殊的线性表,是一种遵循后进先出(LIFO,LastInFirstOut)原则的数据结构。想象一下一摞盘子,你最后放上去的盘子会是你第一个拿掉的;同样地,在栈中,最后存入的数据会是第一个被取出来的。......
  • c语言:用do-while输出前40项的斐波那契数值
    求Fibonacci数列的前40个元素。该数列的特点是第1、2两个数为1、1。从第3个数开始,每数是其前两个数之和。  分析:从题意可以用如下等式来表示斐波那契数列:     1,1,2,3,5,8,13,21…     f1=1     (n=1)     f2=1   ......
  • 深入理解C语言宏定义
    目录一、前言二、宏的相关语法2.1#define2.2#undef2.3#运算符2.4##运算符三、宏替换的规则四、宏与函数一、前言        我们都知道#define语句可以定义常量,在编译器预处理时会全部将名字替换为常量。与此同时,#define也允许把参数替换到文本中,这就是本......
  • 描述C语言中的进程和线程之间的区别
    描述C语言中的进程和线程之间的区别在C语言中,进程和线程是两个非常重要的概念,它们在操作系统中各自扮演着独特的角色。理解它们之间的区别对于编写高效、可维护的并发程序至关重要。下面将详细阐述进程和线程在C语言中的区别。首先,我们来探讨进程的概念。进程是操作系统分配......
  • COMP2017 9017 多类型链表数据结构
    COMP20179017课业2到期时间:2024年3月28日23:59这项任务相当于你最终评估的10%任务描述您的任务是创建一个多类型链表数据结构和与之交互的程序任务分为三个任务,必须按顺序完成。第一部分是链表的基本命令语法、创建、删除、查看等。第二部分是通过插入和删除元素来修改现有的列......