当前位置: 首页 > article >正文

c语言结构体详解

什么是结构体

结构体是 C 语言中一种自定义的数据类型,用于表示不同类型的数据成员的集合。一个结构体可以包含多个数据成员,每个数据成员可以是不同类型的数据,如整数、浮点数、字符、数组、指针等。

结构体的定义通常放在函数的外部,格式如下:

struct <结构体名> {
    <成员1数据类型> <成员1名称>;
    <成员2数据类型> <成员2名称>;
    ...
    <成员n数据类型> <成员n名称>;
};

其中,<结构体名> 表示定义的结构体的名称,<成员数据类型> 表示结构体中每个成员的数据类型,<成员名称> 表示每个成员的名称。

例如,下面是一个表示学生信息的结构体的定义:

struct Student {
    char name[20];
    int age;
    float score;
};

该结构体包含了三个成员:学生姓名(一个 char 数组)、学生年龄(一个整数)和学生成绩(一个浮点数)。

使用结构体

在定义结构体之后,可以通过以下方式创建结构体变量:

struct <结构体名> <结构体变量名>;

例如,可以通过以下方式创建一个名为 stu 的结构体变量:

struct Student stu;

结构体变量的成员可以通过以下方式访问:

<结构体变量名>.<成员名称>;

例如,可以通过以下方式访问 stu 结构体变量的成员:

strcpy(stu.name, "John");  // 修改名字
stu.age = 20;              // 修改年龄
stu.score = 90.5;          // 修改成绩

结构体变量的初始化可以通过以下方式实现:

struct <结构体名> <结构体变量名> = { <成员1初始化值>, <成员2初始化值>, ..., <成员n初始化值> };

例如,可以通过以下方式初始化一个名为 stu 的结构体变量:

struct Student stu = { "John", 20, 90.5 };

在上述代码中,stu 的名字初始化为 “John”,年龄初始化为 20,成绩初始化为 90.5。

结构体可以作为参数传递给函数,也可以作为函数的返回值。此外,结构体还可以嵌套在其他结构体中,形成复杂的数据结构。

结构体中属性如果是字符串的话,怎么赋值

如果结构体中的某个属性是字符串类型,通常可以使用字符串拷贝函数 strcpy 将字符串赋值给该属性。例如,假设有一个结构体 Person,其中有一个名为 name 的属性,类型为字符数组(即字符串),可以通过以下方式给该属性赋值:

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

struct Person {
    char name[20];
    int age;
};

int main() {
    struct Person p;
    strcpy(p.name, "John");  // 给 name 赋值为 "John"
    p.age = 20;              // 给 age 赋值为 20
    printf("Name: %s\n", p.name);
    printf("Age: %d\n", p.age);
    return 0;
}

在上述代码中,我们使用了 strcpy 函数将字符串 “John” 赋值给了 p.name 属性。注意,为了避免字符串溢出,通常需要在定义结构体时指定字符数组的大小,如下所示:

struct Person {
    char name[20];  // 字符数组的大小为 20
    int age;
};

这样,name 属性最多只能存储 19 个字符,因为最后一个字符必须为字符串结束符 \0。如果字符串超过了 19 个字符,strcpy 函数可能会导致缓冲区溢出,从而出现未定义的行为。

为什么字符串赋值要通过strcpy

在 C 语言中,字符串是以字符数组的形式存储的,每个字符串都以一个空字符(‘\0’)作为结尾。因此,如果我们要给一个字符串变量赋值,实际上就是要将一个字符数组中的内容复制到另一个字符数组中,并且在结尾添加一个空字符。

虽然可以使用赋值运算符(“=”)将一个字符数组的内容复制到另一个字符数组中,但是这种方法有一些缺陷。具体来说,赋值运算符只是简单地将一个指针从一个地址指向另一个地址,它并不会检查源数组的长度,也不会自动添加字符串结尾的空字符。如果源数组中的内容超过了目标数组的长度,或者没有在结尾添加空字符,那么就会发生缓冲区溢出等问题。

