3

static有人告诉我,在一个文件中定义的函数.c不能从其他文件访问。但在下面的程序中,我可以static void show()从另一个文件访问该函数。我对staticC 函数的理解是错误的吗?

啊(第一个文件):

static void show()
{
printf("I am in static show function in a.c");
}

bc(另一个文件):

#include"a.h"
void main()
{
show();
}
4

3 回答 3

11

请记住,这#include是通过复制和粘贴包含文件的内容来实现的。因此,在您的示例中,#include处理完之后,您会得到以下信息:

static void show()
{
printf("I am in static show function in a.c");
}

void main()
{
show();
}

如此清晰main可见show1

解决方案是 #include使用.c 文件。通常,您应该只使用#include标头 (.h) 文件。您的静态函数不应在头文件中声明或定义,因此main将无法看到它。


1. 但是,您现在实际上有两个函数定义show,一个 ina.c和一个 in b.c。对于static函数,这不是问题,但对于非static函数,您会收到链接器错误。

于 2012-06-17T17:36:51.793 回答
6

static关键字将链接规范更改为内部链接
标记为的函数static仅在该Translation Unit(TU)中可见

也许,您在访问该功能的特定 TU 中有相同的命名符号可用。其中的部分只有在您向我们展示代码后才能回答。

编辑:
当您static在头文件中定义一个函数时,将在包含它的每个翻译单元中创建相同函数的副本。此类函数的每个实例都被视为一个单独的函数(每个函数的地址不同)并且每个这些函数的实例有自己的static局部变量和字符串文字的副本。

显然,这将起作用,但这也可能会增加生成的二进制文件的大小。

于 2012-06-17T17:24:05.990 回答
1

其他答案是正确的,但说静态函数不能从另一个文件访问并不十分准确。可以通过函数指针访问函数。更准确地说,该函数的名称在另一个翻译单元中是不可访问的。

请记住,将 C 源代码转换为可执行程序包括概念阶段,包括:

  1. 预处理(其中#include指令被包含文件的内容替换
  2. 编译(一次处理一个翻译单元)
  3. 链接(将翻译单元组合到最终程序中)

假设我们有三个文件。 foo.h

typedef void (*void_function_p)(void);

extern void_function_p foo(void);

foo.c

#include "foo.h"
#include <stdio.h>

static void baz(void) {
    printf("worked!\n");
}

void_function_p foo(void) {
    return baz;
}

bar.c

#include "foo.h"
#include <stdio.h>

int main(void) {
    (*foo())();
    return 0;
}

该程序编译并打印“工作!” 当它运行时。

这里有两个翻译单元。一种是预处理中的代码foo.c(由于其#include工作原理,它还包括 和 中的代码foo.hstdio.h。另一个是预处理中的代码(同样,它在andbar.c中有自己的代码副本)。foo.hstdio.h

通过让函数foo返回指向静态函数的指针baz,我们可以bazmain函数中调用。

现在,考虑如果我们修改main为如下所示会发生什么:

int main(void) {
    (*foo())();
    baz();
    return 0;
}

此代码将导致链接器错误,因为baz此翻译单元中的名称无法链接到baz其他翻译单元中的定义。

这是静态函数的第一个优点:另一个程序员不会意外地baz从另一个翻译单元访问我们的函数。

现在,考虑如果我们修改bar.c为如下所示会发生什么:

#include "foo.h"
#include <stdio.h>

static void baz(void) {
        printf("still works!");
}

int main() {
        (*foo())();
        baz();
        return 0;
}

此代码将编译并打印“工作!” 其次是“仍然有效!”

这是静态函数的第二个优点:我们定义了两个同名的函数(在不同的翻译单元中)。

如果您尝试将两个静态定义放在同一个翻译单元中,您将收到关于定义baz两次的编译器错误。

最后要注意的是,如果您使用现在的程序并删除所有statics,则会导致链接器错误,因为baz已经定义了两次(使用外部链接),这是不允许的。

于 2013-01-15T18:24:57.803 回答