关闭

C语言——符号的声明与定义

标签: c语言
61人阅读 评论(0) 收藏 举报
分类:

C语言之所以博大精深,是因为想要真正理解它,就要理解它背后故事。我们知道从源代码到可执行文件的过程分四步:预编译,编译,汇编,链接,而其中核心在于编译(预编译是相对简单的文本操作,汇编是一个相对简单的映射,链接是符号在地址空间中的最终定位)。编译是以文件为单元进行的(每个经过预处理之后的文件都是单独编译,生成一个.o文件)。编译的过程就是编译器理解C的过程,也是C在语言层面上的灵魂所在。本文主要说明符号(idendifier)的声明(declaration)与定义(definition)是怎么回事。


先来个例子吧,比如有这么一个文件func.c:

int var_global;
extern int var_extern;

int func()
{
    int add(int arg1, int arg2);
    int var_local;
    var_global = 100;
    var_local = add(var_global, var_extern);

    return var_local;
}

这里出现了五个符号:var_global,var_extern,func,add和var_local。编译器需要知道这些符号在哪些地方是有意义的(比如这里的var_local在func外面没有意义)以及它们有什么意义,然后决定要不要针对这些符号做些什么(比如分配空间,怎么初始化这些空间等等)。

  • “int var_global;”告诉编译器:var_global是一个int型的变量,需要给它在全局变量区分配空间并初始化为0,它的合法范围是当前文件(在当前文件后面再遇到var_global的时候就可以用它了)。所以我们说:这里声明并且定义了一个全局变量var_global。
  • “extern int var_extern;”告诉编译器:var_extern是一个int型的变量,不要给它分配空间(它的空间一定已经在别的地方分配好了),它的合法范围是当前文件。所以我们说:这里声明了一个全局变量var_extern。
  • “int func() {...}”告诉编译器:func是一个参数为空并且返回int的函数,而且后面跟了函数体的具体定义,需要编译成最终的指令(为它在全局代码区分配一个空间来装编译它得到的指令),它的合法范围是当前文件。所以我们说:这里声明并定义了一个函数func。
  • “int add(int arg1, int arg2);”告诉编译器:add是一个参数为(int,int)并且返回int的函数,没有跟函数的定义,合法范围是当前函数体(即func内)。所以我们说:这里声明了一个函数add。
  • “int var_local;”告诉编译器:var_local是一个int型的变量,需要在栈上给它分配空间,不用对它初始化,它的合法范围是当前函数体。所以我们说:这里声明并定义了一个局部变量var_local。
  • “var_global = 100;”即不是声明也不是定义,而是对已经声明了的符号(var_global)的使用。由于前面我们通过“int var_global;”声明了var_global,编译器就已经知道足够的信息来使用它了。同理,“var_local = add(var_global, var_extern);”和“return var_local;”也是对已经声明了的var_local,var_extern和add的使用。编译器知道var_local是一个int变量,var_extern是一个int变量,add是一个接受两个int并且返回一个int的函数,这些信息足够编译器正确地编译。至于这几个符号是在哪里定义的,在编译这三个语句的时候编译器并不关心。

所以,对于文件中所有出现的符号,编译器都已经知道了足够的信息,因而单独编译这个文件是可以通过的(如果没有任何一个地方提供对var_extern和add的定义,则链接步骤会出问题)。我们可以在其它代码里使用func,比如在main.c中:

#include <stdio.h>

int var_extern = 1000;

int func();

int add(int arg1, int arg2)
{
    return arg1 + arg2;
}

main() 
{
    int var = func();
    printf("value is: %d\n", var);
}

  • “#include <stdio.h>”引用了stdio.h的内容,里面有对下文中用到的printf的声明。
  • “int var_extern = 1000;”声明并定义了var_extern,并且初始化其为1000。
  • “int func();”声明了func,这个函数的定义在别处(在func.c中)。
  • “int add(int arg1, int arg2) {...}”声明并定义了add。

那么编译这两个文件生成的可执行文件执行起来就会打出:“value is: 1100”。


所以,通俗地讲:“声明就是帮助编译器理解和使用一个符号,定义就是给这个符号分配了东西”。


0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:362次
    • 积分:36
    • 等级:
    • 排名:千里之外
    • 原创:3篇
    • 转载:2篇
    • 译文:0篇
    • 评论:0条
    文章分类
    文章存档