为什么你不能在C中返回固定大小/ const数组?

时间:2015-03-16 22:59:47

标签: c arrays pointers

我想知道为什么不能在C中返回数组? 毕竟,数组只是一个由大小信息支持的指针(使sizeof工作)。首先我认为这是为了防止我返回我的堆栈上定义的数组,但没有什么能阻止我将指针返回到我的堆栈上的东西(gcc警告我,但代码编译)。而且我也可以返回字符串文字,这是静态存储的字符数组。顺便说一句,在lunux中它存储在.rodata中,const数组也存储在那里(用objdump检查),所以我可以返回数组(将它转换为指针)并且它可以工作,但是AFAIK这只是特定于实现的(另一个OS /编译器可能将const存储在堆栈中)。

我有2个想法如何实现数组返回:只需将其复制为值(就像为结构所做的那样。我甚至可以将数据包装到结构中!!),并自动创建指针或允许用户返回const数组和创建契约,这样的数组应具有静态存储持续时间(就像它对字符串所做的那样)。这两个想法都是微不足道的! 所以,我的问题是为什么K& R没有实现类似的东西?

4 个答案:

答案 0 :(得分:13)

数组不是“只是一个由大小信息支持的指针”。

数组是某种类型的连续元素块。没有指针。

由于数组是一个对象,因此可以形成一个指向数组或指向数组元素之一的指针。但是这样的指针不是数组的一部分,并且不与数组一起存储。说“int只是一个由1 int”大小支持的指针就更有意义了。

编译器已知数组的大小,其方式与已知任何对象的大小相同。如果我们有double d;,那么就知道sizeof dsizeof(double),因为编译器会记住ddouble类型的对象。

  

没有什么可以阻止我将指针返回到我的堆栈上的东西

C标准阻止您执行此操作(并使用返回的指针)。如果您编写的代码违反了标准,那么您就是自己的代码。

  

我也可以返回字符串文字

字符串文字是一个char数组。在return语句中使用数组时,它将转换为指向第一个元素的指针。

要按值返回(和分配)数组,必须更改有关将数组转换为指针(有时称为“衰减”)的规则。这是可能的,但K& R决定在设计C时几乎无处不在。

事实上,有可能拥有像C这样的语言,但根本没有衰变。也许在后见之明可以省去很多困惑。然而,他们只是选择以他们的方式实施C.


在K& R C中,也无法按值返回结构。任何非原始类型的复制操作都必须使用memcpy或等效的迭代副本完成。考虑到硬件资源在20世纪70年代的发展方式,这似乎是一个合理的设计决策。

ANSI C增加了按值返回结构的可能性,但到那时,即使他们想要改变衰减规则也为时已晚;它会破坏许多依赖于衰变规则的现有代码。

答案 1 :(得分:11)

从技术上讲,你可以返回一个数组;你不能“直接”做到这一点,但必须把它包装在一个结构中:

struct foo {
    int array[5];
};

struct foo returns_array(void) {
    return((struct foo) {
        .array = {2, 4, 6, 8, 10}
    });
}

为什么C不允许你直接这样做,尽管它有能力仍然是一个很好的问题。这可能与它不支持全数组赋值的事实有关:

void bar(int input[5]) {
    int temp[5];

    temp = input;   <-- Doesn't compile
}

当然,更令人陌生的是,支持通过参数传递的整个数组副本。如果有人知道如何找到ANSI委员会关于此事的决定,那将会很有趣。

然而,

  

毕竟,数组只是一个由大小信息支持的指针(使sizeof工作)。

这不正确。数组没有显式指针,也没有任何存储的大小。数组存储为原始值,打包在一起;大小只在编译器中已知,并且永远不会在程序中显式为运行时数据。当您尝试将其用作一个时,数组会衰减为指针

答案 2 :(得分:2)

因为如果突然之间,语言的修订允许函数能够返回一个完整的数组,那么该修订也应该处理这些情况:

  • 允许在数组之间进行赋值(因为如果函数返回一个数组,那么因为它将被分配给调用函数中的数组变量)
  • 允许将完整数组作为值参数传递(因为数组的名称不再是指向其第一个元素的指针,因为这会与第一个元素冲突)

如果允许这些构造,那么将数组名称作为参数传递给函数的现有程序将会停止工作。

希望函数修改该数组。

此外,使用数组名称作为指针将指定给指针变量的现有程序将停止工作。

因此,虽然它在技术上是可行的,但使数组作为完整的实体工作,可以分配,返回等等,这将破坏许多现有的程序。

请注意结构可以升级&#34;因为在K&amp; R C中没有先前的语义将变量结构的名称作为指向其自身的指针。任何必须使用结构作为参数或返回值的函数都必须使用指向它们的指针。

答案 3 :(得分:0)

“原因”是数组在大多数表达式中衰减指针,并且事情会“错误”,就好像你想要允许分配数组一样。如果从函数返回数组,则无法将其与普通指针区分开来。如果f()将返回double[5],例如初始化

double* A=f();

是有效的。 A将获取临时对象的地址,这些内容在C中仅存在于f的调用出现的完整表达式的末尾。那么A将是一个悬空指针,指向一个无效的地址的指针。

总结一下:在大多数情况下,让数组的行为与指针类似的初始决定强加了数组不能被赋值也不能被函数返回。