在这种情况下,通过指针传递结构体和通过值传递有什么区别?

3

我是一个有帮助的助手,可以为您进行翻译。以下是需要翻译的内容:

所以我正在学习《C Primer Plus》这本书,遇到了一个关于结构体和文件的编程任务。主要目标是为飞机制作一个座位预订程序。

我定义了以下结构体:

typedef struct {
    unsigned short identification_number;
    unsigned short status;
    char last_name[MAX_NAM_LEN];
    char first_name[MAX_NAM_LEN];
} Seat;

typedef struct {
    unsigned short empty_seats;
    Seat seats[]; // Flexible Array Member
} Plane;

我有以下函数,用于打印出空座位的身份证号码。
/*
 * Function:  print_empty_seats_ID
 * -------------------------
 * Prints a list of empty seats identification numbers of the given plane.
 *
 */
void print_empty_seats_ID (Plane* plane, int num_seats) {

    // Clear the screen.
    system("clear");

    printf("Identification number of empty seats:");

    for (int i = 0; i < num_seats; i++) {
        if (plane->seats[i].status == EMPTY)
            printf(" %d", plane->seats[i].identification_number);
    }
    puts("");

    // Redirect the user to the main menu.
    puts("Redirecting in 4 seconds...");
    sleep(4);
}

这个函数是这样被另一个函数调用的,print_empty_seats_ID(plane, num_seats),其中plane是之前在堆上定义的指向飞机结构体的指针,num_seats是飞机上座位的总数。

虽然此函数有效,但如果我改成使用按值调用而非指针,则会打印出垃圾值,如下所示。

/*
 * Function:  print_empty_seats_ID
 * -------------------------
 * Prints a list of empty seats identification numbers of the given plane.
 *
 */
void print_empty_seats_ID (Plane plane, int num_seats) {

    // Clear the screen.
    system("clear");

    printf("Identification number of empty seats:");

    for (int i = 0; i < num_seats; i++) {
        if (plane.seats[i].status == EMPTY)
            printf(" %d", plane.seats[i].identification_number);
    }
    puts("");

    // Redirect the user to the main menu.
    puts("Redirecting in 4 seconds...");
    sleep(4);
}

在这种情况下,该函数被这样调用:print_empty_seats_ID (*plane, num_seats); 并输出以下无意义的内容:Identification number of empty seats: 64 0 0
我的问题是第一个函数为什么能够工作而第二个函数不能?

嗯,我想那还算不错!那我们就等待答案吧。 - Amr Mustafa
在第一个例子中,编译器知道指针需要多少堆栈空间。而在第二个例子中,它不知道struct的大小会是多少。 - Weather Vane
1
我非常理解你对于被踩的感受。然而,我也曾经吃过亏,了解到踩人是有意匿名的,许多踩人者也很欣赏这一点。请理解,踩人是使StackOverflow成为现在这个样子的必要机制之一。公开表明自己踩人的人往往很快就会发现,有些人无法接受负面批评。他们可能(即使没有充分的理由)会成为踩人者的麻烦。再次强调,我理解你的感受(我没有踩你),但你可能需要学会与此共存。另外,当前的踩数为0,恭喜。 - Yunnosch
1
@Amr Mustafa:简而言之,在值上下文中,具有灵活数组成员的结构类型的行为就像数组大小为零一样。 - AnT stands with Russia
1
我完全同意。将匿名性纳入设计决策考虑之中。一些人(我们称他们为“强者”,作为一种中立的描述)指出了投票反对的理由。其他人则没有这么做,但也不会因此受到责备。请记住,一个赞可以抵消五个踩。 - Yunnosch
显示剩余4条评论
2个回答

1
问题在于您的Plane有一个可变数组成员seats,即没有指定大小的尾随数组。这些在C11 6.7.2.1p18中定义:

18 作为一种特殊情况,具有多个命名成员的结构的最后一个元素可能具有不完整的数组类型;这称为可变数组成员。在大多数情况下,可变数组成员被忽略。特别地,结构的大小就像省略了可变数组成员一样,除了它可能有比省略所暗示的更多的尾部填充。

示例6.7.2.1p25澄清了这一点:

25 The assignment:

*s1 = *s2;

[where both are pointers to structures with a flexible array member] only copies the [non-flexible members]; if any of the array elements are within the first sizeof (struct s) bytes of the structure, they might be copied or simply overwritten with indeterminate values.

在C语言中,函数参数的传递方式类似于赋值,因此对于传递的plane值也是如此。

感谢您的回答和参考! - Amr Mustafa

1
问题在于你使用了所谓的灵活数组成员,关于此规范说明如下:
“特别地,结构体的大小就好像灵活数组成员被省略一样,只不过可能会有比省略更多的尾部填充。”
因此,当你按值传递时,座位本身并没有被复制。当你按指针传递时,指针指向原始的Plane,它确实拥有座位,所以它有效。

啊哈!还是有点奇怪。但至少现在我知道问题的根源了,谢谢! - Amr Mustafa

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接