【问题标题】:How does this C program compile and run successfully?这个C程序是如何编译运行成功的?
【发布时间】:2011-07-13 12:54:34
【问题描述】:

我在一次采访中被问到这个问题。这个程序的结果是什么?我自信地说这不会编译。我说 a 是一个数组名,不占用任何空间,所以 &a 应该没有任何意义,因此不会编译。但在实践中,事实证明编译和运行都成功。有人可以解释一下,这是如何工作的。

main()    
{
    int a[50];
    &a; // or perhaps the interviewer wanted to know the output of this line..
}

【问题讨论】:

  • "a" 是指向数组的指针。
  • 一个数组确实会占用内存空间(你会在哪里存储它的内容),所以检索数组的地址是完全合理的。
  • 相关信息见an old(ish) answer of mine

标签: c


【解决方案1】:

&a; 行只计算数组a 的地址,然后默默地丢弃它。

【讨论】:

  • 那么a和&a有什么区别。我认为 a 是数组的起始地址。但看起来 &a 也有同样的东西?或者也许 a 和 &a 具有相同的值,但 a+1 和 &a + 1 将具有差异值。
  • a 等价于 &a,其中 a 是一个数组。看到这个解释:fredosaurus.com/notes-cpp/arrayptr/arraysaspointers2.html
  • 好的,基本的想法。 ideone.com/0f7Tk 的程序说明了 a+1 和 &a + 1 的差异。
  • 不,它不会默默地丢弃。这是旧的 K&R C,函数的最后一条语句指定了它的返回值。
  • @p2pnode:表达式a&a 将具有相同的值(数组第一个元素的地址),但表达式的类型是不同的——分别是int *int (*)[50]
【解决方案2】:

这个程序没有可观察到的结果。它已成功编译,但没有任何可观察到的行为。

&a 是一个表达式。因为a是数组类型的对象,所以&a是数组的地址(它的值可能与&a[0]一致,但它的类型不-int* vs int(*)[50])。任何后跟分号的表达式都是表达式语句。函数体是包含在{} 中的零个或多个语句的序列,因此这是一个合法的main() 函数。 (当你写a[0] = 10;时,这也是一个表达式语句。)

在 C++ 标准的 as-if 规则下,实现甚至可能决定不为您的数组分配任何内存,并且什么也不做,因为即使这样做了,您也没有任何方法可以检查它(至少从 C++ 的角度来看)。

以下也是一个有效的C++程序

int main()
{
   1; 2; 3;
}

【讨论】:

    【解决方案3】:

    面试官显然应该得到新的测试材料:)这个……

    main()    
    {
        int a[50];
        &a;
    }
    

    ... 是旧的 K&R C。如果未声明,否则函数将返回 int,并且函数的最后一条语句将隐式返回。您可以将其重写为 ANSI C:

    int main()    
    {
        int a[50];
        return &a;
    }
    

    让我们来剖析一下这个程序:


    int main()    
    

    定义一个函数main,返回一个int,接受任意参数。


    {
        int a[50];
    

    在自动存储中定义一个包含 50 个 int 的数组,并将 一个 int 指针 (int*) 分配给该数组的第一个元素 到文字 a 的变量。


        return (intptr_t)&a;
    }
    

    现在这有点棘手。只需写入 a 即可计算出 数组的第一个元素的地址& 运算符获取存储此特定指针的 变量的地址。在某处分配了 50 个 int 并且此变量当前指向那里并不重要。我们采用的是指向指针的指针,即int 指针指针(int**)。数组,同样是它的第一个元素。

    然而,K&R C 定义了指针类型到整数的隐式转换。此指针的值唯一标识原始指针,并且必须适合指针运算。大多数编译器通过简单地将指针的逐字地址存储在整数中来实现这一点。重要的例外:任何特殊的机器相关的 nil 指针值必须映射到 C 端的 0 并且 0 转换为指针的 intintptr_t 必须映射到架构 nil 值(在大多数架构上,机器 nil 值是0)。

    main 返回指针的整数表示,在大多数系统上它的内存地址,根据 C 标准的定义,它指定它的退出代码。


    编译这个程序

    cc -o interview_question interview_question.c
    

    您可以使用

    执行并显示其退出代码
    ./interview_question ; echo $?
    

    这将是一些任意的,但不是随机数。

    因@John Bodes 的评论和更正而进行编辑。

    【讨论】:

    • Quib​​bles:a 是一个数组,而不是一个指针;在大多数情况下,它将其类型隐式转换为指针。其次&a的类型是int (*)[50],而不是int **
    • 这不一定是 K&R C。如果我使用 gcc(作为 .c 文件)编译并运行,我会得到一个垃圾返回码。 (编译器将其视为旧的 K&R。)但是,如果我使用 g++(作为 .cpp 文件)编译并运行,我会得到一个零返回码。最后的 &a 掉在地上,程序返回 0(参见 ISO/IEC 14882:2003 3.6.1 ¶ 5)。
    • @David Hammen:是的,因为 C++ 明确要求在 return 语句中声明的函数返回值。但令我惊讶的是,C++ 实际上会编译所有 OP 的代码(OTOH,我不知道 C++ 标准的细节,因为我知道 C;而且我的 C 知识并不完整,我还在学习 - 见我的第一个答案版本,约翰的评论并与我更改答案的方式进行比较)。
    【解决方案4】:

    您可以将表达式用作语句,它是完全有效的。它不会做任何事情。

    【讨论】:

      【解决方案5】:

      按照我的感觉,这段代码应该可以成功编译和运行。 a 是一个数组, &a 只不过是第一个索引的地址(与 a 相同)。所以 &a 是一个不做任何事情的表达式...... 它与:

      int x; &x;

      不会有任何输出,因为表达式 2 什么都不做。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2012-03-11
        • 1970-01-01
        • 1970-01-01
        • 2012-10-16
        • 1970-01-01
        • 2018-12-05
        • 1970-01-01
        相关资源
        最近更新 更多