目录
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语言允许在一个结构体中以位为单位来指定其成员所占内存长度,这种以位为单位的成员称为“位段”或”位域" 。利用位段能够用较少的位数存储数据。
作用:减少存储数据的位数,按照二进制位(比特位)来作为单位长度。
声明:在声明时,位段成员必须是整形或枚举类型(通常是无符号类型),且在成员名的后面是一个冒号和一个整数,整数规定了成员所占用的位数。
#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.位段的跨平台问题