第1部分 重新认识C语言

makefile文件的书写及应用

 

【文章摘要】

        makefile用于Linux下整个工程的编译,对于Linux下的C/C++语言的编译是至关重要的。

       本文以实际的C源程序为例子,介绍如何使用makefile来编译Linux下的C语言工程,为相关开发工作的开展提供了参考。

 

【关键词】

        makefile  C语言  Linux  编译  开发

 

一、什么是makefile

        makefile是什么?如果你写的程序只是在Windows下运行,那么很有可能不知道有这个玩意儿。而如果你要在Linux下编译并运行程序,那么你几乎不可避免地要和makefile打交道。

        makefile是一个文件,里面定义了一系列的规则来指定一个工程中的哪些文件需要先编译,哪些文件需要后编译,哪些文件需要重新编译,甚至进行更为复杂的功能操作。简单点说,makefile就是Linux下的一个编译调度器。

 

二、makefile的语法规则

        既然是一个编译调度器,那么它就会有自己的一套规则。makefile的规则如下:

target ... : prerequisites ...

command

......

......

 

       说明:

       (1) target就是一个目标文件,也就是在这个步骤中,我们想要输出的文件名(可以包括后缀)

       (2) prerequisites是要生成那个target所需要的文件,相当于在Windows工程下我们要得到exe文件所需要的源代码文件。

       (3) commandmake需要执行的命令,也就是如何利用prerequisites来生成target。注意,command行要以TAB键开头。

 

三、C程序源代码

        本文以一个实际的Linux下的C程序工程为例,介绍makefile的编写方法及用法。

        本文中的程序实现将一个字符串中的小写字母转换成大写字母的功能。有两个头文件:DataType.hFunDeclaration.h,放在“Header”目录下;有两个源文件:Main.cLowerToUpper.c,分别放在“Main”和“LowerToUpper”目录下。

        该工程MakeFileExample的组织形式如图1所示(release”目录用于存放生成的文件)


1 该工程的组织形式

 

1. DataType.h”文件代码内容

/**********************************************************************
* 版权所有 (C)2014, Zhou Zhaoxiong。
*
* 文件名称: DataType.h
* 文件标识: 无
* 内容摘要: 重新定义数据类型
* 其它说明: 无
* 当前版本: V1.0
* 作    者: Zhou Zhaoxiong
* 完成日期: 20140410
*
**********************************************************************/
#ifndef _DATATYPE_H_       // 防止头文件被重复引用
#define _DATATYPE_H_

#include 
   
   
    
    
#include 
    
    
     
     
#include 
     
     
      
      

// 重新定义数据类型
typedef unsigned char  UINT8;
typedef          int   INT32;
typedef unsigned int   UINT32;

#endif

     
     
    
    
   
   

 

2. FunDeclaration.h”文件代码内容

/**********************************************************************
* 版权所有 (C)2014, Zhou Zhaoxiong。
*
* 文件名称: FunDeclaration.h
* 文件标识: 无
* 内容摘要: 对函数进行声明
* 其它说明: 无
* 当前版本: V1.0
* 作    者: Zhou Zhaoxiong
* 完成日期: 20140410
*
**********************************************************************/
#ifndef _FUNDECLARATION_H_       // 防止头文件被重复引用
#define _FUNDECLARATION_H_

#include 
   
   
    
    
#include 
    
    
     
     
#include 
     
     
      
      

// 对函数进行声明
INT32 StrToUpperCase(UINT8 *pszInStr, UINT32 iInLen); 
INT32 main();

#endif

     
     
    
    
   
   


3. LowerToUpper.c”文件代码内容

/**********************************************************************
* 版权所有 (C)2014, Zhou Zhaoxiong。
*
* 文件名称: LowerToUpper.c
* 文件标识: 无
* 内容摘要: 将输入字符串中的小写字母变成大写字母的函数实现
* 其它说明: 无
* 当前版本: V1.0
* 作    者: Zhou Zhaoxiong
* 完成日期: 20140410
*
**********************************************************************/

