当前位置: 首页 > news >正文

C语言之结构体

一.前言引入.

我们知道在C语言中有内置类型,如:整型,浮点型等。但是只有这些内置类 型还是不够的,假设我想描述学⽣,描述⼀本书,这时单⼀的内置类型是不⾏的。描述⼀个学⽣需要名字、年龄、学号、⾝⾼、体重等;描述⼀本书需要作者、出版社、定价等。C语⾔为了解决这个问 题,增加了结构体,共用体等⾃定义的数据类型,让程序员可以⾃⼰创造适合的类型。今天我们来讲讲结构体。

二.结构体介绍.

定义:结构体是⼀些值的集合,这些值称为成员变量。结构的每个成员可以是不同类型的变量,如: 标量、数组、指针,甚⾄是其他结构体

结构体的声明:

struct tag
{member-list;
}variable-list;

例如:描述一个学生

struct Student
{char name[20];//名字int age;//年龄char sex[5];//性别char id[15];//学号
}s1;//s1为全局变量
int main()
{struct Student s2;//局部变量return 0;
}

结构体特殊声明

在声明结构的时候,可以不完全的声明
//在声明结构的时候,可以不完全的声明,即匿名结构体类型
struct
{int a;char b;float c;
}s1;

请判断下面代码是否正确:

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
//在声明结构的时候,可以不完全的声明,即匿名结构体类型
struct
{int a;char b;float c;
}s1;
struct
{int a;char b;float c;
}s2[20], * p;
int main()
{p = &s1;return 0;
}
警告:
编译器会把上⾯的两个声明当成完全不同的两个类型,所以是⾮法的。
匿名的结构体类型,如果没有对结构体类型重命名的话,基本上只能使⽤⼀次

结构体变量的定义:

struct Point
{int x; int y;
}s1;//结构体变量的定义
struct Point s2;//结构体变量的定义
int main()
{struct Point s3;//结构体变量的定义return 0;
}

结构体变量的初始化:

struct Stu
{char name[20];int age;
};
int main()
{struct Stu s1 = { "zhangsan",18 };///结构体变量初始化	return 0;
}

 镶嵌初始化:

struct Point
{int x;int y;
};
struct Stu
{char name[20];int age;struct Point s2;
};
int main()
{struct Stu s1 = { "zhangsan",18,{2,3} };///结构体变量镶嵌初始化	return 0;
}

三.结构成员访问操作符.

3.1.结构体成员的直接访问

使⽤⽅式:结构体变量.成员名

结构体成员的直接访问是通过点操作符(.)访问的。点操作符接受两个操作数.
struct Point
{int x;int y;
}s1 = {3,5};
int main()
{struct Point s2 = { 2,4 };printf("%d %d\n", s1.x, s1.y);printf("%d %d\n", s2.x, s2.y);return 0;
}

3.2.结构体成员的间接访问

使⽤⽅式:结构体指针->成员名

有时候我们得到的不是⼀个结构体变量,⽽是得到了⼀个指向结构体的指针.

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
struct Point
{int x;int y;
}s1 = { 3,5 };
int main()
{struct Point s2 = { 2,3 };struct Point* pi1 = &s1;struct Point* pi2 = &s2;printf("%d %d\n", pi1->x, pi1->y);printf("%d %d\n", pi2->x, pi2->y);pi1->x = 10;pi1->y = 20;printf("%d %d\n", pi1->x, pi1->y);return 0;
}

综合使用结构体用例:

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <string.h>
struct Stu
{char name[10];int age;
};
void Print(struct Stu s2)
{printf("%s %d\n", s2.name, s2.age);
}
void Set(struct Stu* s3)
{s3->age = 15;strcpy(s3->name, "李四");
}
int main()
{struct Stu s1 = { "zhangsan",18 };Print(s1);Set(&s1);Print(s1);return 0;
}

结果:

四.结构体的⾃引⽤.

