C语言基础---结构、联合、枚举

本文介绍了C语言中的结构、联合和枚举的基础知识。结构允许程序员创建复合数据类型,通过结构体成员来描述事物的属性。联合与结构类似,但所有成员共享同一块内存。枚举是一种受限的int类型,提供了一种为整数值赋予有意义名称的方法。文中还探讨了结构的初始化、成员访问、内存对齐和柔性数组的概念,并提供了相关练习和示例代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

结构:

    是一种由程序员设计的复合数据类型,它由若干个其它类型的成员组成,用于描述事物的各项属性。

    设计结构:

    struct 结构名 // 结构名一般首字母大写

    {

        成员类型 成员名;

        ...

    };

    定义结构变量:

    struct 结构名 结构变量名;

    初始化结构变量:

        顺序初始化:初始化数据与成员的顺序一一对应。

        struct 结构名 结构变量名 = {v1,v2,v3,...};  

        指定成员初始化:

        struct 结构名 结构变量名 = {

            .成员名1 = 初始化数据1,

            .成员名2 = 初始化数据2,

            ...

        };

   

    访问成员:

        结构变量.成员名;

        结构指针->成员名;

   

    注意:可以使用typedef重定义简短的类型名。

        typedef struct 结构名

        {

            成员类型 成员名;

            ...

        }结构名;

    练习1:设计一个教师结构体(姓名,工龄,工号,科目,...),定义教师结构变量,使用scanf从终端输入各成员的值,然后使用printf结构变量。

#include <stdio.h>

#include <stdlib.h>

typedef struct Student

{

int id;

char name[20];

int score_cnt;

float score[]; // 柔性数组

}Student;

void show_stu(Student* stup)

{

printf("%d %s",stup->id,stup->name);

for(int i=0; i<stup->score_cnt; i++)

{

printf(" %g",stup->score[i]);

}

printf("\n");

}

int main(int argc,const char* argv[])

{

int cnt = 0;

printf("该学生有几门成绩:");

scanf("%d",&cnt);

Student* stup = malloc(sizeof(Student)+sizeof(stup->score[0])*cnt);

stup->score_cnt = cnt;

printf("请输入学生的学号、姓名:");

scanf("%d %s",&stup->id,stup->name);

printf("请输入%d门成绩:",stup->score_cnt);

for(int i=0; i<stup->score_cnt; i++)

{

scanf("%f",&stup->score[i]);

}

show_stu(stup);

free(stup);

return 0;

}

    如何计算结构的总字节数:

        成员的顺序会影响结构的总字节数,了解结构总字节数计算规则,可以通过合理安排成员的顺序达到节约内存的目的。

        对齐:

            假定从0字节排列结构的第一个成员,之后所有成员的起始字节数,必须是成员本身字节数的整数倍,如果不是则填充一些空闲字节。

        补齐:

            结构的总字节数必须是它最大成员字节数的整数倍,如果不是则在结构的末尾填充一些空闲字节。

        注意:在Linux32位系统下计算对齐和补齐时,成员的字节数超过4则按4字节计算。

       

    练习2:

    typedef struct Data

    {

        char arr[5];

        int num;

        char ch;

        long double lf;

        short sh;

        long ld;

        int* p;

    }Data;

    计算该结构在Linux32系统、Win32系统、Linux64系统下的总字节数分别是多少?

   long 类型的字节数:

        Linux32系统 4字节

        Linux64系统 8字节

        Win32系统 4字节

        Win64系统 4字节

    指针变量:

        32系统 4字节

        64系统 8字节

    long double 类型的字节数:

        32系统 12字节

        64系统 16字节

结构的成员指针:

    当结构的某一项成员数量不固定,我们可以使用指针数据+指针+堆内存,根据实际情况存储成员,例如不同年级的学生成绩数量是不确定的。

    typedef struct Student

    {

        int id;

        char name[20];

        int score_cnt;

        float* score;

    }Student;

    // 定义成员,使用栈内存

    Student stu = {1001,"hehe",2};

    stu.score = malloc(sizeof(*stu.score)*stu.score_cnt);

    free(stu.score);

    // 定义成员,使用堆内存

    Student* stup = malloc(sizeof(Student));

    stup->score = malloc(sizeof(*stup->score)*stup->score_cnt);

    free(stup->score);

    free(stup);

   

    注意:不管结构变量是否定义的,必须给成员指针单独分配堆内存,否则成员指针就是野指针,在结构变量使用完毕后还必须单独释放,否则就会产生内存泄漏。

 

