以下代码的输出为 0。
int account=2;
int main()
{
static int account;
printf("%d",account);
return 0;
}
为什么它选择静态变量而不是全局变量?因为我的理解是全局变量和静态变量都存储在堆中而不是函数堆栈中,对吧?那么它使用什么方法来选择一个而不是另一个呢?
以下代码的输出为 0。
int account=2;
int main()
{
static int account;
printf("%d",account);
return 0;
}
为什么它选择静态变量而不是全局变量?因为我的理解是全局变量和静态变量都存储在堆中而不是函数堆栈中,对吧?那么它使用什么方法来选择一个而不是另一个呢?
如果在多个范围内存在多个同名变量,则最内层范围内的变量是可访问的。更高范围的变量被隐藏。
在这种情况下,您已在 中account
定义main
。这隐藏了account
在文件范围内声明的命名变量。main
声明内部变量的事实static
并没有改变这一点。
虽然static
对局部变量的声明意味着它通常与全局变量存储在同一位置,但这与名称相同时可见的变量无关。
考虑这个小的自我解释程序:
#include <stdio.h>
int bar = 123; // global variable, can be accessed from other source files
static int blark; // global variable, but can be accessed only in the same
// source file
void foo()
{
static int bar; // static variable : will retain it's value from
// one call of foo to the next
// most compilers will warn here:
// warning declaration of 'bar' hides global declaration
printf("foo() : bar = %d\n", bar); // won't use the global bar but the
// local static bar
bar++;
}
void quork()
{
int bar = 777; // local variable exists only during the execution of quork
// most compilers will warn here as well:
// warning declaration of 'bar' hides global declaration
printf("quork() : bar = %d\n", bar); // won't use the global bar but the
// local bar
bar++;
}
int main() {
foo();
foo();
printf("main() 1 : bar = %d\n", bar);
bar++;
quork();
quork();
foo();
printf("main() 2 : bar = %d\n", bar);
printf("blark = %d\n", blark);
}
输出:
foo() : bar = 0
foo() : bar = 1
main() 1 : bar = 123
quork() : bar = 777
quork() : bar = 777
foo() : bar = 2
main() 2 : bar = 124
blark = 0
只是为了向未来的读者澄清,全局和静态变量不存储在堆或堆栈内存中。 https://www.geeksforgeeks.org/memory-layout-of-c-program/
它们将存储在初始化数据或未初始化数据中。
这不是这里的主要问题,由 dbush 回答,但这是原始问题中的一个误解。
简短的回答:封装。
static
描述变量的生命周期和可见性,其含义根据上下文而变化。我的观点是,它是 c 中封装的更有用和重要的语言特性之一。忽略与 的复杂关系extern
,这里有一个简化的描述:
static
在文件级别定义的变量具有程序生命周期和编译单元可见性。这意味着 .c 文件中的所有函数都可以访问/修改变量,但其他 .c 文件不会知道该变量。这对于确保跨编译单元的函数使用的变量不会意外链接到其他编译单元中的变量非常有用。就个人而言,我强烈建议所有文件变量默认为静态。static
仅当您确实希望另一个编译单元可以访问它时才删除说明符(尽管 getter 函数可能更安全)
static
在块作用域(最重要的是函数作用域)内声明的变量具有程序生命周期和作用域可见性。这意味着它的功能就像您在文件中全局声明变量一样,但只有该块范围内的代码才能看到它。这也意味着从一个调用到下一个调用,变量不会被破坏,并且状态可以从一个调用转移到另一个调用。
静态变量的一个真正重要的区别是它们默认初始化为零。这与 c 中的所有其他变量不同,这也是您的程序打印值 0 的原因。通常,对于琐碎的程序,我们不会注意到差异,因为堆栈还没有被变量污染,但它对于任何程序都变得至关重要程序的大小。
我见过的最常见的用途是在一个范围内启用一次性初始化。它们对于同步原语(如pthread_mutex_t
. 有一次我什至实现了一个带有函数范围静态变量的状态机。
一个例子:
int started;//oops, anybody in the entire program can change this value, especially with such a common name!
static int lastCall;
int callCount(void)
{
// This is default-initialized to 0
static int functionStaticVariable;
//Increment each time I'm called
++functionStaticVariable;
//tell the outside world that I'm the one who was called last
lastCall = 1;
//return (a copy of) my internal state.
return functionStaticVariable;
}
char *getSharedMemory(unsigned int bytes)
{
// Here I cannot see functionStaticVariable, but I can see globalVariable
//functionStaticVariable++; // this would cause a compilation failure
// static pointer is default-initialized to zero (i.e. NULL)
static char *sharedMemory;
if(sharedMemory == 0)
{
// This block only executes once, the first time the function is called.
// Actually this is a nice side-effect because it means if the function is never called we don't clutter the stack with unused memory
// Although we will probably never free this memory
sharedMemory = (char *)malloc(bytes);
}
// tell the outside world that this function has been called
lastCall = 2;//valid
//Woah, this is such a bad idea, but actually does _not_ return memory that gets invalidated
return sharedMemory;
}
希望您可以通过这种模式看到您可以通过将变量放在函数中并执行可选操作(例如获取互斥锁以分配内存)来保护变量。您甚至可以通过这种方式实现双锁模式。
我暗暗希望所有 C++ 程序员都学会了良好的 c 封装,因为实际上该语言确实鼓励它。您可以通过仅将需要相互通信的函数放在一个编译单元中来完成令人难以置信的数量。在非 OOP 语言中,这可能非常强大。
https://en.cppreference.com/w/c/language/storage_duration描述了静态和外部的完整细节。
为什么应该使用最里面的变量声明背后的务实推理:您并不总是可以控制代码之外的内容。您希望能够编写一个确实有效的函数。如果其他程序员(例如,在一个更大的团队中)可以仅仅通过他们在代码的其他部分中命名变量的方式来破坏您的代码,那么编程将比现在更加痛苦。