我的编程空间,编程开发者的网络收藏夹
学习永远不晚

详解C语言结构体,枚举,联合体的使用

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

详解C语言结构体,枚举,联合体的使用

一、匿名结构体

struct
{
    char name[20];
    int age;
}s1;

匿名结构体对象s1过了这一行即销毁。

二、结构体的自引用

1、声明时不要自己引用自己

struct Node
{
 int data;
 struct Node next;//错误的,严禁自己引用自己
};
 
 
struct Node
{
 int data;
 struct Node* next;//正确的引用方式
};

2、结构体重命名时不能使用重命名

typedef struct
{
 int data;
 Node* next;//错误的,不要再重命名中使用重命名
}Node;
 
typedef struct Node
{
 int data;
 struct Node* next;//正确的
}Node;

博主在学数据结构的时候踩过这个坑,在结构体重命名的时候成员变量的类型就使用了重命名,导致整个程序不认识这个成员变量的类型(但是vs在typedef这里不报错,而是在每个使用这个类型的地方报错!!!)。后来把这个成员变量的类型修改为重命名之前的类型,整个程序就可以运行了。(如上图的正确写法)

三、结构体内存对齐规则

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

后续成员变量要放到各自的对齐数的倍数上。对齐数 = 编译器默认对齐数与该成员类型大小的较小值。(vs中默认对齐数是8,gcc没有默认对齐数)

结构体最终大小为最大对齐数的整数倍。

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

1、结构体内存计算

struct S1
{
    char c1;
    int i;
    char c2;
};
int main()
{
    printf("%d\n", sizeof(struct S1));
    return 0;
}

char c1在结构体变量的零偏移量处分配内存

int i的对齐数为4,所以跳过3个字节,在4的整数倍地址处分配内存

char c2的对齐数为1,使用下一个字节空间即可

目前已使用9字节

由于该结构体中所有成员变量中最大的成员类型大小为4字节,所以最大内存对齐数为4字节,结构体总大小为最大对齐数的整数倍。所以该结构体内存为12字节。

可以使用宏offsetof来观察结构体成员在内存中的偏移量:

2、结构体嵌套

struct S3//16
{
 double d;
 char c;
 int i;
};
struct S4//32
{
 char c1;
 struct S3 s3;
 double d;
};

char c1在结构体变量的零偏移量处分配内存

struct S3 s3按照其最大内存对齐数(此处为8)进行对齐

double d按照其最大内存对齐数(8)进行对齐

S4的最大内存对齐数为8,所以结构体的最终大小为32

3、通过调整结构体成员顺序,压缩内存

通过上述例子可以发现,结构体成员之间有很大的空间浪费,哪怕是拥有相同结构体成员的两个结构体类型,其在内存中所占据的空间也不相同,所以为了空间的节省,在不影响数据结构的情况下,有目的的把字节占用小的成员变量放在一起,达到节省空间的目的。

四、存在内存对齐的原因

1. 平台原因(移植原因)

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

2. 性能原因(空间换时间)

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

五、修改默认对齐数

#pragma pack(2)//把默认对齐数改成2
struct S
{
    char c1;
    int i;
    short c2;
};
#pragma pack()//恢复默认对齐数为8
int main()
{
    printf("%d\n", sizeof(struct S));
    return 0;
}
#pragma pack(num)修改默认对齐数,该结构体的内存大小由12字节降低为8字节。

默认对齐数尽量为2的次方。

六、结构体传参

结构体传参要传地址。

传址调用优于传值调用的原因是地址占4/8个字节。

但是传值调用参数需要压栈,当结构体过大时,参数压栈的系统开销较大。

七、位段

位段是在结构体中实现的。

位段的成员可以是 int、unsigned int、signed int或者是char类型

位段的空间增长方式为每次增长4个字节(int)或1个字节(char)

位段涉及很多不确定因素,位段是不跨平台的,注重可移植的程序应该避免使用位段。

1、位段在内存中的存储

1.1位段中char类型的存储方式(vs中舍弃剩余空间)

struct S//占用3个字节
{
    char _a : 3;
    char _b : 4;
    char _c : 5;
    char _d : 4;
};
int main()
{
    printf("%d\n", sizeof(struct S));
    struct S s= { 0 };
    s._a = 10;//1010,截断为010
    s._b = 12;//1100
    s._c = 3;//0011
    s._d = 4;//0100
    return 0;
}

通过调用内存发现,&s中存储的16进制数字为62 03 04,那么可以发现s在内存中的存储方式如下图:

在vs环境中,char成员变量在单个字节中是倒着存储的(有截断先发生截断),当该字节中剩余的比特位不足以存放下一个完整的成员变量时,会将剩余的比特位舍弃。

