【问题标题】:If function declaration is not in header file, is static keyword necessary?如果函数声明不在头文件中,是否需要静态关键字?
【发布时间】:2014-08-22 01:15:46
【问题描述】:

如果函数声明不在头文件 (.h) 中,而只在源文件 (.c) 中,为什么需要使用 static 关键字?当然,如果你只在一个 .c 文件中声明它,它不会被其他文件看到,因为你不应该#include .c 文件,对吧?

我已经阅读了很多关于此的问题和答案(例如 herehere),但我无法完全理解它。

【问题讨论】:

  • 在/不在标题中不会影响可访问性,只会影响可见性。当您声明一个函数static 时,链接器将不允许该函数满足某个其他模块的链接需求。如果你把关键字去掉,然后在另一个模块中声明函数(就像你在头文件中一样),你可以链接到它。
  • 对,所以我猜在小项目中(我是初学者!)这不太可能发生,因为你会知道你所有的函数都被调用了什么以及你在哪里声明了它们,但是在一个更大的项目中,你可能会两次声明一个同名的函数……?
  • 基本上是为了避免命名空间冲突。熟悉在不需要全局可见性的任何地方使用static,否则您最终可能会在不同的编译单元中为不同的目的定义相同的名称。链接器 - 只看到名称 - 将假定它是相同的符号,您可能想知道奇怪的值...

标签: c static


【解决方案1】:

static 所做的就是让其他模块中的函数无法声明和调用,无论是否通过头文件。

回想一下,C 中包含的头文件只是文本替换:

// bar.c
#include "header.h"

int bar()
{
    return foo() + foo();
}

// header.h
int foo(void);

经过预处理成为

int foo(void);

int bar()
{
    return foo() + foo();
}

事实上,你可以去掉header.h,一开始就这样写bar.c。同样,foo 的定义在任何一种情况下都不需要包含标题;包括它只是添加了一个检查 foo 的定义和声明是否一致。

但如果你要将foo 的实现更改为

static int foo()
{
    // whatever
    return 42;
}

那么foo 的声明将在模块和头文件中停止工作(因为头文件只是被替换到模块中)。或者实际上,声明仍然“有效”,但它不再引用您的 foo 函数,并且当您尝试调用 foo 时,链接器会抱怨这一点。

使用static 的主要原因是为了防止链接器冲突:即使foobar 在同一个模块中,并且在模块之外没有任何东西称为foo,如果不是static,它仍然会与称为foo 的任何其他非static 函数发生冲突。第二个原因是优化:当函数是static时,编译器确切地知道程序的哪些部分调用它以及使用什么参数,因此它可以执行常量折叠、死代码消除和/或内联。

【讨论】:

  • 知道了。因此,在小型项目中,您可能不会遇到命名空间冲突,但在任何更大的项目中,确保您不会(对吗?)其次,它优化了代码(这对我来说同样不是问题时刻)因为它让编译器知道只有一个模块需要访问该函数(对吗??)
  • extern 对于所有函数原型都是隐式的
  • @BobBroadley 即使在小型项目中,当您与库链接时,它也是可取的。库应该注意使用带有公共前缀的函数名(例如 bla_ 用于 libbla),但它们并非总是如此,在编写 C 时,防御性编程是一个好习惯。
  • @SteveCox:我不知道 extern 对于原型是明确的;现在更有意义了。我现在明白为什么需要静态“以防万一”。
  • @larsmans:对,关于图书馆的评论非常有道理。即使在一个小项目中,我使用的不仅仅是“我的代码”!我知道我需要使用 static 来限制函数的范围。这里的答案和 cmets 帮助我理解了为什么......
【解决方案2】:

static 关键字降低了函数对文件范围的可见性。这意味着您不能在其他单元中本地声明该函数并使用它,因为链接器不会将其添加到全局符号表中。这也意味着您也可以在其他单位中使用该名称(您可能在每个文件中都有一个static void testOutput();,如果省略了static,这是不可能的。)

根据经验,您应该尽可能限制符号的可见性。因此,如果您不需要外部的例程(并且它不是某些接口的一部分),请保留它static

【讨论】:

    【解决方案3】:
    1. 它允许您在不同的源文件中拥有具有相同名称的函数,因为编译器会在每个静态函数的名称中添加一个隐式前缀(基于函数所在文件的名称),从而防止多重定义链接错误。

    2. 它可以帮助维护代码的人知道该函数没有作为接口的一部分公开,并且仅在文件内部使用(非静态函数可以在其他源文件中使用即使没有在任何头文件中声明,使用extern关键字)。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-04-05
      • 1970-01-01
      • 2017-06-22
      • 2013-03-18
      • 2010-09-10
      • 1970-01-01
      • 1970-01-01
      • 2010-12-10
      相关资源
      最近更新 更多