"C" 语言功能的特殊性

Peculiarity in functions of "C" Language

因为我是 C 语言的新手,所以我是从 tutorials point 学习它的,当时我遇到了 functions 的一个特殊之处,这让我在理解它的时候头疼不已。

我了解到,如果变量是在 function 中定义的,那么 scope 是该函数的局部变量,并且无论何时控制权从函数转移,变量都不再可用和清理。

但它不是我创建的情况,下面的程序仍在打印 test 函数中定义的变量 p 的值,因此当从 test 控制时功能转移了为什么我仍然能够获得它的价值?它仍然在屏幕上打印变量 p 的值 80 为什么?

#include <stdio.h>

int *test();

int main()
{
    int *ab
    ab = test();
    printf("%d\n",*ab);
    return 0;
}

int *test()
{
    int p = 80;
    return (&p);
}


请帮我理解这个特性。

您遇到的是 undefined behavior 的症状。

变量的地址在其生命周期结束后不再有效。当您尝试取消引用该地址时,您可能会看到之前的值,或者您可能会看到其他内容。

尝试在 main 中复制 printf 调用,您可能会看到不同的值。

最有可能发生的情况是,在第一次调用 printf 之前,包含 p 的内存区域尚未被覆盖。然后,当实际调用 printf 时,该函数的堆栈帧使用的内存与 test 的堆栈帧使用的内存相同,并且 p 之前使用的内存现在被覆盖。因此,当您在调用 printf 后取消引用 main 中的 ab 时,您会看到该函数放置在那里的任何值。

虽然这个变量不应该再“存在”了,但是电脑有点懒,不会删除某个地址的值

这就是您可以操纵内存的方式,如果您尝试查看不属于您的程序的内存,您将能够看到一些奇怪的值,因为即使它们不在内存中,它们也会保留在内存中没用了

访问 out-of-scope 变量是 Undefined Behaviour ,因此,根据定义,随着变量的变化(编译器标志、OS 等),结果可能会无限且不可预测地变化。 =11=]

您的变量确实超出了范围,但这仅意味着它不再 'reserved'。但是,与可能引发 compile-time 错误的较新语言不同,在 C 中,这只是一个警告,如果您 明确地 要求编​​译器给您 额外警告.

因此代码 编译。

在这里,变量超出了范围,但可以推测,不会再使用内存,因此 测试地址的值保持不变。如果更多变量是 declared/initialised/used ,那么该地址可能会被 覆盖 为另一个值,并且您会打印一些意想不到的东西 - 真实的,即使这个结果是意想不到的,因此你的问题!

要记住的是 - C 中的变量就像大厅里的椅子(类似于内存)。椅子的位置,椅子的数量都是static/fixed。能改变的是谁坐在什么椅子上

所以,如果你让一个朋友坐在一张方便的椅子上,5 分钟后告诉他不再需要他,那么他是呆在那里还是站起来,有人取代他的位置是你不看就无法预测的并且在 out-of-scope 地址读取同样是未定义的,因为它无法事先预测!

其他类比可能是车位,ship-containers,等等

不同之处在于地址不会被覆盖/'deleted'直到出现其他需要存储的东西(这也是磁盘管理'deletion' 实际上只是 un-reserving 硬件位置).

当你考虑它时,从效率的角度来看它很有意义 - 你不再需要浪费时间做无用的 'overwrite with 0' 或其他什么,而是 only 当你有一个真正的价值要存储到内存时写入!

(1 次操作而不是 2 次,工作量减半。)

下面是一个示例,可以说明您正在查看的 UB:

#include <stdio.h>
#include <time.h>
#include <stdlib.h>

int *test();
void run_random();

int main()
{
    srand(time(NULL));

    int *ab;
    int i=0, N = 5;
    for (i=0; i<N; ++i){
        ab = test();
        run_random();
        printf("%d\n",*ab);
    }
    return 0;
}

int *test()
{
    int p = 80;
    return (&p);
}

void run_random()
{
    int i=0, N=1000;
    for (i=0; i<N; ++i){
        int rvar = rand();
    }
}

在您的代码中,来自 test() 的变量恰好在堆栈内存中仍然具有预期值。这是我在 re-running 循环中的函数时看到的 - *ab 仍然等于 80,如果你注释掉 run_random() 调用,你也可以看到它。

但是当我添加第二个调用时,对 run_random() 的调用 - 我开始让 ab 等于 run_random() 中的 N,即 1000,作为那个记忆location 现在填充了一个不同的变量值(OS 一开始就可以免费使用它,一旦 test() 停止执行)。

在您的情况下,您最终可能会看到其他内容,当然,甚至可能仍然是预期值。这就是UB的意义所在,你真的不知道pop-up.