C语言之结构体详解

C语言之结构体详解

文章目录

  • C语言之结构体详解
    • 1. 结构体类型的声明
    • 2. 结构体变量的创建和初始化
    • 3. 结构体的特殊声明
    • 4. 结构体的自引用
      • 结构体的自引用
      • 匿名结构体的自引用
    • 5. 结构体内存对齐
      • 5.1 练习一
      • 5.2 练习三
    • 6. 为什么存在内存对⻬?

1. 结构体类型的声明

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

结构体标签:tag
结构体类型:struct tag
成员列表:member-list
结构体变量列表:variable-list

例如:

struct Stu
{char name[20];int age;float score;
};//分号不能丢

2. 结构体变量的创建和初始化

#include <stdio.h>struct Stu
{char name[20];int age;float score;
}s1;int main()
{//按照顺序初始化struct Stu s2 = { "zhangsan",18,90.1f };printf("%s %d %.2f\n", s2.name, s2.age, s2.score);//按照指定顺序初始化struct Stu s3 = { .score = 82.4f,.name = "zhangsan",.age = 20 };printf("%s %d %.2f\n", s3.name, s3.age, s3.score);return 0;
}

在结构体创建的时候,在变量列表创建的变量是全局变量,s1也就是全局变量

struct Stu 为结构体类型,和int创建变量一样(int n = 0;),struct Stu s2创建结构体变量
结构体变量在赋值的时候需要加上大括号,再根据成员列表的顺序,输入对应的值

结构体变量初始化的时候,也可以不按顺序初始化,这时候就需要用到 ( . )结构体访问操作符

3. 结构体的特殊声明

在声明结构体的时候,可以不完全声明

#include <stdio.h>struct
{char a;int b;
}x;struct
{char x;int y;
}*p;int main()
{p = &x;return 0;
}

上述两个结构体声明省去了结构体标签(tag)
在对第一个结构体变量取地址的时候,就会报警告
在这里插入图片描述

编译器会将上面声明的两个结构体当成两个不同类型的类型,所以是非法的 匿名的结构体,如果没有对结构体重命名的话,基本上只能使用一次

重命名如下:

#include <stdio.h>typedef struct
{char x;int y;
}S;int main()
{S s1 = { 1,2 };return 0;
}

使用typedef关键字将匿名结构体重命名为S

4. 结构体的自引用

结构体的自引用

代码一:

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

上述代码结构体的自引用是错误的,因为⼀个结构体中再包含⼀个同类型的结构体变量,这样结构体变量的⼤⼩就会⽆穷的⼤,是不合理的

正确的自引用:

struct Node
{int data;//数据域 存放数据struct Node *next;//指针域 存放下一个数据的地址
};

由于指针的大小是固定的,在32位平台下,为4字节,在64为平台下,为8字节,这样就确保了结构体变量的大小

在内存中,有些数据不是连续存放的,要想找到下一个数据,可以使用指针的方式
在这里插入图片描述

匿名结构体的自引用

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

匿名结构体的自引用是不可行的,虽然使用typedef关键字重命名了匿名结构体,但是在重命名之前,Node是在重命名之后才产生的,但是在Node产生之前就已经使用Node创建了结构体指针变量,这是不行的

5. 结构体内存对齐

⾸先得掌握结构体的对⻬规则:

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

5.1 练习一

#include <stdio.h>struct s1
{char c1;char c2;int i;
};struct s2
{char c1;int i;char c2;
};
int main()
{printf("struct s1 = %zd\n", sizeof(struct s1));printf("struct s2 = %zd\n", sizeof(struct s2));return 0;
}

代码运行结果如下:>
struct s1 = 8
struct s2 = 12

