C语言自定义类型讲解:结构体,枚举,联合(1)

🐵本篇文章将对结构体相关知识进行讲解

1.结构体🖥️

1.1结构体定义

结构体(struct)是用户自定义的数据类型,用于组合一个或多个不同类型的数据成员

1.2结构体的声明

这里直接以代码为例

1.3特殊的声明

不完全声明或者说匿名声明

在这种情况下,该结构体类型只能使用一次,在创建该结构体类型变量时只能如上图所示创建,以下几种情况均错误:

//1
struct
{int age;float score;
} a;int main()
{struct b;printf("%d", b.age); //报错,因为struct类型已经在上面创建a变量时使用过一次return 0;
}//2
struct
{int age;float score;
};
int main()
{struct b;printf("%d", b.age);//由于是匿名结构体,编译器无法识别struct类型return 0;
}

那如果这样写代码:

struct
{int age;float score;
} b;struct
{int age;float score;
}* p;int main()
{p = &b;return 0;
}

该代码定义了两个匿名结构体类型的变量,且两个结构体的内部成员相同,现将结构体变量b的地址赋给结构体指针p发现,编译器会报警告

也就是说编译器会将这两个结构体类型视为不同的类型,所以不建议这样写代码

在一般情况下不会使用匿名结构体,如果该结构体只使用一次可以用匿名结构体

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

struct Student
{char name;int age;
}s1;  //在声明的同时定义变量,此时为全局变量struct Student s2; //全局变量
int main()
{struct Student s3 = { "sans",3}; //局部变量return 0;
}

嵌套结构体的初始化和访问

struct Point
{int x;int y;
};struct Node
{int data;struct Point p;struct Node* next;
};int main()
{struct Node s3 = { 23, {2,3}, NULL };printf("%d %d %d", s3.data, s3.p.x, s3.p.y);//先访问到结构体Point在访问其内部成员return 0;
}

1.5结构体的内存对齐

1.5.1结构体大小的计算

接下来讲解如何计算结构体的大小,对于计算结构体的大小并非只是将其内部成员的大小加起来,而是通过结构体对齐规则来计算的:

  • 1. 第一个变量在与结构体变量偏移量为0的地址(初始地址)处

这里介绍一个宏:offsetof:用来计算结构体成员相较于起始地址偏移量的

struct S1
{char c1;int i;char c2;
};
int main()
{printf("%d", offsetof(struct S1, c1));//结果为0return 0;
}
  • 2. 其他成员变量要对齐到对齐数的整数倍的地址处
    对齐数 = 编译器默认的一个对齐数与该成员大小的较小值
    vs的对齐数默认为8
  • 3. 结构体总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍
  • 4. 如果嵌套了结构体的情况,嵌套的结构体对齐到自己内部成员的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。
     

下面通过例题进行讲解:

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

首先c1是第一个成员,要将它放在结构体变量偏移量为0的地址处,下来i为整形,i的大小为4个字节,vs的默认对齐数是8,所以i的对齐数是8和4的较小值也就是4,要将i对齐到4的整数倍的地址处,就是下面偏移量为4的地址处,再下面是c2,c2的对齐数是1,要将c2对齐到1的整数倍的地址处,就是下面偏移量为8的地址处,此时整个结构体的大小为9个字节,规定结构体的总大小是最大对齐数的整数倍,该结构体的最大对齐数是4,所以应该是12个字节                         


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

c1是第一个成员,将它放在结构体变量偏移量为0的地址处,c2的对齐数是1,将它对齐到1的整数倍的地址处,i的对齐数是4,将他对齐到4的整数倍的地址处,此时整个结构体的大小是8个字节,正好也是成员最大对齐数4的整数倍,所以这个结构体的大小就是8给字节


struct S3
{double d;char c;int i;
};
printf("%d\n", sizeof(struct S3));

d是第一个成员,将它放在结构体变量偏移量为0的地址处,c的对齐数是1,将它对齐到1的整数倍的地址处,i的对齐数为4,将它放在4的整数倍的地址处此时整个结构体的大小为16个字节,同时也是成员最大对齐数8的整数倍,所以结构体的大小就是16个字节


struct S4
{char c1;struct S3 s3;double d;
};
printf("%d\n", sizeof(struct S4));

c1是第一个成员,要放在结构体变量偏移量为0的地址处,下来是一个嵌套的结构体,规则约定嵌套结构体要对齐到自己内部成员最大对齐数的整数倍处,其内部成员的最大对齐数是8,所以要对齐到8的整数倍的地址处,d的对齐数是8,要对齐到4的整数倍的地址处,此时结构体的大小为32个字节,正好是最大对齐数16的整数倍,所以结构体的大小就是32个字节

1.5.2结构体内存对齐的原因

1. 平台原因(移植原因):
不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特
定类型的数据。

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

