结构体:
我们之前学过数组吧!结构体和它类似。
1.结构体的声明
结构是一些值的集合,这些值称为成员变量。结构的每个成员变量可以为不同的类型。
其声明为:
struct tag //tag为标签
{
member-list;//不可省略。
}variable-list;//分号不能丢
标签和变量名至少存在一个。
下面给出一断代码,看看是否合法?
struct {
int a;
char b;
float c;
struct {
int a;
char b;
float c;
}y[20],*z; //y是一个结构体数组,z是一个指针,指,z是一个指针,指向此类型结构
z=&x;//不合法!
这两个声明被当做不同的两种类型。即使其结构体成员不同。
结构体的成员
其成员可以是标量,数组,指针甚至是其他结构体。
结构体的成员地址依次增大,结构体本身的地址和成员首地址相同。(关于这部分内容有需要证明者可自行打印证明哦!)
结构体成员的访问
(1).通过点操作符(.)访问,接受两个参数。结合性从左至右。
(2).通过->操作符访问。左操作数必须为一个指向结构的指针。
代码说明
struct Stu {
char name[20];
int age;
};
struct Stu s;//结构体变量
struct Stu *p;
(*p).age = 20;
p->age = 20;
s.age = 20;
结构体的自引用
提个问题先:在一个结构体内包含一个类型为该本身的成员是否可以呢?
结构体自引用要使用完整的标签
举栗说明:
struct Stu {
char name[20];
int age;
struct Stu x; //编译器报错,“x”使用未定义的 struct“Stu” ,编译器不确定其长度
};
正确方式为:
struct Stu {
char name[20];
int age;
struct Stu *x;//x是一个指针,指向同一类型的不同结构。编译器确定其长度,合法。
};
4.结构体的初始化
这里只要记住一句话就可以了,结构体本身允许整体初始化(使用花括号),不允许整体赋值。
5.结构体的内存对齐
注意啦!此部分内容是结构体部分的重点。
结构体的内存对齐是拿空间来换取时间的做法。
为什么存在内存对齐?
(1).平台原因(移植原因)
不是所有的平台都可访问任意地址上的任意数据,某些只能在某些地址处取某些特定的数据,否则会抛出异常。
(2).性能原因
为了访问未对齐的内存,处理器需作两次内存访问,二对齐的内存只需访问一次。
如何计算?
首先必须了解结构体的对齐规则:
a.第一个成员与结构体变量偏移量为0的地址处;
b.其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处;
对齐数=编译器默认的对齐数与该成员大小的较小值。
vs中默认最大对齐数为8 //用#pragma pack ()设置时应小于等于8;
linux默认最大对齐数为4;
c.结构体总大小为最大对齐数的整数倍。
d.若嵌套了结构体,嵌套的结构体对齐到自己最大对齐数的整数倍。结构体的整体大小就是所有最大对齐数(含嵌套的结构体最大对齐数)的整数倍。
接下来举栗说明:
例1:
struct S1
{
char c1;
int i;
char c2;
};
printf("%d\n", sizeof(struct S1));//结果12
例2:
struct S2
{
char c1;
char c2;
int i;
};
printf("%d\n", sizeof(struct S2));//结果为8
例3:
struct S3
{
double d;
char c;
int i;
};
struct S4
{
char c1;//1+7
struct S3 s3;//8+16
double d; //8+16+8=32
};
printf("%d\n", sizeof(struct S4));//32
例4:
//结构体嵌套类型
//用上例的S3结构体,最大对齐数为8
vs2013下
struct S5
{
char c1; //1+7=8
struct S3 a[3]; //8+16*3=56
char ch[5];//56+5=61
struct S3 *p;//61+3+4=68
double d;//68++4+8=80
short b[3];//80+6+2=88
};
printf("%d\n", sizeof(struct S5));//88
Linux下为80,//最大对齐数4
struct S5
{
char c1;//!+3=4
struct S3 a[3];//4+16*3=52
char ch[5];//52+5=57
struct S3 *p;//57+3+4=64
double d;//64+8=72
short b[3];//72+6+2=80
};
5.结构体传参
这块用代码说明即可:
struct S
{
int data[1000];
int num;
};
struct S s = { { 1, 2, 3, 4 }, 1000 };
//结构体传参
void print1(struct S s)
{
sleep(100);
printf("%d\n", s.num);
}
//结构体地址传参
void print2(struct S *ps)
{
sleep(100);
printf("%d\n", ps->num);
}
int main()
{
print1(s); //传结构体,可以借用函数栈帧理解,系统开销大
print2(&s); //传地址,每次4字节
system("pause");
return 0;
}
运行后即可发现二者时间不同
位段
位段的声明和结构是类似的,有两个不同:
1.位段的成员必须是 int、unsigned int 或signed int 或char。
2.位段的成员名后边有一个冒号和一个数字。
struct A
{
int_a:2;
int_b:5;
int_c:10;
int_d:30;
};
printf("%d\n", sizeof(structA));//8 为什么呢?
见图:
吃个栗子吧:
typedef struct{
int a:2;
int b:2;
int c:1;
}test;
int main(){
test t;
t.a=1;
t.b=3;
t.c=1;
printf("%d\n%d\n%d\n",t.a,t.b,t.c);
return 0;
}
结果:
1,-1,-1
为什么?
int a:2表示a占2位,即二进制a=01,因此输出1
b占两位,b=11,但是由于是%d输出,所以先将b转化成32位int型,由于最高位是1,所以默认其为负数,所以扩展为11111111 11111111 11111111 11111111
即-1,c类似
注意:位段是不跨平台的,注重可移植的程序应该避免使用位段。
位段的跨平台问题
1. int位段被当成有符号数还是无符号数是不确定的。
2. 位段中最大位的数目不能确定。(16位机器最大16,32位机器最大32,写成27,在16位机器会出问题。)
3. 位段中的成员在内存中从左向右分配,还是从右向左分配标准尚未定义。
4. 当一个结构包含两个位段,第二个位段成员比较大,无法容纳于第一个位段剩余的位时,是舍弃剩余的位还是利用,这是不确定的。
枚举
顾名思义即一一列举,其本质为整型
enum Color//颜色
{
RED,
GREEN,
BLUE
};
{}中的内容是枚举类型的可能取值,也叫 枚举常量 。这些可能取值都是有值的,默认从0开始,一次递增1,当然在定义的时候也可以赋初值。
enum Color clr = GREEN;//只能拿枚举常量给枚举变量赋值,才不会出现
类型的差异。
联合
联合也是一种特殊的自定义类型.这种类型定义的变量也包含一系列的成员,特征是这些成员公用同一块空间(所以联合也叫共用体)。
union Un
{
char c;
int i;
}un;
printf("%d\n", sizeof(un));//答案为4
union Un
{
int i;
char c;
};
union Un un;
printf("%d\n", &(un.i));
printf("%d\n", &(un.c));
运行后发现结果相同。
//下面输出的结果是什么?
un.i = 0x11223344;
un.c = 0x55;
printf("%x\n", un.i);
结果为:0x11223355
这里牵扯计算机大小端的问题,具体内容可见
http://blog.csdn.net/kai29/article/details/78577698
联合大小的计算
- 联合的大小至少是最大成员的大小。
- 当最大成员大小不是最大对齐数的整数倍的时候,就要对齐到最大对数的整数倍。
举例:
union Un1
{
char c[5];
int i;
};
union Un2
{
short c[7];
int i;
};
//下面输出的结果是什么?
printf("%d\n", sizeof(union Un1)); //8
printf("%d\n", sizeof(union Un2)); //16
好了,以上内容是我对这部分内容的理解!