入门学习计算机第十八天——自定义数据类型(结构体)

article/2025/9/22 17:37:39

入门学习计算机第十八天——自定义数据类型(结构体)

编译器:Microsoft Visual Studio 2019

自定义类型:

  • 结构体
  • 枚举
  • 联合体

结构体

结构体类型的声明

结构的基础知识
结构是一些值的集合,这些值称为成员变量

结构的声明

struct tag
{member-list;//成员列表
}variable-list;//变量列表

例如:

//声明一个结构体类型
//声明一个学生类型,是想通过学生类型来创建学生变量(变量)
//描述学生: 属性+名字+电话+性别+年龄
struct Stu
{char name[20];char tele[12];char sex[10];int age;
}s4,s5,s6;//全局变量,与s3同理
struct Stu s3;//全局变量
int main()
{struct Stu s1;struct Stu s2;//创建结构体变量return 0;
}

特殊的声明:
匿名结构体类型

struct
{int a;int b;int c;
}x;
struct 
{int a;int c;
}sa;
struct
{int a;int c;
}*psa;
int main()
{psa = &sa;return 0;
}

编译器此时会报警告:
在这里插入图片描述
编译器会把上述两个声明当成完全不同的两个类型,所以是非法的

结构体的自引用

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

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

是否可行?
编译器此时也会报错。无法确认结构体类型的大小

正确的自引用方式

struct Node
{int data;struct Node* next;//指针,大小为4或者8个字节0
};

结构体变量的定义和初始化

