static可以用来限制函数的可见性
// static.c
static void func(){}
被static修饰的func函数只能在static.c这个文件内被访问,在其他文件内则无法访问此函数,否则会造成 undefined reference(找不到函数定义)错误。
//static.h
int add(const int a, const int b);
//static.c
static /* 当外部访问此函数会出现 undefined reference to 'add' */
int add(const int a, const int b)
{
return a + b;
}
// main.c
#include "static.h"
#include "stdio.h"
int main()
{
printf("sum is %d\n", add(10, 20));
return 0;
}
// gcc main.c static.c -o main
// main.c:(.text+0xf): undefined reference to `add'
通过static来避免命名冲突
- 明确头文件(.h)与源文件(.c)的区别:
在C语言中头文件提供符号的申明,源文件则提供符号的定义。
这里的符号包含函数名和变量名等。 - 从C源代码到可执行程序需要经历两个步骤:
编译:将源文件翻译成二进制的目标文件.o(bj)
链接:将各个目标文件链接成一个可执行程序 - 在编译时如果遇到未知的符号,则会到各个头文件中去查找符号的定义,找不到就会报错。然后根据相应的词法语法规则翻译成对应的二进制代码。因为只需提供符号的定义就可以实现编译,所以各个源文件可以单独编译成单个.o(bj)文件。
- 链接时会根据入口函数(main)扫描所有目标文件将所有符号的实现全部合并到一个文件中,如果找不到符号实现,则会报错(undefine reference)。而如果在不同文件中有相同的符号名,则可能出现符号的重复定义(函数或变量重名)。
- 编译时符号的可见性默认是全局的(global),而static可以将其修改为本文件可见。
通过static将某些函数定义为内部可见,以此来避免命名冲突。
//demo.h
// definition
int Symbol(const int value);
int Count(const int value);
//demo.c
// implementation
int Symbol(const int value)
{
return value;
}
//demo.bar.c
static /* 如果去掉,则会导致 mutiple definition */
int Symbol(const int value)
{
return value * 100;
}
int Count(const int value)
{
return Symbol(value);
}
// main.c
#include <stdio.h>
#include "demo.h"
int main()
{
int value = 100;
int ret = Symbol(value) + Count(value);
printf("value is %d\n", ret);
return 0;
}
// gcc main.c demo.c demo.bar.c -o main
// sum is 10100
如上面这个例子中,在demo.h头文件中定义了两个函数Symbol与Count,其实现分别放在两个文件demo.c与demo.bar.c中,且两个源文件都实现Symbol函数,通过static隐藏了demo.bar.c的实现来避免命名冲突。
从汇编来看static的实现
// static.c
static void func1(){}
static void func2(){}
// gcc -S static.c
// static.bar.c
void func1(){}
void func2(){}
// gcc -S static.bar.c
下面是汇编代码,可见连个文件基本相同,不同的事在static.bar.s文件中多了 .globl func1 与 .globl func2 这两条伪指令。而 .globl symbol这条伪指令就是告诉链接器在链接时,这个符号是全局的,可以被链接的(外部可见),如果没有这条伪指令修饰的符号,就是不需要链接的。
.file "static.c" .file "static.bar.c"
.text .text
.globl func1 #这里不同
.type func1, @function .type func1, @function
func1:
.LFB0:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
popq %rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE0:
.size func1, .-func1 .size func1, .-func1
.globl func2 #这里不同
.type func2, @function .type func2, @function
func2:
.LFB1:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
popq %rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE1:
.size func2, .-func2
.ident "GCC: (Ubuntu 4.8.4-2ubuntu1~14.04.3) 4.8.4"
.section .note.GNU-stack,"",@progbits