scanf()如何将数据写入其他函数的堆栈帧?

时间:2018-07-31 18:58:00

标签: c memory

我了解如何使用scanf(),但是我很难理解scanf()如何获得对其他功能及其堆栈框架的访问权限。我知道必须为scanf()提供一个向其写入数据的地址,但是我认为每个函数在其自己的堆栈帧中都有自己的私有数据。 例如,以简单的程序段为例

#include <stdio.h>
int main() {
 int x;
 x = 2;
 scanf("%d",&x);
 printf("%d",x);
}

在此程序中,main有自己的堆栈框架,其中本地变量x初始化为2。我的理解是scanf()也有自己的堆栈框架。我知道scanf()的地址为x,但是如何允许scanf()更改main的堆栈帧中的数据?

3 个答案:

答案 0 :(得分:3)

  

在此程序中,main具有自己的堆栈框架,其中局部变量x初始化为2。

正确。

  

我的理解是scanf()也有自己的堆栈框架。

正确。

  

Scanf()被赋予x的地址,但是scanf()如何被允许在main的堆栈帧中更改数据?

由谁或什么允许?没有什么可以阻止它这样做的。它有地址。它是在同一过程中。它可以简单地写入该地址。

答案 1 :(得分:2)

我将尝试给出答案。但首先...请注意,C标准没有定义堆栈。这是一个是否使用堆栈的实现细节。也就是说-几乎每个现代实现都使用堆栈来处理函数调用及其局部变量。

您是正确的,每个函数(也就是每个堆栈帧)都包含自己的局部变量,这些局部变量不能被其他函数直接更改。

让我们举个例子:

void foo(int x)
{
    x = 5;  // Change x to 5 but it does NOT change x in callee
}

int x = 42;
foo(x);

// Here x is still 42 because foo can't change the value of x in the callee

但是如果我们使用指针怎么办?

void foo(int* x)
{
    int y = 8;
    x = &y;   // Change x to point to y
}

int x = 42;
foo(&x);

// Here x is still 42 because foo can't change the address of x in the callee

因此,以上两个示例表明foo无法更改被调用方中的变量。

但是...现在您可以使用指针做什么?

void foo(int* x)
{
    *x = 8;   // Change x to be 8 (notice the * - it is the key)
}

int x = 42;
foo(&x);

// Here x is changed to 8 because foo can use the pointer to x to change its value

所以是的:被调用函数可以更改被调用方中变量的值(其堆栈框架中的变量)。但这要求您传递一个指向变量的指针。这正是scanf所期望的-指向对象的指针,以便它可以更改该对象的值。

在您的特定情况下,您将指针传递给x

scanf("%d",&x);
           ^ address of x, i.e. a pointer to x

因此,scanf可以更改被叫方中x的值。

通常,您可以说:如果将指向局部变量的指针传递给函数,则还使该函数能够更改该变量的值

这是C语言中非常重要的概念,因此在执行C程序时需要充分理解这一点。

答案 2 :(得分:1)

您在正确的轨道上。根据您对问题的表述方式,我认为您可以通过以下一些附加上下文来提高理解:1)指针如何工作,以及2)进程内存如何分离。

我不会赘述过多,但是希望这可以为您解决问题。

x函数中的变量main存储在内存中的某个地址。然后,用scanf将该地址传递到&x中。允许scanf对该地址处的值进行更改。

您可能还想知道为什么允许这样做。难道不应该对内存进行保护,以便没有任何东西可以改变它吗?这也是事实。但是,在您提供的示例中,mainscanf由同一进程调用。是的,在各个进程之间,将围绕可触摸的内存提供更多保护。在继续使用C的过程中,您会遇到分段错误,这是程序崩溃的原因,因为发生了不允许的内存访问。

在上面的示例中,您正在运行一个进程,因此在该进程中执行的任何代码行都可以访问该进程的内存。

此指针资源可帮助您:https://users.cs.cf.ac.uk/Dave.Marshall/C/node10.html