// 包含头文件
#include "../Header/DataType.h"
#include "../Header/FunDeclaration.h"

/**********************************************************************
* 功能描述: 将字符串中的小写字母变为大写字母
* 输入参数: *pszInStr-输入/输出字符串
             iInLen-字符串长度
* 输出参数: *pszInStr-输入/输出字符串
* 返 回 值: 0-成功  -1-失败
* 其它说明: 无
* 修改日期          版本号      修改人             修改内容
* ------------------------------------------------------
* 20140410        V1.0     Zhou Zhaoxiong        创建
***********************************************************************/
INT32 StrToUpperCase(UINT8 *pszInStr, UINT32 iInLen)
{
    UINT32 iLoopFlag = 0;

    if (pszInStr == NULL)       // 异常保护, 判断输入字符串是否为空
    {
        printf("Input string is NULL!");

        return -1;              // 返回-1表示该函数执行失败
    }

    for (iLoopFlag = 0; iLoopFlag < iInLen; iLoopFlag ++)
    { 
        pszInStr[iLoopFlag] = toupper(pszInStr[iLoopFlag]);
    }

    return 0;                  // 返回0表示该函数执行成功
}

 

4. Main.c”文件代码内容

/**********************************************************************
* 版权所有 (C)2014, Zhou Zhaoxiong。
*
* 文件名称: Main.c
* 文件标识: 无
* 内容摘要: 将输入字符串中的小写字母变成大写字母的主函数
* 其它说明: 无
* 当前版本: V1.0
* 作    者: Zhou Zhaoxiong
* 完成日期: 20140410
*
**********************************************************************/

// 包含头文件
#include "../Header/DataType.h"
#include "../Header/FunDeclaration.h"


/**********************************************************************
* 功能描述: 主函数
* 输入参数: 无
* 输出参数: 无
* 返 回 值: 无
* 其它说明: 无
* 修改日期        版本号       修改人             修改内容
* ---------------------------------------------------------------------
* 20140410        V1.0     Zhou Zhaoxiong        创建
***********************************************************************/
INT32 main()
{
    UINT8  szString[100] = {0};    // 用于存放字符串, 在定义的同时进行初始化
    UINT32 iLoopFlag     = 0;      // 用于表示循环变量, 在定义的同时进行初始化
    UINT32 iStrLen       = 0;      // 用于表示字符串长度, 在定义的同时进行初始化
    UINT32 iRetVal       = 0;      // 表示调用函数的返回值, 在定义的同时进行初始化
    
    printf("Input the source string: ");
    scanf("%s", szString);                        // 读入原始字符串(中间不带空格)

    iStrLen = strlen(szString);

    iRetVal = StrToUpperCase(szString, iStrLen);            // 调用封装好的函数
    if (iRetVal == -1)
    {
        printf("exec StrToUpperCase failed!");

        return -1;              // 返回-1表示调用StrToLowerCase函数执行失败
    }

    printf("Output the destination string: %s\n", szString);   // 输出目的字符串

    return 0;               // main函数返回0
}

 

四、makefile文件的内容

       为了对本工程文件进行正确的编译,makefile文件可以如下编写:

