IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> 【C语言】结构体进阶详解 -> 正文阅读

[C++知识库]【C语言】结构体进阶详解

这里主要讲一些结构体得进阶知识,如果想了解得更多可以结合这篇博客一起学习——初始结构体

一.结构体的基本知识

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

二.结构得特殊声明

在声明结构得时候,可以不完全得声明。

例如:

//匿名结构体类型
struct
{
    int a;
    char b;
    float c;
}x;

struct
{
    int a;
    char b;
    float c;
}a[20],*p;
  • 此时定义得结构体变量x,结构体数组a,结构体指针变量p都可以完好得创建出来,但也只能使用这些变量,如果需要其它的变量只能继续在结构体的后边增加,否则只能使用单一的变量。
  • 匿名结构体一般只能用一次,创造后使用的结构体变量只用在结构体末尾处已经声明好的变量,以后就不会再用。
    写道这里我们会发现,上面创建的两个结构体的成员变量是相同的,连成员变量的顺序都是相同的。那么问题来了,这两个结构体的类型是否相同?

想要探究这个问题,我们只需执行下面的操作即可:

//使用结构体指针变量p接收结构体变量x的地址,若能接收说明它们的类型是相同的
p = &x;

执行后,VS编译器给出如下警告:
在这里插入图片描述
"="两端的类型是不兼容的,证明虽然它们的结构体是相同的,但它们是不同的类型

警告:
编译器会把上面的两个声明当成完全不同的两个类型
所以是非法的。

三.结构体的自引用

在结构体中包含一个类型为该结构体本身的成员是否可以呢?

struct Node
{
    int data;
    struct Node next;
};
//这样写是否可以?

这么写明显是不对的,一个结构体类型中包含着一个该结构体类型的成员变量,这么一直下去该类型创建的成员变量的大小将无法确定,这明显是不符合C语言语法的。
在这里插入图片描述

1.正确的自引用

struct Node
{
    int data;
    struct Node* next;
};
  • 在结构体类型中创建该结构体类型的指针成员变量,用改指针指向同类型的结构体成员。

2.问题

在前段时间我学习数据结构时,写了一个非常经典的错误写法,这里写出来共大家参考。

typedef struct Node
{
    int data;
    Nd* next;
}Nd;
  • 使用typedef创建了结构体类型Nd,使用次类型名代替原来的结构体类型struct Node
    这是一个非常金典的错误写发,因为Nd的使用是在它的声明之前的,使用时编译器不会识别Nd

3.解决办法

//方法1
typedef struct Node Nd;

typedef struct Node
{
    int data;
    Nd* next;
}Node;
  • 提前使用typedef将结构体类型名赋给Nd
//方法2
typedef struct Node
{
    int data;
    struct Node* next;
}Node;
  • 按部就班,在创建结构体指针成员变量时,使用最上面的结构体类型名创建。

结构体的自引用是在特殊情况使用的如:数据结构中的链表、二叉树等
如果对这方面的应用感兴趣可以点击链接了解一下线性表之单链表

四.结构体内存对齐

当我们明白了结构体的基本使用后,下一个问题就是:计算结构体的大小。
这是一个特别热门的考点:结构体对齐

1.偏移量

  • 很多学科都包含了偏移量的知识,它的作用范围很广。(这里只研究结构体,其他做了解)
  • 概念:存储单元的实际地址与其所在段的段地址之间的距离称为段内偏移,也称为“有效地址或偏移量”。
  • 通俗的讲,一个结构体类型的变量创建后,该变量的起始位置,到它所含的结构体成员变量的起始位置之间的距离(字节),就是该结构体成员变量的偏移量。

通过下面一个例子你就能明白:

struct text
{
	int a;
	char b;
}text;

我们创建了结构体类型变量text,方便求成员变量a和b的偏移量。

  1. a时第一个成员变量,所以它的偏移量就是0
  2. b时第二个成员变量,在它之前的变量a为int类型占4个字节,所以b的偏移量就是4.
    我们就能得到该结构体的大小为5

在这里插入图片描述

  • 当然结构体的大喜爱绝不是这么简单

2.offsetof宏

offsetof(type,member)
  • 此函数可以返回数据结构或联合类型中成员的偏移量(以字节为单位)
  • type:结构体类型(这里之研究结构体,type还可以表示其他类型)
  • member:属于对应类型的成员
    拿人我们一起看一下下面结构体类型的偏移量:
struct S1
{
	char a;
	int b;
	char c;
};

int main()
{
	struct S1 s1;
	printf("%d\n", offsetof(struct S1, a));
	printf("%d\n", offsetof(struct S1, b));
	printf("%d\n", offsetof(struct S1, c));

	return 0;
}

在这里插入图片描述

a时第一个元素,偏移量为0,而b和c的偏移量为什么时这样呢?
着源于结构体的对齐规则

3.结构体对齐规则

  1. 第一个成员在于结构体变量偏移量为0的地址处。
  2. 处第一个成员外的成员变量要偏移到某个数字(对齐数)的整数倍。
    对齐数 = 编译器默认的一个对齐数 于 该成员大小的比较直
    - VS中默认的对齐数为8
    - Linux环境默认不设对齐数(对齐数时结构体成员的自身大小)。
  3. 结构体总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍
  4. 如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。
  • 这里对齐数不能理解为偏移量,偏移量时从起始位置到目标位置的字节数,对齐数又成员变量类型和编译器默认值决定。

