c语言练习58:⾃定义类型:结构体

⾃定义类型:结构体

 

结构体的概念

结构是⼀些值的集合,这些值称为成员变量。结构的每个成员可以是不同类型的变量。

结构体是一个种自定义的数据类型,它可以由很多个默认数据类型组成。它主要用于描述复杂场景下的变量。

例如,想通过结构体描述一个学生
struct Stu
{char name[20];//名字int age;//年龄char sex[5];//性别char id[20];//学号
}; //分号不能丢

结构体的形式:

struct 结构体名
{数据类型1 成员变量1;数据类型2 成员变量2;..........
};

 特殊的声明

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

//匿名结构体类型
struct
{int a;char b;float c;
}x;
struct
{int a;char b;float c;
}a[20], *p;

上⾯的两个结构在声明的时候省略掉了结构体标签(tag)。

那么问题来了?

//在上⾯代码的基础上,下⾯的代码合法吗? p = &x;

警告: 编译器会把上⾯的两个声明当成完全不同的两个类型,所以是⾮法的。

匿名的结构体类型,如果没有对结构体类型重命名的话,基本上只能使⽤⼀次

结构的⾃引⽤ 在结构中包含⼀个类型为该结构本⾝的成员是否可以呢? ⽐如,定义⼀个链表的节点:

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

上述代码正确吗?

如果正确,那 sizeof(struct Node) 是多少?

仔细分析,其实是不⾏的,因为⼀个结构体中再包含⼀个同类型的结构体变量,这样结构体变量的⼤ ⼩就会⽆穷的⼤,是不合理的。 正确的⾃引⽤⽅式:

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

在结构体⾃引⽤使⽤的过程中,夹杂了typedef对匿名结构体类型重命名,也容易引⼊问题,看看下⾯ 的代码,可⾏吗?

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

答案是不⾏的,

因为Node是对前⾯的匿名结构体类型的重命名产⽣的,但是在匿名结构体内部提前使 ⽤Node类型来创建成员变量,这是不⾏的。 解决⽅案如下:定义结构体不要使⽤匿名结构体了

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

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

创建的两种方式:

struct Stu 
{char name[20];//名字int age;//年龄float score;//成绩
}s1,s2,s3;
struct Stu 
{char name[20];//名字int age;//年龄float score;//成绩
};
int main()
{struct Str s1;return 0;
}

 初始化

struct Stu 
{char name[20];//名字int age;//年龄float score;//成绩
};
int main()
{struct Stu s1 = { "zhangsan",20, 98.5f };struct Stu s2 = { "lisi",33, 68.5f};struct Stu s3 = { "wangwu",24, 98.0f };struct Stu s4 = { .age = 22,.name = "cuihua", .score = 55.5f };printf("%s %d %f\n", s1.name, s1.age, s1.score);printf("%s %d %f\n", s4.name, s4.age, s4.score);return 0;
}

结构成员访问操作符

结构成员访问操作符有两个

⼀个是 . ,⼀个是 -> . 形式如下:

结构体变量.成员变量名

结构体指针—>成员变量名

例如:

#include <stdio.h>
#include <string.h>
struct Stu
{char name[15];//名字int age; //年龄
};
void print_stu(struct Stu s)
{printf("%s %d\n", s.name, s.age);
}
void set_stu(struct Stu* ps)
{strcpy(ps->name, "李四");ps->age = 28;
}
int main()
{struct Stu s = { "张三", 20 };print_stu(s);set_stu(&s);print_stu(s);return 0;
}

结构体内存对⻬

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

1. 结构体的第⼀个成员对⻬到相对结构体变量起始位置偏移量为0的地址处

2. 其他成员变量要对⻬到某个数字(对⻬数)的整数倍的地址处。 对⻬数 = 编译器默认的⼀个对⻬数 与 该成员变量⼤⼩的较⼩值。 - VS中默认的值为8 - Linux中没有默认对⻬数,对⻬数就是成员⾃⾝的⼤⼩

3. 结构体总⼤⼩为最⼤对⻬数(结构体中每个成员变量都有⼀个对⻬数,所有对⻬数中最⼤的)的 整数倍。

4. 如果嵌套了结构体的情况,嵌套的结构体成员对⻬到⾃⼰的成员中最⼤对⻬数的整数倍处,结构 体的整体⼤⼩就是所有最⼤对⻬数(含嵌套结构体中成员的对⻬数)的整数倍。

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

 分析一下S1所占大小。
第一个成员变量大小是 1,默认对齐数是4 ,那么它的对齐数就是1。但依据上面的规则,c1是起始位置偏差为0的地方开始,那么c1占据的就是起始位置,占据1个字节;
第二个成员变量大小是1,默认对齐数是4 ,那么它的对齐数也是1。依据上面的规则,c2是起始位置偏差为1的整倍数的位置,但任何数字都是1的整倍数,所以c2占据的是偏差值=1的地址处,占据1个字节;
第三个成员变量大小是4,默认的整倍数的位置,就也是偏差值0、4、8、12… 这些。所以a的起始位置是偏差值为4的位置,它占据了4个字节。对齐数是4 ,那么它的对齐数也是4。依据上面的规则,a的起始位置必须是偏差值为4
注意,这里偏差值为2和偏差值为3的位置是空的,所以S1占了8个字节。下图中,黄色为c1,蓝色c2,红色c3。旁边的数字为当前地址与起始地址的偏差值。

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

 得知S3的大小是16个字节。最大对齐数是8。那么S4在内存里就是这样的 

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

 

S4大小是32个字节。

为什么存在内存对⻬?

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

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

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

让占⽤空间⼩的成员尽量集中在⼀起

