C语言 | 【耗费一夜总结三本C语言系列】之 结构体、联合、枚举

本文详细介绍了C语言中的结构体、联合和枚举,包括结构体的声明、初始化、指针用法、内存对齐,联合体的初始化,以及枚举的声明和使用。特别强调了结构体在函数参数传递中的应用和内存管理技巧,还涵盖了C99中的复合字面量和伸缩型数组成员。通过对这些概念的深入理解,有助于提升C语言编程能力。
摘要由CSDN通过智能技术生成

前言

本章内容总结了结构体、联合、枚举,对于结构体总结了结构体指针用法,参数传递,内存对齐等较为重要的知识点。

在这里插入图片描述


C语言 | 快速了解C的发展史🧡💛💚💙
C语言 | 【耗费一夜总结三本C语言系列】之 指针、数组 一文透彻~~~🧡💛💚💙
C语言 | 【耗费一夜总结三本C语言系列】之 结构体、联合、枚举🧡💛💚💙
C语言 | 【耗费一夜总结三本C语言系列】之 声明🧡💛💚💙
C语言 | 【耗费一夜总结三本C语言系列】之 作用域 在也不用担心分不清变量的作用域拉!!!🧡💛💚💙
C语言 | 【耗费一夜总结三本C语言系列】之 编译步骤 会用C还不知道C如何编译???🧡💛💚💙
C语言 | 【耗费一夜总结三本C语言系列】之 数据类型总结🧡💛💚💙
C语言 | 【耗费一夜总结三本C语言系列】之 位及进制的用法🧡💛💚💙


一、认识结构化数据形式

在日常的开发过程中,数据总是以组的形式存在。例如:一个学生,他具有相应的成绩、学号、班级等属性。很显然,这种数据存储在数组上是极其不方便的,而且数组存储的仅为同一类型的数据。所以就有了结构化的类型,能够将不同数据类型进行存储,满足了日常开发的需求。

1.1 基础结构

在C种提供了两种聚合数据类型供开发者使用。

  • 数组;
  • 结构;

聚合数据类型

该类型能够同时存储超过一个的单独数据。

数组

数组是存储相同数据类型的元素的集合。

结构

结构中能够容纳不同的数据类型,甚至结构中能够包含结构。其中的值称之为成员。

  • 数组中数组名为数组的首地址,而结构名并不是结构的地址;

二、进一步了解结构

2.1 结构体

2.1.1 结构体声明
// 声明结构体布局
struct 结构标签(可选){
	int i,
	成员....
}变量定义;

// 创建结构体变量
struct 变量定义/结构标签 变量名;
  • 该声明并未创建实际数据对象,只是事先描述了该结构提由哪些成员组成;
  • 结构标签是可选的,那什么是需要的呢?声明结构体时由定义结构体变量则不用,若需要多次使用则需要。

注意

在日常使用中,我们普遍喜欢将typedef运用于struct,但这样似乎不大利于后期源码的解读。如果可以,则还原使用struct来声明,更加直观,一眼即可知道是结构体。

2.1.2 结构体4种初始化
struct stu{
	int sno,
	char sname[20];
};

方式一

struct stu stu1 = {21, "Jxiepc"};

需注意对应的类型,顺序。


方式二

struct stu stu2;

stu2.sno = 21;
stu2.sname = "Jxiepc";

若该变量为指针类型,则使用->


方式三

struct stu stu3 = {
	.sno = 21;
	.sname = "Jxiepc";
}

方式四

struct stu stu3 = {
	sno : 21;
	sname : "Jxiepc";
}
2.1.3 结构体指针

指向结构体的指针比数组更易操控;

  • 常应用于函数中传递
  • 结构体的自引用

定义结构体指针

#include<stdio.h>
#include<stdlib.h>
#include<string.h>

struct stuInfo2
{
    char *name;
    float *scores;
};

int main()
{
    struct stuInfo2 *s = (struct stuInfo2 *)malloc(sizeof(struct stuInfo2)*3);

    for (int i=0; i<3; i++)
    {
        s[i].name = (char *)malloc(sizeof(char)*15);
        s[i].scores = (float *)malloc(sizeof(float)*2);

        strcpy(s[i].name, "小米");
        s[i].scores[0] = 20.2;
        s[i].scores[1] = 22.2;
    }

    for(int i=0; i<3; i++)
    {
        printf("%s, %.2f, %.2f\n", s[i].name, s[i].scores[0], s[i].scores[1]);

        free(s[i].name);
        free(s[i].scores);
    }
	//注意:先先释放内部的,在释放结构体
    free(s);
    return 0;
}

结构体的自引用

struct stu{
	int num;
	struct * stu; 
}

常在链表中出现;

结构体成员为char *类型

  • 需要使用strcpy赋值。
  • 如果使用直接赋值,是浅拷贝修改数组会对结构体产生影响

在这里插入图片描述

使用strcmp深拷贝,把内容搬动进去。

// 结构体
struct stuInfo
{
    char *name;
    int Num;
}stu_;

int main()
{
	// 创建堆空间结构体
	
	// 创建结构体指针
    struct stuInfo *s = &stu_;
	// 申请空间
    s->name = (char *)malloc(sizeof(char)*15);
    
    strcpy(s->name, "虾米");
    s->Num = 001;

    printf("%s, %d", s->name, s->Num);
	
	// 释放空间
    free(s->name);
    return 0;
}
2.1.4 结构体在函数中的特性

