一 结构体数据类型的三种定义方式
1.1 先定义类型,再定义变量(推荐)
1.2 定义类型的同时定义变量
1.3 定义一次性结构体
1.4 注意事项
1. struct是结构体关键字;
2. stu是结构体类型名;
3. num,name,age是结构体中的成员;
4. 定义结构体类型的时候,不要给成员赋值;
5. 定义结构体类型的时候,并没有分配内存空间,所以不要给成员赋值;
6. 结构体中的成员拥有独立的内存空间;
struct stu
{
int num;
char name[32];
int age;
};
struct stu andy,tony;
二 结构体变量的初始化
初始化的顺序必须和结构体成员的顺序一致
[root@ansible9 ~]
struct stu
{
int num;
char name[32];
int age;
};
struct stu andy={200,"andy",18};
int main(int arg, char *argv[])
{
printf("%d\n",andy.num);
printf("%s\n",andy.name);
printf("%d\n",andy.age);
}
[root@ansible9 ~]
[root@ansible9 ~]
200
andy
18
三 结构体变量成员值的清0(用memset)
[root@ansible9 ~]
struct stu
{
int num;
char name[32];
int age;
};
int main(int arg, char *argv[])
{
struct stu andy={200,"andy",18};
memset(&andy,0,(int)sizeof(struct stu));
printf("%d\n",andy.num);
printf("%s\n",andy.name);
printf("%d\n",andy.age);
}
[root@ansible9 ~]
0
0
四 结构体变量的成员从键盘获取值
[root@ansible9 ~]
struct stu
{
int num;
char name[32];
int age;
};
int main(int arg, char *argv[])
{
struct stu andy;
memset(&andy,0,(int)sizeof(struct stu));
printf("请输入num name age\n");
scanf("%d %s %d",&andy.num,andy.name,&andy.age);
printf("%d\n",andy.num);
printf("%s\n",andy.name);
printf("%d\n",andy.age);
}
[root@ansible9 ~]
[root@ansible9 ~]
请输入num name age
100 tony 38
100
tony
38
五 相同结构体类型的变量之间赋值
5.1 一个成员一个成员逐个赋值
5.2 用=整体赋值
5.3 用memcpy内存数据直接拷贝赋值
六 结构体数组
6.1 结构体数组的初始化
七 定义一个结构体数组,从键盘为每个数组元素的每个成员赋值
struct stu
{
int num;
char name[32];
int age;
};
int main(int arg, char *argv[])
{
struct stu arr[5];
//清空数组
memset(arr,0,(int)sizeof(arr));
//数组元素个数n
int n=(int)sizeof(arr) / (int)sizeof(arr[0]);
int i=0;
printf("请输入%d个学生的信息:\n",n);
for(i=0;i<n;i++)
{
scanf("%d %s %d",&arr[i].num,arr[i].name,&arr[i].age);
}
}
八 结构体指针
8.1 定义一个结构体指针
1. 先定义一个指针变量: *p
2. 再定义一个指针变量要指向的变量,即结构体变量: struct stu a
3. 从上到下替换: struct stu *p
8.2 用运算符. 或运算符-> 取结构体变量成员的值
8.2.1 普通结构体变量用运算符. 来获取成员的值
8.2.2 结构体变量的地址用运算符->来获取成员的值
九 结构体的内存对齐
9.1 问题来源
struct data
{
char c;
int i;
}
由于32位的cpu一次性读取4字节内存数据。所以对于上面的结构体,有两种内存排列方式,每种方式cpu读取结构体成员的读取次数不同:
方式一:
cpu读取变量c时,需要一个cpu周期,提取0x01 0x02 0x03 0x04, 只要0x01
cpu读取变量i时,需要两个cpu周期:
第一周期: 提取0x01 0x02 0x03 0x04, 只要0x02 0x03 0x04
第二周期: 提取0x05 0x06 0x07 0x08, 只要0x05
方式二:
cpu读取变量c时,需要一个cpu周期,提取0x01 0x02 0x03 0x04, 只要0x01
cpu读取变量i时,需要一个cpu周期:提取0x05 0x06 0x07 0x08, 四字节全要
总结:方法二是用内存空间换了cpu的执行时间
9.2 结构体的默认对齐规则
规则:
1. 确定分配单位:每一行应该分配的字节数,他由结构体中最大的基本数据类型的长度决定;
2. 确定成员的起始位置的偏移量:成员的基本类型的整数(0-n)倍;
3. 收尾工作:结构体总大小=分配单位的整数倍;
9.2.1 例子1
struct data
{
char c;
int i;
};
- 分配单位: 4字节
- 确定各个成员的起始位置偏移量
[root@ansible9 ~]
struct data
{
char c;
int i;
};
struct data d;
int main(int arg, char *argv[])
{
printf("结构体变量的起始地址:%u\n",&d);
printf("结构体变量所占内存大小:%d\n",(int)sizeof(d));
printf("成员c的起始地址: %u\n",&d.c);
printf("成员i的起始地址: %u\n",&d.i);
}
[root@ansible9 ~]
结构体变量的起始地址:6295600
结构体变量所占内存大小:8
成员c的起始地址: 6295600
成员i的起始地址: 6295604
9.2.2 例子2
struct data
{
int a;
char b;
short c;
char d;
};
- 分配单位: 4字节
- 确定各个成员的起始位置偏移量
[root@ansible9 ~]
typedef struct
{
int a;
char b;
short c;
char d;
} DATA;
DATA data;
int main(int arg, char *argv[])
{
printf("结构体变量的起始地址:%u\n",&data);
printf("结构体变量所占内存大小:%d\n",(int)sizeof(data));
printf("成员a的起始地址: %u\n",&data.a);
printf("成员b的起始地址: %u\n",&data.b);
printf("成员c的起始地址: %u\n",&data.c);
printf("成员d的起始地址: %u\n",&data.d);
}
[root@ansible9 ~]
[root@ansible9 ~]
结构体变量的起始地址:6295600
结构体变量所占内存大小:12
成员a的起始地址: 6295600
成员b的起始地址: 6295604
成员c的起始地址: 6295606
成员d的起始地址: 6295608
9.2.3 例子3
struct data
{
char a;
short b;
short c;
int d;
char e;
};
- 分配单位: 4字节
- 确定各个成员的起始位置偏移量
[root@ansible9 ~]
typedef struct
{
char a;
short b;
short c;
int d;
char e;
} DATA;
DATA data;
int main(int arg, char *argv[])
{
printf("结构体变量的起始地址:%u\n",&data);
printf("结构体变量所占内存大小:%d\n",(int)sizeof(data));
printf("成员a的起始地址: %u\n",&data.a);
printf("成员b的起始地址: %u\n",&data.b);
printf("成员c的起始地址: %u\n",&data.c);
printf("成员d的起始地址: %u\n",&data.d);
printf("成员e的起始地址: %u\n",&data.e);
}
[root@ansible9 ~]
[root@ansible9 ~]
结构体变量的起始地址:6295616
结构体变量所占内存大小:16
成员a的起始地址: 6295616
成员b的起始地址: 6295618
成员c的起始地址: 6295620
成员d的起始地址: 6295624
成员e的起始地址: 6295628
十 结构体嵌套结构体
十一 结构体嵌套结构体的默认内存对齐
对齐规则:
1. 确定分配单位:每一行应该分配的字节数,由所有的结构体中最大的基本类型长度决定;
2. 确定成员的偏移量=自身类型的整数(0-n)倍:
2.1 普通成员的偏移量=自身类型的整数(0-n)倍
2.2 结构体类型成员的偏移量=这个结构体中最大的基本类型的长度的整数(0-n)倍
2.3 被嵌套的结构体的成员的偏移量是相对于被嵌套的结构体的
3. 收尾工作:结构体的总大小=分配单位的整数倍
被嵌套结构体的总大小=被嵌套的结构体里面最大的基本数据类型的整数倍
typedef struct
{
short d;
char e;
} DATA2;
typedef struct
{
short a;
int b;
DATA2 c;
char f;
} DATA1
- 分配单位:4字节
- 确定成员的偏移量:a的偏移量,b的,c的,d的,e的,f的
[root@ansible9 ~]
typedef struct
{
short d;
char e;
} DATA2;
typedef struct
{
short a;
int b;
DATA2 c;
char f;
} DATA1;
DATA1 data;
int main(int arg, char *argv[])
{
printf("一共%d字节\n",(int)sizeof(DATA1));
printf("a的地址%u\n",&data.a);
printf("b的地址%u\n",&data.b);
printf("c的地址%u\n",&data.c);
printf("d的地址%u\n",&data.c.d);
printf("e的地址%u\n",&data.c.e);
printf("f的地址%u\n",&data.f);
}
[root@ansible9 ~]
[root@ansible9 ~]
一共16字节
a的地址6295616
b的地址6295620
c的地址6295624
d的地址6295624
e的地址6295626
f的地址6295628
十二 强制类型对齐
在源码的最上方写#pragma pack (value)
强制对齐规则:
1. 确定分配单位:min(value,默认分配单位)
2. 成员偏移量=min(value,成员自身类型字节数) 的整数(0-n)倍
3. 收尾工作=分配单位的整数(0-n)倍
例子:
[root@ansible9 ~]
typedef struct
{
char a;
int b;
short c;
} DATA1;
DATA1 data;
int main(int arg, char *argv[])
{
printf("一共%d字节\n",(int)sizeof(DATA1));
printf("a的地址%u\n",&data.a);
printf("b的地址%u\n",&data.b);
printf("c的地址%u\n",&data.c);
}
[root@ansible9 ~]
[root@ansible9 ~]
一共8字节
a的地址6295600
b的地址6295602
c的地址6295606
十三 位段
- 信息在计算机中存储长度一般以字节为单位;
- 有时不必用1字节,只需要1位或几位;
13.1 结构体中的位段
1. C语言在结构体中以位为单位来指定成员所占内存大小,以位为单位的成员称为位段或位域;
2. 一般用unsigned int或unsigned char两种类型做位段;
3. 排在前面的位段放内存低位;
4. 相邻位段可以压缩;
5. 段位不能取地址
13.2 无意义的位段(占位而不用)
13.3 位段的应用场景
13.4 另起一个位段
|