一、结构体的声明
1.结构声明
struct S
{
int i;//成员变量
char c;//成员变量
};
2.结构体变量的创建和初始化
strruct Stu
{
char name[20];
int age;
}s1={"lisi",19};//s1结构体变量--全局变量+初始化
struct Stu s2={"zhangsan",18};//s2结构体变量--全局变量+初始化
int main()
{
struct Stu s3={"wanghu",20};//s2结构体变量--局部变量+初始化
return 0;
}
3.匿名结构体类型
struct
{
char c;
int i;
};
如果没有对匿名结构体重命名,该结构体基本上只能用一次。
struct
{
char c;
int i;
}a;
struct
{
char c;
int i;
}b[20],*p;
int main()
{
p=&a;//err
return 0;
}
尽管上面两个结构体内定义的成员变量完全相同,但是编译器还是会把两个结构体变量当成两个完全不同的类型所以p=&a是不合法的。
4.结构体的自引用
struct Node1
{
int data;
struct Node1 next;//err
};//如果这样定义,该结构体的大小就会无限大
struct Node2
{
int data;
struct Node* next;//ok--指针的大小为4/8
};
typedef struct Node3
{
int data;
Node3* next;//err--成员变量先定义后再进行重命名,而这种定义,编译器会报错
}Node3;
typedef struct Node4
{
int data;
struct Node* next;//ok--指针的大小为4/8
}Node4;
二、结构体内存对齐
1.对齐规则
- 结构体第一个成员对齐到和结构体变量起始偏移量为零的地址处。
- 其他成员要对齐到对齐数的整数倍的位置。
- 结构体总大小要是最大对齐数(每个成员的对齐数中最大的一个对齐数)的整数倍。
- 如果有嵌套结构体,嵌套结构体成员的对齐数对齐到该嵌套结构体内最大对齐数的整数倍,结构体的整体大小是嵌套结构体成员的最大对齐数在内最大对齐数的整数倍。
对齐数:编译器默认的对齐数和该成员变量大小中较小的数。
struct S1
{
char c1;//1(成员变量本身的大小) 8(编译器默认对齐数) 1(对齐数)
int i;//4 8 4(最大对齐数)
char c2;//1 8 1(对齐数)
};
struct S2
{
char c1;//1 8 1
char c2;//1 8 1
int i;//4 8 4(最大对齐数)
};
struct S3
{
double d;//8 8 8(最大对齐数)
char c;//1 8 1
int i;//4 8 4
};
struct S4
{
char c1;//1 8 1
struct S3 s3;//8
double d;//8 8 8
};
int main()
{
printf("%zd\n", sizeof(struct S1));//最大对齐数的倍数-->12
printf("%zd\n", sizeof(struct S2));//8
printf("%zd\n", sizeof(struct S3));//16
printf("%zd\n", sizeof(struct S4));//32
return
结构体的内存对⻬是拿空间来换取时间的做法。
2.默认修改对齐数
#pragma这个预处理指令可以修改对齐数,结构体在对⻬⽅式不合适的时候,我们可以⾃⼰更改默认对⻬数.
#pragma pack(1)//将默认对齐数改为1
struct S1
{
char c1;
int i;
char c2;
};
#pragma pack()//取消设置的对齐数,还原为默认对齐数
struct S2
{
char c1;
int i;
char c2;
};
int main()
{
printf("%zd\n", sizeof(struct S1));//6
printf("%zd\n", sizeof(struct S2));//12
return 0;
}
三、结构体传参
结构体传参的时候,要传结构体的地址更好。
struct S
{
int arr[1000];
char a;
int b;
}s = { {1,2,3,4},'q',29 };
void print(struct S s)
{
int i = 0;
for (i = 0; i < 5; i++)
{
printf("%d ", s.arr[i]);
}
printf("%c %d\n", s.a, s.b);
}
void prints(struct S* ps)//指针更好
{
int i = 0;
for (i = 0; i < 5; i++)
{
printf("%d ", ps->arr[i]);
}
printf("%d %c\n", ps->b, ps->a);
}
int main()
{
print(s);//传结构体
prints(&s);//传地址(更好)
return 0;
}
四、结构体实现位段
1.位段
//位段的成员必须是int、unsigned int、signed int、char
//最好一个结构体实现位段只有一种类型
struct s
{
int a : 3;//3bit位
int b : 20;//20bit位
int c : 14;//14bit位
int d : 16;//16bit位
};
//先开辟4个字节,如果不够再开辟4个字节,以此类推
int main()
{
printf("%zd\n", sizeof(struct s));//8(字节)
return 0;
}
2.位段的内存分配
struct S
{
char a : 2;//bit
char b : 5;//bit
char c : 4;//bit
char d : 3;//bit
};
int main()
{
struct S s = { 0 };
s.a = 4;//00000100-->00
s.b = 7;//00000111-->00111
s.c = 10;//00001010-->1010
s.d = 5;//00000101-->101
//00011100 01011010
// 0x1c 0x5a
//低地址 -> 高地址
return 0;
}
3.给位段成员输入值的方法
struct S
{
char a : 2;
char b : 5;
char c : 4;
char d : 3;
};
int main()
{
struct S s = { 0 };
//s.a = 3;
//printf("%d\n", &s.a);//err
//内存中一个字节分配一个空间,里面的每个bit位没有地址
int n = 0;
scanf("%d", &n);
s.a = n;
//只能先输入一个值放在一个变量中,然后再赋给位段的成员
return 0;
}