为什么要编译程序?
• 高级程序语言(C、 fortran)需要通过编译器的编译处理才能生成可执行文件。脚本语言( matlab、 shell)只需要通过脚本命令解释器进行命令解析就可以执行。
• GCC (GNU project C and C++ compiler)
• GCC用于编译c或者C++ 程序, Fortran程序用gfortran或者ifort编译器来编译。
• C/C++程序从开始编码到生成可执行的二进制文件有四步: 1. 预处理; 2.编译; 3.汇编; 4.链接
• 通过设置GCC参数,可以分步骤完成以上四步。或者设置参数一次性完成整个步骤。
• GCC常用选项
选项 | 含义 |
---|---|
-o file |
将经过GCC处理过的结果存为file,这个结果文件可能是预处理文件、汇编问价、目标文件或者最终的可执行文件。假设被处理的源文件为source.suffix ,如果这个选项被省略了,那么生成的可执行文件默认名称为a.out ;目标文件默认名为source.o ;汇编文件默认名为source.s ;生成的预处理文件则发送到标准输出设备 |
-c |
仅对源文件进行编译,不链接生成可执行文件。在对源文件进行查错时,或只需产生目标文件时可以使用该选项 |
-g[gdb] |
在可执行文件中加入调试信息,方便进行程序的调试。如果使用中括号中的选项,表示加入gdb扩展的调试信息,方便使用gdb来进行调试 |
-O [0、1、2、3] |
对生成的代码使用优化,中括号中的部分为优化级别,默认的情况为2级优化,0为不进行优化。注意,采用更高级的优化并不一定得到效率更高的代码 |
-Dname [=definition] |
将名为name的宏定义为definition,如果中括号中的部分缺省,则宏被定义为1 |
-Idir |
在编译源程序时增加一个搜索头文件的额外目录——dir,即include增加一个搜索的额外目录 |
-Ldir |
在编译源文件时增加一个搜索文件的额外目录——dir |
-llibrary |
在编译链接文件时增加一个额外的库,库名为library.a |
-w |
禁止所有警告 |
-Wwarning |
允许产生warining类型的警告,warning可以是main、unused等很多取值,最常用的是all,表示产生所有警告。如果warning取值为error,其含义是将所有警告作为错误(error),即出现警告就停止编译 |
GCC的基本使用格式:
gcc [选项] <文件名>
命令 | 含义 |
---|---|
gcc -E hello.c -o hello.i |
生成预处理文件hello.i |
gcc -S hello.c -o hello.s |
生成汇编文件hello.s |
gcc -c hello.c -o hello.o |
生成目标文件hello.o |
gcc hello.c -o hello |
生成可执行文件hello |
注意参数大小写!!
1.单个文件的C语言程序情形
• 利用命令 gcc hello.c –o hello
生成可执行文件hello
• 再利用命令./hello运行该程序。 注意程序正确运行的前提是源程序没有错误。
2.多个文件的C语言程序情形
• 多个文件的目录结构如下:
• main_function.c
和 summation.c
在当前目录下
• summation.h
在当前目录的下一级目录include
下
多个文件的c语言编译运行:
gcc ‐c main_function.c ‐Iinclude ‐o main_function.o
gcc ‐c summation.c ‐Iinclude ‐o summation.o
gcc main_function.o summation.o ‐o main_function
./main_function
也可以将上面这些代码贴到一个.sh
脚本中,运行这个脚本,从而实现对多个文件的c语言的编译运行。
• Shell脚本管理不失为一种管理多个C程序编译运行的方法。但是,如果C文件过多,我们如何管理程序编译?
• Linux为我们提供了make工具实现程序的编译。但是, 前提是需要我们事先写好Makefile文件。
• 什么是makefile?makefile就是规定
• 程序编译和链接的一整套规则:哪些文件需要先编译,哪些文件需要重新编译,乃至执行操作系统的命令。好处是“自动化编译”。一旦写好makefile,只需要一个make,整个工程就可以实现自动编译,极大地提高软件开发的效率。
Makefile的书写规则:
• 目标:依赖项列表
• (Tab缩进)命令
这是一个文件依赖关系,亦即,目标依赖于列表中的文件
目标的生成规则被定义在命令中。如果依赖列表有一个及以上的文件比目标文件新,那么命令部分就会被执行。
这就是makefile的执行规则,也是makefile中最核心的内容。
ok,下面尝试一下将上面的.sh
脚本写成makefile:
main_function:main_function.o summation.o
gcc main_function.o summation.o -o main_function
main_function.o:main_function.c ./include/summation.h
gcc -c main_function.c -I./include -o main_function.o
summation.0:summation.c ./include/summation.h
gcc -c summation.c -I./include -o summation.o
clean:
rm -rf *.o
文件依赖关系:
1.可执行文件main_funciton依赖于目标文件main_function.o和summation.o
2.目标文件main_function.o依赖于源程序文件main_function.c和头文件summation.h
3.目标文件summation.o依赖于源程序文件summation.c和头文件summation.h
4.clean目标是个伪目标,类似于Fortran中goto语句用到的标签(Label)。
特别要注意的是gcc, rm命令前的空格是一个tab制表符位置!
从上面可以看到,o文件出现了多次,其文件名也很长,这样表示起来比较费事,所以我们可以用个变量来代替o文件的文件名。例如:
OBJ=main_function.o \ #定义变量OBJ表示o文件,\表示换行符
summation.o
main_function:$(OBJ)
gcc $(OBJ) -o main_function
main_function.o:main_function.c ./include/summation.h
gcc -c main_function.c -I./include -o main_function.o
summation.0:summation.c ./include/summation.h
gcc -c summation.c -I./include -o summation.o
clean:
rm -rf *.o
makefile文件的命名
• 当在终端输入不带参数的make命令时,会
在当前目录下寻找名为GNUmakefile、makefile、 Makefile,进行编译连接生成可执行文件
• 当makefile文件是其他名称时,如makefile1
• 则需要在终端输入带参数的make命令:make ‐f makefile1
静态链接库(static library)
• 静态库在链接阶段被链接,所生成的可执行文件就不受库的影响,删除库文件,程序依然可以执行。
ar ‐r libhello.a hello.o
• 静态库约定命名libXXX.a
gcc ‐c hello.c
生成目标文件hello.o
ar ‐r libhello.a hello.o
创建静态链接文件libhello.a
gcc ‐o test test.c ‐L. –lhello
编译链接文件test
./test
执行可执行文件
删除库文件libhello.a
后 ./test
仍可执行
动态链接库(dynamic library)
• 动态库是在程序运行时被链接.删除动态链接库,编译链接通过的程序将不能运行.
• gcc ‐shared hello.c ‐o libhello.so
• 动态库直接从源文件创建
• 动态库约定命名libXXX.so
• 删除动态库,程序无法运行
程序调试分为静态调试和动态调试
• 静态调试涉及编译时源程序中的语法错误
• 动态调试涉及程序运行时由于程序算法或流程涉及错误所致的错误,如死循环
若成功生成了了可执行文件,则可使用gdb进行动态调试:
gdb ./newtest
l #list 显示源代码
break 4 #第4行设置断点
r #run 运行源代码,将在有break 的地方停下来
n #next 执行到下一行
p var #打印变量var的值
Ctrl+z/c #退出gdb,修改c文件
快捷使用:
gcc hello.c -o hello #c程序
g++ hello.cpp -o hello #c++程序
下面提供一个gcc编译运行多个文件的例子:
文件结构:当前目录有俩c文件:main.c、summation.c
,当前目录有个子目录叫include
,里面有个h文件:summation.h
各个文件内容如下所示:
main.c
#include "stdio.h"
#include "summation.h"
int main()
{
double a=1.0;
double b=2.0;
double c;
c=summation(a,b);
printf("a=%5.1f, b=%5.1f, a+b=%5.1f\n",a,b,c);
}
summation.c
#include "summation.h"
double summation(double a, double b)
{
double c;
c=a+b;
return c;
}
summation.h
double summation(double a, double b);
main.sh
创建运行 main.sh 的步骤:
vi main.sh
chmod +x main.sh
./main.sh
main.sh的内容:
#!/bin/bash
gcc -c main.c -Iinclude -o main.o
gcc -c summation.c -Iinclude -o summation.o
gcc main.o summation.o -o main
./main
上面是搞到一个.sh文件中的,也可以将上面.sh文件中的内容逐行复制粘贴到shell里面运行。
makefile
main:main.o summation.o
gcc main.o summation.o -o main
main.o:main.c ./include/summation.h
gcc -c main.c -I./include -o main.o
summation.o:summation.c ./include/summation.h
gcc -c summation.c -I./include -o summation.o
clean:
rm -rf *.o
运行步骤:
make #编译运行,直接出结果
make clean #删除所有o文件
懒人变量版makefile
mc=main.c
mo=mian.o
sc=summation.c
so=summation.o
sh=summation.h
shp=./include/$(sh)
main:$(mo) $(so)
gcc $(mo) $(so) -o main
$(mo):$(mc) $(shp)
gcc -c $(mc) -I./include -o $(mo)
$(so):$(sc) $(shp)
gcc -c $(sc) -I./include -o $(so)
clean:
rm -rf *.o