五.结构体的内存对齐.

计算结构体的⼤⼩

例如:

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <string.h>
struct s1
{char a;int b;char c;
};
struct s2
{char a;char b;int c;
};
int main()
{printf("%zd\n", sizeof(struct s1));printf("%zd\n", sizeof(struct s2));return 0;
}

如果我给你一份这样的代码,请问你认为结果如何呢?两者一样吗?

答案如下:

和你想的一样吗?

如果不一样,不妨来看看我的解读:

结构体的对⻬规则:
1. 结构体的第⼀个成员对⻬到和结构体变量起始位置偏移量为0的地址处
2. 其他成员变量要对⻬到某个数字(对⻬数)的整数倍的地址处。 对⻬数 = 编译器默认的⼀个对⻬数与该成员变量⼤⼩的较⼩值。  VS 中默认的值为 8 ,Linux中 gcc 没有默认对⻬数,对⻬数就是成员⾃⾝的⼤⼩
3. 结构体总⼤⼩为最⼤对⻬数(结构体中每个成员变量都有⼀个对⻬数,所有对⻬数中最⼤的)的
整数倍
4. 如果嵌套了结构体的情况,嵌套的结构体成员对⻬到⾃⼰的成员中最⼤对⻬数的整数倍处,结构
体的整体⼤⼩就是所有最⼤对⻬数(含嵌套结构体中成员的对⻬数)的整数倍

例题1:

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
struct s1
{//1. 结构体的第⼀个成员对⻬到和结构体变量起始位置偏移量为0的地址处char a;//对齐偏移量为0位置处,char一个字节//其他成员变量要对⻬到某个数字(对⻬数)的整数倍的地址处。//对⻬数 = 编译器默认的⼀个对⻬数与该成员变量⼤⼩的较⼩值。  //对齐数VS 中默认的值为 8 char b;//对齐偏移量为1位置处,char一个字节,即 1 8 1(第一个为成员变量的对齐数,第二个为VS对齐数,第三个为该行对齐数)int c;//对齐偏移量为4位置处,int四个字节,即 4 8 4
};	
int main()
{//结构体总⼤⼩为最⼤对⻬数(结构体中每个成员变量都有⼀个对⻬数,所有对⻬数中最⼤的)的整数倍,即1 1 4 8 ->8printf("%zd\n", sizeof(struct s1));//结果为8return 0;
}

结果为:

例题二:

