C-返回局部变量指针的函数

C-返回局部变量指针的函数,c,undefined-behavior,C,Undefined Behavior,考虑以下代码 #include<stdio.h> int *abc(); // this function returns a pointer of type int int main() { int *ptr; ptr = abc(); printf("%d", *ptr); return 0; } int *abc() { int i = 45500, *p; p = &i; return p; } #包括 i

考虑以下代码

#include<stdio.h>
int *abc(); // this function returns a pointer of type int

int main()
{
    int *ptr;
    ptr = abc();
    printf("%d", *ptr);
    return 0;
}

int *abc()
{
    int i = 45500, *p;
    p = &i;
    return p;
}
#包括
int*abc();//此函数返回int类型的指针
int main()
{
int*ptr;
ptr=abc();
printf(“%d”,*ptr);
返回0;
}
int*abc()
{
int i=45500,*p;
p=&i;
返回p;
}
输出:

45500


据我所知,这种行为是没有定义的。但是为什么我每次运行程序时都得到正确的值。

嗯,未定义的行为是,未定义的。您永远不能依赖UB(或调用UB的程序的输出)


可能,只是可能在您的环境和代码中,为局部变量分配的内存位置没有被操作系统回收,并且仍然可以访问,但是不能保证它在任何其他平台上都具有相同的行为。

嗯,未定义的行为是,未定义的。您永远不能依赖UB(或调用UB的程序的输出)


可能,只是可能在您的环境和代码中,为局部变量分配的内存位置不会被操作系统回收,并且仍然可以访问,但不能保证它在任何其他平台上都具有相同的行为。

每次调用
abc
时,它都会“标记”堆栈顶部的一个区域,用于写入所有局部变量。它通过移动指示堆栈顶部位置的指针来实现这一点。该区域称为堆栈帧。当函数返回时,它通过将堆栈指针移动到原来的位置来表示不想再使用该区域。因此,如果您随后调用其他函数,它们将重用堆栈中的该区域以用于自己的目的。但是在您的例子中,您还没有调用任何其他函数。因此堆栈的该区域保持相同的状态


以上所有内容都解释了代码的行为。并非所有C编译器都必须以这种方式实现函数,因此您不应该每次调用
abc
时都依赖于这种行为,它将堆栈顶部的一个区域“标记”为写入其所有局部变量的位置。它通过移动指示堆栈顶部位置的指针来实现这一点。该区域称为堆栈帧。当函数返回时,它通过将堆栈指针移动到原来的位置来表示不想再使用该区域。因此,如果您随后调用其他函数,它们将重用堆栈中的该区域以用于自己的目的。但是在您的例子中,您还没有调用任何其他函数。因此堆栈的该区域保持相同的状态



以上所有内容都解释了代码的行为。并非所有C编译器都必须以这种方式实现函数,因此您不应该依赖于该行为,因为未定义的行为是允许这样做的。顺便说一句,我认为这个问题没有任何问题。因为你的程序很简单。尝试在调用
abc()
printf()
之间插入对其他函数的调用,那又怎样?“Undefined”并不意味着“random”…在调用
abc()
之后和现有的
printf
之前立即添加此项:
printf(“测试字符串%d%d%d\n”,1、2、3、4)
。然后看看会发生什么:)调用abc(),然后调用def(),然后检查p指向什么。。。你可以使我静止,只要不存在重入问题。。。(例如,线程修改
*p
..),因为未定义的行为是允许这样做的。顺便说一句,我认为这个问题没有任何问题。因为你的程序很简单。尝试在调用
abc()
printf()
之间插入对其他函数的调用,那又怎样?“Undefined”并不意味着“random”…在调用
abc()
之后和现有的
printf
之前立即添加此项:
printf(“测试字符串%d%d%d\n”,1、2、3、4)
。然后看看会发生什么:)调用abc(),然后调用def(),然后检查p指向什么。。。你可以使我静止,只要不存在重入问题。。。(例如,线程修改
*p
..)UB是什么??请define@CodyUB->未定义的行为。UB是什么??请define@CodyUB->undefined behavior。这是非常具体的实现。是的,我只是为了满足他的好奇心而解释这个行为。例如,i8051的Keil C编译器:它通过在链接时构建调用树在数据段中分配局部变量,所以堆栈仅用于传递control@Serge:它如何处理递归?这是规范符合实际的地方。我和其他人讨论过这个问题。有时,无法为给定的芯片体系结构创建一致的编译器。这并不意味着几乎兼容的编译器并不比原始汇编程序好。另外,这些编译器通常是C90。这是非常具体的实现。是的,我只是为了满足他的好奇心而解释其行为。例如,适用于i8051的Keil C编译器:它通过在链接时构建调用树来分配数据段中的局部数,所以堆栈仅用于传递control@Serge:它如何处理递归?这是规范符合实际的地方。我和其他人讨论过这个问题。有时,无法为给定的芯片体系结构创建一致的编译器。这并不意味着几乎兼容的编译器并不比原始汇编程序好。此外,这些编译器通常是C90