解释:
struct s1 = 8
在这里插入图片描述

  1. 结构体的第一个成员是 char c1; 会放在偏移量为0的位置,第一个字节,当对于自己偏移量就是0,所以只占一格
  2. 结构体的第二个成员是 char c2; char 为1个字节,在VS中默认的对齐数为8,取较小值 1,同时在偏移量中找到1的整数倍,也就是偏移量为1的位置,所以占一个字节
  3. 结构体的第三个成员是 int i; int 为4个字节,相对于默认值8,取较小值4,同时在偏移量中找到4的整数倍,跳过偏移量2 3 找到4,同时int 占四个字节,所以占4个字节
  4. 总共占了8个字节,在结构体中的对齐数有1 1 4,取最大值4,判断现在是否为最大对齐数4的整数倍
  5. 最终struct s1的大小为8个字节

struct s2 = 12
在这里插入图片描述

  1. 结构体的第一个成员是 char c1; 会放在偏移量为0的位置,第一个字节,当对于自己偏移量就是0,所以只占一格
  2. 结构体中的第二个成员是 int i; int 为4个字节,和默认对齐数8相比,取较小值4,在偏移量中找到4的整数倍,跳过1 2 3 ,找到偏移量为4的位置,int 为4个字节,所以占4格
  3. 结构体中的第三个成员是char c2; char 为1个字节,和默认对齐数8相比,取较小值1,在偏移量中找到1的整数倍,也就是偏移量为8的位置,char占一格
  4. 总共占了9个字节,相比结构体中的对齐数,1 1 4,取最大对齐数4,判断是否为整数倍,不是则取整数倍,也就是12字节,所以结构体的大小为12字节

5.2 练习三

#include <stdio.h>
struct S3
{double d;char c;int i;
};
struct S4
{char c1;struct S3 s3;double d;
};
int main()
{printf("struct s4 = %zd\n", sizeof(struct S4));return 0;
}

代码运行结果:>
struct s4 = 32

解释:
按照练习一的方法得出struct S3 的大小为16
在这里插入图片描述
struct S4
在这里插入图片描述

结构体嵌套时,嵌套的结构体成员对⻬到⾃⼰的成员中最⼤对⻬数的整数倍处,上述代码中在S4中嵌套了一个S3,计算S3的偏移量使用的是S3中最大的对齐数,也就是8字节

  1. char会放在偏移量为0的位置,也就是一个字节
  2. 嵌套的结构体最大对齐数为8,和默认数一致,取偏移量为8的整数倍,也就是在8的位置,S3结构体的大小为16个字节,所以占16格
  3. double占8个字节,和默认对齐数一致,取偏移量为8的整数倍,也就是在24的位置,占8个字节
  4. 总共占了32个字节,S4中的最大对齐数为8字节,是8的整数倍
  5. 所以struct S4的大小为32个字节

6. 为什么存在内存对⻬?

  1. 平台原因 (移植原因):
    不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常。
  2. 性能原因:
    数据结构(尤其是栈)应该尽可能地在⾃然边界上对⻬。原因在于,为了访问未对⻬的内存,处理器需要作两次内存访问;⽽对⻬的内存访问仅需要⼀次访问。假设⼀个处理器总是从内存中取8个字节,则地址必须是8的倍数。如果我们能保证将所有的double类型的数据的地址都对⻬成8的倍数,那么就可以⽤⼀个内存操作来读或者写值了。否则,我们可能需要执⾏两次内存访问,因为对象可能被分放在两个8字节内存块中。

平台原因:在C语言没有明确规定int类型的数据是无符号还是有符号的,这取决于编译器,不同的编译器会有不同的解释

性能原因:结构体的内存对⻬是拿空间来换取时间的做法

TIPS:在设计结构体的时候,我们既要满⾜对⻬,⼜要节省空间,可以将占用空间小的成员尽量聚集在一块

例如:

struct s1
{char c1;char c2;int i;
};struct s2
{char c1;int i;char c2;
};

相比较于s2,s1的占用空间较小一点

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

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

相关文章

MySQL电商管理系统练习题及答案

