linux下GCC编译C程序(一)

时间:2022-06-13 09:14:37
 
Linux内核和许多其他*软件以及开放源码应用程序都是用 C语言编写并使用GCC 编译的。

1. 编译单个源文件

为了进行测试,你可以创建“Hello World”程序:

#include <stdio.h>
#include <stdlib.h>

int main(int argc, char **argv)
{
        printf("Hello world!\n");
        exit(0);
}



使用如下命令 编译并测试这个代码:
引用
# gcc -o hello hello. c
# ./hello
Hello wordl!


在默认情况下产生的可执行程序名为a.out,但你通常可以通过gcc的“-o”选项来指定自己的可执行程序名称。

2. 编译多个源文件

文件message. c包含 一个简单的消息打印函数:
#include <stdio.h>

void goodbye_world(void)
{
        printf("Goodbye, world!\n");
}


使用gcc的“- c”标记来 编译支持库代码:
引用
# gcc - c message. c


这一过程的输出结果是 一个名为message.o的 文件,它包含适合连接到 一个较大程序的已 编译目标代码。

创建 一个简单的示例程序,它包含 一个调用goodbye_world的main函数

#include <stdlib.h>

void goodbye_world(void):

int main(int argc, char **argv)
{
        goodbye_world();
        exit(0);
}


使用GCC 编译这个程序:
引用
# gcc - c main. c


现在有了两个目标 文件:message.o和main.o。它们包含能够被Linux执行的目标代码。要从这个目标代码创建Linux可执行程序,需要再一次调用GCC来执行连接阶段的工作:
引用
# gcc -o goodbye message.o main.o


运行 编译结果:
引用
# ./goodbye
Goodbye, world!


前面这些单独的步骤也可以简化为 一个命令,这是因为GCC对 如何将多个源 文件编译一个可执行程序有内置的规则。
引用
# gcc -o goodbye message. c main. c
# ./goodbye
Goodbye, world!


3. 使用外部函数库
GCC常常与包含标准例程的外部软件库结合使用,几乎每 一个Linux应用程序都依赖于由GNU C函数库GLIBC。
应用外部函数库的例子:
#include <stdio.h>
#include <stdlib.h>
#include <math.h>

#define MAX_INPUT 25

int main(int agrc, char **argv)
{
        char input[MAX_INPUT];
        double angle;

        printf("Give me an angle (in radians) ==>");
        if(!fgets(input, MAX_INPUT, stdin)){
                perror("an error occurred.\n");
        }
        angle = strtod(input, NULL);

        printf("sin(%e) = %e\n", angle, sin(angle));

        return 0;
}
编译命令:
引用
# gcc -o trig -lm trig. c


GCC的"-lm"选项,它告诉GCC查看系统提供的数学库(libm)。因为Linux和UNIX的系统函数库通常以"lib"为前缀,所以我们假设它存在。真正的函数库位置随系统的不同而不同,但它一般会位于目录/lib或/usr/lib中,在这些目录中还有数以百计的其他必需的系统函数库。

4. 共享函数库与静态函数库

Linux系统上的函数库分为两种不同的类型:共享的和静态的

静态函数库:每次当应用程序和静态连接的函数库一起 编译时,任何引用的库函数中的代码都会被直接包含进最终的二进制程序。

共享函数库:包含每个库函数的单一全局版本,它在所有应用程序之间共享。这一过程背后所涉及的机制相当复杂,但主要依靠的是现代计算机的虚拟内存能力,它允许包含库函数的物理内存安全地在多个独立用户程序之间共享。

使用共享函数库不仅减少了 文件的容量和Linux应用程序在内存中覆盖的区域,而且它还提高了系统的安全性。 一个被许多不同程序同时调用的共享函数库很可能会驻留在内存中,以在需要使用它时被立即使用,而不是位于磁盘的交换分区中。这有助于进一步减少一些大型Linux应用程序的装载时间。

将上面的message. c作为共享库函数使用的例子:

引用
# gcc -fPIC - c message. c

“PIC”命令行标记告诉GCC产生的代码不要包含对函数和变量具体内存位置的引用,这是因为现在还无法知道使用该消息代码的应用程序会将它连接到哪一段内存地址空间。这样 编译输出的 文件message.o可以被用于建立共享函数库,我们只需使用gcc的“-shared”标记即可:
引用
# gcc -shared -o libmessage.so message.o


将上面的mian. c使用共享库函数ligmessage.so 编译
引用
# gcc -o goodbye -lmessage -L. message.o
“-lmessage”标记来告诉GCC在连接阶段引用共享函数库libmessage.so。“-L.”标记告诉GCC函数库可能位于当前目录中,否则GNU的连接器会查找标准系统函数库目录,在本例的情况下,就找不到可用的函数库了。

此时运行 编译好的goodbye会提示找不到共享函数库:
引用
#./goodbye
./goodbye: error while loading shared libraries: libmessage.so: cannot open shared object file: No such file or directory


可以使用命令ldd来发现 一个特定应用程序需要使用的函数库。ldd搜索标准系统函数库路径并显示 一个特定程序使用的函数库版本。
引用
#ldd goodbye
        linux-gate.so.1 =>  (0x00493000)
        libmessage.so => not found
        libc.so.6 => /lib/libc.so.6 (0x0097 c000)
        /lib/ld-linux.so.2 (0x0095a000)


文件libmessage.so不能在任何 一个标准搜索路径中找到,而且系统提供的配置 文件/etc/ld.so.conf也没有包含 一个额外的条目来指定包含该库 文件的目录。

需要设置 一个环境变量LD_LIBRARY_PATH来制定额外的共享函数库搜索路径,
引用
# export LD_LIBRARY_PATH=`pwd`
# ldd goodbye
        linux-gate.so.1 =>  (0x002ce000)
        libmessage.so => /tmp/cpro/libmessage.so (0x00b0f000)
        libc.so.6 => /lib/libc.so.6 (0x0097 c000)
        /lib/ld-linux.so.2 (0x0095a000)
运行程序
引用
# ./goodbye
Goodbye, world!