C语言自定义类型:结构体的使用及其内存对齐【超详细建议点赞收藏】

这篇具有很好参考价值的文章主要介绍了C语言自定义类型:结构体的使用及其内存对齐【超详细建议点赞收藏】。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

1. 结构体类型的声明

结构是一些值的集合,这些值称为成员变量结构的每个成员可以是不同类型的变量

1.1 结构的声明

C语言自定义类型:结构体的使用及其内存对齐【超详细建议点赞收藏】,c语言,开发语言
注意:

  1. 成员列表可以是不同类型的变量;
  2. 成员后一定要有分号;
  3. 花括号后也有一个分号。

例如描述一个学生:

struct Stu
{
	char name[20];//姓名
	int age;//年龄
	char tele[12];//电话
	char sex[3];//性别
};//分号不能丢

注意:上述代码没有创建变量,也没有初始化,只是声明了一个结构体类型,就像int,float一样,只是一种类型。

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

接下来我们可以用结构体类型创建变量

方式1:
声明类型的同时创建,这是全局变量

struct Stu
{
	char name[20];//姓名
	int age;//年龄
	char tele[12];//电话
	char sex[3];//性别
}s1,s1;

方式2:
我们也可以在函数内部创建局部变量

# include <stdio.h>

struct Stu
{
	char name[20];//姓名
	int age;//年龄
	char tele[12];//电话
	char sex[3];//性别
};

int main()
{
	struct Stu s3;//s3是局部变量

	return 0;
}

我们再对结构体变量进行初始化:
方式1:直接初始化

struct Point
{
	int x;
	int y;
}p1 = { 2,3 };


 struct Stu
{
	char name[20];//姓名
	int age;//年龄
 }s1 = { "zhangsan",27 };
 

方式2:也可以再函数内部进行初始化

struct Stu
{
	char name[20];//姓名
	int age;//年龄
};

int main()
{
	struct Stu s1 = { "zhangsan",24 };

	return 0;
}

当然,也有结构体的嵌套:

struct score
{
	int n;
	char ch;
};

struct Stu
{
	char name[20];//姓名
	int age;//年龄
	struct score s;//嵌套一个结构体变量
};


int main()
{
	struct Stu s1 = { "zhangsan",24 ,{45,'a'} };

	return 0;
}

我们也可以将其打印出来:

struct score
{
	int n;
	char ch;
};

struct Stu
{
	char name[20];//姓名
	int age;//年龄
	struct score s;
};


int main()
{
	struct Stu s1 = { "zhangsan",24 ,{45,'a'} };

	printf("%s %d %d %c\n", s1.name, s1.age, s1.s.n, s1.s.ch);

	return 0;
}

打印结果:
C语言自定义类型:结构体的使用及其内存对齐【超详细建议点赞收藏】,c语言,开发语言

1.3 结构的特殊声明—匿名结构体

在声明结构的时候,可以不完全声明:

struct 
{
	char name[20];//姓名
	int age;//年龄
	char tele[12];//电话
	char sex[3];//性别
};

上面的结构在声明时省略了结构体标签,我们称为匿名结构体

注意:匿名结构体只能通过创建全局变量使用一次!!

# include <stdio.h>

struct 
{
	char name[20];//姓名
	int age;//年龄
	char tele[12];//电话
	char sex[3];//性别
}s1;

int main()
{

	return 0;
}

1.4 结构的自引用

在结构体中包含一个类型为该结构体本身的成员是否可以呢?比如,定义一个链表的结点:

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

上述代码正确吗?如果正确,那么sizeof(struct Node)是多少呢?
仔细分析,其实是不行的,因为⼀个结构体中再包含⼀个类型的结构体变量,这样结构体变量的大小就会无穷的大,是不合理的。

正确的自引用方式:

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

这样我们就把一个结点分成两部分,一部分存放数据,叫数据域,另一部分存放下一个结点的地址,叫指针域。

在结构体自引用使用的过程中,夹杂了 typedef 对匿名结体类型重命名,也容易引入问题,看看下面的代码,可行吗?

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

这种写法语法是不支持的。
因为Node是对前面的匿名结构体类型的重命名产生的,但是在匿名结构体内部提前使用Node类型来创建成员变量,这就产生了"先有鸡还是先有蛋"的问题,这是不行的。

解决方案如下:定义结构体不要使用匿名结构体了

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

2.结构体内存对齐(重点!!)

上面我们了解了结构体的基本使用。
接下来我们再讨论一个问题 :计算结构体的大小。

2.1 对齐规则