一 、表结构 用户表(user)&#xff1a;id(主键)、username、password、email、phone、age商品表(product)&#xff1a;id(主键)、name、price、stock、description订单表(order)&#xff1a;id(主键)、user_id(外键&#xff0c;关联用户表)、total_price、status、create_time…

05_属性描述符

05_属性描述符 文章目录 05_属性描述符一、属性描述符是什么&#xff1f;二、属性描述符①&#xff1a;查看属性描述②&#xff1a;设置属性描述符③&#xff1a;案例01.代码实现02.代码实现&#xff08;优化&#xff09; 一、属性描述符是什么&#xff1f; 属性描述符的结构 在…

类和对象——(3)再识对象

归纳编程学习的感悟&#xff0c; 记录奋斗路上的点滴&#xff0c; 希望能帮到一样刻苦的你&#xff01; 如有不足欢迎指正&#xff01; 共同学习交流&#xff01; &#x1f30e;欢迎各位→点赞 &#x1f44d; 收藏⭐ 留言​&#x1f4dd; 你说那里有你的梦想&#xff0c;…

MySQL官网推荐书籍

MySQL官网推荐书籍 图片有防盗链csdn转存失败。有图版传送门MySQL官网推荐书籍 高效的MySQL性能&#xff1a;Daniel Nichter的最佳实践和技术 Daniel Nichter 向您展示了如何应用直接影响 MySQL 性能的最佳实践和技术。您将学习如何通过分析查询执行、为常见 SQL 子句和表联接…

KMP基础架构

前言 Kotlin可以用来开发全栈, 我们所熟悉的各个端几乎都支持(除了鸿蒙) 而我们要开发好KMP项目需要一个好的基础架构,这样不仅代码更清晰,而且能共享更多的代码 正文 我们可以先将KMP分为前端和服务端 它们两端也能共享一些代码,比如接口声明,bean类,基础工具类等 前端和…

Go语言 值传递

官方说法&#xff0c;Go中只有值传递&#xff0c;没有引用传递 而Go语言中的一些让你觉得它是引用传递的原因&#xff0c;是因为Go语言有值类型和引用类型&#xff0c;但是它们都是值传递。 值类型 有int、float、bool、string、array、sturct等 引用类型有slice&#xff0c…

Logstash使用指南

介绍 Logstash是一个开源数据收集引擎&#xff0c;具有实时管道功能。它可以动态地将来自不同数据源的数据统一起来&#xff0c;并将数据标准化到你所选择的目的地。尽管Logstash的早期目标是搜集日志&#xff0c;现在它的功能已完全不只于此。任何事件类型都可以加入分析&…

docker (镜像分层、阿里云镜像推送/拉去)-day02

一、镜像概念 Docker 镜像是 Docker 容器的基础&#xff0c;它提供了一种可重复使用的、跨平台的部署方式&#xff0c;使得应用程序的部署和运行变得简单和高效。 把应用程序和配置依赖打包好形成一个可交付的运行环境(包括代码、运行时需要的库、环境变量和配置文件等),打包好…

C语言练习记录(蓝桥杯练习)(小蓝数点)

目录 小蓝数点 第一题程序的输出结果是&#xff1f;: 第二题下面代码的执行结果是什么&#xff1f;: 第三题下面代码的执行结果是什么&#xff1f;: 第四题关于关系操作符说法错误的是&#xff1f;: 第五题对于下面代码段&#xff0c;y的值为&#xff1f; 第六题sum 21 …

informer辅助笔记:exp/exp_informer.py

0 导入库 from data.data_loader import Dataset_ETT_hour, Dataset_ETT_minute, Dataset_Custom, Dataset_Pred from exp.exp_basic import Exp_Basic from models.model import Informer, InformerStackfrom utils.tools import EarlyStopping, adjust_learning_rate from u…

huggingface - pipeline - translate 记录

