跨境派

跨境派

跨境派,专注跨境行业新闻资讯、跨境电商知识分享!

当前位置:首页 > 工具系统 > 建站工具 > C语言——结构体自定义类型

C语言——结构体自定义类型

时间:2024-04-01 08:45:40 来源:网络cs 作者:胡椒 栏目:建站工具 阅读:

标签: 定义  类型  结构  语言 

%20目录

%20结构体类型

%20声明结构体

%20结构体的特殊声明

%20%20创建结构体变量和初始化结构体变量

%20%20结构体的自引用

%20结构体内存对齐

%20对齐规则

%20内存对齐存在意义

%20默认对齐数的修改

%20结构体传参

%20结构体实现位段

%20了解位段是什么

%20位段的内存分配

%20位段有跨平台的问题及使用注意事项

%20%20

C语言中有内置的类型,内置类型如下:

%20%20%20%20内置类型%20%20char%20short

%20intlonglong%20longfloatdoublelong%20double%20

这些都是C语言本身支持的现场类型,但是仅仅有内置类型是不够的。

%20

比如,我们要定义一个人的变量,

%20

人:3.14 %20 —— %20 这种就是不行的

%20

人是一个复杂的对象,有身高、体重、名字等。所以这就要用到一个自定义类型——结构体

%20

C语言中也有自定义类型的。

%20结构体类型%20声明结构体%20

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

%20
//结构体的声明struct%20tag%20%20//tag就是标签名{%20%20%20%20member-list;%20%20//成员列表:1个或者多个,但是不能没有}variable_list;%20%20//变量列表
%20

我们知道结构体的声明之后,我们就可以自定义一个学生练习一下:

