C语言之结构体篇(简)

这篇具有很好参考价值的文章主要介绍了C语言之结构体篇(简)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

结构体是由我们自己创造的一种类型,使得C语言有能力描述复杂类型。比如学生包含了:名字、年龄、性别、学号…

结构体的认知

结构是一些值的集合,这些值叫做成员变量。每个成员可以是不同类型的变量。
————-----------

结构体与数组的区分:
数组:是一组相同类型的元素的集合。
结构体:是一些值的集合,但这些值可以是不同类型:可以是标量、数组、指针、甚至是其他结构体。

结构体的声明

一般声明

假如这里要描述一个学生:

//描述一个学生
struct Stu   //Stu是结构体变量名,可以自定义(根据实际需求)
{
	//里面是成员列表
	char name[20];//学生名字
	int age;      //学生年龄
	char id[10];  //学生学号
	//....

}s1,s2,s3;  //这里是结构体变量,是全局变量,
           //(创建结构体类型时顺带创建3个结构体全局变量)
          //这后面的分号不能丢
int main()
{
	struct Stu s4, s5; //创建结构体变量,这些是局部变量

	return 0;
}

特殊声明

就是再结构体声明的时候,可以不完全的声明。

匿名结构体类型

struct       //没有结构体变量名
{
	int a;
	char b;
	double c;
}s1;      //只能在这里创建结构体变量名,
         //且只能使用一次

结构体自引用

定义:结构体要能够找到和他同类型的下一个结构体;

  • 结构体中可以包含另一个结构体变量
struct book1
{
	char name[20];
	int age;
	int mony;
};

struct book
{
	char name[20];
	int mony;
	struct book1 s1;
};

但是结构体内不能有自身结构体变量,但是可以用指针指向一个本类型结构体来实现结构体自引用

struct Note
{
	int date;        //数据域
	struct Note* next;//指针域
};

关于typedef(定义类型)定义定义匿名结构体类型 :

typedef struct
{
	int data;//数据域
	struct Node* next;//指针域
} Node;
void main() {
	Node n;
 }

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

结构体变量的定义

// struct book 是定义的数据类型的名字,它向编译系统声明这是一个“结构体类型”
struct book  
{
	int mony;
	char name[20];
}s1,s2;//全局变量(创建结构体类型时顺带创建2个结构体全局变量)
       //最后的分号千万不能省略
int main()
{
	struct book s3, s4;//局部变量
	return 0;
}

结构体变量的初始化

struct book
{
int mony;
char name[20];
}s1={30,"xiyouji"}, s2 = {20,"hongloumeng"};//s1,s2也是结构体变量,其是全局变量,
//并对其全部初始化,其实也可不初始化


int main()
{
struct book s3 = { 15,"三国演义" }, s4 = {25,"水浒传"};
//创建结构体对象并完成初始化
//其中初始化一个汉字占两个字节
return 0;
}

结构体嵌套初始化:


struct history
{
int age;
int id;
};


struct book
{
char name[20];
int mony;
struct history s1;
};


int main()
{
struct book s1 = { "abcdef",20,{1000,2023}};//初始化完成
return 0;
}

匿名结构体类型初始化


struct {
char name[20];
int price;
char id[12];
}s = { "git",7,"123" };

注意:

  • 结构体的声明和初始化也可以在函数里面进行
  • 结构体定义完就必须对其进行初始化
  • 结构体也可以通过操作符"."的形式来进行里面值的操作

结构体传参

struct BOOK
{
	char name[20];
	int money;
};

//传值调用
void test1(struct BOOK s)
{
	printf("%s %d", s.name, s.money);
}

//传址调用
void test2(struct BOOK* ps)
{
	printf("%s %d", ps->name, ps->money);
}

int main()
{
	struct BOOK s1 = { "shuihuzhuan",20 };

	test1(s1);//传结构体,不会改变结构体变量
	test2(&s1);//传结构体地址,可以改变结构体变量

	return 0;
}

注意:
结构体传参尽可能传地址,因为传参数需要压栈,如果传递一个结构体对象时,结构体过大,参数压栈的系统开稍过大,所以会导致性能下降;
传址调用时,可以用上const;结构体传参中const的作用:使原结构体只可以进行读取操作不可以进行更改操作,以防止误操作。

结构体内存对齐

现在我们已经了解结构体的基本使用了,接下来我们探讨一个深入的问题:
如何计算结构体的大小?
这里就涉及到结构体内存对齐
要了解这些,首先给大家介绍一个宏( offsetof() ),其可以计算结构体成员相对于结构体起始位置的偏移量

size_t offsetof( structName, memberName );

