我了解如何使用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
的堆栈帧中的数据?
答案 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
对该地址处的值进行更改。
您可能还想知道为什么允许这样做。难道不应该对内存进行保护,以便没有任何东西可以改变它吗?这也是事实。但是,在您提供的示例中,main
和scanf
由同一进程调用。是的,在各个进程之间,将围绕可触摸的内存提供更多保护。在继续使用C的过程中,您会遇到分段错误,这是程序崩溃的原因,因为发生了不允许的内存访问。
在上面的示例中,您正在运行一个进程,因此在该进程中执行的任何代码行都可以访问该进程的内存。
此指针资源可帮助您:https://users.cs.cf.ac.uk/Dave.Marshall/C/node10.html。