function

C语言中的函数调用

函数是C语言的重要组成部分,每个C语言的程序,至少都有一个函数,即入口函数main函数,那么在程序运行时函数是以怎样的方式运行的呢。本文主要讨论C语言中函数的调用原理和存储方式。

形式

这个简单提一下,C语言的函数形式时是[可选的返回值类型]函数名(参数…)。严格的定义如下:

1
2
3
4
5
6
C语言函数的BNF范式(节选,并未完整展开):
function_definition : decl_specs declarator decl_list compound_stat
| declarator decl_list compound_stat
| decl_specs declarator compound_stat
| declarator compound_stat
;

存储

在以前的文章中讲到了进程的内存布局,在进程运行时,程序的函数加载到栈中,栈的单位可以用栈帧来表示。

函数栈

寄存器

栈寄存器

栈在内存中从高地址向低地址增长。一个函数一次入栈的是局部变量,参数列表和返回地址。栈顶指针和栈底指针分别存在专用的寄存器中,栈顶指针存在栈指针寄存器(16位SP寄存器 stack pointer,32位ESP寄存器extended stack pointer,64位RSP寄存器 register stack pointer),栈底指针存在基址指针寄存器(16位BP寄存器 base pointer,32位EBP寄存器 extended base pointer,64位RBP寄存器 register base pointer)。

代码段寄存器

CS寄存器(code segment),代码段寄存器,指向当前代码段的初始内存地址。

指令寄存器

IP寄存器(instruction pointer),指令指针寄存器,存储着当前要执行的下一条指令的指令地址,即相对于代码段初始位置的偏移量。

函数执行过程

汇编指令

push 入栈

pop 出栈

call 调用指令

mov 传递值

jmp (无条件)跳转

函数的汇编指令

注:汇编指令均以32位CPU为例

