C:为啥你可以通过值传递(给函数)一个结构,而不是一个数组?

Posted

技术标签:

【中文标题】C:为啥你可以通过值传递(给函数)一个结构,而不是一个数组?【英文标题】:C: Why can you pass (to a function) a struct by value, but not an array?C:为什么你可以通过值传递(给函数)一个结构,而不是一个数组? 【发布时间】:2016-11-27 09:09:33 【问题描述】:

这背后有什么历史或逻辑原因吗?


说明: 当您将数组传递给 C 中的函数时,实际上您只传递了一个指向数组的指针。 但是,当您传递结构时,您可以传递结构的副本或指针。

//this:
int function(int array[10])
// is equivalent to this:
int function(int *array)
//and they both pass a pointer

//but this:
int function(struct tag name)
//passes a struct by value, where as this:
int function(struct tag *name)
//passes a pointer to it.

为什么不一样?

【问题讨论】:

可以按值传递数组,但数组的值是指向其第一个元素的指针(不是指向数组的指针)。 如果我没记错的话,指向结构的指针也是指向它的第一个值的指针 不,它们是不同的(类型不匹配,仅此而已)。 ***.com/questions/36872230/…的可能重复 我认为这个问题更多地属于programmers.SE。值得注意的是,K&R C 不允许值传递/返回structs。 【参考方案1】:

当然,您可以按值传递数组;您需要做的就是将其包装在struct 中。但这仅在数组具有确定(且非可变)大小时才有效。您可以在结构中包含无限大小的数组,但结果类型不完整,只能用作指针的目标。

这可能与我们将要解释的一样接近。绝大多数作为参数传递的数组都不是固定大小的,并且通过值传递它们是不可能的,即使它是有意的,这也是不太可能的。

函数有类似(但不同)的衰减;函数不能作为参数传递,只能作为函数指针传递。因为每次你想引用一个函数时显式地写& 会很乏味,所以语言会为你处理它。

总体而言,问题形式为“为什么这种语言不像这样?”只能回答“因为就是这样”。

【讨论】:

【参考方案2】:

在最初的 K&R 中,您不能按值传递结构。那是一个语法错误。由于许多编译器供应商将其作为扩展提供,因此按值传递最终进入了标准。

为什么要限制,为什么要进化?开发 C 语言的机器很小。 64 KB 的段大小很常见。内存很宝贵,既然可以传递地址,为什么还要复制一些东西呢?在堆栈上复制一个 64 字节的结构错误,甚至可能不是用户想要的。

到 1990 年代中期,这不再是真的了。 32 位寻址和 4 MB 或更大的 RAM 很常见。该限制是一个障碍并导致了一些复杂性,因为如果没有const,一个通过引用传递的结构可能会被修改,也许是在不知不觉中。

为什么不对数组做同样的事情呢?没有要求。如您所知,数组和指针在 C 中密切相关。 C 标准库在很大程度上依赖于通过引用传递,请考虑 memsetstrcpy。虽然按值传递结构意味着只是在调用中删除&,但按值传递数组将需要添加新语法。以 C 语法形式提供 by value 的编译器供应商会被会议嘲笑。

【讨论】:

【参考方案3】:

structs 用于声明自己的数据类型与原始数据类型,如 int、float、long(或结构的结构)等。它们应该包含其中的一些,例如学生的结构将包含 id、name、rollno、subjects 等。所以大多数结构元素最多包含10-20个字段(在逻辑情况下),所以当你将一个结构传递给一个函数时,它必须复制大约40-100个字节。复制该结构变量。其中数组可以很大并且用于存储相同类型的信息。如果是整数,它们的大小可以是 10^7,所以如果我们实现一种语言来复制整个数组以进行函数调用,它可能必须复制 (10^7)*4 字节,这是一个巨大的数量并且会严重影响性能。数组的典型大小是 10^4 到 10^6,这仍然很多。但是,如果您创建 int 数组(或任何其他数组)的结构,则可以将其作为该数组的副本传递给函数。例如

#include<stdio.h>

typedef struct 
    int arr[10];
arrayStruct;

void change(arrayStruct a)
    a.arr[2]=5;

int main()
    arrayStruct a;
    for(int i=0;i<10;i++)
        a.arr[i]=i;
    
    printf("Before:\n");
    for(int i=0;i<10;i++)
        printf("%d ",a.arr[i]);
    
    change(a);
    printf("\nAfter:\n");
    for(int i=0;i<10;i++)
        printf("%d ",a.arr[i]);
    
    return 0;

这在大多数情况下不会这样做,但是当您需要传递数组但不想更改它们的内容但也需要对其副本进行某种更改并希望返回该副本时,您可以使用这种数组结构并从结构返回类型的函数中返回它们,例如

arrayStruct returnChange(arrayStruct a)
    a.arr[2]=332;
    return a;

【讨论】:

【参考方案4】:

int function(int array[10])int function(int *array) 相同,因为 6.7.5.3 函数声明符(包括原型)(http://www.open-std.org/JTC1/SC22/wg14/www/docs/n1124.pdf 第 118 页)

7 应调整参数声明为“类型数组” 到“限定类型的指针”,其中类型限定符(如果 any) 是在数组类型的 [ 和 ] 中指定的那些 推导。如果关键字 static 也出现在 [ 和 ] 的数组类型推导,然后对于每次调用 函数,对应的实参的值应 提供对数组的第一个元素的访问,其中至少有 由大小表达式指定的元素

.

【讨论】:

【参考方案5】:

两个函数声明中的类型不同 -

struct tag  /* and */ struct tag *

一个是结构变量,另一个是指向结构的指针。

你可以对结构做类似的事情 -

int function(struct tag name[])  /*--> int function(struct tag *name)  */

上面的这些是等价的。

【讨论】:

嗯?这如何回答这个问题? @DavidRefaeli 我会假设。我认为 OP 不知道这一点,否则他不会对此感到困惑。在我看来,问题是这样的,因为所有有问题的声明都无法比较。 @ameyCU “我想”不会回答“怎么样?”。 DavidRefaeli 是 OP。 @ameyCU - 我认为你不明白这个问题。我在问为什么在 C 中可以将 struct 的副本传递给函数,但不能传递数组的副本... @ameyCU 是的,问题似乎是“为什么?”。

以上是关于C:为啥你可以通过值传递(给函数)一个结构,而不是一个数组?的主要内容,如果未能解决你的问题,请参考以下文章

为啥不允许将数组按值传递给 C 和 C++ 中的函数?

为啥我们不能通过值传递数组来函数?

为啥要在 C 中声明一个只包含数组的结构?

调用函数时为啥形参的值不能传给实参

为啥将 C-Array 传递给函数时 sizeof() 值错误? [复制]

为啥在没有 ref 的情况下将 list 传递给一个类似于通过 ref 传递的函数?