在C中使函数静态化有什么意义?
在C中使函数静态化有什么意义?
Answers:
生成函数static
会将其从其他翻译单元中隐藏起来,这有助于提供封装。
helper_file.c
int f1(int); /* prototype */
static int f2(int); /* prototype */
int f1(int foo) {
return f2(foo); /* ok, f2 is in the same translation unit */
/* (basically same .c file) as f1 */
}
int f2(int foo) {
return 42 + foo;
}
main.c:
int f1(int); /* prototype */
int f2(int); /* prototype */
int main(void) {
f1(10); /* ok, f1 is visible to the linker */
f2(12); /* nope, f2 is not visible to the linker */
return 0;
}
#include <helper_file.c>
吗?我认为那将使它成为一个翻译单位……
gcc -std=c99 -pedantic -Wall -Wextra main.c helper_file.c
。这些函数的原型都存在于两个源文件中(不需要头文件)。链接器将解析功能。
pmg关于封装问题。除了将功能隐藏在其他翻译单元之外(或者因为它而已)之外,制作功能static
还可以在存在编译器优化的情况下提高性能。
因为static
不能从当前翻译单元之外的任何地方调用函数(除非代码使用指向其地址的指针),所以编译器控制进入它的所有调用点。
这意味着可以自由使用非标准的ABI,完全内联它或执行许多其他优化,而这些优化可能对于具有外部链接的功能是不可能的。
static
函数的指针转义了当前的翻译单元,则可以直接从其他翻译单元调用该函数。
的 static
C中关键字在编译文件(.c相对于.h)中使用,因此该功能仅存在于该文件中。
通常,当您创建函数时,编译器会生成链接器可用来将函数调用链接到该函数的文件。如果使用static关键字,则同一文件中的其他函数可以调用此函数(因为无需使用链接器即可完成此功能),而链接器没有信息可让其他文件访问该函数。
看着上面的帖子,我想指出一个细节。
假设我们的主文件(“ main.c”)如下所示:
#include "header.h"
int main(void) {
FunctionInHeader();
}
现在考虑三种情况:
情况1:我们的头文件(“ header.h”)如下所示:
#include <stdio.h>
static void FunctionInHeader();
void FunctionInHeader() {
printf("Calling function inside header\n");
}
然后在Linux上执行以下命令:
gcc main.c header.h -o main
会成功的!如果有人跑了
./main
输出将是
标头中的调用函数
静态函数应该打印什么。
情况2:我们的头文件(“ header.h”)如下所示:
static void FunctionInHeader();
而且我们还有一个文件“ header.c”,如下所示:
#include <stdio.h>
#include "header.h"
void FunctionInHeader() {
printf("Calling function inside header\n");
}
然后下面的命令
gcc main.c header.h header.c -o main
将给出一个错误。
情况3:
与情况2相似,除了现在我们的头文件(“ header.h”)为:
void FunctionInHeader(); // keyword static removed
然后,与情况2相同的命令将成功执行,并进一步执行./main将给出预期的结果。
因此,根据这些测试(在Acer x86计算机,Ubuntu OS上执行),我假设
static关键字防止函数在定义位置以外的另一个* .c文件中调用。
如果我错了,请纠正我。
pmg的答案非常令人信服。如果您想知道静态声明在对象级别的工作方式,那么下面的信息对您可能很有趣。我重用了pmg编写的相同程序,并将其编译为.so(共享对象)文件
以下内容是将.so文件转储到人类可读的文件之后的内容
0000000000000675 f1:f1函数的地址
000000000000068c f2:f2(staticc)函数的地址
注意函数地址的差异,表示某种含义。对于用不同地址声明的函数,它可以很好地表示f2位于很远的地方或位于目标文件的不同段中。
链接器使用称为PLT(过程链接表)和GOT(全局偏移量表)的东西来理解它们有权访问的符号。
现在,以为GOT和PLT神奇地绑定了所有地址,而动态节则保存了链接器可见的所有这些功能的信息。
转储.so文件的动态部分后,我们获得了一堆条目,但仅对f1和f2函数感兴趣。
动态部分仅对地址为0000000000000675的f1函数保存条目,而对于f2则不保存!
编号:值大小类型绑定Vis Ndx名称
9: 0000000000000675 23 FUNC GLOBAL DEFAULT 11 f1
就是这样!由此可见,链接器无法找到f2函数,因为它不在.so文件的动态部分中。
当需要限制对某些函数的访问时,我们将在定义和声明函数时使用static关键字。
/* file ab.c */
static void function1(void)
{
puts("function1 called");
}
And store the following code in another file ab1.c
/* file ab1.c */
int main(void)
{
function1();
getchar();
return 0;
}
/* in this code, we'll get a "Undefined reference to function1".Because function 1 is declared static in file ab.c and can't be used in ab1.c */