结构和联合是程序员自己定义的数据类型。
结构:
什么是结构:是一种由程序员自定义的数据类型。
为了全面的描述一个物体的各项数据,由不同数据类型组成的一种新的数据类型。
定义结构:
struct struct_name
{
成员类型 成员名;
成员类型 成员名;
...
}; //分号不能少
注意:1.结构体变量定义时不能缺省struct 格式为:struct struct_name struct_var1;
2.结构成员不能有初始值
3.结构当中不能有函数,但可以有函数指针
4.结构变量可以对结构变量直接赋值 struct_var1 = struct_var2;
当然,为了偷懒,可以使用重定义typedef搭配结构一起使用
typedef struct name
{
type member1;
type member2;
}name;
这样就可以使用name 来定义结构体变量了 例如 name var1;
结构体的大小:
结构体的大小可以通过sizeof()查看,但是结构体的大小不是简单的成员类型的字节数之和。
编译器中提供了#pragma pack(n)来设定变量以n字节对齐方式。n字节对齐就是说变量存放的起始地址的偏移量有两种情况:第一、如果n大于等于该变量所占用的字节数,那么偏移量必须满足默认的对齐方式,第二、如果n小于该变量的类型所占用的字节数,那么偏移量为n的倍数,不用满足默认的对齐方式。
结构的总大小也有个约束条件,分下面两种情况:如果n大于所有成员变量类型所占用的字节数,那么结构的总大小必须为占用空间最大的成员变量类型的字节数的倍数;否则必须为n的倍数。
简单来说,就是遵循两个规则:
对齐:每个结构体成员的起始相对地址(相对于整个结构体的起始地址)即偏移量是其自身类型和n两者中较小值的整数倍,不够则补上字节数来对齐。第一个成员变量的地址与整个结构体的起始地址相同。
补齐:结构体的大小是最大成员类型或n中较小值的整数倍。
举个例子好了
#include <stdio.h>
int main(int argc, char const *argv[])
{
#pragma pack(4)
typedef struct student
{
char name[15];
int age;
char sex;
double grade;
}Stu;
printf("size=%d\n",sizeof(Stu));
return 0;
}
运行结果是 32
分析一下:name的偏移量为0,占15字节,当到age时,此时其偏移量为15,不满足其本身类型int(4字节)与n(4字节)的最小值即4的整数倍,所以补上一个字节为16,占4字节,到sex时,偏移量为20满足本身类型char=1字节整数倍,占1字节,之后由于grade的本身类型double = 8字节>n=4字节,所以选用4,偏移量为21要补上3字节对齐,变量占8字节,最后总偏移量为32。总偏移量此时是最大成员数double和n中较小值4的整数倍,所以不用补齐。
当n改为2时,运行结果为30 ,道理同上。
联合:
几个不同的数据类型共用同一段内存的结构,称为联合。
#include <stdio.h>
int main(int argc, char const *argv[])
{
union data
{
int num;
char s[13];
double p;
};
union data data1;
printf("%d\n",sizeof(union data));
printf("%p\n%p\n%p\n",&data1.num,data1.s,&data1.p);
return 0;
}
运行结果为
大小为16,说明联合也有补齐。联合内的成员起始地址都是相同的,所以是天然对齐的。
说到联合,还要提到一个概念:大端小端系统。
大端系统:低位数据存储在高位地址
小端系统:低位数据存储在低位地址
对于数据来说比如0x1234 那么12是高位数据,34是低位数据 对于地址来说0x0001 0x0002 前者是低位地址,后者是高位地址
可以通过联合来判断机器是大端还是小端。
#include <stdio.h>
union data{
int num1;
char num2;
}data1;
int main()
{
data1.num1 = 0x1234;
if(0x34 == data1.num2)
{
printf("小端系统\n");
}
else if(0x12 == data1.num2)
{
printf("大端系统\n");
}
return 0;
}