自定义类型:结构体----初学者笔记

这篇具有很好参考价值的文章主要介绍了自定义类型:结构体----初学者笔记。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

目录

1. 结构体类型的声明

1.1 结构体类型的简单介绍和声明

1.1.1 结构的声明

1.1.2 特殊的声明

1.1.3 结构的自引用

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

3. 结构成员访问操作符

4. 结构体内存对⻬

4.1 对齐规则

4.2 练习

4.2.1 练习1

4.2.2 练习2

4.3 为什么存在内存对⻬?

4.4 修改默认对齐数

5. 结构体传参

6. 结构体实现位段

6.1 什么是位段

6.2 位段的内存分配

6.3 位段的跨平台问题

6.4 位段使⽤的注意事项


1. 结构体类型的声明

1.1 结构体类型的简单介绍和声明


首先,结构体分为内置类型和自定义类型2种,我们今天介绍的就是自定义类型的结构体。
当内置类型不能满足需求的时候,我们就用自定义类型的结构体,例如:我们要描述一本书,不能只通过一个变量来描述,需要知道书的书名、作者、价格、类型等等

1.1.1 结构的声明

而这就需要自定义类型的结构体来实现,结构体里面包含了各种类型的数据,用来描述一个复杂对象的各种属性。

struct book
{
	char name1[20];//作者名
	char name2[20];//书名
	int price;//价格
	//......
};

1.1.2 特殊的声明

我们有时候会看见这种匿名结构体类型
请看接下来的代码:

struct
{
	char a;
	int c;
	float d;
}s = { 0 };
struct
{
	char a;
	int c;
	float d;
}*ps;
int main()
{
	ps = &s;
	return 0;
}
//在上⾯代码的基础上,下⾯的代码合法吗?
ps= &s;

警告:
编译器会把上⾯的两个声明当成完全不同的两个类型,所以是⾮法的。
匿名的结构体类型,如果没有对结构体类型重命名的话,基本上只能使⽤⼀次。

1.1.3 结构的自引用

大家觉得下面的代码可以运行成功吗?

struct Node
{
 int data;//存储数据--4个字节
 struct Node next;//下一个节点的地址--->指针类型变量4or8个字节
};
int main()
{
 struct Node n;
 return 0;
}

在给出答案之前,我们先来了解一下数据结构

自定义类型:结构体----初学者笔记,c语言,数据结构,自定义类型:结构体,笔记


我们发现不可以,这是为什么呢?
我们可以假想一下:房子里面要放下一个大小一样的房子和另外的东西,这是不可能的
方法:将一个数据结构分成2部分,一半存数据,一半存放下一个节点的地址,这样就可以存储结构体指针了,也就可以结构自引用了

自定义类型:结构体----初学者笔记,c语言,数据结构,自定义类型:结构体,笔记

struct Node
{
	int data;//存储数据--4个字节
	struct Node* next;//下一个节点的地址--->指针类型变量4or8个字节
};
int main()
{
	struct Node n;
	return 0;
}

自定义类型:结构体----初学者笔记,c语言,数据结构,自定义类型:结构体,笔记

这样问题就解决了

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

初始化很简单,请看代码

struct Stu
{
  char name[20];
  int age;
  float score;
}s1,s2;//全局变量
struct Stu
{
  char name[20];
  int age;
  float score;
};

int main
{
  struct Stu s1,s2,s3;//局部变量
  return 0;
}

3. 结构成员访问操作符

结构体访问成员有2种方法:
1.结构体变量.成员变量名
2.结构体指针—>成员变量名

请看代码:

struct Stu
{
	char name[20];
	int age;
	float score;
}s1 = { "zhangsan",22,35.0f };

int main()
{
	struct Stu s2 ={"lisi",19,88.5f };
    struct Stu s3 = { "wangwu",39,76.0f };//按照顺序
    struct Stu s4 = { .age = 33,.name = "xiaofang",.score = 38.5f };//不按顺序
    printf("%s %d %lf\n", s1.name, s1.age, s1.score);
    printf("%s %d %lf\n", s2.name, s2.age, s2.score);
    struct Stu* ps = &s4;
    printf("%s %d %lf\n", ps->name, ps->age, ps->score);
    return 0;
}