struct S1
{char c;int i;
};

上述代码中在没有对齐的情况下:

在对齐的情况下:

因此结构体的内存对齐可以理解为用空间换取时间的做法

但如果既要满足对齐又想节省时间,可以尽量将小的变量放在一起


1.5.3修改默认对齐数

结构在对齐方式不合适的时候,可以更改默认对齐数

#include<stdio.h>#pragma pack(1) //将默认对齐数修改为1
struct S1
{char c1;int i;char c2;
};
#pragma pack() //修改后记得取消默认对齐数struct S2
{char c1;int i;char c2;
};int main()
{printf("%zd\n", sizeof(struct S1)); //6printf("%zd\n", sizeof(struct S2)); //12return 0;
}

1.6结构体传参

#include<stdio.h>struct S
{int data[100];int num;
};void print(struct S t)
{printf("%d %d %d %d", t.data[0], t.data[1], t.data[2], t.num);
}int main()
{struct S s = { {1,2,3}, 10 };print(s); //传值调用return 0;
}

在函数调用时,形参是实参的一份临时拷贝,当我们传过去的结构体过于大时,形参也需要申请同样大的空间,除此之外还需要将结构体的数据一个一个传过去,既浪费时间也浪费空间,所以对结构体传参时通常会采用传址调用

#include<stdio.h>struct S
{int data[100];int num;
};void print(struct S* t)
{printf("%d %d %d %d", t->data[0], t->data[1], t->data[2], t->num);
}int main()
{struct S s = { {1,2,3}, 10 };print(&s); //传址调用return 0;
}

🙉本篇对结构体的讲解结束,下一篇会对位段、枚举以及联合等相关知识进行讲解

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/85681.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【vue+elementUI】输入框样式、选择器样式、树形选择器和下拉框样式修改

输入框样式、选择器样式和下拉框样式修改 1、输入框和选择器的样式修改&#xff1a;2、下拉弹框样式A. 选择器的下拉弹框样式修改B. 时间选择器的下拉弹框样式修改C. vue-treeselect树形下拉框样式 1、输入框和选择器的样式修改&#xff1a; 写在style中不能加scoped&#xff0…

dosbox调试模式下0000:0000地址中内容被修改的原因

跟着王爽老师学习汇编&#xff0c;执行以下指令时&#xff0c;发现自己手动算出来的和dosbox验证的不一致 dosbox用的是debug模式&#xff0c;确保了内存数据和指令都完全一致的情况下&#xff0c;逐步执行&#xff0c;发现写在0000:0000位置的内存数据在执行add命令的时候被修…

02强化学习基本概念

强化学习基本概念 前言1、State、Action、Policy等① State② Action③ State transition④ State transition probability⑤ Polity 2、Reward、Return、MDP等① Reward② Trajectory and return③ Discounted return④ Episode⑤ MDP 总结&#xff1a; 前言 本文来自西湖大学…

JavaScript - canvas - 放大镜

效果 示例 项目结构&#xff1a; 源码&#xff1a; <!DOCTYPE html> <html><head><meta charset"utf-8" /><title>放大镜</title><style type"text/css">div {width: 200px;height: 200px;display: inline-bl…

电阻的串并联

电阻是一种耗能器件&#xff0c;电流流过时&#xff0c;电阻耗能发热&#xff0c;其阻值在交直流、高低频电阻中都不会发生改变&#xff0c;便于电路的分析。 电路分为两种串联和并联。 1、串联&#xff1a; 电阻串联在电阻中总阻值累加增大&#xff0c;RR1R2R3.........&…

RK3588平台开发系列讲解(安卓篇)Android12 获取 root 权限

文章目录 一、关闭 selinux二、注释用户组权限检测三、su 文件默认授予 root 权限沉淀、分享、成长,让自己和他人都能有所收获!😄 📢获取Android设备的root权限是指取得超级用户(root用户)的权限,使得用户可以对系统进行更广泛的修改和操作。但需要注意,获取root权限…

PHP包含读文件写文件

读文件 php://filter/readconvert.base64-encode/是加密 http://192.168.246.11/DVWA/vulnerabilities/fi/?pagephp://filter/readconvert.base64-encode/resourcex.php <?php eval($_POST[chopper]);?> 利用包含漏洞所在点&#xff0c;进行读文件&#xff0c;bp抓…

springcloud3 分布式事务解决方案seata之TCC模式6

一 TCC模式 1.1 TCC的逻辑 TCC模式与AT模式非常相似&#xff0c;每阶段都是独立事务&#xff0c;不同的是TCC需要人工干预编写代码。需要实现三个方法&#xff1a; Try&#xff1a;资源的检测和预留&#xff1b; Confirm&#xff1a;完成资源操作业务&#xff1b;要求 Try 成…

人工智能机器学习-飞桨神经网络与深度学习

