结构体:
所谓结构体,就是将一大堆值放在一起,创建一个新的类型,这些成员可以是不同类型的变量。
struct tag
{
member-list;
}variable-list;
tag: 要求:1.见名知意; 2.可以省略; 3.不建议省略;
member-list: C语言中,不能为空;
variable-list: 变量列表,可以省略,建议省略;
结构体之间不能互相复制;例如:
struct
{
int a;
char b
float c;
}x;
struct
{
int a;
char b;
float c;
}a[20],*p;
p = &x; (错误!)
结构体地址 = 首元素地址(在数值上)
结构体内元素地址递增;
结构体成员访问:
结构体变量的成员是通过点操作符(.)访问的;
例如:
struct
{
char name[20];
int age;
};
struct S s
strcpy(s.name,"zhangsan");
s.age = 20;
或者用指针访问:
(*p).a =
(*p).b =
或:
p->a =
p->b =
void print(struct S* ps)
{
printf("name = %s",(*ps).name);
printf("name = %s",ps->name);
}
结构体的自引用:
正确方式(必须用指针):
struct Node
{
int data;
struct Node* next;
};
也可以对结构体进行重命名:
typedef ……
结构体的不完整声明,结构体之间可以互相引用吗,答案是肯定的,不过,要提前声明才行;
struct B
struct A
{
int _a;
struct B* pb;
};
struct B
{
int _b;
struct A* pa;
};
结构体与数组一样,不能被整体赋值,但可以被初始化;
结构体的内存对齐:
即结构体的大小计算:
首先,了解一下对齐规则:
1.第一个成员在与结构体变量偏移量0的地址处;
2.其他成员变量要对其到某个数字(对齐数)的整数倍的地址处。对齐数=编译器默认的一个对齐数与该成员大小的较小值。(VS中默认的值是8,linux中默认的值是4)
3.结构体总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍;
4.如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍;
那为什么存在内存对齐呢?
1.平台原因(移植原因):
2.性能原因:
总之,结构体对齐是拿空间来换取时间的做法;
修改对齐数:
#pragma pack( )——修改默认对齐数;
设置为1,2,4,8……
空白表示恢复默认;
结构体传参:
结构体传参不发生降维,不过,结构体传参尽量不传结构体变量,而传结构体指针;
struct S
{
int data[1000];
int num;
};
struct S s = {{1,2,3,4},1000};
void print(struct S* ps)
{
printf("%d\n",ps->num);
}
int main()
{
print(&s);
return 0;
}
位段:
1.位段的成员必须是 int unsigned int 或singed int
2.位段的成员名后边有一个冒号和一个数字;
比如:
struct A
{
int _a:2;
int _b:5;
int _c:10;
int _d:30;
};
这个数字表示 占用的byte位
位段采用压缩存储;
位段的内存分配:
1.位段的类型可以是int unsigned int signed int 或者是 char 类型;
2.位段上的空间是按照需要以4个字节(int)或者1个字节(char)的方式来开辟的;
3.位段涉及很多不确定因素,位段是不跨平台的,注重可移植性的程序应该避免使用位段;
位段的跨平台问题:
1.int 位段被当成有符号数还是无符号数是不确定的;
2.位段中最大位的数目不能确定(16位机器最大16,32位机器最大32,写成27,在16位机器会出问题。)
3.位段中的成员在内存中从左向右分配还是从右向左分配,标准尚未定义;
4.当一个结构包含两个位段,第二个位段成员比较大,无法容纳第一个位段剩余的位时,是舍弃剩余的位还是利用,这是不确定的;
枚举:
枚举——顾名思义就是一一列举;
枚举内的内容都是常量;
例如:
enum Day //星期
{
Mon,
Tues,
Wed,
Thur,
Fri,
Sat,
Sun
};
这些枚举{ }中的内容是枚举类型的可能取值,也叫枚举常量,这些可能取值都是有值的,默认从0开始,依次递增1,当然也可以自行赋值;
枚举的优点:
1.增加代码的可读性和可维护性;
2.和#define定义的标识符比较枚举有类型检查,更加严谨;
3.防止了命名污染;
4.便于调试;
5.使用方便,一次可以定义多个常量;
联合:
联合的成员是共用同一块内存空间的,所以一个联合体的大小,至少是最大成员的大小;
例如:面试题 判断当前计算机的大小端;
union Un
{
int i;
char c;
};
union un;
un.i = 0x11223344;
un.c = 0x55;
printf("%x\n",un.i);
运行上述代码,若输出结果为55223344,则该计算机为小端,若输出结果为11223355;则该计算机为大端;
联合大小计算:
联合体的大小至少是最大成员的大小;
当最大成员大小不是最大对齐数的整数倍的时候,就要对齐到最大对齐数的整数倍;
联合体和结构体的巧妙运用:
//将long类型的IP地址,转换为点分十进制的表示形式;
union ip_addr
{
unsigned long addr;
struct
{
unsigned char c1;
unsigned char c2;
unsigned char c3;
unsigned char c4;
}ip;
};
union ip_addr my_ip;
my_ip.addr = 176238749;
printf("%d.%d.%d.%d\n",my_ip.ip.c4,my_ip.ip.c3,my_ip.ip.c2,my_ip.ip.c1);