自动和静态变量的范围仅限于定义它们的块。由于 Auto 变量是在堆栈中定义的,如果函数退出,堆栈将被销毁并释放 auto 变量的内存。但是我在某处读到“但是,它们也可以在其范围之外访问,也可以使用此处给出的指针概念,通过指向变量所在的非常精确的内存位置来访问它们。” 这个对吗?
此外,静态变量在数据部分中定义,因此它在程序结束之前一直存在。范围在定义它的块内。有什么方法可以让我们从任何其他函数访问静态变量?另外,有什么方法可以从任何其他文件访问静态变量?
自动和静态变量的范围仅限于定义它们的块。由于 Auto 变量是在堆栈中定义的,如果函数退出,堆栈将被销毁并释放 auto 变量的内存。但是我在某处读到“但是,它们也可以在其范围之外访问,也可以使用此处给出的指针概念,通过指向变量所在的非常精确的内存位置来访问它们。” 这个对吗?
此外,静态变量在数据部分中定义,因此它在程序结束之前一直存在。范围在定义它的块内。有什么方法可以让我们从任何其他函数访问静态变量?另外,有什么方法可以从任何其他文件访问静态变量?
这是一个非常简单的例子:
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
显然已经获得了它的价值。
不要将“范围”与“生命周期”混淆。变量的范围是程序中变量名称可见(因此可以使用)的部分。值的生命周期是程序执行期间值存在的时间段。范围是关于程序文本;它与编译有关。生命周期与程序执行有关。
正如您所说,静态变量存在于程序的整个生命周期中,即只要程序正在运行,分配给它们的内存就不会被破坏。因此,要在其范围之外访问这样的变量,我们可以通过指针传递指向该内存位置的指针。一个显示相同的小例子
#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()
返回指向它声明的静态变量的指针,任何希望访问该变量a
的人都可以使用该指针。注意:我们只能这样做,因为静态变量的生命是贯穿整个程序的。现在,不管静态变量在不同的函数或不同的文件中,只要你能掌握指向该静态变量的指针,你就可以使用它。
现在来看自动变量的情况。
如果您运行上述程序a
从更改static
为会发生auto
什么?你会看到在编译warning: function returns address of local variable [-Wreturn-local-addr]
时抛出警告,在执行时,我们得到一个segmentation fault
. 造成这种情况的原因是 auto 变量只存在于它的作用域中,即只要函数func()
正在执行,变量就会a
为自己分配内存。一旦函数退出,分配给变量的内存a
就会被释放,因此指针指向的值p
位于某个未分配的内存位置(导致分段错误)。
请注意,正如评论正确指出的那样,我在这里做一个假设,假设调用另一个函数的最简单情况不是问题所在。OP尚未(尚未)确认或拒绝此假设。例如,在 rici 的回答中讨论了这种情况。
自动变量的存在不仅存在于它们的“范围内”(简化:只有同一封闭之间的代码{}
可以使用它们的标识符),它们也被限制在“期间”它们的“时间范围”,即它们的生命周期(简化后开始函数中代码的执行并完成其执行)。可以通过指针访问变量的内存位置,该指针设置为它们的地址(这只能在它们的范围内,因为通过它们的标识符访问是必要的),只要在它们的生命周期内完成,是的。
但是如何从其他任何地方找到该指针?
也许通过被写入(从他们的范围内和在他们的生命周期内)到一个全局变量。
但是哪个“其他”代码应该使用该值?(记住我把函数的调用放在一边)
这需要多线程/多任务/multiwhatevering。可以说有一个中断服务程序在做这件事。它必须看到与变量范围相同的地址空间,即没有内存管理单元妨碍一些虚拟内存魔法。对于许多多方面的实现来说并非如此,但对于其中的一些实现来说,这是不可否认的,所以让我们继续吧。
这个想象中的 ISR 必须确保它只在 auto 变量实际存在时(即在其生命周期内)访问它,否则它几乎会访问实际上是无意义的随机内存位置。这假设 ISR 实际上被允许/能够访问该内存。即使没有 MMU,也有可能/将会有异常的实现。
这引入了对同步机制的需求,例如信号量。
所以在某些环境中它是可能的,但完全没有意义(仍然涉及全局变量)、昂贵、难以理解并且几乎不可能移植。(记住我在这里把一个函数的调用放在一边)
静态变量类似。
在函数局部静态变量的情况下,它们至少会可靠地存在,但是访问它们仍然需要指针值以某种方式传输到它们的范围之外。对于实际上可以通过函数的返回值完成的静态变量,如 yashC 的答案所示。
在被理解为文件范围受限变量的“静态”变量的情况下,指针仍然必须被传输到文件范围之外。
这只会破坏文件范围受限变量的意义所在。但我可以想象某种访问权限方案,例如“这是保险库的钥匙。小心处理”。
如本答案开头所述,我将其他功能的调用放在一边。是的,离开函数范围的最简单方法是调用另一个函数。如果那个其他函数有一个指针参数,它可以用它来读访问和写访问调用函数的自动变量。这是 C 支持的按引用调用参数的正常情况。
调用函数还提供了另一种更简单的方法来读取调用函数的自动变量的值,尽管不是写访问,也不是实际访问自动变量本身,只使用它的值。这种方式是按值调用参数的简单机制,它甚至不需要指针。两种方式(按引用调用参数和按值调用参数)都可以方便地保证在被调用函数执行期间值不会改变。(这次我将多线程案例放在一边,因为这在本答案的主要部分中进行了讨论)。