我们如何在 C 的作用域之外访问 auto 和 static 变量?
How we can access auto and static variables outside their scope in C?
自动变量和静态变量的范围仅限于定义它们的块。由于 Auto 变量是在堆栈中定义的,如果函数退出,堆栈将被销毁并释放 auto 变量的内存。但我在某处读到 "However, they can be accessed outside their scope as well using the concept of pointers given here by pointing to the very exact memory location where the variables reside." 这是正确的吗?
此外,静态变量是在数据部分定义的,因此它会一直存在到程序结束。范围在定义它的块内。有什么方法可以让我们从任何其他函数访问静态变量吗?另外,有什么方法可以从任何其他文件访问静态变量吗?
请注意,正如评论正确指出的那样,我在这里做了一个假设,假设调用另一个函数的最简单情况不是问题所在。该假设尚未(尚未)被 OP 确认或拒绝。例如讨论了这种情况。在 rici 的回答中。
auto变量的存在不仅存在于"within"它们的范围内(简化:只有相同包围{}
之间的代码可以使用它们的标识符),它们也被限制在"during" 它们的 "chronological scope" 即它们的生命周期(在开始执行函数中的代码并完成其执行后简化)。可以通过指针访问变量的内存位置,该指针被设置为它们的地址(这只可能在它们的范围内,因为通过它们的标识符进行访问是必要的)只要它在它们的生命周期内完成,是的。
但是如何从其他地方找到该指针?
也许通过(从它们的范围内和在它们的生命周期内)被写入一个全局变量。
但是哪个 "other" 代码应该使用那个值? (记住我把函数的调用放在一边了)
这需要 multithreading/multitasking/multiwhatevering。可以说有一个中断服务程序在做这件事。它必须看到与变量范围相同的地址 space,即没有内存管理单元妨碍一些虚拟内存魔法。对于许多多线程实现而言,情况并非如此,但不可否认,对于其中的一些实现,让我们继续。
这个想象中的 ISR 必须确保它只在 auto 变量实际存在时(即在其生命周期内)访问它,否则它几乎会访问实际上是无意义的随机内存位置。这假设 ISR 实际上是 allowed/able 来访问该内存。即使没有 MMU,也有 can/will 有异常的实现。
这引入了对同步机制的需求,例如信号量。
所以在某些环境中这是可能的,但完全没有意义(仍然涉及全局变量),昂贵,难以理解并且几乎不可能移植。 (记住我在这里把函数的调用放在一边)
与静态变量类似。
在函数局部静态变量的情况下,它们至少会可靠地存在,但访问它们仍然需要指针值以某种方式传输到它们的范围之外。对于实际上可以通过函数的 return 值完成的静态变量,如 yashC 的答案所示。
在 "static" 变量被理解为文件范围限制变量的情况下,指针仍然必须被传输到文件范围之外。
这只会破坏文件范围受限变量的意义。但我可以想象某种访问权限方案,如 "Here is the key to the vault. Handle with care."
正如本回答开头所述,我将其他功能的调用放在一边。是的,离开函数范围的最简单方法是调用另一个函数。如果那个其他函数有一个指针参数,它可以使用它来读访问和写访问调用函数的自动变量。这是 C 支持的按引用调用参数的正常情况。
调用函数还提供了另一种更简单的方法来读取访问调用函数的自动变量的值,但不是写访问,也不是实际访问自动变量本身,只是使用它的值。这种方式是按值调用参数的简单机制,它甚至不需要指针。这两种方式(按引用调用参数和按值调用参数)都方便地保证了被调用函数执行期间值不会改变。 (这次我把多线程的情况放在一边,因为这个答案的主要部分已经讨论过了)。
如您所说,静态变量存在于程序的整个生命周期中,即只要程序 运行ning,分配给它们的内存就不会被破坏。因此,要在其范围之外访问这样的变量,我们可以通过指针将指针传递到该内存位置。一个小例子来展示相同的
#include <stdio.h>
#include <stdlib.h>
int* func()
{
static int a = 0;
a++;
printf("a in func = %d\n", a);
return &a;
}
int main()
{
int *p;
p = func();
printf("a in main from ptr : %d\n", *p);
*p++;
p = func();
return 0;
}
正如您在示例中看到的,func()
returns 指向已声明的静态变量的指针,任何希望访问变量 a
的人都可以使用那个指针。注意:我们只能这样做,因为静态变量的生命贯穿整个程序。现在不管静态变量在不同的函数或不同的文件中,只要你能找到指向那个静态变量的指针,你就可以使用它。
现在来谈谈自动变量的情况。
如果您 运行 上面的程序将 a
从 static
更改为 auto
会怎样?
您会看到在编译时抛出警告 warning: function returns address of local variable [-Wreturn-local-addr]
,而在执行时,我们得到 segmentation fault
。
造成这种情况的原因是 auto 变量仅存在于其范围内,即只要函数 func()
正在执行,变量 a
就会为自己分配内存。一旦函数退出,分配给变量 a
的内存就会被释放,因此指针 p
指向的值位于某个未分配的内存位置(导致分段错误)。
这是一个非常简单的例子:
void print_msg(const char* msg) {
printf("The message is: %s\n", msg);
}
int main(void) {
char m[] = "Hello, world!";
print_msg(m);
}
这里,m
是一个自动变量,不在print_msg
的范围内。但是 print_msg
显然可以访问它的值。
不要混淆 "scope" 和 "lifetime"。变量的范围是变量名称可见(因此可以使用)的程序部分。值的生命周期是程序执行期间值存在的时间段。范围是关于程序文本;它与编译有关。生命周期与程序执行有关。
自动变量和静态变量的范围仅限于定义它们的块。由于 Auto 变量是在堆栈中定义的,如果函数退出,堆栈将被销毁并释放 auto 变量的内存。但我在某处读到 "However, they can be accessed outside their scope as well using the concept of pointers given here by pointing to the very exact memory location where the variables reside." 这是正确的吗?
此外,静态变量是在数据部分定义的,因此它会一直存在到程序结束。范围在定义它的块内。有什么方法可以让我们从任何其他函数访问静态变量吗?另外,有什么方法可以从任何其他文件访问静态变量吗?
请注意,正如评论正确指出的那样,我在这里做了一个假设,假设调用另一个函数的最简单情况不是问题所在。该假设尚未(尚未)被 OP 确认或拒绝。例如讨论了这种情况。在 rici 的回答中。
auto变量的存在不仅存在于"within"它们的范围内(简化:只有相同包围{}
之间的代码可以使用它们的标识符),它们也被限制在"during" 它们的 "chronological scope" 即它们的生命周期(在开始执行函数中的代码并完成其执行后简化)。可以通过指针访问变量的内存位置,该指针被设置为它们的地址(这只可能在它们的范围内,因为通过它们的标识符进行访问是必要的)只要它在它们的生命周期内完成,是的。
但是如何从其他地方找到该指针?
也许通过(从它们的范围内和在它们的生命周期内)被写入一个全局变量。
但是哪个 "other" 代码应该使用那个值? (记住我把函数的调用放在一边了)
这需要 multithreading/multitasking/multiwhatevering。可以说有一个中断服务程序在做这件事。它必须看到与变量范围相同的地址 space,即没有内存管理单元妨碍一些虚拟内存魔法。对于许多多线程实现而言,情况并非如此,但不可否认,对于其中的一些实现,让我们继续。
这个想象中的 ISR 必须确保它只在 auto 变量实际存在时(即在其生命周期内)访问它,否则它几乎会访问实际上是无意义的随机内存位置。这假设 ISR 实际上是 allowed/able 来访问该内存。即使没有 MMU,也有 can/will 有异常的实现。
这引入了对同步机制的需求,例如信号量。
所以在某些环境中这是可能的,但完全没有意义(仍然涉及全局变量),昂贵,难以理解并且几乎不可能移植。 (记住我在这里把函数的调用放在一边)
与静态变量类似。
在函数局部静态变量的情况下,它们至少会可靠地存在,但访问它们仍然需要指针值以某种方式传输到它们的范围之外。对于实际上可以通过函数的 return 值完成的静态变量,如 yashC 的答案所示。
在 "static" 变量被理解为文件范围限制变量的情况下,指针仍然必须被传输到文件范围之外。
这只会破坏文件范围受限变量的意义。但我可以想象某种访问权限方案,如 "Here is the key to the vault. Handle with care."
正如本回答开头所述,我将其他功能的调用放在一边。是的,离开函数范围的最简单方法是调用另一个函数。如果那个其他函数有一个指针参数,它可以使用它来读访问和写访问调用函数的自动变量。这是 C 支持的按引用调用参数的正常情况。
调用函数还提供了另一种更简单的方法来读取访问调用函数的自动变量的值,但不是写访问,也不是实际访问自动变量本身,只是使用它的值。这种方式是按值调用参数的简单机制,它甚至不需要指针。这两种方式(按引用调用参数和按值调用参数)都方便地保证了被调用函数执行期间值不会改变。 (这次我把多线程的情况放在一边,因为这个答案的主要部分已经讨论过了)。
如您所说,静态变量存在于程序的整个生命周期中,即只要程序 运行ning,分配给它们的内存就不会被破坏。因此,要在其范围之外访问这样的变量,我们可以通过指针将指针传递到该内存位置。一个小例子来展示相同的
#include <stdio.h>
#include <stdlib.h>
int* func()
{
static int a = 0;
a++;
printf("a in func = %d\n", a);
return &a;
}
int main()
{
int *p;
p = func();
printf("a in main from ptr : %d\n", *p);
*p++;
p = func();
return 0;
}
正如您在示例中看到的,func()
returns 指向已声明的静态变量的指针,任何希望访问变量 a
的人都可以使用那个指针。注意:我们只能这样做,因为静态变量的生命贯穿整个程序。现在不管静态变量在不同的函数或不同的文件中,只要你能找到指向那个静态变量的指针,你就可以使用它。
现在来谈谈自动变量的情况。
如果您 运行 上面的程序将 a
从 static
更改为 auto
会怎样?
您会看到在编译时抛出警告 warning: function returns address of local variable [-Wreturn-local-addr]
,而在执行时,我们得到 segmentation fault
。
造成这种情况的原因是 auto 变量仅存在于其范围内,即只要函数 func()
正在执行,变量 a
就会为自己分配内存。一旦函数退出,分配给变量 a
的内存就会被释放,因此指针 p
指向的值位于某个未分配的内存位置(导致分段错误)。
这是一个非常简单的例子:
void print_msg(const char* msg) {
printf("The message is: %s\n", msg);
}
int main(void) {
char m[] = "Hello, world!";
print_msg(m);
}
这里,m
是一个自动变量,不在print_msg
的范围内。但是 print_msg
显然可以访问它的值。
不要混淆 "scope" 和 "lifetime"。变量的范围是变量名称可见(因此可以使用)的程序部分。值的生命周期是程序执行期间值存在的时间段。范围是关于程序文本;它与编译有关。生命周期与程序执行有关。