【在主画面加入捷径】
       
【选择语系】
繁中 简中

[C 语言] 程序设计教学:如何在终端机中使用 GCC (或 Clang)

【赞助商连结】

    本文会选 GCC 而非其他 C 编译器是因为 GCC 在 GNU/Linux 等类 Unix 系统上具有代表性。如果读者使用 Clang,因 Clang 参数刻意兼容于 GCC,仍然可以参考本文来学习 Clang;而且 Clang 的错误讯息比 GCC 友善,倒也不失为一个学习 C 语言的替代工具。

    对于初学者,先记住以下流程即可:

    $ gcc -o hello hello.c
    $ ./hello
    Hello World

    本文的目的是整理一些常见的 GCC (或 Clang) 的使用方式,初学者觉得难以吸收或用不到的话可以先跳过没关系。

    检查 GCC (或 Clang) 的版本

    参考以下指令:

    $ gcc --version
    gcc (Ubuntu 4.8.4-2ubuntu1~14.04.4) 4.8.4
    Copyright (C) 2013 Free Software Foundation, Inc.
    This is free software; see the source for copying conditions.  There is NO
    warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

    在在线讨论区讨论问题时,可以贴上自己所用的 C 编译器的版本,偶尔会从中得到一些有用的回应。

    开启警告讯息

    -Wall 会开启所有警告,一开始练习时建议开启,可以从错误讯息中学习。除此之外,也可以开启 -Wextra,有更多的错误讯息。参考以下指令:

    $ gcc -Wall -Wextra -g -o file file.c

    -Werror 会将警告讯息转为错误,初期对练习写 C 程序会有一些帮助。不过,GCC 有些警告讯息其实不会造成实质的影响,笔者会去看 GCC 的警告讯息,但不会开启这个选项。

    -pedantic 对非标准 C 的语法会出现错误讯息,若注重兼容性可开启此选项。

    开启调试相关资讯

    加入 -g 可以在编译出来的程序中加上调试相关的资讯,如果要对编译出来的可执行文件使用 GDB 等调试器 (debugger) 调试时就需要在编译时加入此参数。由于这个参数对程序运行本身没有帮助,最后要发布实际要上线的程序时,建议关掉这项参数重新编译一次。

    开启剖析 (Profiling) 相关资讯

    加入 -pg 可在编译时加入 gprof 程序可用的讯息,可和 -g 并用。由于在程序中加入剖析相关资讯会影响程序运行效能,程序要正式上线时要关掉这个选项重新编译。

    选择编译最佳化策略

    GCC 有许多和最佳化相关的参数,这些参数相当复杂。为了简化最佳化的过程,GCC 提供一些预先配置好的「套餐」。常见的选项如下:

    • -O0 (关闭最佳化)
    • -O1
    • -O2
    • -O3
    • -Os (空间最佳化)

    -02 是保守但可用的最佳化策略,要效能可试着用 -O3 来编译程序,除此之外,还有一些细部的选项可以把玩。一开始学习时不太需要耗费过多时间在这里。

    编译多个文件

    初学时会把所有的程序代码写在同一个文件中,但实务上的程序会拆成多个文件。可参考以下指令:

    $ gcc -Wall -g -o program main.c file_a.c file_b.c file_c.c

    相关的头文件 (header) 要预先撰写,于后文会再说明。

    指定 C 标准 (C Standard) 的版本

    参考以下指令:

    $ gcc -Wall -g -std=c99 -o program main.c

    GCC 中常见的 C 语言标准:

    • c89c90-ansi:即 ANSI C
    • c99
    • c11
    • c17c18:不要和 C++17 搞混,这是一个小的 C 标准修正版
    • gnu89gnu90c90 加上 GNU C extension
    • gnu99c99 加上 GNU C extension
    • gnu11c11 加上 GNU C extension
    • gnu17c17 加上 GNU C extension

    一开始建议使用 c90c99,需要较新的 C 标准才逐渐加入,以维持兼容性。GNU C extension 不是 C 标准的语法,除非很确定该项目只会用到 GCC 来编译,不建议任意地使用。

    加入外部函式库

    除了一些内建的函式库以外,编译时要加入相关的参数。常见的例子像是 -lm (数学公式)、-lpthread (POSIX 多线程)、-lrt (POSIX realtime extension) 等。参考以下指令:

    $ gcc -Wall -g -o program file.c -lm

    -lm 来说,其读法为 -l (函式库) 加上 m (数学函式库),其他函式库同理可知。

    有些函式库不是位于系统内建的位置上,则要另外加上 -I (头文件位置) 和 -L (函式库位置)。参考以下指令:

    $ gcc -Wall -g -o program file.c -I/path/to/include -L/path/to/lib -lsomething

    pkg-config 是一个用来简化编译第三方函式库的小工具,透过这套工具,我们不用手写 -I-L 等参数。

    例如,我们以 pkg-config 自动产生适用于 libpng 的参数 (于 Mac 上测试):

    $ pkg-config --libs --cflags libpng
    -I/usr/local/Cellar/libpng/1.6.34/include/libpng16 -L/usr/local/Cellar/libpng/1.6.34/lib -lpng16 -lz

    编译时将此段参数插入指令之中即可:

    $ gcc -o program file.c `pkg-config --libs --cflags libpng`

    编译函式库

    函式库是 C (或 C++) 分享程序的方式,分为静态链接 (static linking) 和动态链接 (dynamic linking);前者会将程序编入主程序中,后者则否。

    编译静态链接函式库可参考以下指令:

    $ gcc -c -o something.o something.c
    $ ar rcs libsomething.a something.o

    编译动态链接函式库可参考以下指令:

    $ gcc -fPIC -c -o something.o something.c
    $ gcc -shared -o libsomething.so something.o

    小结

    本文所述大概仅占 GCC 参数的一小部分,但一些冷门的参数其实也很少用,需要时再去查询即可。如果每次都要手动输入编译指令其实蛮辛苦的,我们在这里介绍 GNU Make,这是一个流程自动化软件,可减少手动输入指令的负担。

    【赞助商连结】