柔性数组:

    当结构的某一项成员数量不固定,我可以在结构的末尾定义一个长度为零的数组,这种数组就叫柔性数组,在为结构变量分配内存时多分配一些,多分配的内存就归柔性数组使用。

    typedef struct Student

    {

        int id;

        char name[20];

        int score_cnt;

        float score[]; // 柔性数组

    }Student;

    Student* stup = malloc(sizeof(Student)+sizeof(stup->score[0])*len);

    stup->score = len;

    for(int i=0; i<stup->score_cnt; i++)

    {

        scanf("%f",&stup->score[i]);

    }

    free(stup)

    使用成员指针的缺点:

        1、使用麻烦,必须两次分配,两次释放。

        2、危险性高,容易出现野指针、内存泄漏。

        3、结构变量和成员指针所指向的内存是两个独立的内存块,容易产生内存碎片。

        4、成员指针会占用结构的4|8字节空间,再考虑对齐和补齐,浪费的内存更多。

    柔性数组的优点:

        1、使用方便,不需要单独为柔性数组分配内存,只需要为结构变量分配内存时多分配一些即可。

        2、安全性高,柔性数组只是结构中的标记符,因此不会使用到野指针。

        3、柔性数组所使用内存块与结构变量是一体的,与结构变量的内存一起分配一起释放,因此也不容易产生内存泄漏、内存碎片。

        4、节约内存,柔性数组只是结构中的标记符,不会占用结构额外的内存。

    柔性数组的缺点:

        这种语法在是C99语法标准中提出并实现的,早期的只支持C89的编译器,不支持此用法,通用性不强。

联合:

    联合的使用语法与结构一模一样,联合与结构的区别,联合的所有成员共用一块内存。

    union 联合名

    {

        成员类型 成员名;

        ...

    };

    union Data

    {

        char ch;

        int num;

        long ld;

        int* p;

    };

    union Data d;

    使用联合的意义:

        使用少量内存对应若干个标识符,只要他们不同时使用,就不会冲突,能大大节约内存,还可以对一内存进行不同格式的解释。

        现在已经基本不再使用,只需要在工作中看懂早期的联合相关的代码即可,还需要应付联合相关的笔记面试题。

    联合的总字节数:

        由于所有成员共用一块内存,所以成员天然对齐的,但要考虑补齐。

        union Data

        {

            char arr[5];

            int num;

        };

        sizeof(union Data); // 结果是8

注意:如果联合的成员都是基本类型的变量,则最大成员的字节数就是联合的总字节数,如果成员中有数组则需要考虑补齐。

大端系统与小端系统:

    假定int类型变量,它的4个字节存储内存地址分别是:

        0xbff97558 低地址

        0xbff97559

        0xbff9755a

        0xbff9755b 高位地址

    假如有一个十六进制整数:0xa1b2c3d4

        0xa1 高位数据

        0xb2

        0xc3

        0xd4 低位数据

    大端系统:低位数据存储在高位地址,大型服务器、网络设置采用的是大端系统,所以大端格式也叫网络字节序。

        0xbff97558 存储0xa1

        0xbff97559 存储0xb2

        0xbff9755a 存储0xc3

        0xbff9755b 存储0xd4

    小端系统:低位数据存储在低位地址,一般个人计算机都采用小端系统。

        0xbff97558 存储0xd4

        0xbff97559 存储0xc3

        0xbff9755a 存储0xb2

        0xbff9755b 存储0xa1

    练习:设计一个程序,判断当前系统的是大端还是小端。

#include <stdio.h>

typedef union Data

{

char ch;

int num;

}Data;

int main(int argc,const char* argv[])

{

Data a;

a.num = 0x01020304;

if(a.ch = 0x04)

{

printf("小端系统\n");

}

else

{

printf("大端系统\n");

}

return 0;

}

枚举:

    是一种值的范围受限的int类型。

    设计枚举:

    enum 枚举名

    {

        枚举值1,

        枚举值2,

        枚举值3,

        ...

    };

   

    定义枚举变量:

        enum 枚举名 枚举变量;

        注意:理论上枚举变量只能使用枚举值赋值,但C语言为了编译速度,不会检查枚举值,在C语言中枚举是一种锦上添花的技术,使用它会更好,不使用也不会影响代码编写、程序运行。

       

    枚举值:

        1、枚举值是标识符,默认从0开始,之后的逐渐递增+1。

        2、枚举值可以单独使用,这种用法的好处是给没有意义的字面值常量取一个有意义的名字,提高代码的可读性。

        3、枚举值是常量,一般与switch配合使用,枚举值可以使用在case的后面。

        4、枚举值的作用域是全局的,因此不能与全局变量、函数重名。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值