在UNIX系统中,实现C源程序到可执行文件的这一转换过程的工具是cc。在大多数系统中cc实际上是一个shell命令文件。有些系统中的C编译程序可能并不叫cc而是其它的一个什么名称,如Sun工作站上常用的gcc等等。但这些都无关紧要。大多数系统中C编译命令的用法基本上都是类似的。我们这里介绍的将以SVR4上的C编译系统为基础。
cc基本用法
一般我们只需要将C源程序的名字写在CC命令行中,cc即可对这些源文件(.c文件)进行编译。如果这些源文件中都没有main()函数的定义,那么cc将只能生成与各源文件相对应的目标文件(.o文件)。如果某个源文件中有关于main()函数的定义,则将把所有目标文件链接起来生成相应的可执行文件。缺省的情况下这个可执行文件的名字将是a.out。
例如,假定myprog.c是一个包含有main()函数定义的C语言程序文件,其中代码如下:
/*********************************************
* An example source code with errors *
* Name:myprog.c *
********************************************* /
#include <stdio.h>
#include <ctype.h>
# define TESTOK 1
int TestInput(char * ValuInput)
{while (* ValueInput)
if (! isdigit(* ValueInput )) return (! TESTOK);
else ValueInput + +;
return ((100/atoi(ValueInput))? TESTOK:! TESTOK);
}
void
main(int argc,char * argv[])
{int i;
for (i=1;i<argc;;i++)
if(TestInput (argv[i]) = =TESTOK)
printf("The %dth ">value '%s' \tis OK! \n",i,argv[i]);
else
printf("The %dth value '%s' \tis BAD! \n" ,i,argv[i]);
}
对于此程序中的错误(设计错误)我们暂不理会。下一章我们介绍程序调试时再回过头来看看如何排除这个错误。
我们看到。在这个源程序文件中,定义了两个函数:TestInput()和main(),定义了一个宏TESTOK,同时包含了两个标准的头文件。为了把这个C程序转换成可执行文件,在shell提示符下输入:
$cc myprog.c
在程序中没有任何语法错误的情况下,cc将在当前目录下生成一个名为a.out的可执行文件,如:
$ cc myproc.c
$ ls -l
-rwx------ 1 yxz users 5812 Aug 31 15:32 a.out
-rw------- 1 yxz users 716 Aug 31 15:27 myproc.c
$
还可以看到这里a.out是一个可执行文件。当然这个程序由于在设计上有些失误,我们现在还不能马上就带参数运行。但不带参数运行还是可以的。只不过此时该程序什么都没有干,如:
$ a.out
$
在程序中我们通过main函数的两个参数argc和argv而使程序能够引用shell命令行参数;这是UNIX环境下一种常用的编程技术。
在生成了a.out文件之后,我们自然可用mv命令将其修改为某个合适的名称。但更简单的方法是在cc命令行中加上-o选项,使cc直接将可执行文件写入到指定的文件中而不生成a.out文件,如:
$ cc -o myprog myprog.c
$ ls -l myprog
total 14
-rwx------ 1 yxz users 5812 Aug 31 15:34 myprog
-rw------- 1 yxz users 716 Aug 31 15:27 myprog.c
$
我们看到,myprog这个文件除了文件名及修改时间同a.out不一样外,其他属性同a.out都是一摸一样的。这也说明了两者的等价性。
在某个程序的源代码被存放到多个不同文件中的情况下,我们只需要在命令行中一一指定这多个C文件即可。例如,我们可以将上述myprog.c拆分为两个C文件和一个头文件(.h)如下:
myprog.h
# include <stdio.h>
# inclued <ctype.h>
# define TESTOK 1
myprog.c
#include "myprog.h"
void
main (int argc,char * argv[])
{int i;
for (i=1;i<argc;i + +)
if (TestInput(argv[i])= = TESTOL)
printf("The %dth value '%s' \tis ok! \n",argv[1]);
else
printf("The %dth value ' %s' \tis BAD! \n",iargv[i]);
}
myfunc.c
#include "myprog.h"
int
TestInput(char * ValueInput)
{while (* ValueInput)
if (!isdigit(*ValueInput) return (! TESTOK);
else ValueInput + +
return ((100/atoi(ValueInput))? TESTOK:! TESTOK);
这时要再编译此程序时可输入如下命令:
$ cc -o myprog myprog.c myfunc.c
在这个命令行中如果不指定myfunc.c,此时由于在myprog.c中所调用的TestInput()这个函数不是任何标准的库函数,在链接时链接程序将找不到此符号的定义,故链接过程将以失败而告终,此时cc将给出如下的错误信息:
Undefine first referenced
symbol in file
TestInput myprog.o
id: myprog:fatal error: Symbol referencing errors.No output written to myprog
$
而可执行文件myprog也无法生成。但编译却会生成myprog.c的目标代码(在某个文件固有语法错误而无法正确被编译的情况下(此时为编译过程出错),cc将生成其他无语法错误的源文件的目标文件,但不进行链接)。如下:
$ ls -l
total 8
rw-r--r-- 1 yxz user 454 Sep 1 09:27 myfunc.c
rw-r--r-- 1 yxz user 479 Sep 1 09:28 myprog.c
rw-r--r-- 1 yxz user 298 Sep 1 09:27 myprog.h
rw-r--r-- 1 yxz user 924 Sep 1 09:28 myfunc.o
此时我们可以使用如下命令行得到可执行文件:
$ cc -o myprog myprog.o myfunc.c
这里我们看到,cc命令行中的文件参数可以不全是.c文件,目标文件(.o)文件以后编译过程中所得到的其他文件,如预编译后文件(.i文件),编译后的汇编程序(.s文件)等都可作为文件参数。在了解了UNIX C编译系统的工作过程之后,理解这一点是不困难的。因为编译系统只需要对各种不同类型的文件进行有关的处理就可以了。