马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有账号?立即注册
×
gcc 是 Linux 下最常用的编译工具,g++ 用来编译 C++。这一篇把 gcc/g++ 的核心选项和“程序从源码到可执行文件”的四步翻译过程一次讲透,顺便回答一个高频问题:为什么 Linux 装完系统就能直接跑 C 程序?
一、快速熟悉 gcc / g++
gcc 可以用来编译链接 .c 文件,使之生成可执行文件;-o 选项用来重命名输出文件。
- C 语言既可以用 gcc 也可以用 g++ 编译。
- C++ 只能使用 g++。
- gcc 和 g++ 的使用方法是完全相同的,所以下面主要以 gcc 为例。
# 编译并指定输出名
gcc test.c -o myapp
# 编译 C++ 源文件
g++ main.cpp -o myapp
# 只看语法不生成可执行文件(-c)
gcc -c test.c -o test.o
二、程序的翻译过程:四步走
一个 C 程序从源码到可执行,会经历以下四步:
1、预处理:-E 选项
预处理分为四件事:
- 头文件展开:预处理时把头文件的内容拷贝到源文件里。
- 去注释:把注释去掉。
- 宏替换:编译之前把宏标识符替换成对应的宏定义内容。
- 条件编译:可以通过给编译器传递不同的宏值,对代码做动态裁剪。
条件编译的典型应用:各种软件的专业版和社区版其实是同一套源代码,社区版通过条件编译把某些功能去掉了,所以专业版功能更多。同理,源码里写 #ifndef 就能通过条件编译打印出不同的内容。
# -E 表示从现在开始进行翻译,预处理做完就停下来
gcc -E test.c -o test.i
# 不用 -o 的话,结果会直接打印到终端
gcc -E test.c
.i 文件通常称为预处理文件,里面是预处理四个步骤之后的结果。注意:预处理之后的代码还是 C 语言。
那么 #ifndef、#define、#endif 为什么能防止头文件被重复包含?这也是条件编译的应用场景之一:预处理阶段头文件会被展开,第一次条件检查发现该宏没被定义,于是定义它并展开头文件内容;第二次再包含这个头文件时,又会展开再做条件检测,发现宏已经被定义过了,就不会再使用该头文件里的内容。
2、编译:-S 选项
这一步是 C 语言到汇编语言:把 C 语言翻译成汇编语言。
gcc -S test.i -o test.s
对预处理文件(.i)用 -S 处理,会生成 .s 文件,.s 称为汇编语言源文件。就算不用 -o 指定名字,也会在同级目录自动生成同名的 .s 文件,里面的内容就是由 C 语言转换而来的汇编语言。
3、汇编:-c 选项
这一步是汇编语言到二进制语言,产出的是可重定位目标二进制文件(还不能直接执行)。
gcc -c test.s -o test.o
用 -c 就执行了汇编过程,会生成 .o 文件;即使不使用 -o 选项也会生成同名 .o 文件。.o 称为可重定位目标二进制文件,里面放的全是二进制数据,因为 Linux 终端是用来显示文本内容的,查看二进制数据时显示的就是乱码。
注意 .o 文件是不能执行的(./test.o 会报错),还必须走最后一步链接。
4、链接:-o 选项
gcc test.o -o myapp
通过 -o 进行链接操作,生成的就是可执行文件。注意 -o 一定要指定生成的文件名。生成之后就可以直接运行了。
三、动态库与静态库
检查可执行文件所链接、依赖的库,用 ldd:
ldd ./myapp
库分为动态库和静态库。动态库后缀通常是 .so,静态库通常是 .a。动态库在运行时才加载,多个进程共享同一份;静态库在链接时把代码拷进可执行文件,体积大但自包含。
四、为什么 Linux 可以直接运行 C 语言程序
平台要支持开发,就必须在系统里提前安装各种语言的标准头文件 + 库文件。Linux 发行版默认就会把 C 语言的标准头文件(比如 /usr/include 下的那些)和 libc 等基础库装好,这就是为什么编译一条 gcc hello.c -o hello 就能直接跑起来。
反过来看,一个精简的容器镜像如果没装 gcc、也没装对应的运行时库,跑二进制就会缺库报错,这时候要么装编译工具链,要么拷贝依赖的动态库。
五、一条命令记住四个阶段
# 一步到位
gcc test.c -o myapp
# 分步观察每一阶段的产物,调试宏和汇编问题时非常有用
gcc -E test.c -o test.i
gcc -S test.i -o test.s
gcc -c test.s -o test.o
gcc test.o -o myapp
六、小结
- 翻译四步:预处理(.i)→ 编译(.s)→ 汇编(.o)→ 链接(可执行文件)。
- 对应选项:-E、-S、-c、-o,四个记住就够用。
- 头文件重复包含靠
#ifndef 条件编译挡住。
- 用
ldd 查依赖,缺库就装运行库或补路径。
qidao123.com企服评测-应用市场 编辑提示 |