C语言学习,这一篇就够了!(五)-- 结构体

举报
阿童木 发表于 2021/08/18 07:33:10 2021/08/18
【摘要】 6. 结构体结构体从本质上来讲是一种自定义的数据类型,但是这种数据类型比较复杂,它是由 int、char、float 等多种基本类型组成的从前端js的角度去思考,我会把结构体形象为js中的对象这部分没有写链表的内容,在我之前的博文中有写到了用js实现链表的完整操作思路,实际上思路都一致,只是语法不同,就不过多阐述,有兴趣的可以看之前的博文:一文带你拿下前端必备数据结构 – 链表 !! 6....

6. 结构体

结构体从本质上来讲是一种自定义的数据类型,但是这种数据类型比较复杂,它是由 intcharfloat 等多种基本类型组成的

从前端js的角度去思考,我会把结构体形象为js中的对象

这部分没有写链表的内容,在我之前的博文中有写到了用js实现链表的完整操作思路,实际上思路都一致,只是语法不同,就不过多阐述,有兴趣的可以看之前的博文:一文带你拿下前端必备数据结构 – 链表 !!

6.1 定义和使用结构体

6.1.1 建立结构体类型

可以使用结构体struct来存放一组不同的数据类型

下面采用结构体来存储一个成员的个人信息

struct people
{
    char *name;  //姓名
    int num;     //学号
    int age;     //年龄
    float score; //成绩
};

people为结构体的名字,里面包含了4个成员。结构体成员的定义方式与变量和数组的定义方式相同,只是不能初始化。

特别注意:结构体后的花括号需要打分号

6.1.2 定义结构体变量

在上面我们定义了一个结构体类型,我们可以用它来定义变量

struct people s1, s2;

这样我们就定义了两个变量s1,s2,它们的类型都是people类型,它们都有4个成员组成。形象的说:定义出来的people就相当于一个模板,该类型的变量都会有它的特性

struct people
{
    char *name;  //姓名
    int num;     //学号
    int age;     //年龄
    float score; //成绩
} s1, s2;

也可以直接在结构体最后定义变量

结构体的各个成员中在内存中是连续存储的,和数组相似,但是由于结构体中的数据类型复杂,各个成员间存在着间隙,因此存在着==结构体内存对齐==的问题!

6.1.3 读写结构体成员的值

使用点号.获取单个成员,也可以给成员赋值。.号叫做成员访问运算符!

学前端的现在可以舒一口气了,这个和对象太像了,其实学习一门编程语言当你学到了它的思想后,学其他的语言都会很轻松的,所以各位一定要先学踏过门槛~冲冲冲

通过这样的方式可以获取成员的值,也可以赋值

#include <stdio.h>
int main()
{
    struct people
    {
        char *name;  //姓名
        int num;     //学号
        int age;     //年龄
        float score; //成绩
    };
    struct people my;
    my.name = "LJC";
    my.num = 1023;
    my.age = 19;
    my.score = 100;
    printf("姓名:%s,年龄:%d,学号:%d,分数:%.1f", my.name, my.age, my.num, my.score);
    return 0;
}

运行结果:姓名:LJC,年龄:19,学号:1023,分数:100.0

这样我们就实现了对结构体变量的赋值。

🎃 结构体是一种自定义的数据类型,是创建变量的模板,不占用内存空间。只有在创建结构体变量的时候才会占用内存空间!!!

6.2 结构体数组

结构体数组指数组中的每个元素都是结构体,这样就非常的方便了,我们可以把一个班级的学生放在一个结构体数组里,这样一个班级学生都绑定上了结构体中的成员

struct stu{
    char *name;  //姓名
    int num;  //学号
    int age;  //年龄
    int score;  //成绩
}class[45];

定义了一个有45个学生的班级

读写的方式和结构体变量相同,在结构体数组定义的同时也可以进行初始化,也可以不给出数组的长度,如:

struct stu{
    char *name;  //姓名
    int num;  //学号
    int age;  //年龄
    int score;  //成绩
}class[] = {
    {"ljc",3213,19,100},
    {"ljc",3223,19,99}
};

结构体数组的使用也很方便,访问赋值即可

class[0].score = 99;

6.3 结构体指针

结构体指针就是指向结构体的指针,一个结构体变量的起始地址就是这个结构体变量的指针。

定义结构体指针的一般形式为struct 结构体名 *变量名

struct stu{
    char *name;  //姓名
    int num;  //学号
    int age;  //年龄
    int score;  //成绩
} my = {"ljc", 1023, 19, 100};// 定义了一个结构体变量my
struct stu *pmy = &my; // 定义结构体指针pmy,赋予my的地址,让它指向my