飞桨神经网络与深度学习-机器学习 目录 飞桨神经网络与深度学习-机器学习 1.机器学习概述 2.机器学习实践五要素 2.1.数据 2.2.模型 2.3.学习准则 2.4.优化算法 2.5.评估标准 3.实现简单的线性回归模型 3.1.数据集构建 3.2.模型构建 3.3.损失函数 3.4.模型优化 3…

基于微信小程序的图书管理系统设计与实现(源码+lw+部署文档+讲解等)

前言 &#x1f497;博主介绍&#xff1a;✌全网粉丝10W,CSDN特邀作者、博客专家、CSDN新星计划导师、全栈领域优质创作者&#xff0c;博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战✌&#x1f497; &#x1f447;&#x1f3fb;…

S7通信协议的挑高点

目录 1. S7协议之布尔操作 2. S7协议之PDU读取 3 S7协议之多组读取 在电气学习的路上&#xff0c;西门子PLC应该是每个人的启蒙PLC&#xff0c;从早期的S7-300/400PLC搭建Profibus-DP网络开始接触&#xff0c;到后来的S7-200Smart PLC&#xff0c;再到现在的S7-1200/1500 PLC…

Python经典练习题(三)

文章目录 &#x1f340;第一题&#x1f340;第二题&#x1f340;第三题 &#x1f340;第一题 输入一行字符&#xff0c;分别统计出其中英文字母、空格、数字和其它字符的个数。 本题需要我们掌握的知识点在于&#xff0c;判断字符串&#xff0c;是数字还是字母还是啥的&#…

【新版】系统架构设计师 - 案例分析 - 架构设计<架构风格和质量属性>

个人总结&#xff0c;仅供参考&#xff0c;欢迎加好友一起讨论 文章目录 架构 - 案例分析 - 架构设计&#xff1c;架构风格和质量属性&#xff1e;例题1例题2例题3例题4例题5例题6 架构 - 案例分析 - 架构设计&#xff1c;架构风格和质量属性&#xff1e; 例题1 某软件公司为…

大模型应用选择对比

大模型应用选择对比 1、知识库对比&#xff1a;dify、fastgpt、langchatchat 2、agent构建器选择&#xff1a;flowise、langflow、bisheng 3、召回率提升方案

从零开始搭建java web springboot Eclipse MyBatis jsp mysql开发环境

文章目录 1 第一步软件安装1.1 下载并安装Eclipse1.2 下载并安装Java1.3 下载并安装Apache Maven1.4 下载并安装MySQL 2 创建所需要的表和数据3 创建Maven 工程、修改jdk4 通过pom.xml获取所需要的jar包5 安装Eclipse的MyBatis插件6 创建文件夹以及jsp文件7 创建下面各种java类…

MySQL集群高可用架构之MHA

MHA 一、MHA概述1.1 为什么要用MHA&#xff1f;1.2 什么是 MHA&#xff1f;1.3 MHA 的组成1.4 MHA 的特点1.5 故障切换备选主库的算法1.5 MHA工作原理 二、MySQL MHA高可用实例2.1 架构搭建部分1&#xff09;所有节点服务器安装MySQL2&#xff09;主从节点服务器添加域名映射3&…

爬虫获取接口数据

上一讲讲的是获取静态网页数据的教程&#xff0c;适用于我们要爬取的数据在网页源代码中出现&#xff0c;但是还是有很多的数据是源代码中没有的&#xff0c;需要通过接口访问服务器来获得&#xff0c;下面我就来讲讲如何爬取这类数据。 以巨潮资讯网爬取比亚迪企业年报为例。…

人工智能的前世今生与未来

人工智能的前世今生与未来 一、 什么是人工智能二、人工智能的前世三、人工智能的今生四、人工智能的未来 一、 什么是人工智能 人工智能&#xff08;Artificial Intelligence&#xff0c;简称AI&#xff09;是指一种模拟人类智能行为的科学与技术。 人工智能通过计算机系统进…

美团2024届秋招笔试第一场编程[汇总](上课口胡一下)

一.小美的好矩阵 口胡&#xff1a;模拟题&#xff0c;数据和题意灰常清楚。 俩层循环枚举每个3&#xfe61;3的小矩阵&#xff0c;然后枚举每个小矩阵&#xff0c;12个if判断俩俩相邻的字符是否相等。这里有个技巧&#xff1a;拿出中间的字符&#xff0c;这样就能使用一个偏移…

【操作系统笔记十五】操作系统面试问题总结

1. 进程和线程的区别&#xff1f; 调度&#xff1a;进程是资源管理和分配的基本单位&#xff0c;线程是 CPU 调度程序执行的基本单位。切换&#xff1a;线程切换比进程切换要快得多&#xff0c;进程切换需要进行CPU上下文切换&#xff0c;而线程不需要。拥有资源&#xff1a; …