struct s2
{//1. 结构体的第⼀个成员对⻬到和结构体变量起始位置偏移量为0的地址处char a;//对齐偏移量为0位置处,char一个字节//其他成员变量要对⻬到某个数字(对⻬数)的整数倍的地址处。//对⻬数 = 编译器默认的⼀个对⻬数与该成员变量⼤⼩的较⼩值。  //对齐数VS 中默认的值为 8 int b;//对齐偏移量为4位置处,四个字节,int: 4 8 4(第一个为成员变量的对齐数,第二个为VS对齐数,第三个为该行对齐数)char c;//对齐偏移量为9位置处,char 1个字节;1 8 1};
int main()
{//结构体总⼤⼩为最⼤对⻬数(结构体中每个成员变量都有⼀个对⻬数,所有对⻬数中最⼤的)的整数倍,即1 4 1  ->4的整数倍为12(原来的数据已经达到偏移量为9了)printf("%zd\n", sizeof(struct s2));//结果为12return 0;
}

结果:

例题三:

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
struct s3
{//下面简写//前三个为成员大小 VS大小 该行大小,偏移量的位置double d;//8 8 8,0-7char c;//1 8 1,8int i;//4 8 4.12-15
};
int main()
{printf("%zd\n", sizeof(struct s3));//结果8 1 4->8 15->16return 0;
}

结果:

例题四:

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
练习4-结构体嵌套问题
struct s3
{//下面简写//前三个为成员大小 VS大小 该行大小,偏移量的位置double d;char c;int i;
};
struct s4
{char c1;//1 8 1,0//4. 如果嵌套了结构体的情况,嵌套的结构体成员对⻬到⾃⼰的成员中最⼤对⻬数的整数倍处,结构体的整体⼤⼩就是所有最⼤对⻬数(含嵌套结构体中成员的对⻬数)的整数倍struct s3 a;//16 8 8,8-23double d;//8 8 8,24-31
};
int main()
{printf("%zd\n", sizeof(struct s4));//1 16 8->16 31->32return 0;
}

你是否想过这样一个问题,为什么存在内存对⻬呢?
平台原因 (移植原因):
不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常。
性能原因:
数据结构(尤其是栈)应该尽可能地在⾃然边界上对⻬。原因在于,为了访问未对⻬的内存,处理器需要作两次内存访问;⽽对⻬的内存访问仅需要⼀次访问。假设⼀个处理器总是从内存中取8个字节,则地址必须是8的倍数。如果我们能保证将所有的double类型的数据的地址都对⻬成8的倍数,那么就可以⽤⼀个内存操作来读或者写值了。否则,我们可能需要执⾏两次内存访问,因为对象可能被分放在两个8字节内存块中。
总体来说:结构体的内存对⻬是拿空间来换取时间的做法。
那在设计结构体的时候,我们既要满⾜对⻬,⼜要节省空间,
重点:如何做到: 让占⽤空间⼩的成员尽量集中在⼀起!!!
重点:如何做到: 让占⽤空间⼩的成员尽量集中在⼀起!!!
重点:如何做到: 让占⽤空间⼩的成员尽量集中在⼀起!!!

说三遍!

相信你现在一定会开头的那题了!

那么我们可以自己设置对齐数吗?答案是当然可以啦!

#pragma 这个预处理指令,可以改变编译器的默认对⻬数
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#pragma pack(1)
练习4-结构体嵌套问题
struct s3
{//下面简写//前三个为成员大小 VS大小 该行大小,偏移量的位置double d;char c;int i;
};
struct s4
{char c1;//1 8 1,0//4. 如果嵌套了结构体的情况,嵌套的结构体成员对⻬到⾃⼰的成员中最⼤对⻬数的整数倍处,结构体的整体⼤⼩就是所有最⼤对⻬数(含嵌套结构体中成员的对⻬数)的整数倍struct s3 a;//16 8 8,8-23double d;//8 8 8,24-31
};
int main()
{printf("%zd\n", sizeof(struct s4));//1 16 8->16 31->32return 0;
}

修改之后结果为:

#pragma pack()//取消设置的对⻬数,还原为默认

这样,当结构体在对⻬⽅式不合适的时候,我们可以⾃⼰更改默认对齐数。

六.结构体传参和对比

1.传数据

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
struct S
{int data[1000];int num;
};
void Print(struct S s1)
{printf("%d\n", s1.num);
}
int main()
{struct S s1 = { {1,2,3,4}, 1000 };Print(s1);return 0;
}

结果:

2.传地址

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
struct S
{int data[1000];int num;
};
void Print(struct S* s1)
{printf("%d\n", s1->num);
}
int main()
{struct S s1 = { {1,2,3,4}, 1000 };Print(&s1);return 0;
}

结果:

对比两者,你认为哪个好呢?

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

最后,学习进步!!!

相关文章:

C语言之结构体

一.前言引入. 我们知道在C语言中有内置类型&#xff0c;如&#xff1a;整型&#xff0c;浮点型等。但是只有这些内置类 型还是不够的&#xff0c;假设我想描述学⽣&#xff0c;描述⼀本书&#xff0c;这时单⼀的内置类型是不⾏的。描述⼀个学⽣需要名字、年龄、学号、⾝⾼、体…...

【蓝桥杯软件赛 零基础备赛20周】第5周——高精度大数运算与队列

文章目录 1. 数组的应用–高精度大数运算1.1 Java和Python计算大数1.2 C/C高精度计算大数1.2.1 高精度加法1.2.2 高精度减法 2. 队列2.1 手写队列2.1.1 C/C手写队列2.1.2 Java手写队列2.1.3 Python手写队列 2.2 C STL队列queue2.3 Java队列Queue2.4 Python队列Queue和deque2.5 …...

C#:程序发布的大小控制

.net不讨喜有个大原因就是.net平台本身太大了&#xff0c;不同版本没有兼容性&#xff0c;程序依赖哪个版本用户就要安装哪个版本&#xff0c;除非你恰好用的是操作系统默认安装的版本——问题是不同版本操作系统默认安装的不一样。 所以打包程序就很头疼&#xff0c;不打包平台…...

Python中的split()、rsplit()、splitlines()的区别

split、rsplit、splitlines的区别 1、split()2、rsplit()3、splitlines() Python提供了三种字符串分割的方法&#xff1a;split()、rsplit()和splitlines()&#xff1b;本文主要通过案例介绍这三种字符串分割函数的区别 1、split() split()主要用于从左向右匹配分割符进行分割…...

上位机开发框架:QT与winform/wpf对比

QT QT 是一个跨平台的 C 应用程序框架&#xff0c;它提供了丰富的 UI 组件和功能强大的网络通信、数据库操作等模块。QT 的优势在于其良好的跨平台性能&#xff0c;可以方便地部署在 Windows、Linux、macOS 等不同操作系统上。此外&#xff0c;QT 还具有强大的 UI 设计能力&am…...

Halcon tiff 点云读取以及平面矫正

一、读取tiff 图 dev_close_window () dev_open_window (0, 0, 512, 512, black, WindowHandle)xResolution:0.0025 yResolution:0.0025 zResolution:0.001 read_image (IntputImage, C:/Users/alber/Desktop/2023-08-15_16-38-24-982_/Sta5_002.tif) zoom_image_factor (Intpu…...

详解Spring中基于注解的Aop编程以及Spring对于JDK和CGLIB代理方式的切换

&#x1f609;&#x1f609; 学习交流群&#xff1a; ✅✅1&#xff1a;这是孙哥suns给大家的福利&#xff01; ✨✨2&#xff1a;我们免费分享Netty、Dubbo、k8s、Mybatis、Spring...应用和源码级别的视频资料 &#x1f96d;&#x1f96d;3&#xff1a;QQ群&#xff1a;583783…...

百度/抖音/小红书/微信搜索品牌形象优化怎么做?

搜索口碑是网络营销不可或缺的一部分&#xff0c;企业如何做好品牌搜索口碑优化呢&#xff1f;小马识途营销顾问建议从以下几方面入手。 1. 通过关键字优化提高自身知名度 通过对竞争对手和目标客户的关键字进行分析&#xff0c;企业可以确定哪些关键字可以提高自身品牌知名度。…...

爬虫学习(三)用beautiful 解析html

安装库 import requests from bs4 import BeautifulSoup headers {"User-Agent" : "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36 Edg/119.0.0.0"} for start_num in range(0,250…...

OSG编程指南<十四>:OSG纹理渲染之普通纹理、多重纹理、Mipmap多级渐远纹理及TextureRectangle矩阵纹理

1、纹理映射介绍 物体的外观不仅包括形状&#xff0c;不同物体表面有着不同的颜色和图案。一个简单而有效地实现这种特性的方法就是使用纹理映射。在三维图形中&#xff0c;纹理映射&#xff08;Texture Mapping&#xff09;的方法运用广泛&#xff0c;使用该技术可以大大提高物…...

Langchain-Chatchat的安装过程

参考&#xff1a;LLMs之RAG&#xff1a;LangChain-Chatchat(一款中文友好的全流程本地知识库问答应用)的简介(支持 FastChat 接入的ChatGLM-2/LLaMA-2等多款主流LLMs多款embe_一个处女座的程序猿的博客-CSDN博客 1、安装过程中出现了 GPU驱动版本 是11.8 而 python -c "…...

Windows系列:Windows Server 2012 R2 安装VMware Tools的正确姿势(实现物理机和虚拟机文件互传)

Windows Server 2012 R2 安装VMware Tools的正确姿势(实现物理机和虚拟机文件互传) 安装环境安装步骤一. 安装补丁下面进入教程首先打开虚拟机,点击"虚拟机"选项中的"安装VMware Tools"点击确定如果出现下图中的问题,说明虚拟机中缺少更新程序,我们需…...

最长连续递增序列

最长连续递增序列 描述 : 给定一个未经排序的整数数组&#xff0c;找到最长且 连续递增的子序列&#xff0c;并返回该序列的长度。 连续递增的子序列 可以由两个下标 l 和 r&#xff08;l < r&#xff09;确定&#xff0c;如果对于每个 l < i < r&#xff0c;都有 …...

FreeRTOS入门--任务

目录 一、什么是任务 二、创建任务---xTaskCreate函数 三、任务的删除 四、任务优先级 1.阻塞状态(Blocked) 2.暂停状态(Suspended) 3.就绪状态(Ready) 五、Delay 六、调度算法 一、什么是任务 在FreeRTOS中&#xff0c;任务就是一个函数&#xff0c;原型如下&#xff…...

4个解决特定的任务的Pandas高效代码

在本文中&#xff0c;我将分享4个在一行代码中完成的Pandas操作。这些操作可以有效地解决特定的任务&#xff0c;并以一种好的方式给出结果。 从列表中创建字典 我有一份商品清单&#xff0c;我想看看它们的分布情况。更具体地说&#xff1a;希望得到唯一值以及它们在列表中出…...

【已解决】AttributeError: module ‘gradio‘ has no attribute ‘Image‘

问题描述 AttributeError: module gradio has no attribute Image 不知道作者用的是哪个gradio版本&#xff0c;最新的版本报错AttributeError: module gradio has no attribute outputs &#xff0c; 换一个老一点的版本会报错AttributeError: module gradio has no attribute…...

高级软件工程15本书籍

如果您想学习软件工程技能并提高您的专业知识&#xff0c;那么这里是您的最佳选择。我们有一本很棒的书&#xff0c;可以极大地增强您在软件工程方面的知识。 1&#xff09;干净的代码 Robert C. Martin 写了一本名为“干净代码&#xff1a;敏捷软件工艺手册”的书。在本书中&…...

计网Lesson3 - 计算机网络评价指标与封包解包

文章目录 计算机网络的性能指标1. 速率2. 带宽3. 吞吐量4. 时延5. 时延带宽积6. 往返时间7. 利用率8. 数据的解包和封包 计算机网络的术语实体![实体](https://img-blog.csdnimg.cn/direct/cbf4ca9ed5ab4df290b5a17b4642c6a1.png)协议服务 计算机网络的性能指标 1. 速率 数据…...

深度学习好文记录,反复学习

recent update time&#xff1a;2023.12.2 深度学习入门 - 知乎、这本书也很好&#xff0c;作者写的专栏不错。 机器学习&#xff0c;深度学习一些好文_一只菜得不行的鸟的博客-CSDN博客 卷积神经网络学习路线&#xff08;五&#xff09;| 卷积神经网络参数设置&#xff0c;提…...

CSS浅谈动画性能

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 目的一、举个栗子二、性能分析1.从图层分析2.性能分析 总结 目的 为了探究使用动画时&#xff0c;『transform』和『width、height、margin等』的差异 一、举个栗子…...

万能的视频格式播放器

今天博主给大家带来一款“万能”的视频播放器——VLC Media Player&#xff0c;支持的文件格式非常多&#xff0c;大家快来一起看看吧&#xff01; VLC Media Player 是一款可播放大多数格式&#xff0c;而无需安装编解码器包的媒体播放器。可以播放 MPEG-1、MPEG-2、MPEG-4、D…...

设计模式---第五篇

系列文章目录 文章目录 系列文章目录前言一、知道观察者模式吗?前言 前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站,这篇文章男女通用,看懂了就去分享给你的码吧。 一、知道观察者模式吗? 答:观察者模式是定义对…...

.NET8构建统计Extreme Optimization Numerical Libraries

为 .NET 8 构建统计应用程序 Extreme Optimization Numerical Libraries for .NET V8.1.22 添加了对 .NET 8 的支持&#xff0c;使您可以使用最新版本的 Microsoft 平台。 Extreme Optimization Numerical Libraries for .NET 是通用数学和统计类的集合&#xff0c;为技术和统计…...

07-原型模式-C语言实现

原型模式&#xff1a; Specify the kinds of objects to create using a prototypical instance,and create new objects by copying this prototype.&#xff08;用原型实例指定创建对象的种类&#xff0c; 并且通过拷贝这些原型创建新的对象。 &#xff09; UML图&#xff1…...

深度学习与深度迁移学习有什么区别?

深度学习包含深度迁移学习&#xff0c;它们都利用了深层神经网络&#xff08;Deep Neural Network&#xff0c;DNN&#xff09;来处理数据&#xff0c;并从中学习特征。但是&#xff0c;它们也有一些区别。 深度学习是一种机器学习方法&#xff0c;它通过多层神经网络来自动学…...

创建Asp.net MVC项目Ajax实现视图页面数据与后端Json传值显示

简述回顾 继上篇文章创建的mvc传值这里说明一下Json传值。在mvc框架中&#xff0c;不可避免地会遇到前台传值到后台&#xff0c;前台接收后台的值的情况&#xff08;前台指view&#xff0c;后台指controller&#xff09;&#xff0c;有时只需要从控制器中返回一个处理的结果&a…...

1089 Insert or Merge (插入排序,相邻归并排序,附模拟实现)

注意点1&#xff1a;判断插入排序不能从头开始判断是否为目标数组&#xff0c; 比如&#xff1a;初始为1 2 3 4 3&#xff0c;目标数组也为1 2 3 4 3&#xff0c;则如果是从头开始推的&#xff0c;则下一步会变成1 2 3 4 3&#xff0c;而下一步应该是 1 2 3 3 4。所以我们应该…...

基于算能的国产AI边缘计算盒子8核心A53丨17.6Tops算力

边缘计算盒子 8核心A53丨17.6Tops算力 ● 可提供17.6TOPS&#xff08;INT8&#xff09;的峰值计算能力、2.2TFLOPS&#xff08;FP32&#xff09;的高精度算力&#xff0c;单芯片最高支持32路H.264 & H.265的实时解码能力。 ● 适配Caffe/TensorFlow/MxNet/PyTorch/ ONNX/…...

Eaxyx 让圆球跟随鼠标移动

如果出现2023&#xff0c;代表配置成功: 进入Eaxy官方网站&#xff0c;点击文档&#xff1a; 选择 函数->绘图函数->initgraph: 可以看见initgraph&#xff08;&#xff09;函数有如下三个参数: 现在我们想生成一个1280*720大小的窗口&#xff1a; 我们需写如下代码: 但…...

Node.js 事件循环:定时任务、延迟任务和 I/O 事件的艺术

&#x1f90d; 前端开发工程师&#xff08;主业&#xff09;、技术博主&#xff08;副业&#xff09;、已过CET6 &#x1f368; 阿珊和她的猫_CSDN个人主页 &#x1f560; 牛客高级专题作者、在牛客打造高质量专栏《前端面试必备》 &#x1f35a; 蓝桥云课签约作者、已在蓝桥云…...