也可以在定义结构体的同时定义结构体指针:(多见几种形式考试才不会生疏噢~)

struct stu{
    char *name;  //姓名
    int num;  //学号
    int age;  //年龄
    int score;  //成绩
} my = {"ljc", 1023, 19, 100}, *pmy = &my;

🎨 **特别注意:结构体的变量名和数组名不同,数组名在表达式中会被转换为数组指针指向数组的第一个元素,而结构体变量名不会**因此需要带上&取地址符噢~

6.3.1 获取结构体成员

有两种获取的方法,一种是采用成员访问运算符,另一种是采用->运算符,称为箭头,可以通过结构体指针直接取得结构体成员pmy -> num

示例:通过结构体指针来输出结构体成员的值

#include <stdio.h>
int main()
{
    struct people
    {
        char *name;  //姓名
        int num;     //学号
        int age;     //年龄
        float score; //成绩
    } my = {"LJC", 1023, 19, 100}, *pmy = &my;
    printf("姓名:%s,年龄:%d,学号:%d,分数:%.1f", (*pmy).name, (*pmy).num, pmy->age, pmy->score);
    return 0;
}

特别注意:在使用第一种方法的时候,一定要有括号,因为.的优先级高于*,如果去掉意义就不一样了

6.3.2 结构体数组指针使用

结构体数组指针的使用和结构体指针基本一致,其中定义的结构体指针ps是指向struct stu结构体数据类型的指针变量,在 for 循环中给指针赋初值,指向数组的第一个元素,输出结束后,指针指向数组的下一位即ps++

#include <stdio.h>

struct stu
{
    char *name;  //姓名
    int num;     //学号
    int age;     //年龄
    float score; //成绩
} student[] = {
    {"LJC", 1023, 19, 100},
    {"LIN", 1021, 19, 110},
    {"JC", 1020, 19, 120},
},
  *ps;//定义结构体数组指针

int main()
{
    //求数组的长度
    int len = sizeof(student) / sizeof(struct stu);
    printf("Name\tNum\tAge\tScore\t\n");
    for (ps = student; ps < student + len; ps++)
    {
        printf("%s\t%d\t%d\t%.1f\n", ps->name, ps->num, ps->age, ps->score);
    }
    return 0;
}

输出结果

小丞同学

6.3.3 结构体指针做函数参数

通过示例来复习吧:计算学生成绩的平均分

**注意:**通过传入结构体指针,这样可以减少内存的占用,在传入结构体数组时,会将它所占用的内存单元的内容全部作为参数传递给形参,在函数调用的过程中,形参也需要占用内存,这样对于内存的开销就会很大

#include <stdio.h>
struct stu
{
    char *name;  //姓名
    int num;     //学号
    int age;     //年龄
    float score; //成绩
} student[] = {
    {"LJC", 1023, 19, 100},
    {"LIN", 1021, 19, 110},
    {"JC", 1020, 19, 120},
};

void average(struct stu *ps, int len)
{
    int i;
    float average, sum = 0;
    for (i = 0; i < len; i++)
    {
        sum += (ps + i)->score;
    }
    printf("sum = %.1f\n平均分=%.2f", sum, sum / len);
}
int main()
{
    //求数组长度
    int len = sizeof(student) / sizeof(struct stu);
    average(student, len);
    return 0;
}

输出结果

小丞同学

6.4 共用体类型

共用体是一种特殊的数据类型,不同于结构体的是它允许在相同的内存位置存储不同的数据类型,因此我们可以从名字上来浅析,它们共用同一块内存空间

6.4.1 定义和使用共用体类型

定义的方式和使用的方式和结构体基本一致,定义结构体采用struct,定义共用体采用union关键字,定义格式为:

union 共用体名 {
    成员列表
};

看是不是基本一样呀,相信你应该都忘记了吧~

结构体和共用体的区别在于:结构体的各个成员会占用不同的内存,互相之间没有影响;而共用体的所有成员占用同一段内存,相互之间会有影响。

共用体的内存大小取决于成员中占据内存最大的内存,例如定义以下共用体:

union data{
    int a;
    char b;
    double c;
};

占用内存最大的是double占用了8个字节,所以共用体所占的字节数就是8,它们该共用体在内存中的排列方式如图:

union

~大致是这个意思,画的有点丑~

下面我们通过一个示例来演示共用体成员在内存的分布