结构体传参

结构内的数据较多,则占用存储空间相对于数组较大,使用传值方式比较浪费空间,则效率;一般是传地址

  • 结构体不像数组,他的结构名不是数组名,若要传递其地址需要加上&

结构体指针的双向特性

在函数中可作为参数传入,且又可以作为返回值传出

#include<stdio.h>
#include<stdlib.h>
#include<string.h>


struct stuInfo3
{
    char name[15];
    int Num;
};

传参方式一:传入结构体,值传递不会改变实参的值

// *传入结构体:值传递不会改变实参的值。
void funCom(struct stuInfo3 s)
{
    strcpy(s.name, "小米");
    s.Num = 20;

    printf("%s, %d\n", s.name, s.Num);
}

传参方式二:传入结构体指针,传递地址会改变值

// *传入结构体指针:传递地址会改变值
void funPointer(struct stuInfo3 *s)
{
    strcpy(s->name, "小栏");
    s->Num = 90;
}

传参方式三:返回结构体

// *返回结构体
struct stuInfo3 returnS()
{
    struct stuInfo3 s = {"里斯", 29};

    return s;
};

传参方式四:返回结构体指针

// *返回结构体指针
struct stuInfo3 *returnPointerS()
{
    struct stuInfo3 s;
    strcpy(s.name, "小");
    s.Num = 01;

    return &s; // 返回地址后,前面的值在栈区被销毁
};
2.1.5 嵌套结构体

使用.一层一层往里调用即可。

// 内层
struct inner
{
    int inNum;
    char inName[15];
};
// 外层
struct external
{
    int exAge;
    struct inner sIn;
}; 


int main()
{
    struct external s;
    s.exAge = 20;
    strcpy(s.sIn.inName, "小米");
    s.sIn.inNum = 2;

    printf("%d, %s, %d", s.exAge, s.sIn.inName, s.sIn.inNum);

    return 0;
}

案例

#include<stdio.h>



/***********定义结构体**********/
struct stu2{
    char name[15];
    float score;
};


/***********冒泡排序**********/
void sortStruct(stu2 *s)
{
    for(int i=0; i<3; i++)
    {
        for(int j=0; j<4-i-1; j++)
        {
            if(s[j].score < s[j+1].score)
            {
                stu2 temp = s[j];
                s[j] = s[j+1];
                s[j+1] = temp;
            }
        }
    }
}


int main()
{
    struct stu2 s[4];       // 创建结构体

    // 循环写入多条数据
    for(int i=0; i<4; i++)
    {
        printf("请输入姓名和成绩:\n");
        scanf("%s%f", s[i].name, &s[i].score);
    }

    // 冒泡排序
    sortStruct(s);

    // 循环取出每一条数据
    for(int i=0; i<4; i++)
    {
        printf("请输入姓名:%s,成绩:%.2f\n", s[i].name, s[i].score);
    }
}
2.1.6 内存对齐
  • 结构体中以一个最大的数据类型为单位;
  • 按照排序将每一个都填充满,若有剩余,则下一个刚才满足该空间的,即可在同一个空间,若不满足,则需要下一个空间;
  • 可使用#pragma pack(1)保证结构体对齐。

在这里插入图片描述

2.1.7 复合字面量

C99允许字面量的特性用于结构数组中。

struct 标签名 {成员值};

2.1.8 伸缩型数组成员

C99中使用伸缩型数组特性:

  • 该数组不会立即存在,即程序运行时,不会立即分配内存
  • 使用其创建指定个数的数组
struct stu{
	int num;
	float score[];
}

此时该结构体一般被声明为指针

struct stu *stu1;
stu1 = (struct stu)malloc(sizeof(struct stu) + 3*sizeof(float));
// 即分配了一个num、一个3个score的空间

满足条件

  • 伸缩型数组成员必须要是该结构的最后一个成员
  • 结构中至少有一个成员
  • 声明类似于数组,但[]中间是空的;

注意

  • 若通过指针赋值的方式进行拷贝,则无法将伸缩型数组拷贝。若要拷贝,则使用mencpy
  • 不能以值传递的方式传递给函数,原因是无法将伸缩型数组拷贝;
  • 不要使用带伸缩型数组成员的结构作为数组成员或另一个结构中的成员

2.2 联合体

联合于结构类似,联合的所有成员都是处于内存中相同的位置。

  • 联合的内存数量取决于它的最长成员的长度;
  • 一次只存储一个值;
union {
	int a;
	double b;
	float c;
	char k;
}
2.2.1 联合体的初始化

联合变量可以被初始化,但初始值必须是第一个成员的类型;

  • 初始化于结构体相似;

2.3 枚举

是声明符号来标识整型常量;

  • 提高程序的可读性
  • 常用于switch
  • 只能在内部使用;
enum {
	low,
	medium,
	tiger
}test;
2.3.1 初始化联合体
  • 若枚举内的成员没有被初始化,则默认从0开始依次赋值
  • 若其中有成员被赋值,则后续将依次加1递增

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Jxiepc

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值