静态 - 仅用于限制范围?
Posted
技术标签:
【中文标题】静态 - 仅用于限制范围?【英文标题】:static - used only for limiting scope? 【发布时间】:2010-12-20 04:55:33 【问题描述】:C 中的static
关键字是否仅用于将变量的范围限制为单个文件?
我需要知道我是否理解正确。请假设以下 3 个文件,
file1.c
int a;
file2.c
int b;
file3.c
static int c;
现在,如果 3 个文件一起编译,那么变量“a”和“b”应该具有全局范围,并且可以从 3 个文件中的任何一个访问。但是,变量“c”是静态的,只能从file3.c访问,对吧?
static
在 C 中还有其他用途吗? (除了如上所示限制变量的范围?)
【问题讨论】:
变量 a & b 在其他 C 文件中不可见,除非您“外部化”它们。 @Murali;不完全正确,如果你定义“int a;”你会遇到链接问题在两个文件中。 哦,记录一下。它不限于文件,它的范围是编译单元。很有可能在另一个“.c”文件中包含一个“.c”文件,或者就此而言,声明一个“int a;”在 .h 文件中。这不是很好的风格,但很有可能。 @chronodekar:您似乎遇到了术语混淆。不幸的是,这里的大多数人都这样做。请停止混用“可见性”、“范围”和“链接”。标识符始终只在一个翻译单元中可见:从声明点到翻译单元结束。标识符可见的区域称为“范围”。 IE。 “范围”和“可见性”是同一回事,它总是对单个翻译单元来说是本地的。现在,“联动”是一个更加全球化的概念。链接是从不同翻译单元链接到同一个对象的能力。 可以从另一个翻译单元链接到具有外部链接的对象(通过在此处声明它们)。无法从另一个翻译单元链接到具有内部链接的对象。这就是static
所控制的——链接。 static
不影响范围(和可见性)。
【参考方案1】:
static
关键字在 C 语言中有两个不同的用途,我称之为持续时间(对象的生命周期)和可见性(您可以从中使用对象)。请记住,C 标准实际上对这两个概念使用了不同的词,但我在教授这门语言时发现最好从日常用语开始。
在文件级别(在任何函数之外)使用时,它控制可见性。在文件级别定义的变量的持续时间已经定义为程序的整个持续时间,因此您不需要static
。
文件级别的静态变量对翻译单元之外的任何东西都是不可见的(链接器看不到它)。
在函数级别(函数内部)使用时,它控制持续时间。这是因为可见性已经被定义为该函数的本地。
在这种情况下,变量的持续时间是程序的整个持续时间,并且值在函数调用之间保持不变。
【讨论】:
你所说的“可见性”,其实就是所谓的“联动”。能见度是完全不同的东西。 “可见性”与“范围”相同。static
对可见性没有影响。 static
对范围没有影响。
是的,我正在发明自己的术语。如果我正在教某人标准,我会很乐意使用其中的术语。但是当我只是教学生 C 的基础知识时(而且 chronodekar 似乎处于那个水平),我会使用更有意义的术语。
这里的大部分混淆源于“他们自己的术语的发明者”。不,你不能在这里发明你自己的术语。如果您仍想这样做,则必须为自己的术语选择“未使用”的词,并且必须为这些术语提供精确的定义。至于已经标准化的术语:同样,不,你不会[重新]发明它们,不管你想要多少。并且“可见性”和“范围”已经被占用了,抱歉。
... 这对于刚开始学习该语言的人来说尤其重要。对他们来说,没有比将普遍接受的术语的错误定义“植入”他们的头脑中更大的伤害了。
什么是绝对的瓦罐。您真的相信标准中的所有这些术语会让新手更清楚吗?在这一点上,谈论静态存储持续时间和外部链接对他们没有帮助。好吧,一旦他们学好语言,他们就可以研究深奥的部分,但不是在他们刚刚开始的时候。同样,如果我正在与“语言律师”或非常了解标准的人交谈,我很乐意使用真实的术语。但是,通过以更易于理解的方式解释基本概念,我并没有对任何学习者造成伤害。你需要决定你的观众。【参考方案2】:
内部链接与外部链接示例
//file1.c
#include <stdio.h>
int glb_var=3;//global variable
int func(); //prototype of function
int main()
func();
func();
func();
return 0;
int func()
static int counter=0;//static varible
printf("val of counter=%d",counter);
counter+=5;
return 0;
当我们编译这个程序并运行这个程序时,操作系统会将该程序加载到内存中。然后会发生以下事情:
glb_var 标识符将存储在初始化的数据段中。
计数器标识符将存储在名为“.bss”的未初始化数据段中。
静态变量初始化一次,值在函数调用期间保持不变。因为静态变量存储在数据段而不是堆栈中,所以静态变量在函数调用期间保持不变。 所以程序的输出将是: 0 5 10关于静态变量的重要一点是它具有内部链接。因此我们可以将此变量访问到特定文件。在其中定义它们(而不是在其他文件中)。
我们可以使用 extern 关键字访问其他文件中的全局变量 glb_var。 例如:
//file2.c
#include <stdio.h>
extern glb_var; //for declaration of this variable
int main()
if(glb_var)
printf("glb_var=%d",glb_var);
输出:3 这称为外部链接。
【讨论】:
【参考方案3】:您误用了“范围”一词。 C 中的static
与作用域完全无关。
Scope 是实体名称(变量、函数、类型名等)可见的区域。在 C 语言中,“文件范围”是有史以来最大的范围。出于这个原因,将任何内容限制为单个文件是没有意义的:根本没有什么更大的限制。 C 中没有“全局作用域”之类的东西。“全局作用域”一词有时被非正式地使用,但在这种情况下,它与“文件作用域”具有相同的含义。
同样,C 中的static
与范围 完全无关。 C 中的static
影响对象的存储持续时间 和标识符的链接。当与对象(变量)一起使用时,static
给出对象静态存储持续时间(即,只要程序运行,对象就存在)。并且,当与非本地对象或函数的标识符一起使用时,它为它们提供内部链接,这意味着相同的标识符指代单个翻译单元(定义实体的位置)内的相同实体,但不在其他翻译单元中。
【讨论】:
在C
编程语言书 (K&R) 中,他们说静态的用途之一是限制范围,这相当令人困惑,因为在此之前,他们说最大的范围是文件范围。你对此有何看法?一直让我很困惑。
'static' 表示'不在堆栈上'。不解释函数调用栈就无法真正解释清楚。
@PaulMurrayCbr:语言规范不使用“函数调用堆栈”或任何其他“堆栈”的概念。所以是的,可以在不涉及任何“堆栈”的情况下对其进行解释。【参考方案4】:
static
也在函数定义中用于定义一个变量,该变量在函数调用之间保持其值。我找到了an example here。相反,每次调用函数时重新创建的变量称为自动变量。
【讨论】:
我不会称它为“对立面”。这就像说红色与黄色相反。在 C89/90 中有两种存储持续时间:静态和自动,但它们之间没有“对立”。 C 语言 (C99) 具有三种存储持续时间:静态、自动和分配。同样,这里没有“对立面”。 我稍微改变了答案。【参考方案5】:你是对的,这叫做“静态链接”:声明为static
的符号仅在定义它的编译单元中可用。
static
的另一个用途是在函数内部:
void f()
static int counter = 0;
counter++;
// ...
在这种情况下,变量只初始化一次,并通过对该函数的不同调用保持其值,就像它是一个全局变量一样。在本例中,counter
变量计算函数被调用的次数。
【讨论】:
你可能想给 counter 一个类型 :-) Ooops... 将类型添加到计数器 :-)【参考方案6】:一个变量可能有三种存储方式:
-
在程序的静态区域中
在堆栈上(函数调用期间)
在堆上(使用 new/malloc 分配时)
全局变量总是存储在静态区域中。但是要将局部变量存储在静态区域中,您需要关键字static
。由于静态变量未在堆栈上分配,因此您可以在后续调用中访问该变量。
此外,全局范围内的 static
关键字提供了变量内部链接。因此,无法使用 extern
限定符从其他文件访问该变量。
【讨论】:
【参考方案7】:一个补充 Kinopiko 答案的例子:
#include <stdio.h>
int foo()
static int foo = 0;
return ++foo;
int main()
printf("%i\n", foo()); // 1
printf("%i\n", foo()); // 2
这可以用于例如返回指向局部函数变量的安全指针。或者在 Objective-C 中,它有时用于防止重复的类初始化:
- (void) initialize
static BOOL initialized = NO;
if (initialized)
return;
// …perform initialization…
initialized = YES;
【讨论】:
在您刚刚给出的示例中,让我假设它位于一个名为“foo.c”的文件中。现在,如果我有另一个文件“aaa.c”并从中调用函数 foo(),静态变量是否仍会保留它的值? (我的意思是,如果我从 aaa.c 调用 foo(),它与从另一个编译单元调用它是一样的,对吧?) @chronodekar:你尝试的时候发生了什么?是的,从 aaa.c 调用foo()
与从 foo.c 调用它是一样的:“内部”变量被更新并返回更新后的值。以上是关于静态 - 仅用于限制范围?的主要内容,如果未能解决你的问题,请参考以下文章
如何使用 AppDomain 限制静态类的范围以进行线程安全使用?
是否有用于自动范围的 PyQtGraph 参数来限制可见的点数?
Scapy/ARP 请求不适用于多个/范围 IP。仅针对单个 IP 请求