C中的关键字“静态”具有两个根本不同的含义。
限制范围
在这种情况下,“静态”与“外部”配对以控制变量或函数名称的范围。静态导致变量或函数名称仅在单个编译单元中可用,并且仅对在编译单元文本中声明/定义之后存在的代码可用。
仅当并且仅当您的项目中有多个编译单元时,此限制本身才真正有意义。如果您只有一个编译单元,那么它仍然可以执行操作,但是这些效果几乎没有意义(除非您喜欢挖掘目标文件以读取编译器生成的内容。)
如前所述,在此上下文中,此关键字与关键字“ extern”配对,这相反,通过使变量或函数名称可与其他编译单元中找到的相同名称进行链接。因此,您可以将“静态”视为要求在当前编译单元中找到变量或名称,而“外部”则允许交叉编译单元链接。
静态寿命
静态生存期意味着该变量在程序的整个过程中都存在(无论多长时间。)当您使用“静态”在函数内声明/定义变量时,这意味着该变量是在首次使用之前创建的(这意味着,每次我经历过,该变量都是在main()开始之前创建的,并且之后不会销毁。甚至在函数执行完成并返回到其调用方时也没有。就像在函数外部声明的静态生存期变量一样,它们在main()开始之前同时初始化为语义零(如果未提供初始化)或指定的显式值(如果有的话)。
这与“自动”类型的函数变量不同,后者在每次输入函数时都会被创建(或者,如果是新的话),然后在函数退出时被销毁(或者被销毁)。
与在函数外部对变量定义应用“静态”的影响会直接影响其范围不同,将函数变量(显然在函数体内)声明为“静态” 不会影响其范围。范围由在功能体内定义的事实确定。在函数中定义的静态生存期变量与在函数主体中定义的其他“自动”变量具有相同的作用域-函数作用域。
摘要
因此,“静态”关键字具有不同的上下文,其含义是“非常不同的含义”。之所以以两种方式使用它,是为了避免使用另一个关键字。(对此进行了长时间的讨论。)人们认为程序员可以忍受这种用法,避免在该语言中使用另一个关键字的价值比其他参数更重要。
(在函数外部声明的所有变量均具有静态生存期,不需要使用关键字“ static”即可实现。因此,这种释放关键字的方式将在那里使用,表示完全不同的含义:“仅在单个编译中可见单位。”这是一种hack。)
具体说明
静态易失的无符号字符PORTB @ 0x06;
此处的“静态”一词应解释为意味着链接器不会尝试匹配在多个编译单元中发现的PORTB的多次出现(假设您的代码有多个)。
它使用特殊的(非便携式)语法来指定PORTB的“位置”(或标签的数字值,通常是地址)。因此,链接器将获得一个地址,而无需为此找到一个。如果您有两个使用此行的编译单元,则无论如何它们都会指向相同的位置。因此,无需在此处将其标记为“外部”。
如果他们使用“外部”,可能会造成问题。然后,链接器将能够看到(并尝试匹配)在多个编译中找到的对PORTB的多个引用。如果所有人都指定这样的地址,并且由于某种原因[错误?]这些地址不同,那么该怎么办?抱怨?要么?(从技术上讲,凭经验,经验法则是只有一个编译单元可以指定该值,而其他编译单元则不能。)
将其标记为“静态”更容易,避免使链接程序担心冲突,并且只需将地址不匹配的错误归咎于将地址更改为不应使用的地址的人。
无论哪种方式,该变量都被视为具有“静态寿命”。(以及“易失性”。)
一个声明不是定义,但所有定义都声明
在C语言中,定义创建一个对象。它还声明了它。但是声明通常不创建对象(请参见下面的项目符号说明)。
以下是定义和声明:
static int a;
static int a = 7;
extern int b = 5;
extern int f() { return 10; }
以下不是定义,而只是声明:
extern int b;
extern int f();
请注意,声明不会创建实际的对象。它们仅声明有关它的详细信息,然后编译器可以使用它们来帮助生成正确的代码,并在适当时提供警告和错误消息。
在上面,我建议“通常”说。在某些情况下,声明可以创建一个对象,因此可以由链接程序提升为定义(而不是由编译器升级)。因此,即使在这种罕见的情况下,C编译器仍然认为声明只是一个声明。链接器阶段可以对某些声明进行必要的升级。请牢记这一点。
在以上示例中,应该证明只有 “ extern int b”的声明;在所有链接的编译单元中,链接器负责创建定义。请注意,这是一个链接时事件。在编译期间,编译器完全不知道。如果最多提倡这种类型的声明,则只能在链接时确定。
编译器知道“ static int a”。链接器不能在链接时对其进行升级,因此实际上这是在编译时的定义。