我被告知在一个static
文件中定义为.c
的函数无法从其他文件访问。但是在以下程序中,我可以从另一个文件访问static void show()
函数。我对C中的static
函数的理解是错误的吗?
a.h(第一个文件):
static void show()
{
printf("I am in static show function in a.c");
}
b.c(另一个档案):
#include"a.h"
void main()
{
show();
}
答案 0 :(得分:11)
请记住#include
通过复制并粘贴所包含文件的内容来工作。因此,在您的示例中,在处理#include
之后,您会得到:
static void show()
{
printf("I am in static show function in a.c");
}
void main()
{
show();
}
很明显main
可以看到show
。 1
解决方案是不 #include
.c文件。通常,您应该只有#include
标题(.h)文件。不应在头文件中声明或定义静态函数,因此main
将无法看到它。
<小时/> <子> 1。但是,您现在实际上拥有
show
函数的两个定义,一个在a.c
中,另一个在b.c
中。对于static
函数,这不是问题,但对于非static
函数,您将收到链接器错误。
答案 1 :(得分:6)
static
关键字将链接规范更改为 Internal Linkage 。
标记为static
的功能只能在 Translation Unit(TU) 中可见。
也许,您在该特定TU中可以使用相同的命名符号,您可以在其中访问该函数。只有在向我们展示代码后才能回答它的部分内容。
修改强>
在头文件中定义static
函数时,会在包含它的每个转换单元中创建相同函数的副本。此类函数的每个实例都被视为一个单独的函数(每个函数的地址)函数是不同的),这些函数的每个实例都有自己的static
局部变量&amp;字符串文字。
显然,这会起作用,但这可能会增加生成的二进制文件的大小。
答案 2 :(得分:1)
其他答案是正确的,但是说不能从其他文件访问静态函数并不准确。可以通过函数指针访问该函数。更准确地说,该功能的名称无法在另一个翻译单元中访问。
请记住,将C源代码转换为可执行程序包含概念阶段,包括:
#include
指令被包含文件的内容替换假设我们有三个文件。 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.h
和stdio.h
中的代码。另一个是预处理bar.c
中的代码(同样,它在foo.h
和stdio.h
中有自己的代码副本。)
通过让函数foo
返回指向静态函数baz
的指针,我们可以从baz
函数调用main
。
现在,考虑如果我们将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
两次的编译器错误。
作为最后一点,如果您现在采用该程序并删除所有static
,则会导致链接器错误,因为baz
已定义两次(使用外部链接) ,这是不允许的。