[c语言]——自定义类型

1.结构体

1.1结构体声明

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;
 float c;
}a[20], *p;

p = &x//非法的操作,上面俩种完全是不同的声明

1.2结构体的自引用

struct Node//结构体的自引用
{
 int data;
 struct Node* next;//使用指针自引用,不用指针无法计算结构体的大小
};

1.3结构体嵌套初始化

struct Node
{
 int data;
 struct Point p;
 struct Node* next; 
 }n1 = {10,{4,5},NULL};//直接初始化
struct Node n2 = {20, {5, 6}, NULL};//结构体嵌套初始化

2.结构体内存对齐

先来看下面的这个结构体大小应该为多少

struct S1
{
 char c1;
 int i;
 char c2;
};
printf("%d\n", sizeof(struct S1))//输出的结果应该为12

再来看另外一个代码

struct S1
{
	int a;
	int b;
	short c;
};
struct S2
{
	int m;
	char n;
	struct S1 s1;
};
printf("%d\n", sizeof(struct S2))//输出20

在linux操作系统下(最大对齐数为4)

#pragma pack(4)//修改默认对齐数
struct S1//结构体大小为12
{
	char c;
	double d;
};

结构体对齐规则

  1. 第一个成员在与结构体变量偏移量为0的地址处。
  2. 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。
    对齐数 = 编译器默认的一个对齐数 与 该成员大小的较小值。 VS中默认的值为8 Linux中的默认值为4
  3. 结构体总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍。
  4. 如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是
    所有最大对齐数(含嵌套结构体的对齐数)的整数倍。
    在这里插入图片描述
S1:

char占用一个字节偏移量为0,int需要是4的倍数所以偏移到4的位置,char又占用后一个字节的空间所以他们一共占用了9个字节的空间,但是结构体大小需要是最大元素对齐数的倍数所以9之后是4的倍数的值为12.

s2

同理,只不过嵌套的那个结构体要在对齐数 与 该成员大小的较小值选一个所以是8(vs下),所以这也是为什么他从第8个位置开始占用空间,最后要选择俩个结构体中最大元素并且对齐值是他的倍数,而最大对齐数是int所以结果是20.

怎么知道某个类型的偏移量
#define offsetof(s,m) (size_t)&(((s *)0)->m)//可以打印某个元素的偏移量

1.(s*)将0地址强制 “转换” 为 s结构类型的指针;
2.((s*)0)->m访问结构体中m中的成员
3.&((s*)0)->m取出结构中m成员的地址
4.size_t转换为整形数据输出。

为什么存在内存对齐?

1.平台原因(移植原因): 不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址
处取某些特定类型的数据,否则抛出硬件异常。
2 性能原因: 数据结构(尤其是栈)应该尽可能地在自然边界上对齐。 原因在于,为了访问未对齐的内存,处理
器需要作两次内存访问;而对齐的内存访问仅需要一次访问。

总结:以空间换时间。

3.位段

类似于下面写法的结构体就叫位段(c和指针中有具体讲解)

struct A
{
 int _a:2;//使用了开辟的第一个整型的2个比特位
 int _b:5;//使用了开辟的第一个整型的5个比特位
 int _c:10;//使用了开辟的第一个整型的10个比特位
 int _d:30;//丢弃之前的剩余位重新开辟一个整型,所以sizeof(struct A) == 8;
};

在这里插入图片描述

练习:

struct S1//输出3
{
	char a : 3;
	char b : 4;
	char c : 4;
	char d : 5;
};

在这里插入图片描述

位段的跨平台问题

  1. int 位段被当成有符号数还是无符号数是不确定的。
  2. 位段中最大位的数目不能确定。(16位机器最大16,32位机器最大32,写成27,在16位机器会出问
    题。
  3. 位段中的成员在内存中从左向右分配,还是从右向左分配标准尚未定义

4.枚举

枚举顾名思义就是一一列举。
把可能的取值一一列举。 比如我们现实生活中:
一周的星期一到星期日是有限的7天,可以一一列举。 性别有:男、女、保密,也可以一一列举。 月份有12
个月,也可以一一列举 颜色也可以一一列举。
这里就可以使用枚举了。

enum Day//星期
{
 Mon,
 Tues,
 Wed,
 Thur,
 Fri,
 Sat,
 Sun
};
enum Sex//性别
{
 MALE,
 FEMALE,
 SECRET
}

枚举的优点

  1. 增加代码的可读性和可维护性
  2. 和#define定义的标识符比较枚举有类型检查,更加严谨。
  3. 防止了命名污染(封装)
  4. 便于调试
  5. 使用方便,一次可以定义多个常量

5.联合体

联合类型的定义:联合也是一种特殊的自定义类型 这种类型定义的变量也包含一系列的成员,特征是这些成员公用同一块空间(所以联合也叫共用体)

联合大小的计算:
1.联合的大小至少是最大成员的大小。
2.当最大成员大小不是最大对齐数的整数倍的时候,就要对齐到最大对齐数的整数倍

#include<stdio.h>
union Un1
{
 char c[5];
 int i;
};
union Un2
{
 short c[7];
 int i;
};

int main()
{
	printf("%d\n", sizeof(union Un1));//8
    printf("%d\n", sizeof(union Un2));//16
	return 0;
}
  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值