%20
struct%20Stu{char%20name[20];%20//名字int%20age;%20%20%20%20%20%20%20//年龄char%20sex[5];%20%20%20//性别char%20id[20];%20%20%20//学号};%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20//分号不能丢
%20结构体的特殊声明%20

在声明结构体的时候,可以不完全声明:匿名结构体类型

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

这两个结构体对比前面的,看出tag省略了

%20

在使用这种形式的时候,要注意两点:

%20%20%20%20%20%20%20%20匿名的结构体类型,如果没有对结构体类型重命名的话,基本上只能使用一次,第二次往后基本不能用了。%20%20%20%20%20%20%20%20%20%20%20编译器会把上面的两个声明当成完全不同的两个类型,所以下面操纵是非法的。%20%20%20%20%20%20%20
p%20=%20&x;
%20%20

%20创建结构体变量和初始化结构体变量%20

我们定义了这个类型,要怎么创建这种类型的变量并怎么初始化它呢?

%20

局部结构体变量创建和初始化有两种:

%20

一种按照结构体成员的顺序初始化的,如下:

%20
//按照结构体成员的顺序初始化struct%20Stu%20s%20=%20{%20"张三",%2020,%20"男",%20"20230818001"%20};
%20

我们想打印验证一下这个结构体变量,要怎么打印呢?

%20
printf("name:%20%s\n",%20s.name);printf("age%20:%20%d\n",%20s.age);printf("sex%20:%20%s\n",%20s.sex);printf("id%20:%20%s\n",%20s.id);
%20

                        

注:   ·   和  ->都是用来访问结构体内的变量用的,· 是用来取的这个结构体中的元素,->是取得这个结构体中元素的地址所对应的元素。

例如:struct Stu{char name[20];//名字int age;//年龄char sex[5];//性别char id[20];//学号};             *p=Stu;Stu.name==(*p).name==p->name。

第二种初始化按照指定顺序初始化的(乱序):

//按照指定的顺序初始化struct Stu s2 = { .age = 18, .name = "李四", .id = "20230818002", .sex = "女" };printf("name: %s\n", s2.name);printf("age : %d\n", s2.age);printf("sex : %s\n", s2.sex);printf("id : %s\n", s2.id);

                                

全局变量的定义有两种,初始化方式也跟局部变量相似,下面就只举例全局变量的创建:

struct Stu{char name[20];//名字int age;//年龄char sex[5];//性别char id[20];//学号}b2,b3; //全局变量           struct Stu b1;//全局变量

结构体的自引用

先介绍typedef相关知识, typedef为C语言的关键字:作用是为一种数据类型定义一个新名字

typedef可以声明新的类型名来代替已有的类型名,不能增加新的类型。这里的数据类型有前面所说的内置数据类型(int,float等),还有自定义的数据类型(struct等)。

结构体自引用对于数据结构上的链表是非常有用的,

数据结构——其实是数据在内存种的存储和组织的结构,数据结构有多种

线性数据结构:顺序表、链表、栈、队列。树形数据结构:二叉树图等等……

结构体自引用的正确方式(链表形式):

struct Node{int data;//数据struct Node* next;  //指针——自己里包含一个自己同类型的指针};

我们用typedef重命名一下:

typedef struct Node{int data;//数据struct Node* next;  //指针——自己里包含一个自己同类型的指针}Node;    //将struct Node 类型重命名为 Node//也可也写成struct Node{int data;//数据struct Node* next;  //指针——自己里包含一个自己同类型的指针};typedef struct Node Node;  //typedef在后面重命名

注意,千万不能写成以下这些形式:

struct Node{ int data; struct Node next;};
一个结构体中再包含一个同类型的结构体变量,这样结构体变量的大小就会无穷的大。
typedef struct   //匿名结构体类型{ int data; Node* next;  //这里面还有struct不能省略掉}Node;

这个Node是对前面的匿名结构体类型的重命名产生的,但在匿名结构体内部提前使用Node类型来创建变量成员变量是不可以的。所以匿名结构体类型是不能实现这种自引用的。

所以,定义结构体尽量不要使用匿名结构体。

结构体内存对齐

这个牵扯到计算结构体的大小,并且还有它特有的对齐规则。

对齐规则

结构体的第一个成员对齐到结构体变量起始位置偏移量为0的地址处。(后面举例介绍)其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。                                             对齐数=编译器默认的一个对齐数与该成员变量大小的较小值                                                   VS中默认的一个对齐数为8                                                                                                       Linux中gcc没有默认对齐数,对齐数就是成员变量自身大小。结构体总大小为最大对齐数(结构体中每个成员变量都有一个对齐数,所有对齐数中最大的)的整数倍。如果嵌套了结构体的情况,嵌套的结构体成员对齐到自己的成员中最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体中成员的对齐数)的整数倍。

例如下列例子,并了解这个规则怎么用:

struct S1{ char c1; int i; char c2;};printf("%d\n", sizeof(struct S1));struct S2{ double d; char c; int i;};printf("%d\n", sizeof(struct S2));//结构体嵌套问题struct S3{ char c1; struct S2 s2; double d;};printf("%d\n", sizeof(struct S3));

第一个:

所以会输出:12.

注:若遇到数组,如char[5] 则就是存了五个char。

第二个:

所以这个输出16.

第三个:

所有会输出:32.

内存对齐存在意义

结构体内存对齐是牺牲空间来换取时间的做法。

1. 平台原因 (移植原因) 2. 性能原因 若又想节省一些空间,那么就让空间小的成员尽量集中在一起。
struct S1{    char c1;    int i;    char c2;};  //占12struct S2{    char c1;    char c2;    int i;};  //占8

默认对齐数的修改

我们前面说VS中默认对齐数是8,那么我们可以修改吗?

我们可以用一个预处理指令修改,并且另一个指令可以取消修改,如下:

#pragma pack(1)//设置默认对⻬数为1#pragma pack()//取消设置的对⻬数,还原为默认

根据自己所需,可以用这个预处理指令修改对齐数。

结构体传参

我们函数传参,可以传整型变量、数组、指针变量等。结构体变量也可以传。

实现具体如下列程序:

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

不过上面两个函数,print2的效率高一些。

所以,结构体传参的时候,尽量传结构体的地址。

结构体实现位段

了解位段是什么

位段实现是基于结构体的

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

位段的成员必须是int、unsigned int 或者signed int,C99中位段成员的类型可以选择其他类型。位段成员名后边有一个冒号和一个数字。
struct A{ int _a:2;};

位段的内存分配

位段的成员可以是int、unsigned int、signed int 或者char类型等 位段的空间上是按照需要以4个字节(int)或1个字节(char)的方式来开辟的 位段很多不确定因素,因此不能跨平台的,可移植程序应避免使用位段。

我们看下一段代码:

struct A{int _a : 2;int _b : 5;int _c : 10;int _d : 30;};int main(){printf("%d\n", sizeof(struct A));return 0;}

A所占内存是多少?

输出结果:

位段有跨平台的问题及使用注意事项

int位段被当成有符号还是无符号不确定

位段最大位的数目不确定。

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

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

所以位段可以达到结构体同样的效果,并且可以很好的节省空间,但有跨平台问题。

注意事项:位段内存中每个节分配一个地址,一个字节内部的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;}

制作不易,求各位大佬三连qwq,若有不足的地方,请大佬们多多指点!

本文链接:https://www.kjpai.cn/news/2024-04-01/151829.html,文章来源:网络cs,作者:胡椒,版权归作者所有,如需转载请注明来源和作者,否则将追究法律责任!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。

文章评论