C语言用static修饰函数的作用与原理

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来避免命名冲突

  1. 明确头文件(.h)与源文件(.c)的区别:
    在C语言中头文件提供符号的申明,源文件则提供符号的定义。
    这里的符号包含函数名和变量名等
  2. 从C源代码到可执行程序需要经历两个步骤:
    编译:将源文件翻译成二进制的目标文件.o(bj)
    链接:将各个目标文件链接成一个可执行程序
  3. 在编译时如果遇到未知的符号,则会到各个头文件中去查找符号的定义,找不到就会报错。然后根据相应的词法语法规则翻译成对应的二进制代码。因为只需提供符号的定义就可以实现编译,所以各个源文件可以单独编译成单个.o(bj)文件。
  4. 链接时会根据入口函数(main)扫描所有目标文件将所有符号的实现全部合并到一个文件中,如果找不到符号实现,则会报错(undefine reference)。而如果在不同文件中有相同的符号名,则可能出现符号的重复定义(函数或变量重名)。
  5. 编译时符号的可见性默认是全局的(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

Reference
  1. linux汇编.section与.globl
  2. C语言的编译链接过程详解

转载于:https://my.oschina.net/tigerBin/blog/1611580

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值