首先要知道结构体的对齐规则:

  1. 第一个成员在结构体变量偏移量为0的地址处。

  2. 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。
    注意:
    (1) 对齐数=编译器默认的对齐数与该成员大小的较小值
    (1)VS中默认值为8(注意:其他编译器是没有默认对齐数的,对齐数就是自身大小。)

  3. 结构体总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍

  4. 如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐的整数倍处。
    结构体的整体大小就是所有最大对齐数的(含嵌套结构体的对齐数)的整数倍。

2.2 例题讲解

例1:

#include <stdio.h>

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

int main()
{
	struct S1 s1;
	printf("%d\n", sizeof(struct S1));
	
	return 0;
}

输出结果:
C语言自定义类型:结构体的使用及其内存对齐【超详细建议点赞收藏】,c语言,开发语言

画图解释:
C语言自定义类型:结构体的使用及其内存对齐【超详细建议点赞收藏】,c语言,开发语言

例2:

#include <stdio.h>

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

int main()
{
	struct S2 s1;
	printf("%d\n", sizeof(struct S2));
	
	return 0;
}

输出结果:
C语言自定义类型:结构体的使用及其内存对齐【超详细建议点赞收藏】,c语言,开发语言

画图解释:
C语言自定义类型:结构体的使用及其内存对齐【超详细建议点赞收藏】,c语言,开发语言
例3:

#include <stdio.h>

struct S3
{
	double d;
	char c;
	int i;
};

int main()
{
	struct S3 s3;
	printf("%d\n", sizeof(struct S3));
	
	return 0;
}

输出结果:
C语言自定义类型:结构体的使用及其内存对齐【超详细建议点赞收藏】,c语言,开发语言

画图解释:
C语言自定义类型:结构体的使用及其内存对齐【超详细建议点赞收藏】,c语言,开发语言
例4:结构体嵌套问题

#include <stdio.h>

struct S4
{
	char c1;
	struct S3 s3;
	double d;
};

int main()
{
	struct S4 s4;
	printf("%d\n", sizeof(struct S4));

	return 0;
}

输出结果:
C语言自定义类型:结构体的使用及其内存对齐【超详细建议点赞收藏】,c语言,开发语言

画图解释:
C语言自定义类型:结构体的使用及其内存对齐【超详细建议点赞收藏】,c语言,开发语言

2.3 为什么存在内存对齐?

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

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

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

那在设计结构体的时候,我们既要满足对齐,又要节省空间,如何做到:

让占用空间小的成员尽量集中在⼀起
如例1和例2,s1和s2的成员一模一样,但两者所占空间大小却不同。

2.4 修改默认对齐数

  • #pragma : 预处理指令,可以改变编译器的默认对齐数。
  • 使用时要引用头文件<stddef.h>
  • 注意:一般修改的对齐数都为2的n次方。不会修改为1,3,5……或负数。

例如还是用例1来说明:

#include <stdio.h>
#include <stddef.h>

#pragma pack(1)//设置默认对齐数为1
struct S1
{
	char c1;
	int i;
	char c2;
};
#pragma()//取消设置的对齐数,还原为默认

int main()
{
	printf("%d\n", sizeof(struct S1));
}

输出结果:
C语言自定义类型:结构体的使用及其内存对齐【超详细建议点赞收藏】,c语言,开发语言
修改之前大小是12个字节,修改后变成了6字节。

3. 结构体传参

方式1:传值调用。访问结构体时用点(.)操作符。

#include <stdio.h>

struct S
{
	int data[1000];
	int num;
};

void print1(struct S ss)
{
	int i = 0;
	for (i = 0; i < 3; i++)
	{
		printf("%d ", ss.data[i]);
	}
	printf("%d\n", ss.num);
}

int main()
{
	struct S s = { {1,2,3},200 };
	print1(s);  //传值调用,直接传变量名
	
	return 0;
}

方式2:传址调用。访问结构体时用箭头(->)操作符。

#include <stdio.h>

struct S
{
	int data[1000];
	int num;
};

void print2(const struct S* ps)
{
	int i = 0;
	for (i = 0; i < 3; i++)
	{
		printf("%d ", ps->data[i]);
	}
	printf("%d\n", ps->num);
}

