C语言_结构体、共用体及枚举学习

知识点【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;       
    }
}

在这里插入图片描述
注意:在定义枚举类型的时候枚举元素可以用而等号给它赋值,用来代表元素从几开始编号在程序中,不能再次枚举元素赋值,因为枚举元素是常量。
实力有限,创作不易,多多支持!

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值