自定义类型

一、结构体(struct)

1.声明

声明一个学生的结构体如下:

struct Stu
{
    char name[20];//名字
    int age;//年龄
    char sex[5];//性别
    char id[20];//学号
}; //分号不能丢

注:结构体在声明时可省略标识符'Stu',但这种声明方法只能在声明时创建对应的变量,而且若存在另一个成员变量与其完全相同的匿名结构体,编译器会把这两个声明当成完全不同的类型,因此不建议这样声明。

  • 结构体的成员变量可以是其他结构体类型,但不能是自身结构体变量
  • 结构体的成员变量可以是自身结构体指针

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

结构体变量可以在声明的同时定义,也可在主函数内部需要时定义。区别在于前者为全局变量,后者为局部变量。

定义变量的同时赋初值即可进行初始化。

struct Point
{
    int x;
    int y;
}p1; //声明类型的同时定义变量p1
struct Point p2; //定义结构体变量p2

//初始化:定义变量的同时赋初值。

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};//结构体嵌套初始化

3.结构体内存对齐

(1)对齐规则

1>每个成员变量相对于结构体首地址的偏移量是对齐数的整数倍

  • 对齐数:编译器默认对齐数(VS为8,gcc无默认值)和成员大小(若为数组,是每个元素的大小;若为结构体,是该结构体中最大的对齐数)中的较小值

 2>整个结构体的大小是各个成员中最大对齐数的整数倍

(2)对齐原因

①平台原因(移植原因):

不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特 定类型的数据,否则抛出硬件异常

②性能原因:

数据结构(尤其是栈)应该尽可能地在自然边界上对齐。 原因在于,为了访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要一次访问

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

(3)举例

struct s1         //对齐数:
{
	double a;	  //8
	char b[5];	  //1
	int c;		  //4
};

struct s2
{
	char x;		 //1
	struct s1 y; //8(s1中最大的对齐数为8)
	float z;	 //4
};
int main()
{
	printf("%d  %d\n", sizeof(struct s1), sizeof(struct s2));
	return 0;
}

输出结果为:24  40

解释:对于s1,a的对齐数是8,因为a是第一个成员变量,可以直接存放在首地址处(偏移量为对齐数的0倍)并开辟8个字节存放a, b的对齐数为1,8是1的整数倍,故直接开辟1*5=5个字节存放b,c的对齐数为4,但目前开辟的8+5=13个字节不是4的整数倍,故先开辟3个字节(13+3=16是4的整数倍),再开辟4个字节存放c,现在共开辟了20个字节,但并不是最大对齐数8的整数倍,故再开辟4个字节(20+4=24是8的整数倍)。因此,s1的大小为24个字节。

对于s2,x的对齐数为1,直接开辟1个字节存放x,y的对齐数为8,先开辟7个字节(1+7=8是8的整数倍),再开辟24个字节(s1的大小)存放y,z的对齐数为4,8+24=32是4的整数倍,直接开辟4个字节存放z,现在共开辟了36个字节,不是最大对齐数8的整数倍,继续开辟4个字节(36+4=40是8的整数倍)。因此,s2的大小为40个字节。

(4)修改默认对齐数

使用预处理指令#pragma pack()可以修改编译器的默认对齐数

例如,将上面代码结构体的默认对齐数改为4,其大小分别变为了20    28

#pragma pack(4)//修改默认对齐数为4
struct s1         //对齐数:
{
	double a;	  //8->4
	char b[5];	  //1
	int c;		  //4
};

struct s2
{
	char x;		 //1
	struct s1 y; //8(s1中最大的对齐数为8)->4
	float z;	 //4
};
#pragma pack()//恢复默认对齐数8
int main()
{
	printf("%zd  %zd\n", sizeof(struct s1), sizeof(struct s2));
	return 0;
}

4.结构体传参

在结构体传参时,最好传结构体的地址。原因:

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

二、枚举(enum)

1.枚举类型的定义

enum Day//星期
{
     Mon,
     Tues,
     Wed,
     Thur,
     Fri,
     Sat,
     Sun
};
enum Sex//性别
{
     MALE,
     FEMALE,
     SECRET
};
enum Color//颜色
{
     RED,
     GREEN,
     BLUE
};

以上定义的 enum Day , enum Sex , enum Color 都是枚举类型。 {}中的内容是枚举类型的可能取值,也叫 枚举常量

枚举常量都是有值的,默认从0开始,一次递增1,当然在定义的时候也可以赋初值。

例如:enum Color  //颜色

        {

                RED=1,

                GREEN=2,

                BLUE=4

        };

2.枚举的优点

我们可以使用 #define 定义常量,为什么非要使用枚举?

枚举的优点:

1. 增加代码的可读性和可维护性

2. 和#define定义的标识符相比枚举有类型检查,更加严谨。

3. 防止了命名污染(封装)

4. 便于调试

5. 使用方便,一次可以定义多个常量

三、联合(union)

1.联合类型的定义

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

2.联合的特点

联合的成员是共用同一块内存空间的,这样一个联合变量的大小,至少是最大成员的大小

union Un
{
     int i;
     char c;
};
union Un un;

int main()
{
    printf("%p\n", &(un.i));
    printf("%p\n", &(un.c));


    un.i = 0x11223344;
    un.c = 0x55;
    printf("%x\n", un.i);
}

输出为:

因为联合的成员共用一块内存,因此i和c的地址是相同的;i占4个字节,因为小端存储,第一个字节存放的是44,c只占一个字节,将原来44改为了55,所以输出为11223355

3.联合大小的计算

  • 联合的大小至少是最大成员的大小。
  • 当最大成员大小不是最大对齐数的整数倍的时候,就要对齐到最大对齐数的整数倍

例如:

union Un1
{
     char c[5];
     int i;
};
union Un2
{
     short c[7];
     int i;
};

Un1的大小为8(=5+3)  ,Un2的大小为16(=14+2)

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值