1.gcc组成
gcc是一组编译工具的总称,包含:C编译器、C++编译器、源码预处理程序和库文件。
2.gcc编译
1.生成一个程序
gcc hello.c -o hello把hello.c编译成一个可执行程序、
gcc hello.c不指定输出名,生成一个a.out文件。
通过./hello或者./a.out来运行程序。
2.gcc编译程序步骤
预处理:gcc -E hello.c -o hello.i
完成对代码的预处理
编译:gcc -S hello.i -o hello.s
将源代码编译成汇编代码
汇编:gcc -c hello.c -o hello.o
将汇编代码转换成目标文件(二进制)
链接:gcc hello.o -o hello
将目标代码和所需要的库链成一个完整的应用程序。
gcc的结果输出是后缀名不相关的,只与输入参数有关。
3.使用多个源码的项目
如果项目中包含2个以上的源代码,一般要先各自将源代码编译成目标代码。最后一次链接成可执行程序。
若头文件在多个目录:需要在编译时多次使用-I参数加入头文件所在目录。
gcc -I. -I/usr/include -I/home/hxy -c test_link.c -o ...
也可以选择在头文件中,直接写明头文件的相对位置。但是尽量不要这样做。
3.练习gcc多源码项目编译
请开发一个程序,使用结构定义:
struct student {
int no;
char name[64];
float height;
}
要求创建3个学生定义,并给三个学生的三个成员都赋值。
写一个show函数依次把所有学生信息显示。
提示:创建一个工程student目录,将结构体类型、show()函数原型写到show.h,将show()定义写到show.c里,main()写到main.c里,*.h文件放到include目录下,*.c文件放到source目录下。
*.h文件一般存放的都是函数定义和全局变量,对于*.c文件和*.h文件应该如何处理,题目中描述的非常清楚。下面是操作步骤:
new@ubuntu:~$ cd Desktop
new@ubuntu:~/Desktop$ mkdir student
new@ubuntu:~/Desktop$ cd student
new@ubuntu:~/Desktop/student$ mkdir include
new@ubuntu:~/Desktop/student$ mkdir source
new@ubuntu:~/Desktop/student$ cd include
new@ubuntu:~/Desktop/student/include$ vim show.h
new@ubuntu:~/Desktop/student/include$ cd ../source
new@ubuntu:~/Desktop/student/source$ vim main.c
new@ubuntu:~/Desktop/student/source$ vim show.c
new@ubuntu:~/Desktop/student/source$ gcc -c main.c -I../include
new@ubuntu:~/Desktop/student/source$ ls
main.c main.o show.c
new@ubuntu:~/Desktop/student/source$ gcc -c show.c -I../include
new@ubuntu:~/Desktop/student/source$ ls
main.c main.o show.c show.o
new@ubuntu:~/Desktop/student/source$ gcc show.o main.o -o main
new@ubuntu:~/Desktop/student/source$ ./main
1
Jessica
1.670000
2
Jack
1.890000
3
Ben
1.790000
show.h
typedef struct student{
int no;
char name[64];
float height;
}STU;
void show(STU stu);
main.c
或者字符串拷贝,不能直接等于,#include <string.h>后strcpy()
#include <stdio.h>
#include "show.h"
int main()
{
STU stu1 = {1, "Jessica", 1.67};
STU stu2 = {2, "Jack", 1.89};
STU stu3 = {3, "Ben", 1.79};
show(stu1);
show(stu2);
show(stu3);
return 0;
}
show.c
#include <stdio.h>
#include "show.h"
void show(STU stu)
{
printf("%d\n",stu.no);
printf("%s\n",stu.name);
printf("%f\n",stu.height);
}
需要注意的是,不要给name字符串用等号赋值。需要#include <string.h>以及strcpy函数拷贝字符串。
4.gcc关于宏的选项
1.关于宏的选项
-Dmacro:
定义宏macro,宏的内容定义为printf。
gcc test.c -DPRINTF=printf -o test
-Dmacro=defn:
定义宏DBG_NUM的内容为10
gcc test.c -DDEBUG=10 -o test
2.练习
将hello.c用gcc的-D选项生成两个版本的可执行程序,如果增加了DEBUG宏,则打印“DEBUG”,如果没有DEBUG宏,打印“RELEASE”。
首先编写a.c文件:
#include <stdio.h>
int main()
{
#ifdef DEBUG
printf("DEBUG\n");
#else
printf("RELEASE\n");
#endif
return 0;
}
之后进行测试:
new@ubuntu:~/Desktop$ gcc a.c -DDEBUG -o a
new@ubuntu:~/Desktop$ ./a
DEBUG
new@ubuntu:~/Desktop$ gcc a.c -o b
new@ubuntu:~/Desktop$ ./b
RELEASE
5.gcc使用第三方库
1.函数库概念
函数库可以看做头文件(.h)和库文件(.so或.a)的集合。
Linux下头文件默认放在/usr/include/下,库文件放到/usr/lib/中
链接时会用到库,gcc -l是用来链接库的标准方式,链接时采用-L参数指明从哪一个目录加载库。
如:gcc foo.c -lpthread -o foo其中-lpthread表示链接标准库libpthread.so。
gcc foo.c -L/home/hxy/lib -lfoo -o foo表示在/home/hxy/lib/目录下有链接时需要的库文件libfoo.so。
2.静态库与动态库
静态库在链接时会把库目标代码和最终的可执行程序一起链接到一个文件,尺寸大,处理简单。
动态库是可执行程序在运行,动态加载到进程内存中去。动态库与可执行程序是分离的两部分文件。文件小,处理相对麻烦。
Windows下静态库以lib为后缀名,动态库以dll为后缀名。
Linux下静态库以a为后缀名,动态库以so为后缀名。Linux中库的命名需要以lib打头,.so或者.a结尾。
gcc -l链接库的规则:
如果gcc能找到库目录同时有两种版本,优先链接动态链接库的版本。
如果只能找到静态版本,那么采用静态版本。
如果加上-static,gcc foo.c -static -lpthread -o foo强制链接静态版本。
3.pkg-config的使用
pkg-config --list-all列出系统内可使用的软件包
pkg-config gtk+-2.0 --cflags 其中gtk+-2.0为库的名称,命令返回的结果是头文件的编译参数。
pkg-config gtk+-2.0 --libs,命令返回的结果是库文件的编译参数。
gcc gtk.c `pkg-config gtk±2.0 --cflags --libs` -o gtk 在编译过程中使用
注意上面--cflags --libs是两个横杠。
6.makefile文件的写法
从上面的一大堆长长的带着各种参数的gcc可以看出,编译和链接的时候手动一遍遍敲一定很麻烦。所以可以把这些命令按照一定的格式组织进一种makefile文件里。等到需要编译的时候,只需要输入make,就能生成可执行文件。
1.makefile的相关概念
make需要得到两方面的信息:
一是关于可执行文件和各程序模块之间的关系,这个会在makefile文件里写明。
而是文件的修改日期,如果文件之前已经被编译过,则make不会被执行。
一般来说直接输入make,它就会自动查找当前目录下的名称为makefile或者Makefile的文件。如果起了别的名字,就需要make -f hello.mk,但是一般不这么做。
如果makefile文件不在当前目录下,需要使用-C参数,如:make -C /home/hxy。
2.makefile的规则和结构
目标名称:[依赖对象]
命令列表
具体规则是:为了创建这个目标,必须先创建依赖对象,调用命令行列表进行创建。
具体做法一般为,将源代码编译成目标代码(.o)文件,再将所有中间文件链接在一起。
例如:
以下两条不能颠倒,如果颠倒则默认生成hello.o文件,无法得到hello文件。
hello:hello.o
gcc hello.o -o hello
hello.o:hello.c
gcc -c hello.c
makefile中一般包含几个部分:
目标项目:需要由make工具创建,通常是目标文件和可执行文件。
目标项目的依赖文件。
命令列表:每命令需要由tab打头。
一些固定的伪目标:如all,install,clean用于编译,安装和清楚文件。
3.makefile的变量和隐含规则
自定义变量:
CC = gcc,需要放到文件开头,与其他部分隔离
引用时$(CC)
预定义变量:
$<第一个依赖文件的名称
$@目标的完整名称
$^所有的依赖文件,空格分开,不包含重复的依赖文件
后缀规则:
定义了将一个具有某个后缀的文件转换为具有另一种后缀的文件的方法。
例如将.c转化为.o文件:
.c.o:
$(CC) $(CCFLAGS) -c $<
模式规则:
更加通用。在目标名称前多了一个%号,同时可用来定义目标和依赖文件之间的关系。
例如还是将任意.c文件转化为.o文件:
%.o:%.c
...
伪目标:
一般目标的最终结果都是生成一个文件。但有一些目标是可以不生成结果文件,只是为了调用命令或依赖对象,这就是伪目标。
all缺省的目标入口,如果执行make没有指定目标名称,all目标会被自动调用。
clean清除项目生成的中间文件,如何清除需要自行编写。
install项目要如何安装,需要自行编写。
uninstall同。
4.makefile目标编译
如果不指定目标,make会默认第一个目标。
比较规范的目标有:
make all编译所有目标
make clean在编译后删除所有.o文件
make install在编译结束后将最终的可执行文件安装到系统的某一个位置。
可以通过include <filename>引用别的makefile。
5.举例
只是gcc命令集合的makefile:
main: main.o add.o del.o modify.o
gcc main.o add.o del.o modify.o -o main
main.o:main.c
gcc -c main.c
add.o:add.c
gcc -c add.c
del.o:del.c
gcc -c del.c
modify.o:modify.c
gcc -c modify.c
采用扩展用法后的makefile:
main: main.o add.o del.o modify.o
gcc $^ -o $@
%.o:%.c
gcc -c $<
all:main
clean:all
rm -f *.o
install:clean
cp main /usr/local/bin
本文详细介绍了Linux环境下gcc的使用,包括gcc的组成、编译过程、多源码项目的编译、宏定义选项、第三方库的使用及makefile的编写技巧。内容涵盖从简单的命令行编译到复杂的项目配置,帮助读者掌握gcc的全面知识。

1011

被折叠的 条评论
为什么被折叠?



