首页 > 解决方案 > C中静态函数的优先级

问题描述

假设您有一个带有静态函数 Foo 的文件 File1.c,并且该函数在 File1.c 中被调用。此外,在另一个文件 (File2.c) 中,您还有另一个 Foo 函数,它不是静态的。我知道静态函数在声明它的文件之外是不可见的,并且对于链接器实际上是不可见的。

但这是否意味着 File1.c 中 Foo 函数的内部调用总是在编译期间被解析?

是否存在 File1.c 中的 Foo 调用可以链接到 File2.c 的全局 Foo 函数的情况?

标签: cfunctionstaticlinkage

解决方案


概括

在翻译单元中定义静态函数foo后,为翻译单元的其余部分引用该函数,除非它可以被为翻译单元的一部分foo命名的非函数(例如对象或类型定义)隐藏foo. 它不会链接到名为 的外部函数foo

通过修改下面解释的声明,一个标识符在理论上可以引用另一个翻译单元中的一个函数,该函数static在这个翻译单元中的同名声明之后。不幸的是,由于 C 2018 6.2.2 7,C 标准没有定义该行为:

如果在翻译单元内,相同的标识符同时出现在内部和外部链接中,则行为未定义。

这意味着您不能仅依靠 C 标准来确保这种行为,但 C 实现可以将其定义为扩展。

细节

C 的范围和链接规则回答了这些问题。

假设File1.c我们有一个函数的静态定义:

static int foo(int x) { return x*x; }

由于标识符foo是在任何函数之外声明的,因此它具有文件范围(C 2018 6.2.1 4)。这意味着标识符foo是可见的,并为File1.c. 此外,由于static被使用,它具有内部链接(6.2.2 3)。

范围有一个例外。对于其他作用域内的作用域,例如{ … }在文件内定义函数的块或块内的块,相同标识符的声明可以隐藏外部声明。所以让我们考虑foo在块内重新声明。

为了引用foo外部定义的File1.c,我们需要声明foo外部链接,以便这个 newfoo可以链接到外部定义的foo。有没有办法在 C 中做到这一点?

如果我们尝试extern int foo(int x);在块内声明,则适用 6.2.2 4:

对于在该标识符的先前声明可见的范围内使用存储类说明符extern声明的标识符,如果先前声明指定内部或外部链接,则后面声明的标识符的链接与指定的链接相同在事先声明。

所以这个声明只会重新声明相同的foo

如果我们声明它没有extern, using int foo(int x);, 6.2.2 5 适用:

如果函数标识符的声明没有存储类说明符,则它的链接将完全确定,就好像它是使用存储类说明符声明的一样extern

所以,似乎我们不能声明一个不同的 foo 有或没有extern。但是,等等,我们还有一个技巧。我们可以通过使用没有链接的声明来隐藏指定内部或外部链接的先前声明。要获得没有链接的声明,我们可以声明一个没有链接的对象(而不是函数)extern

#include <stdio.h>

static int foo(int x) { return x*x; }

void bar(void)
{
    int foo; // Not used except to hide the function foo.
    {
        extern int foo(int x);
        printf("%d\n", foo(3));
    }
}

由于在 whereextern int foo(int x);出现的地方,与内部链接的先前声明foo不可见,因此上面引用的 6.2.2 4 中的第一个条件不适用,而 6.2.2 4 的其余部分则适用:

如果前面的声明不可见,或者前面的声明没有指定链接,则标识符具有外部链接。

这是“合法的”C 代码。不幸的是,它没有被 6.2.2 7 定义:

如果在翻译单元内,相同的标识符同时出现在内部和外部链接中,则行为未定义。


推荐阅读