为了避免这些问题,C 语言提供了一个专门的字符串拷贝函数 strcpy,它的定义如下:

char *strcpy(char *dest, const char *src);

其中,dest 是目标字符数组,src 是源字符数组。strcpy 函数会将源数组中的内容复制到目标数组中,并在目标数组结尾添加一个空字符。在执行拷贝操作之前,strcpy 函数会检查源数组的长度,以确保不会发生缓冲区溢出等问题。因此,使用 strcpy 函数可以更安全地进行字符串赋值操作。

结构体数组

结构体数组是由多个结构体变量组成的数组,每个数组元素都是一个结构体变量。结构体数组通常用于存储一组具有相似属性的数据。

下面是一个简单的例子,展示如何定义和使用结构体数组:

#include <stdio.h>

struct Student {
    char name[20];
    int age;
    double score;
};

int main() {
    // 定义一个包含 3 个结构体变量的结构体数组
    struct Student students[3];

    // 为结构体数组中的每个结构体变量赋值
    strcpy(students[0].name, "Alice");
    students[0].age = 18;
    students[0].score = 90.5;

    strcpy(students[1].name, "Bob");
    students[1].age = 20;
    students[1].score = 85.0;

    strcpy(students[2].name, "Charlie");
    students[2].age = 22;
    students[2].score = 95.5;

    // 输出结构体数组中的所有结构体变量的属性值
    for (int i = 0; i < 3; i++) {
        printf("Name: %s\n", students[i].name);
        printf("Age: %d\n", students[i].age);
        printf("Score: %.1lf\n\n", students[i].score);
    }

    return 0;
}

在上述代码中,我们定义了一个包含 3 个结构体变量的结构体数组 students,其中每个结构体变量包含一个名为 name、一个名为 age 和一个名为 score 的属性。我们通过下标运算符 [ ] 来访问结构体数组中的每个结构体变量,并使用点运算符 . 来访问每个结构体变量的属性。最后,我们使用循环遍历结构体数组中的所有结构体变量,并输出它们的属性值。

结构体指针

结构体指针是指向结构体变量的指针,可以通过指针访问结构体变量中的属性。结构体指针通常用于动态分配内存和传递结构体变量作为参数。

下面是一个简单的例子,展示如何定义和使用结构体指针:

#include <stdio.h>

struct Student {
    char name[20];
    int age;
    double score;
};

int main() {
    // 定义一个结构体变量
    struct Student s = {"Alice", 18, 90.5};

    // 定义一个指向结构体变量的指针
    struct Student *p = &s;

    // 使用指针访问结构体变量中的属性
    printf("Name: %s\n", p->name);
    printf("Age: %d\n", p->age);
    printf("Score: %.1lf\n", p->score);

    return 0;
}

在上述代码中,我们首先定义了一个包含名为 name、age 和 score 三个属性的结构体变量 s。接着,我们定义一个指向结构体变量 s 的指针 p,并使用取地址运算符 & 获取结构体变量 s 的地址。最后,我们使用指针变量 p 访问结构体变量中的属性,其中 -> 是结构体指针访问结构体变量中属性的运算符,相当于 (*p).name、(*p).age 和 (*p).score。

另外,还可以使用动态分配内存的方式来创建一个结构体指针,如下所示:

struct Student *p = (struct Student *)malloc(sizeof(struct Student));

这里使用了 malloc() 函数来分配一个 struct Student 类型大小的内存空间,并将其转换为结构体指针类型 struct Student *,然后将返回的指针赋值给指针变量 p。这样就可以使用指针变量 p 访问动态分配的结构体变量中的属性了。注意,在使用完动态分配的内存后,需要使用 free() 函数释放该内存。

结构体指针不进行初始化的时候就进行赋值会出现什么问题?

在 C 语言中,结构体指针可以在定义时进行初始化赋值,也可以在定义之后进行赋值操作。如果结构体指针在定义时没有进行初始化,直接进行赋值操作可能会出现问题,因为未经初始化的指针不指向有效的内存地址,无法正确访问结构体变量的属性。