LowerToUpper : Main/Main.c LowerToUpper/LowerToUpper.c
gcc -c -g Main/Main.c
gcc -c -g LowerToUpper/LowerToUpper.c
gcc -g -o release/LowerToUpper Main.o LowerToUpper.o
rm *.o

 

       说明:

         (1) 本文件可以命名为“makefile”或“Makefile”,不能有后缀,也只能有第一个字母是大写的。

         (2) 在第一行,我们最终生成的文件名为“TestMakeFile”,要生成该文件,需要两个源文件“TestMakeFileMore1.c”和“TestMakeFileMore2.c”;对比makefile文件语法规则,target为“TestMakeFile”,prerequisites为“exec1/TestMakeFileMore1.c”和“exec2/TestMakeFileMore2.c”。

         (3) 从第二行开始是命令行,即command

         (4) 第二行和第三行要以TAB键开头,是对两个源文件进行编译,生成.o文件的语句。“-g”是为了调试用的,“-c”用于产生.o文件(就是obj文件),不产生执行文件。“gcc -c -g exec1/TestMakeFileMore1.c”的结果是生成“TestMakeFileMore1.o”,“gcc -c -g exec2/TestMakeFileMore2.c”的结果是生成“TestMakeFileMore2.o”。第二行和第三行可以互换位置。

        (5)第四行也要以TAB键开头,是将“TestMakeFileMore1.o”和“TestMakeFileMore2.o”编译生成“TestMakeFile”的语句。“-o outputfilename”,让输出文件的名称为“outputfilename”,而这个名称不能和已有的文件重名。在本例中,我们将“TestMakeFile”文件放到了release目录下。如果想把生成文件放到任意目录下,都可以用此方法来实现。

       (6) 第五行也要以TAB键开头,用于删除.o文件。因为在编译过程中,有很多.o文件生成(本例中包括“TestMakeFileMore1.o”和“TestMakeFileMore2.o),如果我们不将它们清除掉,它们会一直留在工程目录下。本语句相当于是一个完成任务后的清理工作。当然,如果想对.o文件进行专门的清除,那么makefile文件可以如下编写:

LowerToUpper : Main/Main.c LowerToUpper/LowerToUpper.c
gcc -c -g Main/Main.c
gcc -c -g LowerToUpper/LowerToUpper.c
gcc -g -o release/LowerToUpper Main.o LowerToUpper.o
clean:
rm *.o

        在Linux下输入“make clean”命令即可将所有的o文件清除掉。

       (7) 如果源文件和头文件为单个或多个,可参照本makefile进行编写。

 

五、makefile文件运行过程及程序执行结果

        按照如图1所示的目录结构组织本工程,并将之上传到Linux机器上。

1. makefile文件运行过程

       登录到Linux机器上,在makefile文件所在目录下输入“make”命令,执行结果如下:

gcc -c -g Main/Main.c
gcc -c -g LowerToUpper/LowerToUpper.c
gcc -g -o release/LowerToUpper Main.o LowerToUpper.o
rm *.o

 

        只要没有出现报错信息,那么代码和makefile的编写就是正确的。

        如果代码中有语法问题,那么输入“make”命令后,在运行的结果中会打印代码出现问题的行数,方便对问题进行修改。这与VC的编译功能很相似。

 

2. 程序执行结果

       转到release目录下(执行cd release命令),输入“./TestMakeFile”命令,执行结果如下:

Input the source string: aBcDeFg

Output the destination string: ABCDEFG

 

       可见,达到了将小写字母变成大写字母的功能。

 

六、总结

        本文对makefile文件的语法规则进行了介绍,并用一个实际的C程序工程来说明了它的用法。

        在编写和使用makefile文件的过程中,我们要注意以下问题:

        (1) 要牢记makefile的语法,将不同程序文件的依赖关系理清楚。

        (2) 命令(command)行一定要以TAB键开头,不要误写成了空格。

        (3) makefile文件的最后,要将没有用的中间文件(如本例中的.o文件)清理掉。

        (4) 对于make命令中出现的错误或警告,一定要尽量修改。

 

        不管是Windows下的程序员也好,还是Linux下的程序员也罢,一定要对makefile有一定的了解,这可以从侧面反映一个程序员的专业程度。

 

 

 

(欢迎访问南邮BBS:http://bbs.njupt.edu.cn/)
(欢迎访问重邮BBS:http://bbs.cqupt.edu.cn/nForum/index)

(本系列文章每周更新两篇,敬请期待!本人微博:http://weibo.com/zhouzxi?topnav=1&wvr=5,微信号:245924426,欢迎关注!)

Logo

更多推荐