【问题标题】:What's the difference between declaring a function static and not including it in a header? [duplicate]将函数声明为静态和不将其包含在标头中有什么区别? [复制]
【发布时间】:2020-08-23 15:29:51
【问题描述】:

以下场景有什么区别?

// some_file.c
#include "some_file.h" // doesn't declare some_func

int some_func(int i) {
    return i * 5;
}
// ...

// some_file.c
#include "some_file.h" // doesn't declare some_func

static int some_func(int i) {
    return i * 5;
}
// ...

如果所有static 对函数所做的只是限制它们对其文件的可访问性,那么这两种情况都不是意味着some_func(int i) 只能从some_file.c 访问,因为在这两种情况下some_func(int i) 都不会放在头文件中?

【问题讨论】:

  • 在第一个例子中,即使你没有在some_file.h中声明some_func,它仍然可以被其他文件使用extern int some_func(int);访问(extern是可选的),在第二个例子中一个some_func 不可访问。

标签: c static header-files


【解决方案1】:

静态函数对于声明它的 .c 文件是“本地的”。因此,您可以在另一个 .c 文件中拥有另一个函数(静态或非),而不会发生名称冲突。

如果您在 .c 文件中有一个未在任何头文件中声明的非静态函数,则您不能从另一个 .c 文件调用该函数,但您也不能在另一个 .c 文件中拥有另一个同名的函数文件,因为这会导致名称冲突。

结论:所有纯局部函数(仅在 .c 函数内部使用的函数,例如局部辅助函数)都应声明为静态,以防止命名空间的污染。

正确用法示例:

file1.c

static void LocalHelper()
{
}
...

file2.c

static void LocalHelper()
{
}
...

半正确用法示例

file1.c

static LocalHelper()   // function is local to file1.c
{
}
...

file2.c

void LocalHelper()     // global functio
{
}
...

file3.c

void Foo()
{
   LocalHelper();    // will call LocalHelper from file2.c
}
...

在这种情况下,程序将正确链接,即使 LocalHelper 在 file2.c 中应该是静态的

错误使用示例

file1.c

LocalHelper()          // global function
{
}
...

file2.c

void LocalHelper()     // global function
{
}
...

file3.c

void Foo()
{
   LocalHelper();       // which LocalHelper should be called?
}
...

在最后一种情况下,我们有一个 nema 冲突,程序甚至不会链接。

【讨论】:

  • 不是 C 文件的本地文件,而是其翻译单元的本地文件
  • @BasileStarynkevitch 说得对,但我确实觉得翻译单元这个词对初学者来说有些混乱。
【解决方案2】:

不同之处在于,对于非静态函数,它仍然可以在其他一些翻译单元中声明(头文件与这一点无关)并被调用。任何其他翻译单元根本看不到静态函数。

在另一个函数中声明一个函数甚至是合法的:

foo.c:
void foo()
{
  void bar();
  bar();
}

bar.c:
void bar()
{ ... }

【讨论】:

    【解决方案3】:

    将函数声明为静态与不将其包含在标头中有什么区别?

    阅读有关C programming language(例如Modern C)、C11 标准n1570 的更多信息。另请阅读linkersloaders

    在 POSIX 系统上,尤其是 Linux,阅读有关 dlopen(3)dlsym(3) 等的信息...

    实际上:

    如果您声明一个函数 static,它对其他 translation units 保持不可见(具体而言,您的 *.c 源文件,根据您编译它们的方式:您可以 - 至少在原则上,即使它令人困惑——在 Linux 上用GCC 编译foo.c 两次:一次是gcc -c -O -DOPTION=1 foo.c -o foo1.o,另一次是gcc -c -O -DOPTION=2 -DWITHOUT_MAIN foo.c -o foo2.o,在某些 情况下可以使用gcc foo1.o foo2.o -o foofoo1.ofoo2.oobject files链接成一个executablefoo

    如果你不声明它static,你可以(即使它的品味很差)在一些其他翻译单元中编码:

    if (x > 2) {
      extern int some_func(int); // extern is here for readability
      return some_func(x);
    }
    

    在实践中,我习惯用唯一的名称命名所有 C 函数(包括 static 的),并且我通常有与之相关的命名约定(例如使用公共前缀命名它们,例如 @987654332 @ 做)。这使得调试程序(使用GDB)更容易(因为 GDB 具有自动完成函数名称)。

    最后,一个好的optimizing compiler 可以并且实际上经常会调用inline 调用其主体已知的静态函数。使用最近的GCC,使用gcc -O2 -Wall 编译您的代码。如果您想检查内联是如何发生的,请查看生成的汇编程序(使用gcc -O2 -S -fverbose-asm)。

    在 Linux 上,您可以使用 dlsym 获取未声明为 static 的函数的地址。

    【讨论】:

    • extern 不是必须的,是吗?
    • 但它使代码可读。
    猜你喜欢
    • 2013-10-19
    • 2020-10-20
    • 1970-01-01
    • 1970-01-01
    • 2016-10-21
    • 2016-01-25
    • 1970-01-01
    • 1970-01-01
    • 2011-01-20
    相关资源
    最近更新 更多