一、聚合数据类型:
数组和结构体。数组是相同数据类型的集合,它的每个数据元素可以通过下标或者是指针间接访问,而结构体的不同点是它是不同元素类型的数据的集合。结构体不能通过下标来访问原因是它的每个成员(元素)的类型不同,长度不同。它们可以通过名字来访问。
结构体的声明:
struct tag
{ int a;
short b;
char c;
}x;
tag标签可以结构体起名字,{ }里的成员列表表示是每个元素的类型,和名字。x表示是结构体变量它包含三个成员整形a,短整形b,字符型c。二、结构体元素地址分配规则:
既然和数组一样都是聚合类型,是否地址也是从低到高呢?下面通过例子来验证下:
typedef struct A
{
int a;
float b;
char c;
}obj;
int main()
{
obj x;
x.a = 10;
x.b = 3.14;
x.c = 'c';
printf("%p\n%p\n%p\n", &x.a, &x.b, &x.c);
system("pause");
return 0;
}
通过结果可以看出结构体成员的地址分配规律也是从低到高的。
三、结构体成员的访问:
1、直接访问:结构体名加点操作符.的形式:点操作符接受两个操作数,左边表示结构体名,右边表示要访问的成员。点操作符的访问顺序是从左到右。
typedef struct A
{
int a;
float b;
char c;
}obj;
int main()
{
obj x;
x.a = 10;
x.b = 3.14;
x.c = 'c';
system("pause");
return 0;
}
使用typedef定义一个新类型,此时obj不是变量名而是类型名。2.也可以间接访问,用->操作符来访问其成员。
struct A
{
int a;
float b;
char c;
int arr[5];
}x = { 10,
3.14,
'c',
{1,2,3,4,5},
};
int main()
{
struct A *p = &x;
printf("%d\n", p->a);
printf("%f\n", p->b);
printf("%c\n", p->c);
printf("%d\n", p->arr[3]);
system("pause");
return 0;
}
&x表示是整个结构体的地址。3.访问嵌套的成员:如:typedef struct A
{
int arr[5];
}obj;
struct B
{
int a;
float b;
char c;
obj d;
} x = {
1,
3.14,
'c',
{1,2,3,4,5}
};
int main()
{
struct B *px = &x;
px->a;
px->b;
px->d.arr[1];
system("pause");
return 0;
}
struct A
{
int a;
char b;
float c;
};
int main()
{
printf("%d\n", sizeof(struct A));
system("pause");
return 0;
}
结果是多少呢?
首先明白对齐原因:
1:平台原因:不是所有的硬件平台都能访问任意地址的任意数据,某些平台只能在某些地址取某些特定类型的数据,否则抛出硬件异常
2.性能原因:
数据结构(尤其是栈)应该尽肯能地自然边界对齐,原因在于,为了访问未对齐的的内存,处理器需要做两次内存访问,两次对齐的内存访问仅需要一次访问。
结构体内存对齐的规则:
1:第一个成员在与及结构体变量偏移量为0的地址处;
2:其他成员变量要对齐到对齐数的整数倍的地址处;
(对齐数=编译器默认一个对齐数与该成员大小的较小值)
编译器默认对齐数:
vs默认对齐数是8;
Linus默认对齐数是4
3:结构体的总大小为最大对齐数(每个成员除第一个成员都有一个对齐数)的整数倍;
4:如果嵌套结构体的情况,嵌套结构体对齐到自己最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。
接下来我们按照规则来分析:当前是在vs环境下,结构体第一个成员int 型4个字节没有对齐数,到char型自身大小是1个字节,与编辑器默认的对齐数是8的较小值是1,所以char型的对齐数1对齐到前面4个字节, 此时偏移量5,float型自身4个字节,与默认值8的较小值是4,4 的整数倍是8但前面的只有5个字节,系统再开辟3个字节刚好是8对齐到对齐数4的整数倍处。此时内存的大小 12,12 是结构体成员最大(第一个除外)对齐数4的整数。
嵌套结构体:
struct B
{
int x;// 4不用对齐;
double y;
char c;
//
}obj;
//24
struct A
{
int a;//4
float b;//4
char c;//+1
struct B obj[2];//结构体数组;//48 +16
int *p;//4 (68)+4
};
第一个按照规则很容易得到其内存的大小是24,那么嵌套该怎么理解对结构体B分析:int 型4个字节,没有对齐数,float4个字节与默认值8的较小值是4,对起到其对齐数整数倍4处,此时内存大小是8;char型1个字节,与默认值的的较小值是1,对齐到8的位置,此时有9个字节,结构体数组B的大小是48;由前面分析可知结构体B对齐数的最大对齐数是4,对起到对齐数的整数倍前面为9再开辟3个字节到12此时为60;最后指针4个字节,与默认最小值为4;对齐到对齐数整数倍(60是4的整数倍)。此时大小为72。
当然我还可以通过修改默认值来改变其大小
可以通过宏指令##pragma pack (1)此时1表示编辑器的默认值是1
五、位段
位段,C语言允许在一个结构体中以位为单位来指定其成员所占内存长度,这种以位为单位的成员称为“位段”或称“位域”(利用位段能够用较少的位数存储数据。
位段的声明和结构体类似区别在于,位段的成员声明必须是int,unsinged int ,signed int 型,成员的后面是一个冒号和一个整数,这个整数是该位段所占字节的数目;如:
struct A
{
int a : 4;
int b : 4;
int c : 4;
}obj;
int main()
{
obj.a = 10;
obj.b = 5;
obj.c = 7;
printf("%d\n%d\n%d\n",obj.a,obj.b,obj.c);
printf("%d\n", sizeof(struct A));
system("pause");
return 0;
}
把位段声明为整形,它究竟是被解释为有符号的数还是无符号的数由编辑器决定。