为什么这个C程序在VC++;2008?

为什么这个C程序在VC++;2008?,c,visual-c++,C,Visual C++,我们知道自动变量在函数返回时被销毁 那么,为什么这个C程序返回正确的值呢 #include <stdio.h> #include <process.h> int * ReturningPointer() { int myInteger = 99; int * ptrToMyInteger = &myInteger; return ptrToMyInteger; } main() { int * pointerToIntege

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

那么,为什么这个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");
}
编辑 那为什么这会给出垃圾值呢

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

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

    return array;
}

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

    system("PAUSE");
}

这个问题没有答案:您的代码表现出未定义的行为。它可以像你看到的那样打印“正确的价值”,它可以打印任何其他东西,它可以出错,它可以用你的信用卡在线订购比萨饼

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

两个示例之间有很大的区别:在第一种情况下,
*指针
在调用
printf
之前进行计算。因此,假设在获取指针值的行与
printf
之间没有函数调用,那么堆栈位置
指针
指向的对象很可能不会被覆盖。因此,在调用
printf
之前存储在那里的值很可能会被输出(该值将传递到
printf
的堆栈,而不是指针)

在第二种情况下,将指向堆栈的指针传递到
printf
。调用
printf
会覆盖指针指向的同一堆栈区域(其中的一部分),并且
printf
最终会尝试打印自己的堆栈(或多或少),该堆栈不太可能包含可读的内容


请注意,你也不能依靠胡言乱语。您的实现可以自由地为
printf
调用使用不同的堆栈,只要它符合标准规定的要求。

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

想想看,这有点道理——你还指望什么?它应该给你零吗?如果是这样,那么编译器必须在作用域端插入特殊指令来删除变量的内容——这是资源的浪费。编译器要做的最自然的事情是保持内容不变——因此您只是碰巧从未定义的行为中获得了正确的输出

您可以说这种行为是实现定义的。例如另一个编译器(或处于“Release”模式的同一个编译器)可能决定纯粹在寄存器中分配
myInteger
(不确定当您获取它的地址时,它是否真的可以这样做,但为了参数的缘故…),在这种情况下,将不会为
99
分配内存,并且您将得到垃圾输出

作为一个更具说明性(但完全未经测试)的示例,如果您在
printf
之前插入一些
malloc
并使用一些内存,您可能会找到您要查找的垃圾值:p

对“已编辑”部分的答复
您想要的“真实”答案需要在反汇编中得到回答。一个好的起点是
gcc-S
gcc-O3-S
。我将把深入分析留给以后的向导。但是我用GCC粗略地看了一下,结果是
printf(“%s\n”)
被翻译成
put
,所以调用约定不同。由于局部变量是在堆栈上分配的,因此调用函数可能会“销毁”以前分配的局部变量。

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

当然,这样的代码是有麻烦的,因为按照C和C++标准,它表现出未定义的行为。
  • 毁灭是个错误的词。局部变量驻留在堆栈上,如果函数返回,堆栈空间可以再次重用。在此之前,它不会被覆盖,并且仍然可以被您可能并不真正想要的指针访问(因为这可能永远不会指向有效的东西)

  • 指针用于寻址内存中的空间,对于本地指针,与我在1中描述的相同是有效的。然而,指针似乎被传递到主程序

  • 如果它真的是存储前一个整数的地址,则在程序覆盖此内存位置时,在执行程序的那一点之前,它将导致“99”。这也可能是另一个99个巧合。无论如何,不要这样做


  • 这些类型的错误有一天会导致麻烦,可能在其他机器、其他操作系统、其他编译器或编译器选项上-想象一下您升级了编译器,这可能会改变内存使用的行为,甚至是带有优化标志的生成,例如发布生成与默认调试生成,你说得对。

    那是
    未定义的行为
    。这意味着任何事情都可能发生,甚至是你所期望的

    UB最棘手的部分是当它给了你你期望的结果,所以你认为你做得对。然后,程序中不相关部分的任何更改都会改变

    更具体地回答你的问题,你正在返回一个指向自动变量的指针,当函数返回时,它不再存在,但是因为你在中间没有调用其他函数,所以它保留了旧的值。


    例如,如果调用两次
    printf
    ,第二次很可能会打印不同的值。

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


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

    这并不能回答我的问题。我没有使用这个代码。我
    #include <stdio.h>
    #include <process.h>
    
    char * ReturningPointer()
    {
        char array[13] = "Hello World!";
    
        return array;
    }
    
    main()
    {
        printf("%s\n", ReturningPointer());
    
        system("PAUSE");
    }
    
    xŤ