Linux GCC的使用与开发

为什么要编译程序?
• 高级程序语言(C、 fortran)需要通过编译器的编译处理才能生成可执行文件。脚本语言( matlab、 shell)只需要通过脚本命令解释器进行命令解析就可以执行。


1、GCC 的使用

• 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

注意参数大小写!!

2、GCC开发c语言程序

1.单个文件的C语言程序情形
• 利用命令 gcc hello.c –o hello 生成可执行文件hello
• 再利用命令./hello运行该程序。 注意程序正确运行的前提是源程序没有错误。
2.多个文件的C语言程序情形
• 多个文件的目录结构如下:
main_function.csummation.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缩进)命令

  • 目标可以是一个目标文件, 也可以是一个可执行文件, 还可以是一个标签(Label)
  • 依赖项列表就是要生成那个目标所需要的文件或者目标。
  • Tab缩进就是自文本最左端按一下tab键光标向右移动的距离。这个缩进是必须且唯一的,不能用空格键移动同等距离。
  • 命令就是make需要执行的命令。(如任意的shell命令)

这是一个文件依赖关系,亦即,目标依赖于列表中的文件
目标的生成规则被定义在命令中。如果依赖列表有一个及以上的文件比目标文件新,那么命令部分就会被执行。
这就是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
• 删除动态库,程序无法运行

3、程序调试

程序调试分为静态调试和动态调试
• 静态调试涉及编译时源程序中的语法错误
• 动态调试涉及程序运行时由于程序算法或流程涉及错误所致的错误,如死循环
若成功生成了了可执行文件,则可使用gdb进行动态调试:

gdb ./newtest
l        #list 显示源代码
break 4  #第4行设置断点
r        #run  运行源代码,将在有break 的地方停下来
n        #next 执行到下一行
p var    #打印变量var的值
Ctrl+z/c #退出gdb,修改c文件

4、例子

快捷使用

gcc hello.c -o hello     #c程序
g++ hello.cpp -o hello   #c++程序

下面提供一个gcc编译运行多个文件的例子:
文件结构:当前目录有俩c文件:main.c、summation.c,当前目录有个子目录叫include,里面有个h文件:summation.h

1. 文件内容

各个文件内容如下所示:
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);

2. 调试运行

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

猜你喜欢

转载自blog.csdn.net/Gou_Hailong/article/details/109745963