为什么这个C程序在VC ++ 2008中返回正确的值?

时间:2011-12-10 10:21:55

标签: c visual-c++

我们知道自动变量在函数返回时被销毁。

那么,为什么这个C程序返回正确的值?

#include <stdio.h>
#include <process.h>

int * ReturningPointer()
{
    int myInteger = 99;

    int * ptrToMyInteger = &myInteger;

    return ptrToMyInteger;
}

main()
{
    int * pointerToInteger = ReturningPointer();

    printf("*pointerToInteger = %d\n", *pointerToInteger);

    system("PAUSE");
}

输出

*pointerToInteger = 99

修改

那为什么这会给出垃圾值?

#include <stdio.h>
#include <process.h>

char * ReturningPointer()
{
    char array[13] = "Hello World!";

    return array;
}

main()
{
    printf("%s\n", ReturningPointer());

    system("PAUSE");
}

输出

xŤ

6 个答案:

答案 0 :(得分:4)

该问题没有答案:您的代码表现出未定义的行为。它可以打印出“正确的价值”,如你所见,它可以打印任何其他东西,它可能是段错误,它可以用你的信用卡在线订购披萨。

取消引用main中的指针是非法的,它不会指向该点的有效内存。不要这样做。

两个示例之间存在很大差异:在第一种情况下,*pointer在调用printf之前进行评估。因此,假设在获得指针值的行与printf之间没有函数调用,则堆栈位置pointer指向的位置可能不会被覆盖。因此,可能会输出在调用printf之前存储的值(将传递到printf的堆栈,而不是指针)。< / p>

在第二种情况下,您将指向堆栈的指针传递给printf。对printf的调用会覆盖指针所指向的相同堆栈区域(的一部分),而printf最终会尝试打印自己的堆栈(或多或少),这些堆栈没有很高包含可读内容的机会。

请注意你不能依赖于乱搞。只要符合标准规定的要求,您的实现就可以免费为printf调用使用不同的堆栈。

答案 1 :(得分:2)

这是未定义的行为,它本可以发射导弹。但它恰好给你正确答案。

想一想,它有道理 - 你还期待什么?它应该给你零吗?如果是这样,那么编译器必须在作用域末端插入特殊指令以擦除变量的内容 - 浪费资源。编译器最自然的做法是保持内容不变 - 所以你只是偶然得到了未定义行为的正确输出。

您可以说此行为是实现定义的。例如。另一个编译器(或“发布”模式下的相同编译器)可能决定纯粹在寄存器中分配myInteger(不确定它是否实际上可以在获取它的地址时执行此操作,但是为了参数... 。),在这种情况下,不会为99分配任何内存,你会得到垃圾输出。

作为一个更具说明性(但完全未经测试)的示例 - 如果您插入一些malloc并在printf之前运用一些内存使用量,您可能会发现您正在寻找的垃圾值:P

回答“已编辑”部分

您需要的“真实”答案需要在反汇编中得到解答。一个好的起点是gcc -Sgcc -O3 -S。我将对将要出现的向导进行深入分析。但是我使用GCC粗略地看了一眼,结果printf("%s\n")被转换为puts,因此调用约定是不同的。由于局部变量是在堆栈上分配的,因此调用函数可以“破坏”先前分配的局部变量。

答案 2 :(得分:1)

  1. 毁灭是错误的单词imho。本地驻留在堆栈上,如果函数返回堆栈空间,则可以再次重用。在此之前它不会被覆盖,仍然可以通过你可能不想要的指针访问(因为这可能永远不会指向有效的东西)

  2. 指针用于寻址内存中的空间,因为本地指针与我在1中描述的相同是有效的。但是指针似乎传递给主程序。

  3. 如果它确实是存储前一个整数的地址,那么当程序覆盖此内存位置时,在执行程序时直到“99”为止。它也可能是巧合的另一个99。任何方式:不要这样做。

  4. 这些错误有一天会导致麻烦,可能会出现在其他机器,其他操作系统,其他编译器或编译器选项上 - 想象一下您升级编译器可能会改变内存使用行为甚至构建优化标志,例如发布版本与默认调试版本,您可以命名。

答案 3 :(得分:0)

在大多数C / C ++程序中,它们的局部变量存在于堆栈中,destroyed表示overwritten表示其他内容。在这种情况下,当特定位置作为参数传递给printf()时,该位置尚未被覆盖。

当然,拥有这样的代码会遇到麻烦,因为根据C和C ++标准,它会表现出不确定的行为。

答案 4 :(得分:0)

那是undefined behavior。这意味着任何事情都可能发生,甚至是你所期望的。

UB的棘手部分在于它能为您提供您期望的结果,因此您认为自己正在做得正确。然后,程序中不相关部分的任何变化都会改变......

更具体地回答您的问题,您正在返回一个指向自动变量的指针,该函数在函数返回时不再存在,但由于您在中间不调用其他函数,因此它会保留旧值。

如果你打电话,例如printf两次,那么第二次它很可能打印出不同的值。

答案 5 :(得分:0)

关键思想是变量表示存储在内存中的值的名称和类型。当它被“销毁”时,意味着a)不能再使用该名称访问该值,并且b)可以自由覆盖内存位置。

行为未定义,因为编译器的实现可以自由选择“破坏”后实际覆盖位置的时间。