函数调用开始时

  1. 函数调用时,栈帧中压入上一个栈帧的栈底地址 汇编指令是

    ebp```
    1
    2

    2. 上一个栈帧的栈顶地址作为当前栈帧的栈底地址 汇编指令是```mov ebp, esp

  2. 为当前函数的局部变量开辟足够的存储空间 汇编指令时

    esp, M```
    1
    2
    3
    4
    5
    6
    7
    8
    9

    **函数调用返回时**

    1. 将函数为局部变量分配的空间释放,只需要将栈顶指针指向开辟空间前的位置即可 汇编指令是```mov esp, ebp``` 此时esp中的值是上一个栈帧的栈底地址。
    2. 将ebp恢复为上一个栈帧的栈底指针 汇编指令是```mov ebp, [esp]``` 此时esp指向存放返回地址的地址。
    3. 将指令寄存器指向call指令操作值的地址 汇编指令```mov eip, [esp]```, 此时esp寄存器指向函数第一个参数(从右向左顺序加载)
    4. 将栈顶指针指向函数为参数开辟空间之前的地址 汇编指令为 `pop ...`,此时esp 的值是第一个局部变量的地址。

    ## 附录:C语言语法的完整BNF范式:

translation_unit : external_decl
| translation_unit external_decl
;
external_decl : function_definition
| decl
;
function_definition : decl_specs declarator decl_list compound_stat
| declarator decl_list compound_stat
| decl_specs declarator compound_stat
| declarator compound_stat
;
decl : decl_specs init_declarator_list ‘;’
| decl_specs ‘;’
;
decl_list : decl
| decl_list decl
;
decl_specs : storage_class_spec decl_specs
| storage_class_spec
| type_spec decl_specs
| type_spec
| type_qualifier decl_specs
| type_qualifier
;
storage_class_spec : ‘auto’ | ‘register’ | ‘static’ | ‘extern’ | ‘typedef’
;
type_spec : ‘void’ | ‘char’ | ‘short’ | ‘int’ | ‘long’ | ‘float’
| ‘double’ | ‘signed’ | ‘unsigned’
| struct_or_union_spec
| enum_spec
| typedef_name
;
type_qualifier : ‘const’ | ‘volatile’
;
struct_or_union_spec : struct_or_union id ‘{‘ struct_decl_list ‘}’
| struct_or_union ‘{‘ struct_decl_list ‘}’
| struct_or_union id
;
struct_or_union : ‘struct’ | ‘union’
;
struct_decl_list : struct_decl
| struct_decl_list struct_decl
;
init_declarator_list : init_declarator
| init_declarator_list ‘,’ init_declarator
;
init_declarator : declarator
| declarator ‘=’ initializer
;
struct_decl : spec_qualifier_list struct_declarator_list ‘;’
;
spec_qualifier_list : type_spec spec_qualifier_list
| type_spec
| type_qualifier spec_qualifier_list
| type_qualifier
;
struct_declarator_list : struct_declarator
| struct_declarator_list ‘,’ struct_declarator
;
struct_declarator : declarator
| declarator ‘:’ const_exp
| ‘:’ const_exp
;
enum_spec : ‘enum’ id ‘{‘ enumerator_list ‘}’
| ‘enum’ ‘{‘ enumerator_list ‘}’
| ‘enum’ id
;
enumerator_list : enumerator
| enumerator_list ‘,’ enumerator
;
enumerator : id
| id ‘=’ const_exp
;
declarator : pointer direct_declarator
| direct_declarator
;
direct_declarator : id
| ‘(‘ declarator ‘)’
| direct_declarator ‘[‘ const_exp ‘]’
| direct_declarator ‘[‘ ‘]’
| direct_declarator ‘(‘ param_type_list ‘)’
| direct_declarator ‘(‘ id_list ‘)’
| direct_declarator ‘(‘ ‘)’
;
pointer : ‘‘ type_qualifier_list
| ‘
‘ | ‘‘ type_qualifier_list pointer
| ‘
‘ pointer
;
type_qualifier_list : type_qualifier
| type_qualifier_list type_qualifier
;
param_type_list : param_list
| param_list ‘,’ ‘…’
;
param_list : param_decl
| param_list ‘,’ param_decl
;
param_decl : decl_specs declarator
| decl_specs abstract_declarator
| decl_specs
;
id_list : id
| id_list ‘,’ id
;
initializer : assignment_exp
| ‘{‘ initializer_list ‘}’
| ‘{‘ initializer_list ‘,’ ‘}’
;
initializer_list : initializer
| initializer_list ‘,’ initializer
;
type_name : spec_qualifier_list abstract_declarator
| spec_qualifier_list
;
abstract_declarator : pointer
| pointer direct_abstract_declarator
| direct_abstract_declarator
;
direct_abstract_declarator: ‘(‘ abstract_declarator ‘)’
| direct_abstract_declarator ‘[‘ const_exp ‘]’
| ‘[‘ const_exp ‘]’
| direct_abstract_declarator ‘[‘ ‘]’
| ‘[‘ ‘]’
| direct_abstract_declarator ‘(‘ param_type_list ‘)’
| ‘(‘ param_type_list ‘)’
| direct_abstract_declarator ‘(‘ ‘)’
| ‘(‘ ‘)’
;
typedef_name : id
;
stat : labeled_stat
| exp_stat
| compound_stat
| selection_stat
| iteration_stat
| jump_stat
;
labeled_stat : id ‘:’ stat
| ‘case’ const_exp ‘:’ stat
| ‘default’ ‘:’ stat
;
exp_stat : exp ‘;’
| ‘;’
;
compound_stat : ‘{‘ decl_list stat_list ‘}’
| ‘{‘ stat_list ‘}’
| ‘{‘ decl_list ‘}’
| ‘{‘ ‘}’
;
stat_list : stat
| stat_list stat
;
selection_stat : ‘if’ ‘(‘ exp ‘)’ stat
| ‘if’ ‘(‘ exp ‘)’ stat ‘else’ stat
| ‘switch’ ‘(‘ exp ‘)’ stat
;
iteration_stat : ‘while’ ‘(‘ exp ‘)’ stat
| ‘do’ stat ‘while’ ‘(‘ exp ‘)’ ‘;’
| ‘for’ ‘(‘ exp ‘;’ exp ‘;’ exp ‘)’ stat
| ‘for’ ‘(‘ exp ‘;’ exp ‘;’ ‘)’ stat
| ‘for’ ‘(‘ exp ‘;’ ‘;’ exp ‘)’ stat
| ‘for’ ‘(‘ exp ‘;’ ‘;’ ‘)’ stat
| ‘for’ ‘(‘ ‘;’ exp ‘;’ exp ‘)’ stat
| ‘for’ ‘(‘ ‘;’ exp ‘;’ ‘)’ stat
| ‘for’ ‘(‘ ‘;’ ‘;’ exp ‘)’ stat
| ‘for’ ‘(‘ ‘;’ ‘;’ ‘)’ stat
;
jump_stat : ‘goto’ id ‘;’
| ‘continue’ ‘;’
| ‘break’ ‘;’
| ‘return’ exp ‘;’
| ‘return’ ‘;’
;
exp : assignment_exp
| exp ‘,’ assignment_exp
;
assignment_exp : conditional_exp
| unary_exp assignment_operator assignment_exp
;
assignment_operator : ‘=’ | ‘=’ | ‘/=’ | ‘%=’ | ‘+=’ | ‘-=’ | ‘<<=’
| ‘>>=’ | ‘&=’ | ‘^=’ | ‘|=’
;
conditional_exp : logical_or_exp
| logical_or_exp ‘?’ exp ‘:’ conditional_exp
;
const_exp : conditional_exp
;
logical_or_exp : logical_and_exp
| logical_or_exp ‘||’ logical_and_exp
;
logical_and_exp : inclusive_or_exp
| logical_and_exp ‘&&’ inclusive_or_exp
;
inclusive_or_exp : exclusive_or_exp
| inclusive_or_exp ‘|’ exclusive_or_exp
;
exclusive_or_exp : and_exp
| exclusive_or_exp ‘^’ and_exp
;
and_exp : equality_exp
| and_exp ‘&’ equality_exp
;
equality_exp : relational_exp
| equality_exp ‘==’ relational_exp
| equality_exp ‘!=’ relational_exp
;
relational_exp : shift_expression
| relational_exp ‘<’ shift_expression
| relational_exp ‘>’ shift_expression
| relational_exp ‘<=’ shift_expression
| relational_exp ‘>=’ shift_expression
;
shift_expression : additive_exp
| shift_expression ‘<<’ additive_exp
| shift_expression ‘>>’ additive_exp
;
additive_exp : mult_exp
| additive_exp ‘+’ mult_exp
| additive_exp ‘-‘ mult_exp
;
mult_exp : cast_exp
| mult_exp ‘
‘ cast_exp
| mult_exp ‘/‘ cast_exp
| mult_exp ‘%’ cast_exp
;
cast_exp : unary_exp
| ‘(‘ type_name ‘)’ cast_exp
;
unary_exp : postfix_exp
| ‘++’ unary_exp
| ‘–’ unary_exp
| unary_operator cast_exp
| ‘sizeof’ unary_exp
| ‘sizeof’ ‘(‘ type_name ‘)’
;
unary_operator : ‘&’ | ‘*’ | ‘+’ | ‘-‘ | ‘~’ | ‘!’
;
postfix_exp : primary_exp
| postfix_exp ‘[‘ exp ‘]’
| postfix_exp ‘(‘ argument_exp_list ‘)’
| postfix_exp ‘(‘ ‘)’
| postfix_exp ‘.’ id
| postfix_exp ‘->’ id
| postfix_exp ‘++’
| postfix_exp ‘–’
;
primary_exp : id
| const
| string
| ‘(‘ exp ‘)’
;
argument_exp_list : assignment_exp
| argument_exp_list ‘,’ assignment_exp
;
const : int_const
| char_const
| float_const
| enumeration_const
;


## 参考

[iamsile-c语言的bnf总结](https://blog.csdn.net/sileiam/article/details/16362719)

[HubbardHuang-C/C++ 函数调用原理](https://www.jianshu.com/p/9f547d8428c3)