结构体
回顾(结构体)
结构体声明
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);
}