自定义类型:结构体----初学者笔记,c语言,数据结构,自定义类型:结构体,笔记

4. 结构体内存对⻬

我们先看下面的代码,你觉得输出的是什么?

int main()
{
struct S1
{
 char c1;//1个字节
 int I;//4个字节
 char c2;//1个字节
};
printf("%d\n", sizeof(struct S1));

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

自定义类型:结构体----初学者笔记,c语言,数据结构,自定义类型:结构体,笔记


结果和你想的一样吗?
为什么成员一样,顺序不一样,存放的数据类型和变量也一样,但是结果不一样呢?
为什么开辟的空间不一样?s1和s2到底是怎么分配内存空间的?这里就涉及到了内存对齐!!!

4.1 对齐规则

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

那我们来看看刚刚的s1和s2是怎么存储的

自定义类型:结构体----初学者笔记,c语言,数据结构,自定义类型:结构体,笔记


4.2 练习

4.2.1 练习1

struct S3
{
 double d;//8个字节
 char c;//1个字节
 int I;//4个字节
};
printf("%d\n", sizeof(struct S3));

自定义类型:结构体----初学者笔记,c语言,数据结构,自定义类型:结构体,笔记

自定义类型:结构体----初学者笔记,c语言,数据结构,自定义类型:结构体,笔记


4.2.2 练习2

struct S3
{
 double d;//8个字节
 char c;//1个字节
 int I;//4个字节
};
printf("%d\n", sizeof(struct S3));

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

自定义类型:结构体----初学者笔记,c语言,数据结构,自定义类型:结构体,笔记

自定义类型:结构体----初学者笔记,c语言,数据结构,自定义类型:结构体,笔记

4.3 为什么存在内存对⻬?

简单来说:结构体的内存对⻬是拿空间来换取时间的做法。

那在设计结构体的时候,我们既要满⾜对⻬,⼜要节省空间,如何做到:

//例如:
struct S1
{
 char c1;
 int i;
 char c2;
};
struct S2
{
 char c1;
 char c2;
 int i;
};

S1 和 S2 类型的成员⼀模⼀样,但是 S1 和 S2 所占空间的⼤⼩有了⼀些区别。

4.4 修改默认对齐数

#pragma 这个预处理指令,可以改变编译器的默认对⻬数。

#include <stdio.h>
#pragma pack(1)//设置默认对⻬数为1
struct S
{
 char c1;
 int i;
 char c2;
};
#pragma pack()//取消设置的默认对⻬数,还原为默认

但是,我们一般设置成2的次方数,如果乱修改的话,可能适得其反

5. 结构体传参

结构体传参有2种方式,分别是传值调用和传址调用

struct S
{
 int data[1000];
 int num;
};
struct S s = {{1,2,3,4}, 1000};
//结构体传参
void print1(struct S s)
{
 printf("%d\n", s.num);
}
//结构体地址传参
void print2(struct S* ps)
{
 printf("%d\n", ps->num);
}
int main()
{
 print1(s); //传结构体
 print2(&s); //传地址
 return 0;
}

自定义类型:结构体----初学者笔记,c语言,数据结构,自定义类型:结构体,笔记

综合来看,还是传址调用比较好

6. 结构体实现位段

6.1 什么是位段

位段是基于结构体的,是可以节省空间的

位指的是二进制位​​​​​​​

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

1. 位段的成员必须是 int、unsigned int 或signed int ,在C99中位段成员的类型也可以选择其他类型。

2. 位段的成员名后边有⼀个冒号和⼀个数字

使用举例:

struct A
{
 int _a:2;
 int _b:5;
 int _c:10;
 int _d:30;
};
struct B
{
 int _a;
 int _b; 
 int _c;
 int _d;
};
int main( )
{
 printf("%d\n",sizeof(struct A));
 printf("%d\n",sizeof(struct B));
 return 0;
}
//空间是如何开辟的?

后面的数字指的是变量所占的空间大小, 例如:2指的就是变量a占两个二进制位,
以此类推,总共要占到42个二进制位, 我们初步推测,struct A大概需要6个字节

自定义类型:结构体----初学者笔记,c语言,数据结构,自定义类型:结构体,笔记

但是通过运行我们发现,实际上需要占到8个字节,但是还是比第二种方法所占内存小

我们发现和猜测的不一样,这就得讲讲位段的内存空间是怎么分配的了

6.2 位段的内存分配

1. 位段的成员可以是 int unsigned int signed int 或者是 char 等类型

2. 位段的空间上是按照需要以4个字节( int )或者1个字节( char )的⽅式来开辟的。

3. 位段涉及很多不确定因素,位段是不跨平台的,注重可移植的程序应该避免使⽤位段。

我们以接下来的代码来分析一下:

struct S
{
 char a:3;
 char b:4;
 char c:5;
 char d:4;
};
int main()
{
 struct S s = {0};
 //s.a = 10;
 //s.b = 12;
 //s.c = 3;
 //s.d = 4;
 printf("%d\n",sizeof(struct S));
 return 0;
}

自定义类型:结构体----初学者笔记,c语言,数据结构,自定义类型:结构体,笔记

先根据位段大小,开辟空间;再根据存储数据的二进制位读取相应空间位数,存放至内存空间(统一假设从右向左储存,剩余空位不够下一个使用就空着不使用),开辟新的内存空间

自定义类型:结构体----初学者笔记,c语言,数据结构,自定义类型:结构体,笔记

下面,我们看看完整代码的内存存储是怎么样的

struct S
{
 char a:3;
 char b:4;
 char c:5;
 char d:4;
};
int main()
{
 struct S s = {0};
 s.a = 10;
 s.b = 12;
 s.c = 3;
 s.d = 4;
 printf("%d\n",sizeof(struct S));
 return 0;
}

为了方便观察,我们在内存中以16进制的形式查看自定义类型:结构体----初学者笔记,c语言,数据结构,自定义类型:结构体,笔记
但是,这是为什么呢?

自定义类型:结构体----初学者笔记,c语言,数据结构,自定义类型:结构体,笔记

6.3 位段的跨平台问题

1. int 位段被当成有符号数还是⽆符号数是不确定的。

2. 位段中最⼤位的数⽬不能确定。(16位机器最⼤16,32位机器最⼤32,写成27,在16位机器会出问题。

3. 位段中的成员在内存中从左向右分配,还是从右向左分配标准尚未定义。

4. 当⼀个结构包含两个位段,第⼆个位段成员⽐较⼤,⽆法容纳于第⼀个位段剩余的位时,是舍弃剩余的位还是利⽤,这是不确定的。

总结:

跟结构相⽐,位段可以达到同样的效果,并且可以很好的节省空间,但是有跨平台的问题存在。

6.4 位段使⽤的注意事项

内存中每个字节分配⼀个地址,⼀个字节内部的bit位是没有地址的

所以不能对位段的成员使⽤&操作符,这样就不能使⽤scanf直接给位段的成员输⼊值,只能是先输⼊放在⼀个变量中,然后赋值给位段的成员。

struct A
{
 int _a : 2;
 int _b : 5;
 int _c : 10;
 int _d : 30;
};
int main()
{
 struct A sa = {0};
 scanf("%d", &sa._b);//这是错误的
 
 //正确的⽰范
 int b = 0;
 scanf("%d", &b);
 sa._b = b;
 return 0;
}

本次的分享到这里就结束了!!!

PS:小江目前只是个新手小白。欢迎大家在评论区讨论哦!有问题也可以讨论的!

如果对你有帮助的话,记得点赞👍+收藏⭐️+关注➕

自定义类型:结构体----初学者笔记,c语言,数据结构,自定义类型:结构体,笔记文章来源地址https://www.toymoban.com/news/detail-732689.html

到了这里,关于自定义类型:结构体----初学者笔记的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

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

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

相关文章

  • 大语言模型初学者指南 (2023)

    大语言模型 (LLM) 是深度学习的一个子集,它正在彻底改变自然语言处理领域。它们是功能强大的通用语言模型,可以针对大量数据进行预训练,然后针对特定任务进行微调。这使得LLM能够拥有大量的一般数据。如果一个人想将LLM用于特定目的,他们可以简单地根据各自的目的

    2024年02月11日
    浏览(47)
  • R语言初学者书籍推荐

    Home | Bookdown 这个网站上有很多R语言的书籍,并且一直在更新,阅读起来没有难度。 今天搜索材料的时候,检索到下面这本书:  有输入,才会有输出。 《R包开发(2ed)》 2rd的未出版的中文翻译版:https://r-packages-zh-cn.readthedocs.io/zh_CN/latest/index.html#  英文版本:R Packages (2e

    2024年02月13日
    浏览(24)
  • R语言爬虫实例 初学者自用

    本文记录了使用rvest RSelenium 包进行爬虫与网页渲染的相关知识点及本人的编程操作过程。涉及到基本爬取操作、爬取缺失部分如何处理、操作网页过滤等步骤。 本人非计算机专业,如有措辞不慎敬请提出。 这学期为了凑学分,选了一门R语言的课,才发现R语言远比我们想象

    2024年02月02日
    浏览(33)
  • 有哪些适合初学者的编程语言?

    那为什么我还要教你C语言呢?因为我想要让你成为一个更好、更强大的程序员。如果你要变得更好,C语言是一个极佳的选择,其原因有二。首先,C语言缺乏任何现代的安全功能,这意味着你必须更为警惕,时刻了解真正发生的事情。如果你能写出安全、健壮的C代码,那你就

    2024年02月09日
    浏览(52)
  • GEE初学者笔记之快速上手篇

    (1)谷歌云平台         整个GEE是基于Google Cloud云平台的一整套API开发环境。因此整个数据的处理全部都是在Google Cloud平台上实现的,无需本地机器参与运算。一般开发流程是在线/离线编辑代码,然后提交服务器端运行,完成之后会输出给我们一些结果。这个思路适合离线

    2024年02月07日
    浏览(55)
  • python初学者笔记-continue介绍及使用

    1.continue:     如果在循环中达到到某些条件则跳过当次循环而不是终止整个循环的时候可以利用 continue 来实现。 ① continue 会跳过当次循环,即结束本次循环,进入下一个循环。 执行原理:   continue 会跳过当次循环,即结束本次循环,进入下一个循环。 2.continue与break: 注

    2024年02月08日
    浏览(43)
  • 【新手解答】Python中Pandas的初学者笔记

    🌈你好呀!我是 是Yu欸 🌌 2024每日百字篆刻时光,感谢你的陪伴与支持 ~ 🚀 欢迎一起踏上探险之旅,挖掘无限可能,共同成长! 前些天发现了一个人工智能学习网站,内容深入浅出、易于理解。如果对人工智能感兴趣,不妨点击查看。 一位CSDN好友询问是否有 python里的p

    2024年03月14日
    浏览(43)
  • “C“浒传——初识C语言(更适合初学者体质哦!)

    第一章  “C“浒传——初识C语言(更适合初学者体质哦!) 目录 系列文章目录    前言  一、什么是C语言 二、第一个C语言程序  三、数据类型 四、变量,常量 4.1 定义变量的方法 4.1.1 变量的命名 4.2 变量的分类 4.3 变量的使用 4.4 变量的作用域和生命周期 4.4.1 作用域

    2024年02月07日
    浏览(42)
  • C语言初学者:原来分支和循环语句可以这样简单!

    目录 1.分支语句 1.if 语句 2.else语句 3.嵌套if  4.悬空else的问题 5.Switch语句 2.循环语句  1.while循环 2.for循环 3do-while循环 3.break和continue语句 1.break语句 2.continue语句 4.goto语句  5.小结 6.为你们点赞呢~ 7.完结散花                            不是每个人都能做自己想做的事,成

    2024年04月12日
    浏览(33)
  • 【C语言】代码实现 扫雷 游戏及进阶功能(初学者详解)

    扫雷游戏的起源可以追溯到20世纪60年代,当时这款游戏是由IBM开发出来的。在80年代初,微软公司将其收归旗下,并将其作为Windows操作系统自带的一款游戏。自此以后,扫雷成为了Windows用户最喜欢的休闲游戏之一,也受到了全球范围内的玩家喜爱。 现在,我们使用C语言,来

    2024年01月20日
    浏览(30)

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

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

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

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

二维码1

领取红包

二维码2

领红包