1. 第一个参数是结构体名称
2.第二个参数是结构体成员
输入两个参数,就会返回结构体成员相对于结构体起始位置的偏移量。

以这个代码来探究:

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

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

int main()
{
	printf("%d\n", offsetof(s1, c1));
	printf("%d\n", offsetof(s1, i));
	printf("%d\n", offsetof(s1, c2));
	return 0;
}

C语言之结构体篇(简),c语言,开发语言

当知道了偏移量,咱们就可以来分析结构体储存在内存中的方式;
结构体存储在栈区

C语言之结构体篇(简),c语言,开发语言

这样的话应该是9个字节,但结果是12字节,这是为什么?

C语言之结构体篇(简),c语言,开发语言

上面出现的问题,说明结构体成员不是按照顺序在内存中连续存放的,而是有一定规律的。

结构体内存对齐的规则:

1. 结构体的第一个成员永远放在相较于结构体变量起始位置偏移量为0的位置。

2.从第二个成员开始,往后的每个成员都要对齐到某个数(对齐数)的整数倍的地址处,
对齐数:编译器默认的一个对齐数与该成员大小值的较小值。
VS编译器上对齐数默认是:8
gcc:没有默认对齐数,对齐数就是该结构体成员的大小。

3.结构体的总大小必须是最大对齐数的整数倍。
最大对齐数:所有对齐数的最大值

4.如果嵌套了结构体,嵌套的结构体对齐到自己的最大对齐数的整数倍出,结构体的整数大小,就是所以最大对齐数(包含了嵌套结构体的对齐数)的整数倍。

那怎么运用这个规则呢?
看图说话:
C语言之结构体篇(简),c语言,开发语言

特殊一点 : 如果成员变量是一个数组。


举个例子:int arr [3];则把其看成三个整形变量依次存放就好。

为什么要内存对齐?

1.平台原因:

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

2.性能原因

  • 数据结构应该尽可能的在自然边界上对齐。原因在于:为了访问未对齐的内存,处理器需要两次内存访问;而对齐的内存访问仅只需要一次访问。

3.如图解释:
C语言之结构体篇(简),c语言,开发语言
总结:结构体内存对齐是拿空间换时间的做法

所以在设计结构体时我们既要满足对齐,又要节省空间:

1.合理安排好结构体成员空间


2.也可以修改默认对齐数

修改默认对齐数:

#include <stdio.h>
//修改默认对齐数为1
#pragma pack(1)
struct Hand
{
	char c;
	int size;
	char q;
};
#pragma pack()//取消设置的默认对齐,还原默认
int main() {
	printf("%d\n", sizeof(struct Hand));//默认对齐数8时——12,默认对齐数1时——6
	return 0;
}

位段

相比结构体更能节省空间,但是有跨平台问题;

为什么存在位段:因为如果一个字节里有32bit,当你只用到了2个bit,其他的空间就都浪费了,而位段就可以选择你的每一个成员占多大的空间,所以可以更好的节约空间。

位段声明

其声明和结构体声明是类似的,有两点不同:

1.位段成员可以是 int 、unsigned int 、signed int 或者是 char (整形家族)类型。
__
2.位段的成员名后边有一个冒号和一个数字。
__

举例:

** S 就是一个位段类型**

struct S
{
	char a : 3;
	char b : 4;
	char c : 5;
	char d : 4;
};

位段的内存分配

就拿上述的位段类型,来探究;

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

struct S
{
	//因为先是char类型,先开辟一个字节--8个bit位
	char a : 3;//a成员占3个bit
	char b : 4;//b成员占4个bit
	char c : 5;//这时用了7个bit,还剩1个,因为下面还是char类型,不够就再开辟一个字节
	//c成员在新开辟的字节占5个bit
	char d : 4;//这时还剩3个bit,char类型,开辟一个字节,d成员占4个bit
	//因此S位段类型所占3个字节
};

int main()
{
	struct S s = { 0 };
	s.a = 10;
	s.b = 20;
	s.c = 3;
	s.d = 4;

	printf("%d ", sizeof S);//3
	return 0;
}

在VS编译器下一个字节内部的数据,先使用低比特位的地址,再使用高比特位的地址(在内存中分配从右往左使用)
C语言之结构体篇(简),c语言,开发语言

位段跨平台问题:

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

总结:和结构相比,位段可达到同样的效果,可以很好的节省空间,但是有跨平台的问题。

连肝6个小时真不是盖的呀 ^ _ ^ 最后希望兄弟姐妹们小小支持一下呗,咱下次肝到爆。文章来源地址https://www.toymoban.com/news/detail-634436.html