#include<stdio.h>
//定义共用体
union data {
    int i;
    char ch;
};
int main() {
    union data a; //定义共用体变量
    printf("%d,%d\n",sizeof(a),sizeof(union data)); //输出 8 8 
    a.i = 0x62;
    printf("%X,%c\n",a.i,a.ch);
    a.ch = '9';
    printf("%X,%c\n",a.i,a.ch);
}

输出结果

image-20210612221241695

在代码的第10行我们给共用体成员i赋值0x62,会被存入内存中的第一个字节中,当给ch赋值为'9'时,字符9的ascII码为57,对应的16进制就是0x39,因此第一个字节中会被改写成39,这样成员i的值也被改变了

image-20210613010845842

6.5 枚举类型

在高级程序设计中是这样解释的:所谓枚举,就是指把可能的指一一列举出来,变量的值只限于列举出来的值的范围

枚举类型的定义

声明枚举类型采用enum开头,格式如下:

enum typeName {valueName1,valueName2,...}

示例:列出一个星期

enum Weekdday {sun, mon, tue, wed, thu, fri, sat};

这样我们就声明了一个枚举类型enum Weekday

这样我们就可以定义这个枚举类型的变量enum Weekday workday,这样就定义了一个枚举变量

注意:枚举变量和其他变量不同,它们的值只限于花括号中的指定值之一

每一个枚举元素都代表一个整数,默认是0,1,2,3....,在上面的定义中sun = 0 mon = 1 依次下去,我们也可以手动设定每一个枚举元素的值

enum Weekday {sun = 7,mon = 1,tue, wed, thu, fri, sat} workday;

指定了sun的值为7,mon为1,依次递增,则sat为6

几点注意事项:

  1. 枚举列表中的标识符的作用范围是全局的,不能定义相同的变量名
  2. 枚举元素又叫枚举常量,不能赋值,例如sun = 7是不合法的,只能在定义枚举类型时设定好
  3. 枚举元素不再是变量,不能使用&来获取它们的地址

6.6 typeof 声明新类型名

typeof关键字,可以用来为类型取一个新的名字,例如我可以给int类型取名为a之后我要定义新的int类型时,就可以使用a来声明

typeof int a;
a num;

当然实际中我们并不会这样用,因为这样的新名字别人根本看不懂,假设在一个程序中,需要用一个变量来计数,我们可以这样做:

typeof int Count; // 指定Count代表int
Count i; // 用count定义变量i

这样可以一目了然的知道它是用于计数的

注意:用typeof声明的新类型名,只是一个别名,原类型名仍然可以正常使用

typeof用于处理复杂的类型会非常的好用,例如结构体,共用体这些

typeof struct stu
{
    char *name;  //姓名
    int num;     //学号
    int age;     //年龄
    float score; //成绩
} Student;

像上面的代码中就定义了一个结构体,并且使用typeof取了别名student,因此在我们需要定义一个 stu 结构体变量时,我们只需

Student ljc;

这样我们就定义了一个结构体变量,上面的代码语义就非常的清晰,学生ljc,代码读起来写起来都会比较方便

下面我们看多几个来熟悉以下

命名一个新的类型名代替数组

typeof int Num[100];
Num a; //定义了一个整型数组a,相当于 int a[100]

代替指针类型

typeof char *String;
String p,s[10];// p为字符指针变量,s为字符指针数组

代替指向函数的指针类型

typeof int (*Pointer)();
Pointer p1,p2; //p1,p2为Pointer类型指针变量

总结:按定义变量的方式,把变量名换成新的类型名,并且在最前面加typeof,就声明了新类型名代表原类型

一步步来

// 1.按照定义变量的方式写 
int a[100];
//2. 把变量名换成新类型名
int Num[100];
//3. 在前面加typeof
typeof int Num[100];
//over

6.7 练习题

🎀第一题

#include <stdio.h>
int main()
{
    struct sk
    {
        int a;
        float b;
    } data, *p;
    p = &data;
}

以上代码块对data中的成员a的正确引用是:

A)(*p).data.a B) (*p).a C)p->data.a D) p.data.a

==答案:B==

🩱第二题

#include<stdio.h>
struct name1{
	char str;
	short x;
	int num;
}A;
int  main()
{
    int size =  sizeof(A);
    printf("%d\n",size);
    return 0;
}

上面的代码输出结果是多少____

==答案:8== 考点:结构体内存对齐

🚩第三题

和上一题不一样噢,将第5行和第六行位置调换

#include <stdio.h>
struct name1
{
    char str;
    int num;
    short x;
} A;
int main()
{
    int size = sizeof(A);
    printf("%d\n", size);
    return 0;
}

输出多少____

==答案:12==

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。