"static" 变量的这个定义是错误的、误导性的还是两者都不是?
Is this definition of "static" variables incorrect, misleading, or neither?
根据https://www.learn-c.org/en/Static:
By default, variables are local to the scope in which they are defined. Variables can be declared as static to increase their scope up to file containing them. As a result, these variables can be accessed anywhere inside a file.
提供了两个代码示例。
首先,在函数 runner()
returns:
之后从内存中删除局部变量 int count
的示例
#include<stdio.h>
int runner() {
int count = 0;
count++;
return count;
}
int main()
{
printf("%d ", runner());
printf("%d ", runner());
return 0;
}
输出为:1 1
。
给出的下一个示例使 count
静态:
#include<stdio.h>
int runner()
{
static int count = 0;
count++;
return count;
}
int main()
{
printf("%d ", runner());
printf("%d ", runner());
return 0;
}
这次的输出是:1 2
。
count
在此示例中增加到 2,因为它没有在 runner()
的 return 处从内存中删除。
...但是,这似乎与页面开头关于在文件内的任何位置访问静态变量的声明无关。这两个示例仅表明 static
允许 count
在对 runner()
的多次调用中保留在内存中(并且每次调用都未将其设置为 0)。它们不显示 count
是否可以访问“文件中的任何地方”,因为 main()
只是打印任何内容 runner()
returns.
为了说明我的观点,我举了一个例子来说明 static
不会使 count()
在文件内的任何地方都可以访问:
#include<stdio.h>
void runner()
{
static int count = 0;
count++;
return;
}
int main()
{
runner();
printf("%d ", count);
runner();
printf("%d ", count);
return 0;
}
输出为:
prog.c: In function 'main':
prog.c:12:23: error: 'count' undeclared (first use in this function)
printf("%d ", count);
^
prog.c:12:23: note: each undeclared identifier is reported only once for each function it appears in
这是我所期望的。
我又举了一个例子,这个用了x
,runner()
和main()
都可以访问:
#include<stdio.h>
int x = 0;
void runner()
{
static int count = 0;
count++;
x = count;
return;
}
int main()
{
runner();
printf("%d ", x);
runner();
printf("%d ", x);
return 0;
}
输出为:1 2
。
这个问题顶部的引用是不正确的、具有误导性的,还是两者都不是?我误解了语义问题吗?
你是对的,引用是错误的。在函数块 static
中声明一个变量会增加它的生命周期,而不是它的作用域。 (范围是您可以使用名称的代码部分。)
我实在想不出正确的解释方式"accessed anywhere inside a file"。如果您有指向此类变量的指针,则在其他函数中取消引用该指针是有效的,但所有函数都是如此,而不仅仅是同一文件中的函数。
可能是时候停止使用该网站了。
TL;DR - 具有静态存储的变量 在程序的整个执行过程中存在 并且对其进行的任何访问都是有效的。是否可以从任何部分访问,取决于定义的范围。
因此,回答标题:我同意,它具有误导性,但与此同时,我将添加更多上下文来详细说明试图传达的实际信息。
你已经有了答案,再加上我的两分钱(使用标准中的权威词):
static
是一个存储class说明符,它有助于确定存储持续时间。
引用 C11
,章节 §6.2.4
An object has a storage duration that determines its lifetime. [....]
然后,
The lifetime of an object is the portion of program execution during which storage is
guaranteed to be reserved for it. An object exists, has a constant address,33) and retains
its last-stored value throughout its lifetime. [...]
因此,对于具有 静态存储持续时间的变量 ,
An object whose identifier is declared without the storage-class specifier
_Thread_local
, and either with external or internal linkage or with the storage-class
specifier static
, has static storage duration. Its lifetime is the entire execution of the
program and its stored value is initialized only once, prior to program startup.
废话,给我看代码:
所以,为了进行比较,让我们看一下片段:
- 代码段一:无效代码
#include<stdio.h>
char *func()
{
char arr [ ] = "Hello!";
return arr;
}
int main()
{
printf("%s ", func()); // you're accessing `arr`, some or other way!!
return 0;
}
这里返回的指针指向无效内存,随着函数调用结束,自动局部变量arr
被销毁(生命期已过),现在返回地址指向无效内存。
因此,即使您可以访问 arr
,该访问也不再有效。
- 代码段 2:有效代码
#include<stdio.h>
char *func()
{
static char arr [ ] = "Hello!";
return arr;
}
int main()
{
printf("%s ", func()); // you're accessing `arr`, some or other way!!
return 0;
}
这是一个完全有效的代码,因为它使 arr
"accessible" 超出了它的定义范围,因为 静态存储持续时间 。
引用混淆了 scope 和 lifetime。
变量的作用域描述了可以在何处通过名称访问变量。变量的 lifetime,或者更准确地说是 object,描述了对象何时有效。对于非静态局部变量,它的范围和生命周期都是它定义的块。
在您的示例中的 static
的情况下,它的范围仍然是封闭块,但它的生命周期是整个程序的生命周期。这意味着您可以 return 一个 static
变量的地址并且取消引用该地址是有效的。例如:
#include<stdio.h>
int *runner()
{
static int count = 0;
count++;
return &count; // ok, count has full program lifetime
}
int main()
{
int *p = runner();
printf("%d ", *p);
p = runner();
printf("%d ", *p);
return 0;
}
输出:
1 2
如果您尝试使用非静态变量执行此操作:
#include<stdio.h>
int *runner()
{
int count = 0;
count++;
return &count; // BAD: count no longer exists when function returns
}
int main()
{
int *p = runner();
printf("%d ", *p); // UNDEFINED BEHAVIOR
p = runner();
printf("%d ", *p); // UNDEFINED BEHAVIOR
return 0;
}
您通过使用指向不再存在的对象的指针调用 undefined behavior。
该教程非常混乱,需要从 Internet 上删除。
C 有两个不同但相关的术语:scope 和 storage duration。范围指定可以通过名称访问变量的位置。存储持续时间指定变量将保留其值的时间。
在你的前两个例子中,你根本没有改变变量的范围,你改变了它的存储持续时间。
作用域保持不变:变量具有函数的局部作用域 int runner()
,并且不能在该函数之外通过其名称访问。关键字 static
不影响范围。
但是,您更改了存储期限。所有 static
变量都有(毫不奇怪)静态存储持续时间,这意味着它们将在整个程序执行期间保持其内容有效。这就是函数在第二次调用时会记住该值的原因。
最后,为了解决对本教程的进一步误解,还有另一个术语称为 链接,它也与范围有关。具有 内部链接 的变量绝对只能在声明它们的 .c 文件中访问(以及该 .c 文件中包含的所有 .h 文件)。
而具有 外部链接 的变量可在整个程序中显式访问,使用关键字 extern
。您不能组合内部和外部链接 - 声明为 static
的所有变量都将获得内部链接。这意味着您将无法从其他 .c 文件访问它们。
通过声明一个变量static
,你给它静态存储持续时间和内部链接。
函数遵循与变量相同的范围和链接规则,除了存储持续时间对函数没有意义。
根据https://www.learn-c.org/en/Static:
By default, variables are local to the scope in which they are defined. Variables can be declared as static to increase their scope up to file containing them. As a result, these variables can be accessed anywhere inside a file.
提供了两个代码示例。
首先,在函数 runner()
returns:
int count
的示例
#include<stdio.h>
int runner() {
int count = 0;
count++;
return count;
}
int main()
{
printf("%d ", runner());
printf("%d ", runner());
return 0;
}
输出为:1 1
。
给出的下一个示例使 count
静态:
#include<stdio.h>
int runner()
{
static int count = 0;
count++;
return count;
}
int main()
{
printf("%d ", runner());
printf("%d ", runner());
return 0;
}
这次的输出是:1 2
。
count
在此示例中增加到 2,因为它没有在 runner()
的 return 处从内存中删除。
...但是,这似乎与页面开头关于在文件内的任何位置访问静态变量的声明无关。这两个示例仅表明 static
允许 count
在对 runner()
的多次调用中保留在内存中(并且每次调用都未将其设置为 0)。它们不显示 count
是否可以访问“文件中的任何地方”,因为 main()
只是打印任何内容 runner()
returns.
为了说明我的观点,我举了一个例子来说明 static
不会使 count()
在文件内的任何地方都可以访问:
#include<stdio.h>
void runner()
{
static int count = 0;
count++;
return;
}
int main()
{
runner();
printf("%d ", count);
runner();
printf("%d ", count);
return 0;
}
输出为:
prog.c: In function 'main':
prog.c:12:23: error: 'count' undeclared (first use in this function)
printf("%d ", count);
^
prog.c:12:23: note: each undeclared identifier is reported only once for each function it appears in
这是我所期望的。
我又举了一个例子,这个用了x
,runner()
和main()
都可以访问:
#include<stdio.h>
int x = 0;
void runner()
{
static int count = 0;
count++;
x = count;
return;
}
int main()
{
runner();
printf("%d ", x);
runner();
printf("%d ", x);
return 0;
}
输出为:1 2
。
这个问题顶部的引用是不正确的、具有误导性的,还是两者都不是?我误解了语义问题吗?
你是对的,引用是错误的。在函数块 static
中声明一个变量会增加它的生命周期,而不是它的作用域。 (范围是您可以使用名称的代码部分。)
我实在想不出正确的解释方式"accessed anywhere inside a file"。如果您有指向此类变量的指针,则在其他函数中取消引用该指针是有效的,但所有函数都是如此,而不仅仅是同一文件中的函数。
可能是时候停止使用该网站了。
TL;DR - 具有静态存储的变量 在程序的整个执行过程中存在 并且对其进行的任何访问都是有效的。是否可以从任何部分访问,取决于定义的范围。
因此,回答标题:我同意,它具有误导性,但与此同时,我将添加更多上下文来详细说明试图传达的实际信息。
你已经有了答案,再加上我的两分钱(使用标准中的权威词):
static
是一个存储class说明符,它有助于确定存储持续时间。
引用 C11
,章节 §6.2.4
An object has a storage duration that determines its lifetime. [....]
然后,
The lifetime of an object is the portion of program execution during which storage is guaranteed to be reserved for it. An object exists, has a constant address,33) and retains its last-stored value throughout its lifetime. [...]
因此,对于具有 静态存储持续时间的变量 ,
An object whose identifier is declared without the storage-class specifier
_Thread_local
, and either with external or internal linkage or with the storage-class specifierstatic
, has static storage duration. Its lifetime is the entire execution of the program and its stored value is initialized only once, prior to program startup.
废话,给我看代码:
所以,为了进行比较,让我们看一下片段:
- 代码段一:无效代码
#include<stdio.h>
char *func()
{
char arr [ ] = "Hello!";
return arr;
}
int main()
{
printf("%s ", func()); // you're accessing `arr`, some or other way!!
return 0;
}
这里返回的指针指向无效内存,随着函数调用结束,自动局部变量arr
被销毁(生命期已过),现在返回地址指向无效内存。
因此,即使您可以访问 arr
,该访问也不再有效。
- 代码段 2:有效代码
#include<stdio.h>
char *func()
{
static char arr [ ] = "Hello!";
return arr;
}
int main()
{
printf("%s ", func()); // you're accessing `arr`, some or other way!!
return 0;
}
这是一个完全有效的代码,因为它使 arr
"accessible" 超出了它的定义范围,因为 静态存储持续时间 。
引用混淆了 scope 和 lifetime。
变量的作用域描述了可以在何处通过名称访问变量。变量的 lifetime,或者更准确地说是 object,描述了对象何时有效。对于非静态局部变量,它的范围和生命周期都是它定义的块。
在您的示例中的 static
的情况下,它的范围仍然是封闭块,但它的生命周期是整个程序的生命周期。这意味着您可以 return 一个 static
变量的地址并且取消引用该地址是有效的。例如:
#include<stdio.h>
int *runner()
{
static int count = 0;
count++;
return &count; // ok, count has full program lifetime
}
int main()
{
int *p = runner();
printf("%d ", *p);
p = runner();
printf("%d ", *p);
return 0;
}
输出:
1 2
如果您尝试使用非静态变量执行此操作:
#include<stdio.h>
int *runner()
{
int count = 0;
count++;
return &count; // BAD: count no longer exists when function returns
}
int main()
{
int *p = runner();
printf("%d ", *p); // UNDEFINED BEHAVIOR
p = runner();
printf("%d ", *p); // UNDEFINED BEHAVIOR
return 0;
}
您通过使用指向不再存在的对象的指针调用 undefined behavior。
该教程非常混乱,需要从 Internet 上删除。
C 有两个不同但相关的术语:scope 和 storage duration。范围指定可以通过名称访问变量的位置。存储持续时间指定变量将保留其值的时间。
在你的前两个例子中,你根本没有改变变量的范围,你改变了它的存储持续时间。
作用域保持不变:变量具有函数的局部作用域 int runner()
,并且不能在该函数之外通过其名称访问。关键字 static
不影响范围。
但是,您更改了存储期限。所有 static
变量都有(毫不奇怪)静态存储持续时间,这意味着它们将在整个程序执行期间保持其内容有效。这就是函数在第二次调用时会记住该值的原因。
最后,为了解决对本教程的进一步误解,还有另一个术语称为 链接,它也与范围有关。具有 内部链接 的变量绝对只能在声明它们的 .c 文件中访问(以及该 .c 文件中包含的所有 .h 文件)。
而具有 外部链接 的变量可在整个程序中显式访问,使用关键字 extern
。您不能组合内部和外部链接 - 声明为 static
的所有变量都将获得内部链接。这意味着您将无法从其他 .c 文件访问它们。
通过声明一个变量static
,你给它静态存储持续时间和内部链接。
函数遵循与变量相同的范围和链接规则,除了存储持续时间对函数没有意义。