【问题标题】:Why you cannot return fixed size / const array in C? [closed]为什么不能在 C 中返回固定大小/常量数组? [关闭]
【发布时间】:2015-03-16 22:59:46
【问题描述】:

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

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

【问题讨论】:

  • 当然,技术上可以像其他内置函数一样使数组可复制和可分配,但令人讨厌的是它们不是。我经常这样问自己。
  • @Prof.Falken 如果我理解正确,OP 会问为什么会这样。设计决策有原因的情况并不少见。
  • @Mr.F 是的,这绝对不是重复的。
  • @SamProtsenko Jeez。不能返回int 吗?还是包含数组的struct?这是一个设计决策。没有技术原因无法完成。
  • @SamProtsenko 在 C 标准中,没有提到 stack 来传递和返回函数参数和返回。 (不过,大多数实现确实使用堆栈)

标签: c arrays pointers


【解决方案1】:

从技术上讲,您可以返回一个数组;您不能“直接”执行此操作,而必须将其包装在结构中:

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】:

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

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

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

编译器知道数组的大小就像知道任何对象的大小一样。如果我们有double d;,那么就知道sizeof d 是sizeof(double),因为编译器记得d 是double 类型的对象。

没有什么能阻止我返回指向堆栈中某物的指针

C 标准阻止您这样做(并使用返回的指针)。如果您编写的代码违反了标准,那么您就只能靠自己了。

我也可以返回字符串文字

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

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

事实上,有可能拥有像 C 这样的语言,但完全没有衰退。也许事后看来,这会避免很多混乱。然而,他们只是选择以他们所做的方式实现 C。


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

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

【讨论】:

    【解决方案3】:

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

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

    如果允许这些构造,将数组名称作为参数传递给函数的现有程序,期望函数修改该数组,将停止工作。

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

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

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

    【讨论】:

      【解决方案4】:

      “原因”是大多数表达式中指向指针的数组衰减,事情会“错误”,就好像您希望允许分配数组一样。如果你从一个函数返回一个数组,你将无法将它与普通指针区分开来。如果f() 会返回double[5],比如说初始化

      double* A=f();
      

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

      总结一下:在大多数情况下,最初决定让数组的行为类似于指针,这意味着数组不能被函数赋值或返回。

      【讨论】:

        猜你喜欢
        • 2013-04-06
        • 1970-01-01
        • 2014-11-23
        • 2015-05-24
        • 2020-12-21
        • 1970-01-01
        • 2021-10-25
        • 2018-12-30
        • 2013-01-08
        相关资源
        最近更新 更多