看了这些规则后,让我们接着上面的问题继续分析

a:是第一个成员,char类型只占一个字节
b:不是第一个成员,int类型占4个字节。按上边的规则,b的偏移量是某个数字(对齐数)的整数倍的地址处,4小于8(编译器默认对齐数),所以对齐数是4,要偏移到4的整数倍处,此时的偏移量为4的地址是空的,该位置又符合4的整数倍这一要求。
c:b从偏移量为4的地址处开始,一直到7,而c的类型为char占1个字节,所有数字都是1的整数倍,所以偏移量是8.
从0开始,此时一共使用9字节空间。
而结构体的大小又是最大对齐数的整数倍,a——1 , b——4 ,c——1,
最大对齐数为4,而9不是4的整数倍,需要继续偏移,最终,该结构体的大小为12.
在这里插入图片描述

//查看struct S1类型的大小
printf("%d\n", sizeof(struct S1));

在这里插入图片描述

4.练习

//练习1
struct S2
{
	char c1;
	char c2;
	int i;
};
	printf("%d\n", sizeof(struct S2));

在这里插入图片描述

  • 结构体S1(上面举例使用的结构体)和S2的结构体成员变量类型相同,但结构体大小不同,原因在于结构体成员变量的位置不同,我们写结构体时,可以将占用空间小的类型放在前面,更加节省空间。
//练习2
struct S3
{
    double d;
    char c;
    int i;
};
	printf("%d\n", sizeof(struct S3));

在这里插入图片描述

//练习3-结构体嵌套问题
struct S4
{
    char c1;
    struct S3 s3;
    double d;
};
	printf("%d\n", sizeof(struct S4));

在这里插入图片描述

  • 以练习3中struct S3为例,结构体中成员变量的地址时由底到高的
    在这里插入图片描述

五.为什么存在内存对齐

这里官方没有给出原因,大部分参考书给出的原因如下

  1. 平台原因(移植原因)
    不是所有的平台都能访问容易地址上的容易数据的;某些硬件平台只能在某些特点的地址处存储特点类型的数据,否则会抛出异常。

    比如因为硬件的原因当我们要定义一个整型的变量时,要求它必须存储在偏移量为4的倍数的位置,这样它的对齐数就只能是4.(基于一些特殊的平台)

  2. 性能原因
    数据结构(尤其是栈)应该尽可能地在自然边界上对齐。
    为了访问未对其的内存,处理器需要作两次访问;而对齐的内存仅需要一次访问。

    由于CPU和数据存储方式的原因,当一个整数,放在偏移量为1的位置上时,CPU需要访问两次,才能完全取出数据。大家要是对这个原理感兴趣,可以看一下我总结的这篇博客 探索未对齐内存CPU的访问逻辑

总体来说:

结构体的内存就是拿空间换取时间的做法

六.修改默认对齐数

使用#pragma预处理指令改变默认对齐数。

  • 设置默认对齐数:#pragma pack() —— 括号内设默认对齐数的值,值为整数,不能为空
  • 回复默认对齐数:#pragma pack() —— 括号内什么都不写
#include <stdio.h>
#pragma pack(8)//设置默认对齐数为8
struct S1
{
    char c1;
    int i;
    char c2;
};
#pragma pack()//取消设置的默认对齐数,还原为默认
#pragma pack(1)//设置默认对齐数为1
struct S2
{
    char c1;
    int i;
    char c2;
};
#pragma pack()//取消设置的默认对齐数,还原为默认
int main()
{
//输出的结果是什么?
    printf("%d\n", sizeof(struct S1));
    printf("%d\n", sizeof(struct S2));
    return 0;
}

在这里插入图片描述

七.结构体传参

struct S
{
    int data[1000];
    int num;
};
struct S s = {{1,2,3,4}, 1000};
//结构体传参
void print1(struct S s)
{
	printf("%d\n", s.num);
}
//结构体地址传参
void print2(struct S* ps)
{
	printf("%d\n", ps->num);
}
int main()
{
    print1(s); //传结构体
    print2(&s); //传地址
    return 0;
}

上述代码哪一种传参方式更好?

  • 地址传参更好
    原因

函数传参是,参会需要压栈,会有时间和空间上的系统开销。
如果传递一个结构体变量值时,如果该结构体变量过大,参数压栈的系统开销比较大,所以会导致性能的下降。
简单的说,传结构体变量过去,形参会复制一份实参的数据,当数据过大时,计算机性能就会下降。
而传地址,只是传递过去一个地址,在32位平台下,地址的大小时固定的,占4个字节。

八.总结

  • 在学习和做题过程中结构体是非常重要的,每当遇到一个对象有多种特性的问题时,都可以用结构体来解决。
  • 该博客结构体的内存对齐那一块是面试时的高频考点,务必要掌握。
  • 结构体应用在学校生涯中应当是数据结构,在学习数据结构之前大家要一起加油打好基础。
    在这里插入图片描述
  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2022-09-24 20:38:18  更:2022-09-24 20:40:46 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/19 15:34:37-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码