江小南

V1

2023/01/09阅读:20主题:萌绿

【C语言】浅聊结构体

阅读本文,需要对C语言的基础知识有所了解。C语言基础,我给大家准备了详细的学习资料,微信公众号主页回复“C语言”即可领取。

引言

在C语言中我们可以定义很多属性,例如:一名学生有学号、姓名、性别、年龄、地址等属性。如果针对学生的这些属性单独定义一个变量,那么在有多名学生时,变量就难以分清。这时就需要用到结构体。C语言提供结构体来管理不同类型的数据组合

1. 结构体定义与初始化

声明结构体类型的一般形式:

struct 结构体名
    {
成员列表};

示例如下:

struct student{
    int num;
    char name[20];
    char sex;
    int age;
    float score;  // 成绩
    char addr[30];
};   // 结构体类型声明,注意最后一定要加分号

说明:这里把学号、姓名、性别等属性叫做成员。

先声明结构体名,再定义变量名,示例如下。

struct student s;

初始化方式如下:

s={1001,"lele",'M',20,85.4,"beijing"};

2. 结构体数组

如果有多个学生,我们可以将他们放到一个数组中,例如:

struct student sarr[3];

代码示例:

#include <stdio.h>

struct student{
    int num;
    char name[20];
    char sex;
    int age;
    float score;
    char addr[30];
};   // 结构体类型声明,注意最后一定要加分号

int main() {
    struct student s={1001,"lele",'M',20,85.4,"beijing"};
    printf("%d %s %c %d %f %s\n",s.num,s.name,s.sex,s.age,s.score,s.addr); // 定义及初始化
    printf("-------------------------\n");
    struct student sarr[3];
    int i;
    for(i=0;i<3;i++){
        scanf("%d%s %c%d%f%s",&sarr[i].num,sarr[i].name,&sarr[i].sex,&sarr[i].age,&sarr[i].score,sarr[i].addr);
    }
    printf("*************************\n");
    for (i=0;i<3;i++) {
        printf("%d %s %c %d %f %s\n",sarr[i].num,sarr[i].name,sarr[i].sex,sarr[i].age,sarr[i].score,sarr[i].addr); // 注意普通变量赋值时需要使用&获取地址,数组不需要,因为本身保存的就是初始地址
    }
    return 0;
}
F:\Computer\Project\practice\8\8.1-statement\cmake-build-debug\8_1_statement.exe
1001 lele M 20 85.400002 beijing
-------------------------
1002 lala M 25 86.4 tianjin
1003 lilei W 23 87.2 shanxi
1004 hubing M 22 88.3 qingdao
*************************
1002 lala M 25 86.400002 tianjin
1003 lilei W 23 87.199997 shanxi
1004 hubing M 22 88.300003 qingdao

进程已结束,退出代码为 0

注意:

  1. 结构体类型声明要放在main函数之前,这样main函数中才可以使用这个结构体,工作中往往把结构体声明放在头文件中。
  2. 如果结构体变量已经定义,那么只能对它的每个成员单独赋值,如s.num=1005。
  3. 普通变量赋值时需要使用&获取地址,数组不需要,因为本身保存的就是初始地址。

3. 结构体对齐

结构体对齐也叫结构体大小。结构体的大小必须是其最大成员的整数倍,我们通过代码来分析。

#include <stdio.h>

struct student_type1{
    double score;  // double是一种浮点型,占8个字节
    short age;  // short是短整型,占2个字节
};
struct student_type2{
    double score;
    int height;  // int是整型,占4个字节
    short age;
};
struct student_type3{
    int height;
    char sex;  // char是字符型,占2个字节
    short age;
};

int main() {
    struct student_type1 s1;
    struct student_type2 s2;
    struct student_type3 s3;
    printf("s1 size=%d\n",sizeof(s1));
    printf("s2 size=%d\n",sizeof(s2));
    printf("s3 size=%d\n",sizeof(s3));
    return 0;
}
F:\Computer\Project\practice\8\8.1-alignment\cmake-build-debug\8_1_alignment.exe
s1 size=16
s2 size=16
s3 size=8

进程已结束,退出代码为 0

分析:对于student_type1,因为最大成员score占8个字节,age占2个字节,所以最少需要16个字节。student_type2同理。student_type3最大成员height占了4个字节,所以最少需要8个字节。下图是每个成员占用的内存情况。

思考:为什么要对齐?

为了CPU更高效取内存数据(地址总线与数据总线的角度考虑,后面文章会说)

有时,我们可以通过变换属性的位置来调节占用空间的大小,比如将char和short放到一起,他们会共同占用4个字节(比如上面的s3)。

4. 结构体指针与typedef的使用

结构体指针

一个结构体变量的指针就是该变量所占据的内存段的起始地址,可以设置一个指针变量,用它指向一个结构体变量,此时该指针变量的值是结构体变量的起始地址,指针变量也可以用来指向结构体数组中的元素,从而能够通过结构体指针快速访问结构体内的每个成员。

#include <stdio.h>

struct student{
    int num;
    char name[20];
    char sex;
};

int main() {
    struct student s={1001,"wangle",'M'};
    struct student sarr[3]={1001,"lilei",'M',1002,"shangsan",'W',1003,"lili",'M'};
    struct student *p;  // 定义结构体指针
    int num;
    p=&s;
    printf("%d %s %c\n",p->num,p->name,p->sex);
    p=sarr;
    printf("%d %s %c\n",(*p).num,(*p).name,(*p).sex);  // 方式一,获取成员,之一有括号
    printf("%d %s %c\n",p->num,p->name,p->sex); // 方式二,获取成员
    printf("-------------------------\n");
    p=p+1;
    printf("%d %s %c\n",p->num,p->name,p->sex);
    p=p+1;  // 注意指针已经在上面的基础上偏移了,所以还是加1
    printf("%d %s %c\n",p->num,p->name,p->sex);
    return 0;
}
F:\Computer\Project\practice\8\8.2-morphology-pointer\cmake-build-debug\8_2_morphology_pointer.exe
1001 wangle M
1001 lilei M
1001 lilei M
-------------------------
1002 shangsan W
1003 lili M

进程已结束,退出代码为 0

注意

  1. p就是一个结构体指针,可以对结构体s取地址并赋给p,p就可以访问结构体每个成员。而数组名存储的是数据首地址,所以将sarr赋给p。
  2. “.”成员选择器的优先级高于“*”运算符,所以需要加括号,通过*p得到sarr[0],然后获取对应成员。

typedef的使用

可以使用typedef声明新的类型名来代替已有的类型名。相当于取别名。

#include <stdio.h>

// 结构体指针
typedef struct student{
    int num;
    char name[20];
    char sex;
}stu,*pstu;   // 对定义的student结构体取别名

typedef int INTEGER;  // 对已有的类型取别名

int main() {
    stu s={1001,"wangle",'M'};
    pstu p;  // 结构体指针变量
    p=&s;
    printf("p->num=%d,p->name=%s,p->sex=%c\n",p->num,p->name,p->sex);
    INTEGER i=10;
    printf("i=%d\n",i);
    return 0;
}
F:\Computer\Project\practice\8\8.2-typedef\cmake-build-debug\8_2_typedef.exe
p->num=1001,p->name=wangle,p->sex=M
i=10

进程已结束,退出代码为 0

说明:使用stu定义结构体变量和使用struct student定义结构体变量是等价的。使用INTEGER定义变量i和使用int定义变量i是等价的。pstu等价于struct student*,所以p是结构体指针变量。

分类:

后端

标签:

C++

作者介绍

江小南
V1