到了这里,关于C语言之结构体篇(简)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 软件开发中常用数据结构介绍:C语言队列

    工作之余来写写C语言相关知识,以免忘记。今天就来聊聊 C语言实现循环队列 ,我是分享人M哥,目前从事车载控制器的软件开发及测试工作。 学习过程中如有任何疑问,可底下评论! 如果觉得文章内容在工作学习中有帮助到你,麻烦 点赞收藏评论+关注 走一波!感谢各位的

    2024年02月11日
    浏览(48)
  • 【05】STM32·HAL库开发-C语言基础知识 | stdint.h介绍 | 位操作 | 宏定义的使用 | 条件编译 | extern声明 | typdef使用 | 结构体、指针、代码规范介绍。

      stdint.h 是从 C99 中引进的一个标准 C 库的文件,可以在MDK5的安装路径:D:MDK5.34ARMARMCCinclude中找到。   stdint.h 定义了很多类型别名,将有符号的char类型定义别名为int8_t等,使用此套别名有易于移植。   在MDK中需要配置才能支持使用S99标准, 默认是勾选的 。   只

    2024年02月08日
    浏览(44)
  • C语言系统化精讲(六):C语言选择结构和循环结构

    C语言中有三大结构,分别是顺序结构、选择结构和循环结构: C语言顺序结构就是让程序按照从头到尾的顺序依次执行每一条C语言代码,不重复执行任何代码,也不跳过任何代码 C语言选择结构也称分支结构,就是让程序 拐弯 , 有选择性的执行代码;换句话说,可以跳过没

    2024年02月04日
    浏览(42)
  • 【数据结构】C语言结构体详解

    目录 前言 一、结构体的定义 二、定义结构体变量 三、结构体变量的初始化 四、使用typedef声明新数据类型名 五、指向结构体变量的指针 总结 🌈嗨!我是Filotimo__🌈。很高兴与大家相识,希望我的博客能对你有所帮助。 💡本文由Filotimo__✍️原创,首发于CSDN📚。 📣如需转

    2024年02月04日
    浏览(49)
  • C语言 - 结构体、结构体数组、结构体指针和结构体嵌套

    问题:学籍管理需要每个学生的下列数据:学号、姓名、性别、年龄、分数,请用 C 语言程序存储并处理一组学生的学籍。 单个学生学籍的数据结构: 学号(num): int 型 姓名(name) :char [ ] 型 性别(sex):char 型 年龄(age):int 型 分数(score):float 型 思考:如果有多

    2024年02月11日
    浏览(39)
  • 【Go】Go 语言教程--语言结构(二)

    Go 语言教程–介绍(一) Go 语言的基础组成有以下几个部分: 包声明 引入包 函数 变量 语句 表达式 注释 接下来让我们来看下简单的代码,该代码输出了\\\"Hello World!\\\": 实例 让我们来看下以上程序的各个部分: 第一行代码 package main 定义了包名。你必须在源文件中非注释的第

    2024年02月12日
    浏览(53)
  • C语言——结构体类型(二)【结构体内存对齐,结构体数组】

    📝前言: 上一讲结构体类型(一)中,我们讲述了有关 结构体定义,创建,初始化和引用 的内容,这一讲,我们进一步学习结构体的相关知识: 1,结构体内存对齐 2,结构体数组 🎬个人简介:努力学习ing 📋个人专栏:C语言入门基础 🎀CSDN主页 愚润求学 🌄每日鸡汤:

    2024年01月24日
    浏览(71)
  • C语言学习笔记——C语言结构

    C语言是一种面向过程的结构化的语言,同时具有高级语言和汇编语言的优点 所有的C语言程序经过编译和连接之后才能被计算机执行 头文件是一种特殊的文件,记录了很多可以直接引用头文件然后使用的函数,也可通过#define声明函数、变量、宏等的定义 头文件通常包含在源

    2024年02月11日
    浏览(45)
  • 【C语言】【数据结构】自定义类型:结构体

    这是一篇对结构体的详细介绍,这篇文章对结构体声明、结构体的自引用、结构体的初始化、结构体的内存分布和对齐规则、库函数offsetof、以及进行内存对齐的原因、如何修改默认对齐数、结构体传参进行介绍和说明。                  ✨  猪巴戒 :个人主页✨      

    2024年02月05日
    浏览(35)
  • 码蹄杯语言基础:选择结构(C语言)

    请编写一个简单程序,输入一个整数,和10比较,输出比较结果 格式 输入格式: 输入整型 输出格式: 输出…大于或者等于或者小于10 输入a,b两个整数,输出他们之间的最小值 格式 输入格式: 输入2个整数用空格分隔 输出格式: 输出为整型 输入a,b两个整数,输出他们之间

    2024年02月06日
    浏览(39)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包