1.2位段中int类型的存储方式(vs中利用剩余空间)

struct A//占4个字节
{
    int a : 2;
    int b : 3;
    int c : 4;
};
int main()
{
    struct A s = { 0 };
    s.a = 12;//1100,截断为00
    s.b = 13;//1101,截断为101
    s.c = 14;//1110
    return 0;
}

通过调用内存发现,&s中存储的16进制数字为d4 01 00 00,那么可以发现s在内存中的存储方式如下图:

在vs环境中,int成员变量在单个字节中是倒着存储的(有截断先发生截断),当该字节中剩余的比特位不足以存放下一个完整的成员变量时,会将继续存储,存不下的二进制位将存放至下一个字节的右侧。

注意:位段冒号后面的数字只能小于等于类型大小(例如char a:9是错误的)

2、位段的跨平台问题

1.int 位段被当成有符号数还是无符号数是不确定的。

2. 位段中最大位的数目不能确定。(16位机器最大16,32位机器最大32,写成27,在16位机器会出问题。

3. 位段中的成员在内存中从左向右分配,还是从右向左分配标准尚未定义。

4. 当一个结构包含两个位段,第二个位段成员比较大,无法容纳于第一个位段剩余的位时,是舍弃剩余的位还是利用,这是不确定的。

八、枚举

1、枚举的定义

enum color
{
    RED,//枚举常量
    BLUE,
    YELLOW
};

不赋值那么默认从0开始,后续枚举成员的值递增1

enum color
{
    RED=1,
    BLUE,
    YELLOW
};

只需要对第一个成员进行赋值,后续枚举成员的值递增1

在写枚举成员的时候建议全大写,博主在写通讯录枚举了exit,使用时vs提示该命名和exit函数冲突。

2、枚举的优点

增加代码的可读性和可维护性

枚举使用时有类型检查,#define定义的标识符没有

防止了命名污染(封装)

便于调试(#define定义宏在预处理时是直接替换)

使用方便,一次可以定义多个常量

九、联合体(共用体)

联合也是一种特殊的自定义类型 这种类型定义的变量也包含一系列的成员,特征是这些成员共用同一块空间(所以联合也叫共用体)。

1、联合体大小的计算

#include <stdio.h>
union un
{
    char arr[5];
    int a;
}u;
int main()
{
    printf("%d", sizeof(u));//8
    return 0;
}

联合体的大小至少是最大成员的大小。

最大内存对齐数的整数倍要大于等于最大成员的大小。

(这里最大成员是arr,占5个字节,最大内存对齐数是4,所以需要为祖国联合体开辟8个字节空间)

2、使用联合体判断计算机的大小端字节序

#include <stdio.h>
union un
{
    int m;
    char n;
}u;
int check_sys()
{
    u.m = 1;
    return u.n;
}
 
int main()
{
    int a = check_sys();
    if (a == 1)
        printf("小端存储\n");
    else
        printf("大端存储\n");
    return 0;
}

以上就是详解C语言结构体,枚举,联合体的使用的详细内容,更多关于C语言 结构体 枚举 联合体的资料请关注编程网其它相关文章!

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

详解C语言结构体,枚举,联合体的使用

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

C语言结构体,枚举,联合体如何使用

本篇内容介绍了“C语言结构体,枚举,联合体如何使用”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!一、匿名结构体struct{ char
2023-07-02

C语言中联合体与共用体和枚举使用语法示例

这篇文章主要介绍了C语言中联合体与共用体和枚举使用语法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习吧
2022-12-26

C语言中枚举和联合体的示例分析

这篇文章主要介绍了C语言中枚举和联合体的示例分析,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。枚举什么是枚举?顾名思义,就是一一列举,把所有的情况,所有的取值,一一列举出来。
2023-06-25

C++中的结构体和枚举怎么使用

本篇内容主要讲解“C++中的结构体和枚举怎么使用”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“C++中的结构体和枚举怎么使用”吧!1、结构体(struct)的使用使用struct定义一个结构:s
2023-06-17

怎么在C语言中自定义结构体和枚举

这篇文章将为大家详细讲解有关怎么在C语言中自定义结构体和枚举,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。C语言是什么C语言是一门面向过程的、抽象化的通用程序设计语言,广泛应用于底层开发,使
2023-06-14

c语言结构体和联合体的区别是什么

结构体和联合体在C语言中都是用来存储多个不同类型的数据的数据结构,但它们之间有一些重要的区别:结构体(struct):结构体中的每个成员都有自己的内存空间,因此结构体的大小等于所有成员大小的总和。结构体的成员在内存中是连续存储的,可以通过成
c语言结构体和联合体的区别是什么
2024-03-15

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录