文章目录 #!/usr/bin/env python # -*- encoding: utf-8 -*-import os ,sys from transformers import pipelinemodel_checkpoint "Helsinki-NLP/opus-mt-zh-en" translator pipeline("translation", modelmodel_checkpoint)def translate_arr(arr): r…

Java 数据结构篇-用链表、数组实现栈

&#x1f525;博客主页&#xff1a; 【小扳_-CSDN博客】 ❤感谢大家点赞&#x1f44d;收藏⭐评论✍ 文章目录 1.0 栈的说明 2.0 用链表来实现栈 2.1 实现栈 - 入栈方法&#xff08;push&#xff09; 2.2 实现栈 - 出栈&#xff08;pop&#xff09; 2.3 实现栈 - 查看栈顶元素…

mybatis 实现批量更新的三种方式

注&#xff1a;Mybatis实现批量更新有三种方式&#xff0c;分别是使用foreach标签、使用SQL的case when语句和使用动态SQL的choose语句。具体实现方法如下&#xff1a; 1&#xff1a;使用foreach标签 <update id"batchUpdate" parameterType"java.util.Lis…

C 标准库 <errno.h>与 <float.h>

C 标准库 <errno.h> C 标准库的 errno.h 头文件定义了整数变量 errno&#xff0c;它是通过系统调用设置的&#xff0c;在错误事件中的某些库函数表明了什么发生了错误。该宏扩展为类型为 int 的可更改的左值&#xff0c;因此它可以被一个程序读取和修改。 在程序启动时…

国产linux单用户模式破解无密码登陆 (麒麟系统用户登录密码遗忘解决办法)

笔者手里有一批国产linu系统&#xff0c;目前开始用在日常的工作生产环境中&#xff0c;我这个老程序猿勉为其难的充当运维的或网管的角色。 国产linux系统常见的为麒麟Linux&#xff0c;统信UOS等&#xff0c;基本都是基于debian再开发的linux。 问题描述&#xff1a; 因为…

基于AT89C51单片机的倒数计时器设计

1&#xff0e;设计任务 利用AT89C51单片机为核心控制元件,设计一个简易的数字电压表&#xff0c;设计的系统实用性强、操作简单&#xff0c;实现了智能化、数字化。 本设计采用单片机为主控芯片&#xff0c;结合周边电路组成LED彩灯的闪烁控制系统器&#xff0c;用来控制红色…

用于缓存一些固定名称的小组件

项目中&#xff0c;用于缓存姓名、地名、单位名称等一些较固定名称的id-name小组件。用于减少一些表的关连操作和冗余字段。优化代码结构。扩展也方便&#xff0c;写不同的枚举就行了。 具体用法&#xff1a; {NameCacheUser.USER.getName(userId);NameCacheUser.ACCOUNT.getN…

excel合并单元格教程

在表格里&#xff0c;总是会遇到一级表格、二级表格的区别&#xff0c;这时候一级表格会需要合并成一个大格子&#xff0c;那么excel如何合并单元格呢&#xff0c;其实使用快捷键或者功能键就可以了。 excel如何合并单元格&#xff1a; 1、首先我们用鼠标选中所有要合并的单元…

最大公约数的C语言实现xdoj31

时间限制: 1 S 内存限制: 1000 Kb 问题描述: 最大公约数&#xff08;GCD&#xff09;指某几个整数共有因子中最大的一个&#xff0c;最大公约数具有如下性质&#xff0c; gcd(a,0)a gcd(a,1)1 因此当两个数中有一个为0时&#xff0c;gcd是不为0的那个整数&#xff…

Redis编码类型及对应含义

对象类型编码类型(encoding)取值范围Stringintlong长度范围内的数字embstr长度小于40的value值。数字和字符。raw长度大于40的value值Listziplist所有元素长度小于64字节&#xff0c;并且列表元素的个数小于512个linkedlist不满足ziplist的数据Setintset纯数字&#xff0c;列表…