修改默认对⻬数(修改后可以不考虑内存对齐)

#include <stdio.h>
#pragma pack(1)//设置默认对⻬数为1
struct S
{char c1;int i;char c2;
};
#pragma pack()//取消设置的默认对⻬数,还原为默认
int main()
{//输出的结果是什么?printf("%d\n", sizeof(struct S));return 0;
}

结构体传参

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;
}

上⾯的 print1 和 print2 函数哪个好些?

答案是:⾸选print2函数。

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

结构体类型变量需要访问其成员

 B应该为:(*p).a

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

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

相关文章

2023-简单点-IOU计算

机器视觉中的坐标体系 注意区分x,y坐标系和row,col排布 IOU交集 代码 def IOU(RecA, RecB):recA是坐标形式是[X[左上点],y[左上点],x[右下点],y[右下点]]#找到交集框的左上和右下点&#xff0c;可以计算交集面积xA max(RecA[0], RecB[0])yA max(RecA[1], RecB[1])xB min(R…

games101 作业2

题目 光栅化一个三角形 1. 创建三角形的 2 维 bounding box。 2. 遍历此 bounding box 内的所有像素&#xff08;使用其整数索引&#xff09;。然后&#xff0c;使用像素中心的屏幕空间坐标来检查中心点是否在三角形内。 3. 如果在内部&#xff0c;则将其位置处的插值深度值 (…

UINT64整型数据在格式化时使用了不匹配的格式化符%d导致其他参数无法打印的问题排查

目录 1、问题描述 2、格式化函数内部解析待格式化参数的完整机制说明 2.1、传递给被调用函数的参数是通过栈传递的 2.2、格式化函数是如何从栈上找到待格式化的参数值&#xff0c;并完成格式化的&#xff1f; 2.3、字符串格式化符%s对应的异常问题场景说明 2.4、为了方便…

旋转角度对迭代次数的影响

( A, B )---3*30*2---( 1, 0 )( 0, 1 ) 让网络的输入只有3个节点&#xff0c;AB训练集各由5张二值化的图片组成&#xff0c;让A中有3个1&#xff0c;B中全是0&#xff0c;统计迭代次数并排序。 在3*5的空间内分布3个点有19种可能&#xff0c;但不同的分布只有6种 差值就诶够 …

C生万物之函数

前言&#xff1a; &#x1f4d5;作者简介&#xff1a;热爱编程的小七&#xff0c;致力于C、Java、Python等多编程语言&#xff0c;热爱编程和长板的运动少年&#xff01; &#x1f4d8;相关专栏Java基础语法&#xff0c;JavaEE初阶&#xff0c;数据库&#xff0c;数据结构和算法…

什么是集成测试?集成测试方法有哪些?

1、基本概念&#xff1a; 将软件集成起来后进行测试。集成测试又叫子系统测试、组装测试、部件测试等。集成测试主要是针对软件高层设计进行测试&#xff0c;一般来说是以模块和子系统为单位进行测试。 2、集成测试包含的层次&#xff1a; 1. 模块内的集成&#xff0c;主要是…

【微信小程序】文章设置

设置基本字体样式&#xff1a;行高、首行缩进 font-size: 32rpx;line-height: 1.6em;text-indent: 2em;padding: 20rpx 0;border-bottom: 1px dashed var(--themColor); 两端对齐 text-align: justify; css文字两行或者几行显示省略号 css文字两行或者几行显示省略号_css…

蓝牙核心规范(V5.4)10.1-BLE 入门笔记(1)

ble 规范 深入了解蓝牙LE需要熟悉相关的规格。蓝牙LE的架构、程序和协议由一项关键规范完全定义,称为蓝牙核心规范。产品如何使用蓝牙以实现互操作性由两种特殊类型称为配置文件和服务的规范集合所涵盖。图1展示了BLE规范类型及其相互关系。 1.1 蓝牙核心规范 蓝牙核心规范是…

多线程和并发编程(3)—AQS和ReentrantLock实现的互斥锁

一、管程模型—MESA模型 管程是什么&#xff1f; 管程就是指管理共享变量&#xff0c;以及对共享变量的相关操作。 在管程的发展史上&#xff0c;先后出现过三种不同的管程模型&#xff0c;分别是Hasen模型、Hoare模型和MESA模型。现在正在广泛使用的是MESA模型。 MESA模型…

C++ 太卷,转 Java?

最近看到知乎、牛客等论坛上关于 C 很多帖子&#xff0c;比如&#xff1a; 2023年大量劝入C 2023年还建议走C方向吗&#xff1f; 看了一圈&#xff0c;基本上都是说 C 这个领域唯一共同点就是都使用 C 语言&#xff0c;其它几乎没有相关性。 的确是这样&#xff0c;比如量化交…

MT4和MT5的共同点,anzo capital昂首资本说一个,没人有意见吧

相信很多交易者对MT4和MT5都不会陌生&#xff0c;但您了解他们背后之间的关系吗?今天anzo capital昂首资本就和各位交易者一起聊聊&#xff0c;没人有意见的MT4和MT5的共同点。 其实谈起MT4和MT5&#xff0c;就不得不聊聊他们背后的公司MetaQuotes&#xff0c;MetaQuotes 是…

【开发记录01】开发环境副本/页的导入&带用户权限管理系统

在蒋老师的指导下大概了解了: 1.开发环境的数据导入/导出 共享组件的同步 因为应用程序277是应用程序100的子程序&#xff0c;所以共享组件必须和100保持一致。 但是会出现一个小问题&#xff1a; 在APEX开发过程中同时打开两个不同的应用程序&#xff0c;但是编辑过程中经…