自定义类型:结构体,枚举,联合

结构体
◆结构体声明

struct tag
{
 member_list;
}variable_list;

例如定义一个学生的信息

struct Stu
{
char name[20];//姓名
int age;//年龄
char sex[5];//性别
char id[20];//学号
}//这里的分号不可以丢,是语法规定

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

struct
{
int a ;
char b;
float c;
}x;
struct
{
int a;
char b;
folat c;
}a[20],*p;

以上两个结构体在声明的时候忽略了结构体标签(tag)
问:在上面代码的基础上,
p=&x;
合理吗?
答案当然是不合理的,编译器会把上面的两个声明当初完全不同的两个类型,所以是非法的。

结构体自引用

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

以上代码是否可行,如果可以,那么sizeof(struct Node)是多少?
答案当然是不行的,编译显示错误,这种方式是结构体里面套了结构体,无限的嵌套循环。分配内存时根本无法确定结构体的长度,所以非法。在这种情况下,应该使用指针来完成嵌套,原因是因为在系统中指针的长度是确定的。
正确的自引用方式如下

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

结构体内存对齐

☆计算结构体的大小
结构体的对齐规则:
①第一个成员在与结构体变量偏移量为0的地址处
②其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处
对齐数=编译器默认的第一个对齐数 与 该成员大小的较小值
vs中默认的对齐数为8
③结构体总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍
④如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。

存在内存对齐的原因
结构体的内存对齐是用空间来换取时间的做法,那么在设计结构体的时候,我们既要满足对齐,又要节省空间,就尽量让占用空间小的成员集中在一起
修改默认对齐数
结构在对齐方式不合适的时候,我们可以自己更改默认对齐数
可以使用#pragma这个预处理指令

#pragma pack(4)//设置默认对齐数为8
#pragma pack()//取消设置的默认对齐数,还原为默认

结构体传参
举一个例子

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

问:print1和print2函数哪个好?
答案首选print2。因为函数在传参的时候,参数是需要压栈的,会有时间和空间上的系统开销。如果传递一个结构体对象的时候,结构体过大,参数压栈的系统开销比较大,所以会导致性能的下降。因此在结构体传参的时候,要选择结构的地址传参

位段
◆位段的声明
①位段的成员必须是 int、unsigned int 或 signed int。
②位段的成员名后面必须有一个冒号和一个数字。

举个例子

struct A
{
int a:2;
int b:5;
int c:10;
int d:30;
};

A就是一个位段类型。冒号后的数字表示该数据分配了几个比特位。
举例计算位段的大小

struct S
{
char a:3;
char b:4;
char c:5;
char d:4;
};
struct S s={0};
s.a=10;
s.b=12;
s.c=3;
s.d=4;

大小为 3个字节
◆位段的内存分配
①位段的成员可以是 int、unsigned int 或 signed int 或者是char(属于整形家族)类型。
②位段的空间上是以4个字节(int)或者一个字节(char)的方式来开辟的
③位段涉及很多不确定因素,位段是不跨平台的,注重可移植的程序应该避免使用位段。
枚举
枚举也就是列举
◆枚举的定义

enum Color
{
RED,
GREEN,
BLUE
};

以上定义的 enum Color 就是枚举类型。{ }中的内容是枚举类型的可能取值,也叫做枚举常量。
这些值都是有值的,默认从0开始,一次递增1,也可以在自定义的时候赋初值。
◆枚举的优点
①增加代码的可读性和可维护性
②和#define定义的标识符比较枚举有类型检查,更严谨
③防止了命名污染
④便于调试
⑤使用方便,一次可以定义多个常量
联合(共用体)
◆定义:
联合也是一种特殊的自定义类型,这种类型定义的变量也包含一系列的成员,特征是这些成员公用一块空间(所以也叫做共用体)例如:

//联合体类型声明
union Un
{
char c;
int i;
}
//联合变量的定义
union Un un;
;

计算这个联合体的大小
结果为4个字节

◆联合体的特点
联合体的成员是共用一块内存空间的,这样一个联合体的大小,至上是最大成员的大小(因为联合至少得有能力保存那个最大的成员)。
◆联合大小的计算
联合的大小至少是最大成员的大小
当前最大成员大小不是最大对齐数的整数倍的时候,就要对齐到最大对齐数的整数倍。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
C语言中,结构体是一种自定义数据类型,可以将不同类型的变量组合在一起,形成一个新的数据类型结构体定义的基本形式如下: ```c struct 结构体名 { 数据类型 成员名1; 数据类型 成员名2; // ... }; ``` 其中,结构体名是用户自定义的名称,成员名是结构体中每个成员的名称,数据类型可以是任意C语言的数据类型,包括基本数据类型自定义数据类型结构体变量的定义方式如下: ```c struct 结构体名 变量名; ``` 读取结构体中的成员变量可以通过“.”运算符来实现,例如: ```c #include <stdio.h> struct Person { char name[20]; int age; }; int main() { struct Person p; printf("请输入姓名:"); scanf("%s", p.name); printf("请输入年龄:"); scanf("%d", &p.age); printf("姓名:%s,年龄:%d\n", p.name, p.age); return 0; } ``` 枚举是一种特殊的数据类型,用于定义一组常量。枚举的定义方式如下: ```c enum 枚举名 { 常量名1, 常量名2, // ... }; ``` 其中,枚举名是用户自定义的名称,常量名是枚举中每个常量的名称。枚举常量的值默认是从0开始自动递增的,也可以手动指定值。例如: ```c #include <stdio.h> enum Weekday { Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday }; int main() { enum Weekday today = Tuesday; printf("今天是星期%d\n", today + 1); return 0; } ``` 联合是一种特殊的数据类型,它的成员变量共享同一块内存空间。联合的定义方式如下: ```c union 联合名 { 数据类型 成员名1; 数据类型 成员名2; // ... }; ``` 其中,联合名是用户自定义的名称,成员名是联合中每个成员的名称,数据类型可以是任意C语言的数据类型,但所有成员的大小不能超过联合的大小。例如: ```c #include <stdio.h> union Number { int i; float f; }; int main() { union Number n; n.i = 123; printf("int: %d, float: %.2f\n", n.i, n.f); n.f = 3.14; printf("int: %d, float: %.2f\n", n.i, n.f); return 0; } ``` 以上就是C语言中自定义数据类型中的结构体枚举联合的基本用法和注意事项。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值