3. 结构体赋值

这节课我们来学习 C 语言的结构体赋值。

前面我们学过两个相同数据类型的数组是不能够直接使用赋值语句修改数据内容的。但 C 语言的两个相同类型的结构体是可以直接赋值来修改数据的。

结构体赋值的方式通常有如下两种:

  1. 逐个成员变量赋值
  2. 结构体整体赋值

下面我们来举例说明上述两种赋值的方法。

现在我们定义两个 struct student 类型的结构体如下:

struct student {
    char name[32];  // 姓名
    int age;  // 年龄
    float height; // 身高
};

接下来我们创建两个结构体类型的变量。

struct student s1 = {"zhang san", 18, 1.73};
struct student s2;

1、 使用逐个成员赋值的方法来复制 s1 的值到 s2 中则需要如下代码实现。

strcpy(s2.name, s1.name);
s2.age = s1.age;
s2.height = s1.height;

2、 使用结构体整体赋值赋值的方法来复制 s1 的值到 s2 中 则需要如下代码实现。

s2 = s1;

可见结构体赋值整体赋值相对比较简单。

当使用 结构体整体赋值 时,由于大部分的处理器没有对应的指令能够实现整体赋值,因此整体赋值实际是整体内存拷贝,即 s2 = s1; 实际等同于 memcpy(&s2, &s1, sizeof(s1));

函数的结构体传参

在函数调用的过程中,实际参数的值传递给形式参数时,实际就是赋值。因此在函数调用传递结构体时,通常使用传址的方式来代替传值,这样可以大大提高软件的执行效率。因为指针赋值的开销远远小于结构体赋值的开销。

在函数中使用传址的方式传递结构体数据时,如果函数内不改动结构体的数据,我们经常在形参的结构体指针中加入一个 const 修饰符。以避免在函数中错误的修改了结构体的数据。

示例:

// filename: struct_assigment.c
#include <stdio.h>

struct student {
    char name[32];  // 姓名
    int age;   // 年龄
    float height; // 身高
};

// 使用传址的方式传递结构体,参数的复制的开销比较大
void print_stu_info1(struct student s) {
    printf("姓名: %s, 年龄: %d, 身高: %.2f\n", s.name, s.age, s.height);
}

// 使用传址的方式传递结构体,并添加 const 修饰符
void print_stu_info2(const struct student *ps) {
    printf("姓名: %s, 年龄: %d, 身高: %.2f\n", ps->name, ps->age, ps->height);
}

int main(int argc, char * argv[]) {
    struct student s1 = {"zhang san", 18, 1.73};

    print_stu_info1(s1);
    print_stu_info2(&s1);

    return 0;
}

运行结果如下:

weimingze@mzstudio:~$ gcc -o struct_assigment struct_assigment.c
weimingze@mzstudio:~$ ./struct_assigment
姓名: zhang san, 年龄: 18, 身高: 1.73
姓名: zhang san, 年龄: 18, 身高: 1.73

可见上述两个函数 print_stu_info1print_stu_info2 都能实现打印结构体数据的功能,但 print_stu_info2 的传参效率会优于 print_stu_info1。这也是我们在查看很多的 C 语言源代码时发现很多人写的函数调用在传递结构体时多数都是使用传址代替传值的主要原因。

复合字面值(Compound literal)

在 C99 的 C 语言标准中,我们可以通过 复合字面值 来构建一个结构体类型的字面值,通常这个字面值用于整体赋值给某个结构体或函数的传参。

复合字面值的语法

(结构体类型){初始化列表}

复合字面值是一个表达式,它会返回一个结构体类型的临时对象。

示例:

改写上述程序,我们使用 复合字面值 为结构体变量赋值和传参。

// filename: struct_assigment.c
#include <stdio.h>

struct student {
    char name[32];  // 姓名
    int age;   // 年龄
    float height; // 身高
};

void print_stu_info1(struct student s) {
    printf("姓名: %s, 年龄: %d, 身高: %.2f\n", s.name, s.age, s.height);
}

int main(int argc, char * argv[]) {
    struct student s1;

    s1 = (struct student){"wang5", 12, 1.41};
    print_stu_info1(s1);

    s1 = (struct student){.name="zhao6", .height=1.80, .age=19};
    print_stu_info1(s1);

    print_stu_info1((struct student){"qian7", 20, 1.69});

    return 0;
}

编译和运行结果如下:

weimingze@mzstudio:~$ gcc -o struct_assigment struct_assigment.c
weimingze@mzstudio:~$ ./struct_assigment
姓名: wang5, 年龄: 12, 身高: 1.41
姓名: zhao6, 年龄: 19, 身高: 1.80
姓名: qian7, 年龄: 20, 身高: 1.69

可见,使用复合字面值也可以临时的创建一个 结构体类型的对象。

实验