自定义类型的深度理解

结构体

回顾(结构体)

结构体声明

struct tag
{
    member_list;
}variable_list;
//定义tag为类型名
typedef struct tag
{
    member_list;
}tag;

匿名声明,一般用于结构体内嵌套结构体;

        声明结构的时候,不完全声明(省略结构体标签tag)

struct
{
    int a;
    char b;
    float c;
}x;
struct
{
    int a;
    char b;
    float c;
}*p;

合法吗?

p=&x; //不合法

即使结构体内容相同只要是两个声明,就是不同的类型;

结构体自引用

在结构中包含一个类型为该结构自身的成员可以吗?

即:

strut Node
{
    int data;
    struct Node next;
};
sizeof(struct Node)?

不可以,大小未知;

正确自引用

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

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

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};//初始化
//s.name="lisi";
//不可行,name为数组首地址,没有存储空间;
//strcpy(s1.name, "xyz");//可行

struct Stu1        //类型声明
{
     char *name;   //名字
     int age;      //年龄
};
struct Stu1 s = {"zhangsan", 20};
s.name="lisi";
//可行,name为指针,存储lisi 字符串的首地址;

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

struct Node n2 = {20, {5, 6}, NULL};//结构体嵌套初始化

结构体内存对齐(热门考点)

计算结构体的大小,高效程序

为什么存在内存对齐?

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

在某些地址处取某些特定类型的数据,否则抛出硬件异常。

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

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

总体来讲:拿空间换时间!

准则

  • 基本数据类型的自身对齐值
  • 自定义类型的自身对齐值(结构内最大的类型)
  • 程序的指定对齐值(#pragma pack(num)) num只能为2的整数幂;(1,2,4,8...)
  • 程序的有效对齐值(默认为8,一般为自身对其值,有指定值时为指定值,优先级最高;总大小为有效对齐值的整数倍)

练习

//1.   24
typedef struct Test	//自身对齐值(double)=有效值
{
	char a;   //1 + 7	//char 向double对齐
	double b; //8		
	int c;    //4 + 4	//8+8+4不能整除有效值
}Test;
//2.   16
typedef struct Test	//自身对齐值(double)=有效值
{
	char a;   //1 + 3	//char 向int对齐
	int c;    //4 		//a+c  为有效对齐值
	double b; //8		//8+4+4整除8
}Test;
//3.   14
#pragma pack(2)		//程序指定对齐值

typedef struct Test	//自身对齐值(有效值)
{
	char a;   //1 + 1	//char 向double对齐,但有效值为2,向有效值对齐
	double b; //8
	int c;    //4 
}Test;
#pragma pack()		//解除指定值
//4.   7
#pragma pack(1)
typedef struct Test
{
	char a;   //1 
	short b;  //2
	int c;    //4 
}Test;
//5.   40
typedef struct Test	//嵌套定义时,有限计算内部结构体	自身对齐值(8)=有效对齐值
{
	short a;    //2 + 6		//与struct自身对齐值对齐
	struct				//自身对齐值(double 8),总大小 24
	{
		int b;    //4 + 4
		double c; //8
		char d;   //1 + 7
	};
	int e; //4 + 4			8+24+4无法整除8,+4整除
}Test;
//6.    16
#pragma pack(16)
struct S3
{
	double d; //8			
	char c;   //1 +3
	int i;    //4
};
//7.    32

struct S4
{
	char c1;  //1 + 7
	//struct S3 s3; //16
	struct 
	{
		double d; //8
		char c;   //1 +3
		int i;    //4
	};
	double e; //8
};
//8.   96
#pragma pack(4)
typedef struct Test
{
	int a; //4
	struct
	{
		double b[10]; //80
		int c;        //4
		char d;       //1+3
	};
	int e;//4
}Test;

结构体传参

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

上面的 print1 和 print2 函数哪个好些?

答案是:首选print2函数。 原因:

函数传参的时候,参数是需要压栈,会有时间和空间上的系统开销。

如果传递一个结构体对象的时候,结构体过大,参数压栈的的系统开销比较大,所以会导致性能

的下降。

结论: 结构体传参的时候,要传结构体的地址。

位端(一般应用与网络协议定义)

什么是位段?

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

1.位段的成员必须是 int、unsigned int 或signed int 。

2.位段的成员名后边有一个冒号和一个数字。

内存开辟:按位开辟,不够,先补齐再分配

struct A	//总大小8字节,7,内存补齐后8字节
{
    char_a:1;	//开辟一字节,分配一位,剩7位
    char_b:6;	//继续分配6位,剩余1位
    char_c:2;	//不够分,7位补齐,再开辟1字节,分配1位,剩余7位
    int i;		//先补齐,开辟整型大小,4字节
    char_d:1;     	//开辟一字节,分配一位,剩7位
};
struct A a={0};
s.a=0;	//0
s.b=6;	//000110
s.c=4;	//100,越位,截断为00
s.i=89;	
s.d=2;	//10,越位,截断为0

枚举

顾名思义:一一列举

一周7天

一年12月

定义

enum Day
{
    Mon,
    Tues,
    Wed,
    Thur,
    Fri,
    Sat,
    Sun
};

enum Day为枚举类型,里面的Mon等为枚举类型的可能取值,一叫枚举常量。

默认从0开始,一次递增1,当然在定义时也可以赋初值!

优点

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

使用

只能用枚举常量给枚举类型赋值

enum Day
{
    Mon,
    Tues,
    Wed,
    Thur,
    Fri,
    Sat,
    Sun
};

联合(共用体)

定义

特殊的自定义类型 这种类型定义的变量也包含一系列的成员,

特征是这些成员公用同一块 空间(所以联合也叫共用体)。

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

特点

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

联合至少得有能力保存最大的那个成员)。

union Un			//4字节
{
    int i;
    char c;
};
union Un un;
printf("%p\n",&(un.i));
printf("%p\n",&(un.c));
un.i=0x11223344;		//小端模式存储 	44 33 22 11
un.c=0x55;				     // 55
printf("%x\n",un.i);	//输出	11223355

判断大小端的存储方式

bool check_modle()
{
	int a = 0x01; //00 00 00 01
	return *(char*)&a == 0x01;
}
void main()
{
	if(check_modle())
		printf("小端.\n");
	else
		printf("大端.\n");
}

联合大小计算

  • 联合的大小至少是最大成员的大小
  • 当最大成员的大小不是最大对齐数的整数倍时,内存对齐
union Un1		//最大对齐数 4,大小 8
{
    char c[5];	//5
    int i;		//4
}
union Un2		//最大对齐数 4,大小 16
{
    short s[7];	//14
    int i;		//4
}
printf("%d\n",sizeof(union Un1));
printf("%d\n",sizeof(union Un2));

联合体与结构体结合应用

typedef union IP
{
	struct
	{
		unsigned char ip1; 
		unsigned char ip2;
		unsigned char ip3;
		unsigned char ip4;
	};
	size_t ip;
}IP_t;

void main()
{
	size_t value = 3893012672; //IP 192.168.10.232
	//C0 A8 0A E8

	IP_t myip;
	myip.ip = value;
	printf("%u.%u.%u.%u\n", myip.ip1, myip.ip2, myip.ip3, myip.ip4);
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值