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

原创 2015年11月18日 10:50:56

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”。


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


版权声明:本文为博主原创文章,转载请注明出处http://blog.csdn.net/tsx851。

C语言中的定义和声明的区别

变量的定义用于为变量分配存储空间,还可以为变量指定初始值。在一个程序中,变量有且仅有一个定义。 声明用于向程序表明变量的类型和名字,定义包括声明:当定义变量时我们声明了它的类型和名字。可以通过使...
  • linux12121
  • linux12121
  • 2016年05月31日 22:53
  • 276

C语言中声明和定义详解

变量声明和变量定义 变量定义:用于为变量分配存储空间,还可为变量指定初始值。程序中,变量有且仅有一个定义。 变量声明:用于向程序表明变量的类型和名字。 定义也是声明,extern声明不是定义 ...
  • gatieme
  • gatieme
  • 2016年02月06日 16:56
  • 7796

C语言中的声明与定义的区别

1、对于下面的声明语句 int a;       如果其位置出现在所有的函数体之外,那么它就被称为外部对象a的定义。这个语句说明了a是一个外部整型变量,同时为a分配了存储空间。因为外部对象a并没有被...
  • kerry0071
  • kerry0071
  • 2014年06月23日 08:54
  • 3817

C语言结构体类型声明和定义标准形式

结构体类型声明和定义的标准形式
  • JQ_AK47
  • JQ_AK47
  • 2016年01月19日 14:57
  • 7529

C语言中声明、定义和初始化的区别

此外,其声明用于说明变量的属性(主要是变量的类型),且可多次声明;而变量的定义除此以外还将引起存储器的分配,且只能定义一次。 注意:定义是一种特殊的声明。 int sp; double val[MAX...
  • Mark_meego
  • Mark_meego
  • 2015年05月02日 17:03
  • 5346

C语言中变量的复杂声明和定义

我们知道:任何C语言变量的声明都由两部分组成:类型以及一组类似表达式的声明符。一旦我们知道如何声明一个给定类型的变量,那么该类型的类型转换符就很容易得到:只需要把声明中的变量名和末尾的分号去掉,再将剩...
  • GHX05130319
  • GHX05130319
  • 2015年05月08日 09:17
  • 1200

C语言中声明和定义的区别——分析extern关键词。

一直很迷惑C语言中的声明和定义的有些实践中的用法,说迷惑实践是因为声明和定义的概念上的区别是很明确的。 定义和声明的区别(主要针对变量): 定义是要为变量分配存储空间,还可以在定义的时候为变量指定...
  • littledouble
  • littledouble
  • 2015年06月03日 08:28
  • 1843

C语言全局变量定义与声明技巧

在实际的编程中,全局变量对我们编程来说既是喜又是泪,虽然说项目中尽量避免使用全局变量,但总有些时候不得不使用它,并且可能使用得不少,各个模块可能有含有或多或少的几个全局变量,而当别的模块需要引用的时候...
  • fukai555
  • fukai555
  • 2013年09月08日 23:16
  • 2404

C语言定义和声明的区别

定义和声明的根本区别: 定义创建了对象并为之分配了内存,声明没有分配内存! C语言中有32个关键字:如下表 1.auto:编译器在默认的缺省情况下,所有变量都是aut...
  • Jackiellx
  • Jackiellx
  • 2016年06月18日 23:55
  • 191

C语言指针详解----指针声明定义赋值

C语言的指针是让新手很头疼的事情,但是由于其太过于灵活,以至于可以很好得的解决一些复杂的问题,因此不得不掌握。我最近正在学习指针相关的内容,因此在这里做一个小的总结。本篇是不涉及到函数以及结构体等复杂...
  • sinat_35821976
  • sinat_35821976
  • 2017年02月24日 10:22
  • 461
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:C语言——符号的声明与定义
举报原因:
原因补充:

(最多只允许输入30个字)