GCC 编译命令
GCC编译通常分为4步骤处理
创建文件hello.c
vim hello.c
#include <stdio.h>
/* 执行命令: ./hello dengzj
* argc = 2
* argv[0] = ./hello
* argv[1] = dengzj
*/
#define xxx 10
/*argc:argument count argv: argument value*/
int main(int argc, char **argv)
{
if (argc >= 2)
printf("Hello, %s,%d!\n", argv[1],xxx);
else
printf("Hello, world! %d\n",xxx);
return 0;
}
gcc基本用法:gcc [option] [filenames]
编译hello.c文件 不加参数(option) ;gcc filenames则 默认输出
为a.out
gcc hello.c
通常编译hello.c 使用-o参数可命名文件,将hello.c编译后命名为hello,即gcc命令后的-o参数
的位置不固定,带-o参数后面一定接编译后的文件名
。这也是后续编译.c文件使用最多的命令
。结果如下图所示
gcc hello.c -o hello
//或者
gcc -o hello hello.c
-o file 指定输出文件名为file 。无论是预处理、编译、汇编还是链接,这个选项都可
以使用。如果没有使用 o’ 选项,默认的输出结果是:可执行文件为a.out
。
以上是学习中大多数时候使用的gcc编译指令,上面代码是一步到位的得到链接后的文件,其本质是要经过预处理 编译 汇编 链接这四步。等同于下面的4行指令依次完成。
gcc -E -o hello.i hello.c
gcc -S -o hello.s hello.i
gcc -c -o hello.o hello.s
gcc -o hello hello.o
1、预处理 gcc -E
最开始编写的hello.c文件变为hello.i
文件, 展开宏、头文件,替换条件编译,删除注释、空行、空白。
gcc -E -o hello.i hello.c
打开文件里面就是一些头文件路径即展开宏。打开hello.i文件,在vim的命令模式下输入/main
后会看见源代码的xxx宏已经展开后得到数值为10。
2、编译 gcc -S
.i文件编译得到汇编形式的hello.s
,检测语法规范,消耗时间、系统资源最多。同样用vim 打开文件可查看到汇编形式的代码。
gcc -S -o hello.s hello.i
同理,灵活使用,可将上面融合为一步。gcc -S -o hello.s hello.c,可直接将hello.c文件编译为汇编形式,并命名为hello.s
。
3、汇编 gcc -c
(1) 得到hello.o文件,将汇编指令翻译成机器指令,进入则看到的是一堆机器码。
gcc -c -o hello.o hello.s
(2) .c文件直接-c,预处理、编译和汇编源文件,但是不作链接,编译器根据源文件生成OBJ 文件。多用于多文件编译。
gcc -o hello.o -c hello.s
4、链接 无参数
数据段合并 将生成的.obj文件与库文件.lib等文件链接,生成可执行文件ELF格式,地址回填。
将hell.o和库文件、系统文件的一系列.o文件进行链接。
gcc -o hello hello.o
最终输出的文件结果
ELF格式查看
file hello
*选看
查看gcc完整编译过程,带-v参数,输出结果复制到Notepad++打开
gcc -o hello hello.o -v
仅供自学参考,谢谢!
进阶用法
多文件编译组成一个应用程序
vim main.c
#include <stdio.h>
#include "sub.h"
int main(int argc, char *argv[])
{
int i;
printf("Main fun!\n");
sub_fun();
return 0;
}
vim sub.c
void sub_fun(void)
{
printf("Sub fun!\n");
}
vim sub.h
void sub_fun(void);
一条命令,处理多个源文件。以此类推,10个文件编写,在后面加10个文件名即可。实际工程中,文件一多起来就不行了(需要makefile,但手搓makefile已过时,现在是cmake的天下,自动生成makefile,makefile是时代的悲哀),因此gcc的缺点是对所有文件都需要再一次处理
。
gcc -o test main.c sub.c
当多文件(比如100个文件)进行编译,其中一个文件需要修改时,因为只是某些修改文件的内容,而后其余文件却要被重新处理一次,使用上面这条命令就显得很浪费时间。因此可以使用-c
命令只进行前三个步骤(预处理、编译、汇编),最后再统一链接。
即:
gcc -c -o main.o main.c
gcc -c -o sub.o sub.c
gcc -o test main.o sub.o
这样在只修改某些文件时,只需要执行对应的-c命令即可。
-I的作用
main.c的头文件中,<>
代表在系统目录或工具链的默认的指定路径目录下查找,编译时加上-v选项可查看, " "
代表在当前目录下查找文件。
利用-I来指定目录,修改mian.c 该为<sub.h>
#include <stdio.h>
#include <sub.h>
int main(int argc, char *argv[])
{
int i;
printf("Main fun!\n");
sub_fun();
return 0;
}
-I ./ 在当前路径查看头文件,编译成功。
gcc -c -o main.o main.c -I ./
静态库
静态库:程序的链接阶段被复制到程序中
每个需要使用静态库的程序都要将整个静态库编译到可执行程序中,若静态库50M,10个程序要500M。
库函数本地化,优点调用速度快
,缺点是系统资源消耗大
,多用于对时间要求较高的核心程序。
未链接sub.c文件,文件编译时可以将其他文件生成.o文件制作成库来使用
ar rcs lib库名.a 一系列.o文件
解决办法:当然可以在代码后加入sub.c文件进行链接生成test文件
或者制作静态库,先生成sub.o文件。
gcc -c -o sub.o sub.c
制作静态库,在编译生成库文件时,对于静态库要使用ar命令对.o文件链接。
ar crs libsub.a sub.o
使用静态库
gcc -o test main.o libsub.a
动态库
动态库:在程序运行时由系统动态加载到内存中供程序调用。可以实现进程间资源共享(共享库)多个程序使用同个动态库,内存中只会出现一个动态库,所以程序共享。
机制:多个可执行程序共享一份库文件(共享代码、不共享数据)。
使用 gcc -shared 制作动态库,-shared表示要生成的是一个动态库文件
gcc -shared -o lib库名.so 一系列.o文件 (如:add.o sub.o div.o)
制作动态库
gcc -shared -o libsub.so sub.o
使用动态库1
gcc -o test2 main.o libsub.so
链接可执行程序时,指定所使用的动态库, -l:指定动态库名 -L:指定动态库路径。
补充:-i(大写)包含要引入的头文件路径
使用动态库2
gcc -o test2 main.o -L./ -lsub
运行时,需要添加动态指定路径,:./
添加当前路径
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:./
./test2
总结:
1、动态库是否加载到内存,取决于程序是否运行
2、动态库每次加载的位置不固定
3、动、静态库共存时,编译器默认使用动态库
头文件依赖
查看c.c文件的依赖
gcc -M c.c
利用-MF指令生成名为c.e的依赖文件
gcc -M -MF c.e c.c
即生成依赖文件 且编译
gcc -c -o c.o c.c -MD -MF c.e