知识点【1】结构体类型的概念及定义
1.1概念
结构体是一种构造类型的数据结构,是一种或多种基本类型或构造类型的数据的集合
1.2结构体的定义
1、先定义结构体类型,再定义结构体变量,每个变量都有两个成员分别是age,name
struct st_stu{
int age;
char *name;
};
struct st_stu stu1, stu2;
2、定义结构体类型时,声明了两个变量lucy,bob
struct st_stu{
int age;
char *name;
}lucy,bob;
没有给结构体起名字,这样在以后就无法定义该类型的结构体变量
struct {
int age;
char *name;
}lucy,bob;
1.3最常用的办法
通常咱们将一个结构体类型重新起个类型名,用新的类型名替代原先的类型
typedef struct STU{
int age;
char name[20];
}st_stu_t, *st_stu_p;
int main(int argc, char const *argv[]){
st_stu_t stu1;
st_stu_p stu2;
stu1.age = 20;
strcpy(stu1.name, "bob");
printf("age = %d, name = %s\n", stu1.age, stu1.name);
stu2 = (st_stu_p)calloc(1, sizeof(st_stu_t));
stu2->age = 10;
strcpy(stu2->name, "lucy");
printf("age = %d, name = %s\n", stu2->age, stu2->name);
free(stu2);
stu2 = NULL;
return 0;
}
1.4结构体成员初始化
void test02(){
//st_stu_t stu = {23, "bob"};///<初始化成员的顺序一定要和结构体的定义保持一致
st_stu_t stu[] = {
{.name = "bob", .age = 13},///< 根据成员名来进行结构体的初始化
{.age = 20, .name = "lucy"},
};
int n = sizeof(stu) / sizeof(stu[0]);
for(int i = 0; i < n; i++){
printf("age = %d, name = %s\n", stu[i].age, stu[i].name);
}
}
1.5从键盘获取结构体成员的数值
void test03(){
st_stu_t stu;
printf("please intput the member of struct:\n");
scanf("%d %s", &stu.age, stu.name);
printf("age = %d, name = %s\n", stu.age, stu.name);
}
1.6结构体变量间的赋值
方式一:逐个成员赋值(不推荐)
//方式一:逐个成员赋值(准许成员自身的类型)
bob.num = lucy.num;
strcpy(bob.name, lucy.name);
bob.score = lucy.score;
方式二:相同类型的结构体变量 可以整体赋值(推荐)
//lucy赋值给bob
bob = lucy;
1.7结构体大小计算
void test04(){
struct stu{
int num;
char name[20];
int score;
char *addr;
};
struct stu1{
int num;
//char name[20];
//int score;
char *addr;
};
struct stu1 lucy;
memset(&lucy, 0, sizeof(struct stu1));
printf("size = %lu\n", sizeof(struct stu));
printf("addr = %p, num = %p\n", &(lucy.addr), (long)&(lucy.num));
}
字节对齐:结构体成员中成员的对齐方法,各个默认的对齐字节节数和value相比,取较小值
#include<stdio.h>
#pragma pack(2)///以2字节对齐
struct stu{
char a;
int b;
}temp;
int main(){
printf("%d\n", sizeof(temp));
printf("%p\n", &(temp.a));
printf("%p\n", &(temp.b));
return 0;
}
注意:如果指定对齐值:设为1:则short、int、float等均为1,设为2:则char仍为1,short为2,int变为2.
1.8memcpy函数
函数原型:void *memcpy(void *dest, const void *src,size_t n);
函数功能:从src拷贝n个字节到dest
char buf[100] = {0};
memcpy(buf, "hello world", strlen("hello world"));
知识点【2】结构体指针变量
2.1本质是指针变量 保存是结构体变量的地址
void test05(){
st_stu_t stu1 = {20, "lucy"};
st_stu_p stu2 = &stu1;
printf("age = %d, name = %s\n", stu1.age, stu1.name);
printf("age = %d, name = %s\n", (*stu2).age, (*stu2).name);
printf("age = %d, name = %s\n", stu2->age, stu2->name);
}
2.2结构体指针做函数的形参
void fun01(st_stu_p stu){
stu->age = 100;
strcpy(stu->name, "lucy");
}
void test06(){
st_stu_t stu;
fun01(&stu);
printf("age = %d, name = %s\n", stu.age, stu.name);
}
注意:结构体变量的地址编号和结构体第一个成员的地址编号相同,但是指针的类型不同
void test07({
struct stu lucy;
printf("lucy = %p, num = %p\n", &lucy, &lucy.num);
})
2.3通过结构体指针做协议转换
void test06(){
char buf[] = {'a', '3', '4' '1'};
typedef struct{
char len;
char fun_code;
short val;
}st_stu_t;
st_stu_t *test = (st_stu_t *)buf;
printf("len = %d, fun_code = %d, val = %d\n", test->len, test->fun_code, test->val);
}
知识点【3】结构体嵌套
案例1:
typedef struct{
int num;
int age;
}st_a_t;
typedef struct{
int tmp;
st_a_t _a;
}st_b_t;
void test09(){
st_b_t _b;
memset(&_b, 0, sizeof(st_b_t));
_b.tmp = 10;
_b._a.num = 20;
_b._a.age = 23;
printf("tmp = %d, a.num = %d, a.age = %d\n", _b.tmp, _b._a.num, _b._a.age);
}
知识点【4】结构体内的指针成员
案例1;
typedef struct{
int age;
char *name;
}st_stu, *st_pstu;
void test10(){
st_pstu stu = NULL;
stu = (st_pstu)calloc(1, sizeof(st_stu));
stu->age = 10;
// stu->name = "bob";
stu->name = (char *)calloc(10, 1);
strcpy(stu->name, "bob");
printf("age = %d, name = %s\n", stu->age, stu->name);
if(stu->name != NULL){
free(stu->name);
stu->name = NULL;
}
if(stu != NULL){
free(stu);
stu = NULL;
}
}
案例2:结构体变量被清零
st_stu stu1;
stu1.age = 20;
stu1.name = (char *)calloc(10, 1);
strcpy(stu1.name, "bob");
memset(&stu1, 0, sizeof(st_stu));
strcpy(stu1.name, "bob");
结构体指针数组在堆区,结构体数组元素在堆区,指针成员也在堆区。
void test11(){
st_stu **stu = (st_stu **)calloc(5, sizeof(st_stu *));
for(int i = 0; i < 5; i++){
stu[i] = (st_stu *)calloc(1, sizeof(st_stu *));
stu[i]->name = (char *)calloc(10, 1);
stu[i]->age = i * 10;
sprintf(stu[i]->name, "bob%d", i);
}
for(int i = 0; i < 5; i++){
printf("age = %d, name = %s\n", stu[i]->age, stu[i]->name);
free(stu[i]->name);
stu[i]->name = NULL;
free(stu[i]);
stu[i] = NULL;
}
free(stu);
stu = NULL;
}
结构体的浅拷贝
相同类型的结构体变量可以整体赋值,默认赋值方式为:浅拷贝
浅拷贝:将结构体变量空间内容 赋值一份 到另一个相同类型的结构体变量空间中
如果结构体中没有指针成员 浅拷贝不会带来问题
如果结构体中有指针成员浅拷贝会带来多次释放堆区空间的问题
如果结构体中有指针成员 容易发生浅拷贝(有危害)
struct Data5{
int num;
char *name;
};
void test12(){
struct Data5 lucy;
lucy.num = 100;
lucy.name = (char *)calloc(1, 32);
strcpy(lucy.name, "demaxiya");
struct Data5 bob;
bob = lucy;//(浅拷贝,有危害)///<指向了同一个地址,最终会报double free错误>
printf("%d %s\n", bob.num, bob.name);
if(lucy.name != NULL){
free(lucy.name);
lucy.name = NULL;
}
if(bob.name != NULL){
free(bob.name);
bob.name = NULL;
}
}
结构体的深拷贝
如果结构体中有指针成员 尽量使用深拷贝
所谓的深拷贝,就是为结构体的指针成员分配独立空间 然后再内容拷贝
知识点【5】位段
5.1位段的定义
在结构体中,以位为单位的成员,咱们称之为位段(位域)。
printf("size = %lu\n", sizeof(struct packed_data));
a的类型还是unsigned int, abcd叫相邻位域,可以压缩。
不能超过自身类型大小。
注意:不能对位段成员取地址。
struct packed_data data = {1, 32, 18, 8, 6, 100}
&data.a ///<错误的>
5.2位段成员的引用
案例1:赋值时,不要超出位段定义的范围;如超出位段定义的范围高位将丢弃。
struct packed_data{
unsigned int a:2;
unsigned int b:7;
unsigned int c:4;
unsigned int d:4;
unsigned int e:7;
unsigned int i;
};
void test13(){
printf("size = %lu\n", sizeof(struct packed_data));
struct packed_data data = {1, 32, 18, 8, 6, 100};
printf("data.b = %u, data.c = %u, data.d = %u\n", data.b, data.c, data.d);
printf("data = %p, data.i = %p\n", &data, &data.i);
}
注意:位段成员的类型必须指定为整型或字符型,同时位段的长度不能大于存储单元的长度。
案例2:位段的长度不能大于存储单元的长度
struct Data{
unsigned char a:9;
unsigned short b:12;
};
案例3:一个段要从另一个存储单元开始
typedef struct{
unsigned char a:2;
unsigned char b:2;
unsigned char :0; ///<告诉编译器从另一个存储单元开始存储>
unsigned char d:2;
}data_t;
void test14(){
printf("size = %lu\n", sizeof(data_t));
}
案例4:寄存器赋值
typedef struct{
unsigned char a:2;
unsigned char :1;
unsigned char b:2;
unsigned char :1;
unsigned char c:2;
}reg_t;
void test14(){
printf("size = %lu\n", sizeof(reg_t));
//方法1:
unsigned char reg = 0;
reg &= ~((0x3) | (0x3 << 3) | (0x3 << 6));
reg |= (0x1 | (0x1 << 3) | (0x2 << 6));
printf("reg = %#x\n", rge);
//方法2:
reg_t reg8_t;
reg8_t.a = 0x2;
reg8_t.b = 0x1;
reg8_t.c = 0x1;
printf("a = %#x, b = %#x, c = %#x\n", reg8_t.a, reg8_t.b, reg8_t.c);
}
知识点【6】共用体
定义:共用体和结构体类似,也是一种构造类型的数据结构。既然是构造类型的,咱们得先定义出类型,然后用类型定义变量。
共用体定义方式:定义共用体类型的方法和结构体非常相似,把struct改成union就可以了。
6.1共用体与结构体之间的不同点
1、所占空间的差异,共用体所有成员占有同一段地址空间,共用体的大小是共用体内部最大成员的大小。
2、共用体内所有成员的首地址相同。
案例1:利用共用体实现数据类型转换
typedef union{
char ch[4];
int ival;
float fval;
}un_ch2f_t;
void test01(){
un_ch2f_t ch2f;
ch2f.ch[0] = 0x01;
ch2f.ch[1] = 0x02;
ch2f.ch[2] = 0x03;
ch2f.ch[3] = 0x04;
printf("fval = %#x\n", ch2f.fval);
}
6.2共用体特点
1、同一内存段可以用来存放几种不同类型的成员,但每一瞬时只有一种起作用。
2、共用体变量中起作用的成员最后一次存放的成员,在存入一个新的成员后原有的成员的值会被覆盖。
3、共用体变量的地址和它的各个成员的地址都是同一地址。
知识点【7】枚举
枚举:将变量的值一一列举出来,变量的值只限于列举出来的值的范围内。
7.1枚举类型定义方法
enum 枚举类型名称{
成员列表,
。。。。。
成员列表,
};
7.2枚举变量的定义及使用
enum WEEK{
mon = 1;
tue,
wen,
thu,
fri,
sat,
sun,
};
void test01(){
enum WEEK day;
scanf("%d", &day);
switch (day){
case mon:
case wen:
case fri:
printf("do something !\n");
break;
case tue:
case thu:
case sat:
printf("do other something !\n");
break;
default:
printf("have a rest!\n");
break;
}
}
注意:在定义枚举类型的时候枚举元素可以用而等号给它赋值,用来代表元素从几开始编号在程序中,不能再次枚举元素赋值,因为枚举元素是常量。
实力有限,创作不易,多多支持!