C语言:结构体

目录

结构体类型的声明

匿名结构体

全局结构体变量

嵌套结构体

访问结构体成员

结构的自引用

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

结构体内存对齐

结构体内存对齐规则

修改默认对齐数 #pragma pack(n) 

offsetof 求结构体成员相对于结构体开头的偏移量的宏。 

为什么存在内存对齐?

结构体传参

结构体实现位段(位段的填充&可移植性)

什么是位段

位段的内存分配规则

位段的跨平台问题

位段的应用


结构体类型的声明

匿名结构体

/* 匿名结构体类型——只能用一次,即在声明的时候顺带创建一个变量 */
struct
{char name[20];int age;
} s4;

全局结构体变量

struct Stu
{char name[20];int age;
} s1, s2; // s1 s2是全局变量struct Stu
{char name[20];int age;
} s1 = {"zhanghai", 22}; // 声明并初始化全局变量s1

嵌套结构体

struct Score
{int score;char str[20];
};struct Stu
{char name[20];int age;struct Score s;
};int main()
{// 初始化局部变量s3struct Stu s3 = {"zhanghai", 22, {100, "q"}};printf("%s %d %d %s", s3.name, s3.age, s3.s.score, s3.s.str); // zhanghai 22 100 qreturn 0;
}

访问结构体成员

#include <stdio.h>/* 本例论如何声明结构体,并使用三种方式访问结构体各成员 */struct MyType
{/* 成员 */char name[20];int age;char sex[10];char tele[12];
};/* 接受结构体指针地址,struct MyType 类型int,表示一种类型;  *p是因为要指针赋值给指针变量时需加* */
void myPrintf(struct MyType *p)
{/* *p 表示使用*将指针变量p解引用,得到 *p == obj(可参考09_pointer) */printf("%s %d %s %s\n", (*p).name, (*p).age, (*p).sex, (*p).tele); // "zhanghai", 20, "nan", "15556201597"/* p 表示指针变量,结构体指针变量 -> 成员名,也就是结构体指针变量若想访问成员,需要操作符 -> */printf("%s %d %s %s\n", p->name, p->age, p->sex, p->tele); // "zhanghai", 20, "nan", "15556201597"
}int main()
{struct MyType obj = {"zhanghai", 20, "nan", "15556201597"};// 结构体对象.成员名printf("%s %d %s %s\n", obj.name, obj.age, obj.sex, obj.tele); // "zhanghai", 20, "nan", "15556201597"// 将结构体指针传入myPrintf(&obj);return 0;
}

结构的自引用

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

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

struct Point
{int x;int y;
} p1; //声明类型的同时定义变量p1
struct Point p2; //定义结构体变量p2//初始化:定义变量的同时赋初值。
struct Point p3 = {x, y};
struct Stu        //类型声明
{char name[15];//名字int age;      //年龄
};
struct Stu s = {"zhangsan", 20};//初始化
struct Node
{int data;struct Point p;struct Node* next; 
} n1 = {10, {4,5}, NULL}; //结构体嵌套初始化
struct Node n2 = {20, {5, 6}, NULL};//结构体嵌套初始化

结构体内存对齐

结构体内存对齐规则

1. 第一个成员在与结构体变量偏移量为0的地址处。

2. 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。

对齐数 = 编译器默认的一个对齐数 与 该成员大小的较小值。(vscode没有编译器对齐数,对齐数就是成员自身大小)

3. 结构体总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍。

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

 

struct S1
{char c1;int i;char c2;
};struct S2
{char c1;char c2;int i;
};struct S3
{double d;char c;int i;
};printf("%d\n", sizeof(struct S1)); // 12
printf("%d\n", sizeof(struct S2)); // 8
printf("%d\n", sizeof(struct S3)); // 16

修改默认对齐数 #pragma pack(n) 

/* 修改编译器默认对齐数为4,请看结构体规则第2点 */
#pragma pack(4)
struct S
{int i;    // 0double d; // 4 8 ——> 4
};
#pragma pack()#pragma pack(1)
struct Sd
{char c;int i;char c1;
};
#pragma pack()printf("%d\n", sizeof(struct S));  // 12 (修改对齐数之前是16)
printf("%d\n", sizeof(struct Sd)); // 6

offsetof 求结构体成员相对于结构体开头的偏移量的宏。 

    
struct S1
{char c1;int i;char c2;
};struct S3
{double d;char c;int i;
};// S1
printf("%d\n", offsetof(struct S1, c1)); // 0
printf("%d\n", offsetof(struct S1, i));  // 4
printf("%d\n", offsetof(struct S1, c2)); // 8
// s3
printf("%d\n", offsetof(struct S3, d)); // 0
printf("%d\n", offsetof(struct S3, c)); // 8
printf("%d\n", offsetof(struct S3, i)); // 12

结构在对齐方式不合适的时候,我们可以自己更改默认对齐数。 

为什么存在内存对齐?

1. 平台原因(移植原因): 不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些

2. 性能原因: 数据结构(尤其是栈)应该尽可能地在自然边界上对齐。 原因在于,为了访问未对齐

的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要一次访问。

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

结构体传参

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

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

结构体实现位段(位段的填充&可移植性)

什么是位段

位段的声明和结构是类似的,有两个不同:
  1. 位段的成员必须是 int、unsigned int 或signed int 。
  1. 位段的成员名后边有一个冒号和一个数字。
struct A
{int _a : 2; int _b : 5;int _c : 10; int _d : 30; 
};

 A就是一个位段类型。

位段的内存分配规则

1. 位段的成员可以是 int unsigned int signed int 或者是 char (属于整形家族)类型

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

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

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

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

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