struct S//结构体变量的定义
{char c;int a;double b;char arr[20];
};int main()
{struct S s = { 'c',100,3.0,"hello bit" };//结构体变量的初始化printf("%c %d %lf %s\n", s.c, s.a, s.b, s.arr);//结构体成员的访问

输出的结果:
在这里插入图片描述

结构体内存对齐

已经掌握了结构体的基本使用了
现在深入讨论一个问题:计算结构体的大小
这也是一个特别热门的考点:结构体内存对齐

以下输出的结果是?

struct S1
{char c1;int a;char c2;
};
struct S2
{char c1;char c2;int a;
};
int main()
{struct S1 s1 = { 0 };printf("%d\n", sizeof(s1));//struct S2 s2 = { 0 };printf("%d\n", sizeof(s2));return 0;
}

在这里插入图片描述
为什么是12和8?
如何计算?首先要掌握结构体的对齐规则

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

  2. 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处
    (对齐数 = 编译器默认的一个对齐数与 该成员大小的较小值
    vs的中默认值为8)

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

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

在struct s1中

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

第一个成员变量要对齐到结构体变量为0的地址处,也就是最一开始的地址,类型是char,所以只占1个字节
第二个成员变量要对齐到对齐数的整数倍的地址处,第二个变量是int ,该成员的大小是4,小于vs的默认值,所以对齐数就是4,所以要在上一个成员的地址处空出到地址为4的地址,此时是1倍的关系。
第三个成员变量同理,变量类型是char,对齐数是1,倍数为1,所以在第二个成员变量的下一个地址处。
此时3个成员的大小为9,
但又因为结构体总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍, 最大的对齐数是4,只有当是12的时候,大小才是最大对齐数的整数倍。
在这里插入图片描述
同理s2

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

第一个成员变量要对齐到结构体变量为0的地址处,也就是最一开始的地址,类型是char,所以只占1个字节.
第二个成员变量要对齐到对齐数的整数倍的地址处,第二个变量是char,该成员的大小是1,小于vs的默认值,所以对齐数就是1,所以要在上一个成员的地址处空出到地址为1的地址,此时是1倍的关系。
第三个成员变量要对齐到对齐数的整数倍的地址处,第三个变量是int,该成员的大小是4,小于vs的默认值,所以对齐数就是4,所以要在上一个成员的地址处空出到地址为4的地址,此时是1倍的关系。
此时,占用的大小是8,已经是最大对齐数的整数倍,所以8就是结构体变量S2的大小。
在这里插入图片描述

struct S3
{double d;char c;int a;
};
struct S4
{char c1;struct S3;double d;
};
int main()
{struct S3 s3 = { 0 };printf("%d\n", sizeof(s3));struct S4 s4 = { 0 };printf("%d\n", sizeof(s4));return 0;
}

输出的结果是 16\32
主要讲s4的大小,因为其中有嵌套
首先是第一个成员在与结构体变量为0的地址处,因为是char类型,占一个字节。
第二个成员嵌套了结构体,其中嵌套的struct s3最大对齐数是其中的double对应的8,所以第二个成员在8的地址对齐,第二个成员的大小又是16。
第三个成员是double类型,前两个成员的地址加起来是8+16,是8的倍数,所以直接在后面加入8个字节。
8+16+8=32
为什么存在内存对齐?
1,平台原因(移植原因)
不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常
2.性能原因
数据结构(尤其是栈)应该尽可能地在自然边界上对齐。原因在于 ,为了访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要一次访问。
总体来说:
结构体的内存对齐是拿空间来换取时间的做法。
在这里插入图片描述
在32位机器下,一次可以访问4个字节的内容,如果没有内存对齐,c与a是紧挨着存储的,在一次访问时,访问了c以及a的一部分内存,读取一次就可以得到c。再读取a时,先从读取过的部分再向后读取4个字节,但只有a的后半部分,需要再读取一次,获取a的前半部分。所以再读取a的时候需要两次。
如果有内存对齐,再牺牲了一定的内存之后,a与c都只需要一次访问即可。

那么在设计结构体的时候,既要满足对齐,又要节省空间:

让占空间小的成员尽量集中在一起

修改默认对齐数
#pragma 这个预处理指令,可以用来改变默认对齐数

#pragma pack(4)
//设置默认对齐数为4
struct S
{char c1;double d;
};
#pragma ()
//取消设置的默认对齐数
int main()
{return 0;
}

offsetof宏 需要引头文件<stddef.h>
求结构体成员偏移量

struct S
{char c1;int a;double d;
};int main()
{printf("%d\n",offsetof(struct S,c1));printf("%d\n",offsetof(struct S,a));printf("%d\n",offsetof(struct S,d));return 0;
}

输出的结果是 0,4,8

结构体传参

struct S
{char c1;int a;double d;
};
void Inits(struct S tmp)
{tmp.c1 = 'w';tmp.a = 100;tmp.d = 3.14;
}
void Inits1(struct S* ps)
{ps->c1 = 'w';ps->a = 100;ps->d = 3.14;
}
void print(struct S tmp)
{printf("%c %d %lf\n", tmp.c1, tmp.a, tmp.d);
}
void print1(const struct S* ps)
{printf("%c %d %lf\n",ps->c1,ps->a,ps->d);
}
int main()
{struct S s = { 0 };Inits(s);Inits1(&s);print(s);print1(&s);return 0;
}

函数Inits 传的是s的值,函数中tmp改变只是tmp本身的值,没有对s进行改变,
函数Inits1 传的是s的地址,函数中ps指向s的值,对s直接进行改变。
函数print函数传的是s的值,函数中tmp是s的临时拷贝,对其可以打印出s的值,但是拷贝占用新的空间
函数print1函数传的是s的地址,函数中ps指向s对其进行打印,没有占用额外的空间。但是可能会把s的值进行修改,所以需要const修饰

结构体实现位段(结构体的填充&可移植性)

什么是位段?

位段的声明和结构是类似的,有两个不同:

  1. 位段的成员必须是int , unsigned int 或 signed int
  2. 位段的成员名后边有一个冒号和一个数字
//位段:二进制位
struct A
{int _a : 2;//只需要2个bitint _b : 5;//只需要5个bitint _c : 10;//只需要10个bitint _d : 30;//只需要30个bit
};
//47bit 一共是6个字节,但是答案是8个字节?
//
int main()
{struct A a;printf("%d\n", sizeof(a));//8return 0;
}

位段的内存分配

  1. 位段的成员可以是int , unsigned int , signed int 或者是char(属于整形家族)类型
  2. 位段的空间是按照需要以4个字节(int)或者1个字节(char)的方式来开辟的
  3. 位段涉及很多不确定因素,位段是不跨平台的,注重可移植的程序应该避免使用位段

结构体变量首先开辟一个整形大小的空间(32个bit),首先分配给a 2个bit,b 5个bit,c 10个bit,一共先分配了17个bit,剩下的15个bit无法存放d,所以剩下的空间浪费掉了。再开辟新的整形大小空间用来存放d,分配给d 30个bit,剩下2个bit也浪费掉了。所以一共是开辟了两个整形大小的空间,一共8个字节
在这里插入图片描述

位段的跨平台问题

  1. int 位段被当成有符号数还是无符号数是不确定的
  2. 位段中最大位的数目不能确定
  3. 位段中的成员在内存中从左向右分配,还是从右向左分配标准尚未定义
  4. 当一个结构包含两个位段,第二个位段成员比较大,无法容纳第一个位段剩余的位时,是舍弃剩余的位还是利用,这是不确定的。

在这里插入图片描述

枚举

枚举类型的定义

enum Day//enum 枚举关键字
{Mon,Tues,...
};

枚举的优点

可以使用#define 定义常量,为什么非要使用枚举?

  1. 增加代码的可读性和可维护性
  2. 和#define定义的标识符比较枚举有类型检查,更加严谨
  3. 防止了命名污染(封装)
  4. 便于调试
  5. 使用方便,一次可以定义多个常量

枚举的使用

enum color//颜色
{RED = 1,GRERN = 2,BLUE = 4,};
int main()
{enum color clr = GRERN;return 0;
}

联合

联合类型的定义

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

union Un
{char c;int i;
};
int main()
{union Un u;printf("%d\n", sizeof(u));printf("%p\n", &(u.c));printf("%p\n", &(u.i));printf("%p\n", &u);return 0;
}

在这里插入图片描述

联合的特点

联合的成员是共用同一块内存空间的,这样的一个联合变量的大小,至少是最大成员的大小(因为联合至少得有能力保存最大的那个成员)
用联合的特点求出该机器的字节序存储模式

int check_sys()
{union Un{char c;int i;}u;u.i = 1;return u.c;
}
int main()
{int ret = check_sys();if (1 == ret){printf("小端\n");}else{printf("大端\n");}
}

输出的结果是小端

联合大小的计算

  • 联合的的大小至少是最大成员的大小
  • 当最大成员大小不是最大对齐数的整数倍的时候,就要对齐到最大对齐数的整数倍
union Un
{int a;//对齐数是4char arr[5];//对齐数是1,而不是5
};
int main()
{union Un u = { 0 };printf("%d\n", sizeof(u));return 0;
}

输出的结果是8


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

相关文章

【计算机基础】

文章目录 线程、协程的状态 计算机网络DNS解析 计算机基础进程间通讯的7种方式讲一下程序的虚拟内存、常驻内存和逻辑内存。堆和栈是存啥的&#xff1f;什么是大小端? 什么是死锁&#xff1f;什么情况下会发生&#xff08;避免&#xff09;死锁&#xff1f;死锁产生的4个必要条…

计算机基础(1)

目录 一、计算机发展 1.1计算机发展史 1.2计算机分类 1.2.1按规模划分 1.2.2服务器与客户机 1.2.2.1 服务器按外形分类 1.3服务器硬件 一、计算机发展 1.1计算机发展史 1、第一代计算机(1946-1957) 电子管时代 冯诺依曼体系结构&#xff1a;数字计算机的数制采用二进…

计算机基础介绍

1、组成部分 中央处理器、内存、存储设备、输入/输出设备、通信设备&#xff08;各部分通过总线来连接&#xff09; 2、冯诺依曼体系结构&#xff1a;现代计算机的基础 3、中央处理器&#xff08;CPU&#xff09;——计算机的大脑 &#xff08;1&#xff09;作用&#xff1a…

C语言基础入门——自定义类型

学习目标&#xff1a; 学习并了解C语言自定义类型 目录 学习目标&#xff1a; 一、结构体 ●结构体的声明 ●结构体的引用 ●结构体的定义和初始化 ●结构体的内存对齐 ●结构体的传参 ●结构体的位段&#xff08;位段的可填充和可移植性&#xff09; 二、枚举 ●枚举的定…

1.计算机基础

目录 1.1计算机系统 1.3 计算机硬件 1.4 计算机常用计数制 1.5 数据表示 1.1计算机系统 计算机多种形式&#xff1a;电脑&#xff0c;笔记本&#xff0c;智能手机&#xff0c;服务器等&#xff1b; 两部分组成&#xff1a;硬件系统和软件系统。 1.1.1 世界上第一台计算机 世…

一、计算机基础

计算机基础 一、第 1 章 计算机基础知识1-1 计算机概述1-1-1 计算机的产生与发展&#xff08;一&#xff09;计算工具的发展&#xff08;二&#xff09;现代计算机的发展&#xff08;三&#xff09;计算机的分代&#xff08;四&#xff09;计算机新技术的发展 1-1-2 计算机的特…

计算机基础及入门

1、计算机基本概念 1.1、计算机是什么 计算机&#xff08;computer&#xff09;&#xff1a; 俗称电脑&#xff0c;是现代一种用于高速计算的电子计算机器 特点&#xff1a; 数值计算&#xff1b;逻辑计算&#xff1b;存储记忆&#xff1b;能按照程序运行、自动、高速处理处理…

计算机基础入门

说明&#xff1a;计算机包含硬件(hardware)和软件(software)两部分。 计算机分为6大硬件&#xff1a; 1.CPU 即中央处理器&#xff08;central processing unit&#xff0c;简称CPU&#xff09;作为计算机系统的运算和控制核心&#xff0c;是信息处理、程序运行的最终执行单元。…

计算机基础的介绍

计算机概念 这是一个家用计算机的硬件和软件组成&#xff1a; 计算机软件代码最基层 — 固件&#xff0c;在计算机首次启动时由处理器运行一些引导加载程序&#xff0c;比如从U盘、光盘等读取操作 系统。这些程序永久储存在计算机内存中&#xff0c;所以称作固件。嵌入式操作…

计算机基础

文章目录 一、数据结构1. 排序算法快排归并排序堆 2. 结构堆和栈区别数组与链表数据库相关的B树、B树跳表LSM树红黑树前缀树 二、Java基础0 基础问题1. 抽象类和接口的区别2. final关键字、内部类、static3. 多态4. 成员变量和局部变量5. String类、StringBuilder类6. Java集合…

计算机基础知识——基础入门(一)

目录 CPU 一、CPU是什么 二、CPU 实际做什么 三、CPU 的内部结构 CPU 是寄存器的集合体 四、CPU 指令执行过程 内存 一、什么是内存 二、内存的物理结构 内存的读写过程 内存的现实模型 三、内存的使用 指针 数组是内存的实现 栈和队列 链表 二叉树 磁盘 认…

计算机基础教程(一)

《大学计算机基础教程PDF》下载链接 链接&#xff1a;https://pan.baidu.com/s/1PGsvh1oRD1euWUedEXNl8w 密码&#xff1a;zt4x 推荐先看下面的两本&#xff1a; 计算机是怎样跑起来的 程序是怎样跑起来的 这两本百度云连接&#xff0c;CSDN审核不通过&#xff0c;可以自己…

韩漫《绿色镌像》又名《绿色镌刻》

雕塑专业的学生马修在第一眼遇到陈时感受到了命运&#xff0c;而陈青宇看透了充满肮脏金钱和背叛的冷酷世界&#xff0c;他不断忍受着心中的疼痛&#xff0c;直到与马修相见&#xff0c;他开始展露自己的欲望...隐藏着不同欲望的两个男人的故事自此开幕——

推荐几个免费看漫画的资源网站,请低调收藏

1、木马动漫 https://omyschool.com/ 2、第6夜漫画 http://www.d6ye.com/ 3、土豪漫画 https://www.tohomh123.com/ 4、动漫屋 http://www.dm5.com/ 5、漫画柜 https://www.manhuagui.com/

电子漫画 - 轻轻松松

▲ 有的器件的功能只有在它损坏的时候才能够体现 1970,5 《大众电子》 ▲ 欧姆定律漫画 ▲ 另外一个欧姆定律漫画 ▲ LED应用时总是需要串联一个限流电阻 LED&#xff1a;你使我闪亮&#xff0c;否则我会灭亡 电阻&#xff1a;你是那么漂亮&#xff0c;直戳我的新房

有哪些漫画在线观看?最好是好看的完结漫画

在线漫画就是指把原有的漫画书籍以图片加文字的形式绘画出来&#xff0c;再连载于各大漫画网站或者漫画软件上&#xff0c;漫画在线观看&#xff0c;不用下载。而现在好看的漫画太多了&#xff0c;有些漫画是新番&#xff0c;有些漫画是完结作品。机智的我&#xff0c;当然是要…

【破解软件】知音漫客免费看更多漫画

历史文章推荐&#xff1a; 1、漫客栈破解版 2、漫画岛破解版 3、漫画台破解版 4、二次元漫画破解版 【软件名称】: 知音漫客&#xff08;免费版&#xff09; 【软件版本】: v_4.9.5 【软件大小】: 23.25MB 【支持系统】: 安卓系统 【亲测演示】: 软件无需登录包含更多漫画…

没有好看的百合漫画,纯纯的校园爱情漫画也不错

百合漫画一般都是指女孩与女孩之间的爱情故事&#xff0c;有很多漫迷小伙伴喜欢看这一类好看的百合漫画。现在动漫为了迎合读者的口味&#xff0c;越来越多的动漫作品中也加入了百合情节。不过呢&#xff0c;没有找到好看的百合漫画的同学&#xff0c;其实纯纯的校园爱情漫画也…

专属EE的精美电子漫画

关注、星标公众号&#xff0c;精彩内容每日送达 来源&#xff1a;网络素材 ▲ 图1 硬盘表面的指纹 ▲ 图2 电路中的维修人员 ▲ 图3 电路中的拆卸工人 ▲ 图4 电路进行局部维修 ▲ 图5 电路环境下的钻探工 ▲ 图6 磁盘表面的施工人员 ▲ 图7 搬运电阻 ▲ 图8 这个电容与有问题 …

HGAME

Week1 前端游戏 看代码 通关之后调用mota&#xff08;&#xff09;方法 mota方法是一个算法&#xff0c;计算flag 想办法先调用mota方法就可以了 改一下代码即可&#xff08;源代码dump一下&#xff0c;重新跑一下&#xff09; 身份伪造类型 补充知识点 headers&#xff08;…