int main()
{
	struct S s = { {1,2,3},200 };
	print2(&s); //传址调用,传变量名的地址

	return 0;

输出结果都为:
C语言自定义类型:结构体的使用及其内存对齐【超详细建议点赞收藏】,c语言,开发语言

上面的两种方式哪个更好呢?
答案是:传址调用更好。

原因:
函数传参的时候,参数是需要压栈,会有时间和空间上的系统开销。
如果传递⼀个结构体对象的时候,结构体过大,参数压栈的的系统开销比较大,所以会导致性能的下降。

结论:
结构体传参的时候,要传结构体的地址文章来源地址https://www.toymoban.com/news/detail-839428.html

到了这里,关于C语言自定义类型:结构体的使用及其内存对齐【超详细建议点赞收藏】的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【C语言高阶篇】C语言面试必考知识点,结构体的内存对齐我看还有谁不会!

    🎬 鸽芷咕 :个人主页  🔥 个人专栏 :《C语言初阶篇》 《C语言进阶篇》 ⛺️生活的理想,就是为了理想的生活!    🌈 hello! 各位宝子们大家好啊,今天给大家带来的是结构体的内存对齐这部分知识,可以说是干货满满啦!    ⛳️ 在我们C语言的面试题中,结构体最

    2024年02月16日
    浏览(48)
  • C语言——结构体类型(二)【结构体内存对齐,结构体数组】

    📝前言: 上一讲结构体类型(一)中,我们讲述了有关 结构体定义,创建,初始化和引用 的内容,这一讲,我们进一步学习结构体的相关知识: 1,结构体内存对齐 2,结构体数组 🎬个人简介:努力学习ing 📋个人专栏:C语言入门基础 🎀CSDN主页 愚润求学 🌄每日鸡汤:

    2024年01月24日
    浏览(71)
  • 深入了解C语言中的结构体类型与内存对齐

    在C语言中,结构体是一种 自定义的数据类型 , 它允许我们将不同类型的数据组合在一起,形成一个新的数据类型 。结构体的使用为我们解决了一些复杂数据的表示和处理问题,不仅限于单单的整型或者字符。本文将深入探讨结构体类型、结构体变量的创建和初始化,并详

    2024年04月11日
    浏览(43)
  • C语言结构体的定义与使用

    参考:C语言指针 1.定义与使用 结构体是一种构造数据类型,把不同类型的数据组合成一个整体 结构体的定义形式:   结构体是一种集合,它里面包含了多个变量或数组,它们的类型可以相同,也可以不同,每个这样的变量或数组都称为结构体的成员(Member)。请看下面的

    2024年02月05日
    浏览(49)
  • 结构体和数据结构--从基本数据类型到抽象数据类型、结构体的定义

            在冯-诺依曼体系结构中,程序代码和数据都是以二进制存储的,因此对计算机系统和硬件本身而言,数据类型的概念其实是不存在的。         在高级语言中,为了有效的组织数据,规范数据的使用,提高程序的可读性,方便用户使用,引入了整型、实型等基本数

    2024年02月11日
    浏览(48)
  • 【C语言】自定义类型:结构体【结构体内存具详细】,枚举,联合

      目录 一、结构体 1.结构的声明  2.特殊的声明 3.结构的自引用 4.结构体变量的定义和初始化   5.结构体内存对齐(重点来了) 6.为什么会存在内存对齐  7.修改默认对齐数 8.结构体传参 二、位段 1.什么是位段 2.位段的内存分配 3.位段的跨平台问题 三、枚举 1.枚举类型的定

    2024年02月07日
    浏览(49)
  • C语言结构体字节对齐(内存对齐)之#pragma pack和__attribute__((packed)的使用

    在不使用 #pragma pack 和 __attribute__((packed) 等选项来自定义字节对齐大小的情况下,关于正常字节对齐的描述,可参考博文: C/C++计算类/结构体和联合体(union)所占内存大小(内存对齐问题)_联合体占用的内存空间_SOC罗三炮的博客-CSDN博客 同学可以尝试将char f 注释,最后将

    2024年02月12日
    浏览(46)
  • 结构体的三种定义方法、结构体类型名(可选标志符)什么时候可以省略

    一、单独定义:   先定义结构体类型,再定义变量   定义结构体的格式如下:    struct 结构体名 {    若干数据项;    } ;   其中,struct为; 结构体名是用户定义的类型标识。 { }中是组成该结构体的成员。成员的数据类型可以是C语言所允许的任何数据

    2024年02月05日
    浏览(51)
  • C语言之结构体内存对齐与内存的简单理解

    文章目录 内存单元的理解 结构体中内存对齐的规则 为什么会存在内存对齐 首先先要介绍一下C语言中一些常见的存储单元     bit       存放一个二进制位     Byte   1Byte = 8 bit     KB     1KB   = 1024 Byte     MB     1MB   = 1024 KB     GB     1GB   = 1024 MB     TB 

    2023年04月26日
    浏览(44)
  • 【C语言高阶篇】结构体 —— 什么是内存对齐?

    🎬 鸽芷咕 :个人主页  🔥 个人专栏 :《C语言初阶篇》 《C语言进阶篇》 ⛺️生活的理想,就是为了理想的生活!    🌈 hello! 各位宝子们大家好啊,今天给大家带来的是结构体的内存对齐这部分知识,可以说是干货满满啦!    ⛳️ 在我们C语言的面试题中,结构体最

    2024年02月14日
    浏览(51)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包