目录
1.结构体概念
2.结构体类型的定义
3.结构体类型变量的声明
4.结构体的嵌套
5.结构体变量的初始化和赋值
6.结构数组
7.结构指针变量说明和使用
8.结构体内存对齐
??9.内存对齐的必要性
?10.修改默认对齐数
?11.结构体变量传参
12.位段
1.结构体概念
结构体是由一批数据组合而成的结构型数据。组成结构型数据的每个数据称为结构型数据的“成员”。每一个成员可以是一个基本数据类型或者是一个构造类型。
2.结构体类型的定义
1.定义一个结构体类型的一般形式为
struct 结构名
{
成员列表;
};
?比如:book称为结构名,该结构有两个成员变量,arr和name。
struct book
{
int arr;
char name[10];
};
2、定义结构体类型的第二种形式 ? ? ? ? ?
struct 结构名{
成员列表;
}变量列表;? ?? ? ? ? ? ?
#include<stdio.h>
struct book
{
int price;
char name[10];
}b1, b2;
struct book b3;
int main()
{
struct book b4;
return 0;
}
?b1,b2,b3完全等价,是全局变量,b4是局部变量。
3.结构体类型变量的声明
结构体不会占据空间,结构体变量才会占据空间。
1.先定义结构体,然后定义结构体类型变量
#include<stdio.h>
struct book
{
int price;
char name[10];
};
int main()
{
struct book b1 ;
return 0;
}
2.定义结构体类型的时候同时声明结构体变量
#include<stdio.h>
struct book
{
int price;
char *name;
}b1, b2;
3.匿名结构体类型:省略结构名,定义结构体变量
#include<stdio.h>
struct
{
int price;
char *name;
}b1, b2;
4.结构体的嵌套
1.嵌套其他的结构体
struct custmer
{
char* name;
int age;
};
struct book
{
int price;
char* name;
struct custmer x;
}b1, b2;
customer是一个结构,这个结构有两个成员name和age。book是一个结构,b1,b2是book的结构体变量,有三个成员,price,name,x,其中x是custmer结构类型。
2.结构体类型的自引用:使用指针
struct book
{
int price;
char* name;
struct book *next;
struct book next;//error 结构体的大小无限大
};
5.结构体变量的初始化和赋值
1.结构体变量的初始化用{}
#include<stdio.h>
struct point
{
int x;
int y;
}p1 = { 8,9 }, p2 = { 3,8 };
struct ps
{
double y;
struct point m;
}m = { 2.34,{3,7} };
int main()
{
struct point p3 = { 2,4 };
struct ps p = { 2.34,{3,7} };
return 0;
}
2.结构体变量的赋值
对成员变量进行逐一赋值
#include<stdio.h>
struct point
{
int x;
int y;
}p;
struct ps
{
double y;
struct point m;
char name[10];
}k;
int main()
{
p.x = 10;
p.y = 20;
printf("%d\n", p.x);
printf("%d\n", p.y);
k.m.x = 30;
k.m.y = 24;
scanf("%s", &k.name);
// k.name = "hi"; //error
// 字符数组只有在定义的时候可以使用=对字符数组赋值,其余情况都不可以
//char arr[10] = "hi"; 正确
//char brr[10];brr = "ji"; 错误
printf("%d\n", k.m.x);
printf("%d\n", k.m.y);
printf("%s", k.name);
return 0;
}
6.结构数组
#include<stdio.h>
struct book
{
int price;
char name[20];
}s[3] = {
{50,"哈利波特"},{20,"英语"},{30,"数学"}};
int main()
{
s[2].price = 100;
strcpy(s[2].name, "喵星人");
for (int i = 0; i < 3; i++)
{ printf("%d\n", s[i].price);
printf("%s\n", s[i].name);
}
return 0;
}
7.结构指针变量说明和使用
1.指向结构体变量的指针
#include<stdio.h>
struct book
{
int price;
char name[20];
};
int main()
{
struct book s = { 100,"哈利波特" };
struct book* p = &s;//p是结构体类型的指针
printf("%s\n", p->name);
printf("%d\n", p->price);
printf("%s\n", (*p).name);
printf("%d\n",(*p).price);
return 0;
}
2.访问成员的方式
(*结构指针变量).成员名
结构指针变量->成员名
?3.指向结构数组的指针
#include<stdio.h>
struct book
{
int price;
char name[20];
}s[3] = {
{50,"哈利波特"},{20,"英语"},{30,"数学"} };
int main()
{
struct book* p = s;//p指向了结构体数组
for (int i = 0; i < 3; i++)
{
printf("%d\n", (p + i)->price);
printf("%s\n", (p + i)->name);
}
return 0;
}
8.结构体内存对齐
结构体内存对齐原则
- 结构体变量的第一个成员永远放置在结构体偏移量为0的位置
- 变量从第二个成员开始,放置位置的偏移量是对齐数的整数倍?对齐数:成员类型的字节数和默认对齐数的最小值? ??Linux没有默认对齐数 vs的默认对齐数是8
- 结构体的总大小必须是各个成员中最大对齐数的整数倍
- 如果说存在嵌套了结构体类型,那么这个结构体类型对齐到自己的最大对齐数的整数倍,整个结构体类型的总大小是自己最大对齐数(包括嵌套结构体类型)的整数倍
?例题1:
#include<stdio.h>
struct book
{
char a;
int b;
short c;
}s;
int main()
{
printf("%d ", sizeof(s));
return 0;
}
?例题2:
#include<stdio.h>
struct book
{
char a;
short c;
int b;
}s;
int main()
{
printf("%d ", sizeof(s));
return 0;
}
?例题3:
#include<stdio.h>
struct book
{
double l;
char a;
int b;
}s;
int main()
{
printf("%d ", sizeof(s));
return 0;
}
?例题4:
#include<stdio.h>
struct book
{
char a;
int b;
struct book* next;
}s;
int main()
{
printf("%d ", sizeof(s));
return 0;
}
?例题5:
#include<stdio.h>
struct book
{
char a;
int b;
char arr[5];
}s;
int main()
{
printf("%d ", sizeof(s));
return 0;
}
?例题6?
#include<stdio.h>
struct point
{
char a;
int s;
};
struct book
{
char a;
short b;
struct point c;
}s;
int main()
{
printf("%d ", sizeof(s));
return 0;
}
?例题7
#include<stdio.h>
struct point
{
char a;
int s;
};
struct book
{
char a;
short b;
struct point *c;
}s;
int main()
{
printf("%d ", sizeof(s));
return 0;
}
?9.内存对齐的必要性
1.硬件原因:某一些硬件只可以读取特定位置上的特定类型数据,否则硬件错误。
2.性能原因:数据结构(尤其是栈)应该尽可能的在自然边界对齐
对于某一些没有对齐的数据,有可能要读取两次,对齐的数据读取一次就可以了。
内存对齐以空间换取时间
减少空间浪费:让小字节的数据靠在一起
?10.修改默认对齐数
#pragma pack()
#include<stdio.h>
#pragma pack(4)//设置默认对齐数
struct point
{
short s;
double k;
};
struct book
{
char a;
short b;
struct point c;
}s;
#pragma pack()//让默认对齐数还原为系统默认对齐数
int main()
{
printf("%d ", sizeof(s));
return 0;
}
?11.结构体变量传参
传值调用 传址调用
#include<stdio.h>
struct book
{
int a[10];
char name[20];
}b = { {1,2,3,4,5,6,7,8,9,10},"hello" };
void print(struct book s)
{
for (int i = 0; i < 10; i++)
printf("%d ", s.a[i]);
printf("\n%s", s.name);
}
void change(struct book *s)
{
memset(s->a, 0, 40);
strcpy(s->name, "hi");
}
int main()
{
change(&b);
print(b);
return 0;
}
12.位段
1.位段
概念:位段,C语言允许在一个结构体中以位为单位来指定其成员所占内存长度,这种以位为单位的成员称为“位段”或”位域"?。利用位段能够用较少的位数存储数据。
作用:减少存储数据的位数,按照二进制位(比特位)来作为单位长度。
声明:在声明时,位段成员必须是整形或枚举类型(通常是无符号类型),且在成员名的后面是一个冒号和一个整数,整数规定了成员所占用的位数。?
空间开辟:位段的空间上是按照需要以
4
个字节(
int
)或者
1
个字节(
char
)的方式来开辟的。
位段涉及很多不确定因素,位段是不跨平台的。
以下是vs-2019的计算方式分析:当空间可以容纳下一个成员时,继续存储,如果说容纳不了,那么剩余空间浪费,重新申请空间。
例题1:结果s占据8个字节
#include<stdio.h>
struct a
{
int a : 3;
int m : 20;
int b : 30;
}s={0};
int main()
{
s.a = 10;
s.m = 3;
s.b = 4;
printf("%d", sizeof(s));
return 0;
}
?
?例题2:结果s占据3个字节
#include<stdio.h>
struct a
{
char a : 3;
char m : 4;
char b : 5;
char n : 4;
}s = { 0 };
int main()
{
s.a = 10;
s.m = 3;
s.b = 4;
s.n = 8;
printf("%d", sizeof(s));
return 0;
}
???2.位段的跨平台问题
1.?
位段被当成有符号数还是无符号数是不确定的。
2.
位段中最大位的数目不能确定。(16位机器int是2个字节,最大值是16,32位机器int是4字节,使用bite位的最大值32)
3.
位段中的成员在内存中从左向右分配,还是从右向左分配标准尚未定义。
4.
当一个结构包含两个位段,第二个位段成员比较大,无法容纳于第一个位段剩余的位时,是舍弃剩余的位还是利用,这是不确定的。
总结:位段和结构体作用类似,只是会减小空间的浪费,但是有跨平台问题。
|