下面是一个例子,演示了结构体指针在定义之后直接赋值的错误示例:

#include <stdio.h>

struct Student {
    char name[20];
    int age;
    double score;
};

int main() {
    // 定义一个结构体指针
    struct Student *sPtr;

    // 直接给指针变量赋值
    sPtr->age = 18;
    sPtr->score = 90.5;
    strcpy(sPtr->name, "Alice");

    // 输出结构体变量中的属性
    printf("Name: %s\n", sPtr->name);
    printf("Age: %d\n", sPtr->age);
    printf("Score: %.1lf\n", sPtr->score);

    return 0;
}

在上述代码中,我们首先定义了一个结构体指针 sPtr,然后直接对其进行赋值操作,即分别给 age、score 和 name 属性赋值。在赋值 name 属性时,同样需要使用 strcpy() 函数将字符串拷贝到 name 中。最后,我们输出结构体变量中的属性。

然而,这段代码是错误的,因为结构体指针 sPtr 在定义之后未经初始化,不知道指向哪块内存,所以直接对其进行赋值操作会导致未定义行为,可能会访问非法内存地址,甚至会导致程序崩溃。正确的做法是先为结构体指针分配内存空间,并将其初始化为一个合法的地址,然后再进行赋值操作,例如:

int main() {
    // 定义一个结构体指针
    struct Student *sPtr;

    // 分配内存空间并初始化结构体指针
    sPtr = (struct Student*) malloc(sizeof(struct Student));
    strcpy(sPtr->name, "");
    sPtr->age = 0;
    sPtr->score = 0.0;

    // 对结构体指针进行赋值
    sPtr->age = 18;
    sPtr->score = 90.5;
    strcpy(sPtr->name, "Alice");

    // 输出结构体变量中的属性
    printf("Name: %s\n", sPtr->name);
    printf("Age: %d\n", sPtr->age);
    printf("Score: %.1lf\n", sPtr->score);

    return 0;
}

拓展: 指针类型的变量,包含数组,结构体,函数等, 不进行初始化的话,直接进行赋值是不是出现问题?

如果指针类型的变量未初始化,直接进行赋值会出现问题。因为未初始化的指针变量指向的内存地址是未知的,赋值会导致指向错误的内存地址,进而导致程序出现未定义的行为,例如崩溃或错误输出等。

需要先对指针变量进行初始化,然后再进行赋值操作,以确保指针变量指向有效的内存地址。


http://www.kler.cn/a/4766.html

相关文章:

  • ConvNeXt V2: Co-designing and Scaling ConvNets with Masked Autoencoders论文解读
  • WPF系列八:图形控件Path
  • 200.Spark(七):SparkSQL项目实战
  • day10—编程题
  • 为了开放互联,明道云做了十件事
  • SM3哈希算法的FPGA实现 I
  • 【Unity 手写PBR】Build-in管线:实现间接光部分
  • 基于springboot实现家政服务管理平台【源码+论文】
  • 《Netty》从零开始学netty源码(十七)之AbstractUnsafe
  • 2023年湖北省建筑八大员(建设厅七大员)报考流程和拿证流程来咯!
  • 【redis】redis淘汰策略
  • 推荐一款自动生成财务报表分析的软件
  • 中介变量、调节变量与协变量
  • 人事文件签署单调、重复、繁重?君子签电子合同提升HR工作质效
  • Ajax:服务器的基本概念与初识Ajax
  • [水]与ChatGPT谈Java
  • 怎么将pdf压缩?pdf文件如何压缩?
  • 文心一言发布前一天,GPT-4震撼发布
  • 即时零售:不可逆的进化
  • 【WEB前端进阶之路】 HTML 全路线学习知识点梳理(下)
  • Air700E开发板|移芯EC618|4G Cat.1模组:概述及PinOut
  • vue3+SpringBoot+postgresql 项目前后端传参