7. 当一个结构包含两个位段,第二个位段成员比较大,无法容纳于第一个位段剩余的位时,是舍弃剩余的位还是利用,这是不确定的

struct A
{/* 上来二话不说先开辟4个字节空间,也就是 32个bit位 */int _a : 2;  // _a占2个bit位int _b : 5;  // _b占5个bit位int _c : 10; // _c占10个bit位/* 到这里发现 只剩 15 个bit位,不够存放_d的30个bit,那么会再次开辟4个字节用来存放 */int _d : 30; // _d占30个bit位
};printf("%d\n", sizeof(struct A)); // 8 未使用位段就是16个字节

位段的跨平台问题

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

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

位段的应用

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

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

相关文章

【计算思维】蓝桥杯STEMA 科技素养考试真题及解析 4

1、下列哪个选项填到填到下图空缺处最合适 A、 B、 C、 D、 答案&#xff1a;D 2、按照如下图的规律摆放正方形&#xff0c;第 5 堆正方形的个数是 A、13 B、14 C、15 D、16 答案&#xff1a;D 3、从右面观察下面的立体图形&#xff0c;看到的是 A、 B、 C、 D、 答…

C语言——操作符详解

前言&#xff1a;这篇文章主要讲解一下C语言中常见的操作符的使用&#xff0c;做一下整理&#xff0c;便于日后回顾&#xff0c;同时也希望能帮助到大家。 &#x1f3ac;个人简介&#xff1a;努力学习ing &#x1f4dd;CSDN主页 愚润求学 &#x1f304;每日鸡汤&#xff1a;念…

ClickHouse数据一致性

查询CK手册发现&#xff0c;即便对数据一致性支持最好的Mergetree&#xff0c;也只是保证最终一致性&#xff1a; 我们在使用 ReplacingMergeTree、SummingMergeTree 这类表引擎的时候&#xff0c;会出现短暂数据不一致的情况。 在某些对一致性非常敏感的场景&#xff0c;通常有…

c语言-输入输出详解

文章目录 格式化输入输出占位符printfscanf 字符串输入输出puts&#xff08;&#xff09;gets&#xff08;&#xff09; 字符输入输出putchar&#xff08;&#xff09;getchar&#xff08;&#xff09; 区别 格式化输入输出 输入输出的库函数的头文件&#xff1a; #include<…

原理Redis-动态字符串SDS

动态字符串SDS Redis中保存的Key是字符串&#xff0c;value往往是字符串或者字符串的集合。可见字符串是Redis中最常用的一种数据结构。 不过Redis没有直接使用C语言中的字符串&#xff0c;因为C语言字符串存在很多问题&#xff1a; 获取字符串长度的需要通过运算非二进制安全…

(数据库管理系统)DBMS与(数据库系统)DBS的区别

数据库管理系统&#xff08;DBMS&#xff0c;Database Management System&#xff09;和数据库系统&#xff08;DBS&#xff0c;Database System&#xff09;是两个相关但不同的概念。 DBS是一个更广泛的概念&#xff0c;指的是计算机系统引入数据库后的系统&#xff0c;包括数…

PgSQL技术内幕-Bitmap Index Scan

PgSQL技术内幕-Bitmap Index Scan 1、简介 Bitmap索引扫描是对索引扫描的一个优化&#xff0c;通过建立位图的方式将原来的随机堆表访问转换成顺序堆表访问。主要分为两点&#xff1a;1&#xff09;管理每个Bitmap的hash slot没用完时&#xff0c;每个Bitmap代表每个heap页中满…

nn.KLDivLoss,nn.CrossEntropyLoss,nn.MSELoss,Focal_Loss

KL loss&#xff1a;https://blog.csdn.net/qq_50001789/article/details/128974654 https://pytorch.org/docs/stable/nn.html 1. nn.L1Loss 1.1 公式 L1Loss: 计算预测 x和 目标y之间的平均绝对值误差MAE, 即L1损失&#xff1a; l o s s 1 n ∑ i 1 , . . . n ∣ x i…

【Flink】系统架构

DataStream API 将你的应用构建为一个 job graph&#xff0c;并附加到 StreamExecutionEnvironment 。当调用 env.execute() 时此 graph 就被打包并发送到 JobManager 上&#xff0c;后者对作业并行处理并将其子任务分发给 Task Manager 来执行。每个作业的并行子任务将在 task…

矩阵运算_矩阵的协方差矩阵/两个矩阵的协方差矩阵_求解详细步骤示例

1. 协方差矩阵定义 在统计学中&#xff0c;方差是用来度量单个随机变量的离散程度&#xff0c;而协方差则一般用来刻画两个随机变量的相似程度。 参考&#xff1a; 带你了解什么是Covariance Matrix协方差矩阵 - 知乎 2. 协方差矩阵计算过程 将输入数据A进行中心化处理得到A…

【开源】基于Vue.js的创意工坊双创管理系统

项目编号&#xff1a; S 049 &#xff0c;文末获取源码。 \color{red}{项目编号&#xff1a;S049&#xff0c;文末获取源码。} 项目编号&#xff1a;S049&#xff0c;文末获取源码。 目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 管理员端2.2 Web 端2.3 移动端 三、…

你知道什么是SaaS吗?

你知道什么是SaaS吗&#xff1f; 云服务架构的三个概念 PaaS 英文就是 Platform-as-a-Service&#xff08;平台即服务&#xff09;PaaS&#xff0c;某些时候也叫做中间件。就是把客户采用提供的开发语言和工具&#xff08;例如Java&#xff0c;python, .Net等&#xff09;开…