An Introduction to GCC - 2 Compiling a C program (编译 C 程序)
for the GNU Compilers gcc
and g++
Brian Gough
Foreword by Richard M. Stallman
2 Compiling a C program (编译 C 程序)
This chapter describes how to compile C programs using gcc
. Programs can be compiled from a single source file or from multiple source files, and may use system libraries and header files.
本章介绍了怎样用 gcc
来编译 C 程序。程序可以编译自单个源文件或多个源文件,还可以用到系统的库文件和头文件。
Compilation refers to the process of converting a program from the textual source code
, in a programming language such as C or C++, into machine code
, the sequence of 1’s and 0’s used to control the central processing unit (CPU) of the computer. This machine code is then stored in a file known as an executable file
, sometimes referred to as a binary file
.
编译是指把一个是纯文本的源代码的程序
,比如 C 或 C++ 这种编程语言,转变成机器码,即用于控制计算机的中央处理单元 (CPU) 的 1 和 0 的序列。这种机器码被存放在称为可执行文件的文件中,有时候也被称为二进制文件。
2.1 Compiling a simple C program (编译一个简单的 C 程序)
The classic example program for the C language is Hello World
. Here is the source code for our version of the program:
Hello World 是 C 语言中的经典例子。下面是我们版本的该程序的源代码:
#include <stdio.h>
int
main (void)
{
printf ("Hello, world!\n");
return 0;
}
We will assume that the source code is stored in a file called hello.c
. To compile the file hello.c
with gcc
, use the following command:
我们假设源代码被存放在 hello.c
的文件中。用 gcc
编译 hello.c
文件,可以用下面的命令:
$ gcc -Wall hello.c -o hello
This compiles the source code in hello.c
to machine code and stores it in an executable file hello
. The output file for the machine code is specified using the -o
option. This option is usually given as the last argument on the command line. If it is omitted, the output is written to a default file called a.out
.
这样就把在 hello.c
中的源代码编译成机器码并存储在可执行文件 hello
中。用 -o
选项可以指定存储机器码的输出文件,该选项通常是命令行上的最后一个参数。如果省略它,输出将被写到默认文件 a.out
中。
Note that if a file with the same name as the executable file already exists in the current directory it will be overwritten.
要注意的是,如果同名的可执行文件在当前目录下已经存在,则会被覆盖。
The option -Wall
turns on all the most commonly-used compiler warnings – it is recommended that you always use this option! There are many other warning options which will be discussed in later chapters, but -Wall
is the most important. GCC will not produce any warnings unless they are enabled. Compiler warnings are an essential aid in detecting problems when programming in C and C++.
-Wall
选项打开所有最常用到的编译警告 – 推荐你总是使用该选项!在后面的章节中我们将讨论许多另外的警告选项,但 -Wall
是最重要的。除非你打开使能这些选项,否则 GCC 不输出任何警告。在用 C 和 C++ 编程时,编译器警告对检查错误是一种有用的帮助。
In this case, the compiler does not produce any warnings with the -Wall
option, since the program is completely valid. Source code which does not produce any warnings is said to compile cleanly
.
由于上面的例子程序是完全合法的,在这种情况下,即使带了 -Wall
选项编译器也不会输出任何警告。编译时不输出任何警告的源代码被称为干净地编译
。
To run the program, type the path name of the executable like this:
要运行该程序,像下面那样输入该可执行文件的路径名即可:
$ ./hello
Hello, world!
This loads the executable file into memory and causes the CPU to begin executing the instructions contained within it. The path ./
refers to the current directory, so ./hello
loads and runs the executable file ‘hello’ located in the current directory.
这会把可执行文件载入内存,CPU 开始执行包含其中的指令。路径中的 ./
是指当前目录,所以 ./hello
载入并运行当前目录中的可执行文件 hello
。
strong@foreverstrong:~$ pwd
/home/strong
strong@foreverstrong:~$
strong@foreverstrong:~$ cd Desktop/makefile_work/
strong@foreverstrong:~/Desktop/makefile_work$ ll
total 12
drwxrwxr-x 2 strong strong 4096 Sep 26 17:23 ./
drwxr-xr-x 8 strong strong 4096 Sep 21 11:38 ../
-rw-rw-r-- 1 strong strong 82 Sep 26 17:23 hello.c
strong@foreverstrong:~/Desktop/makefile_work$
strong@foreverstrong:~/Desktop/makefile_work$ cat hello.c
#include <stdio.h>
int main (void)
{
printf ("Hello, world!\n");
return 0;
}
strong@foreverstrong:~/Desktop/makefile_work$
strong@foreverstrong:~/Desktop/makefile_work$ gcc -Wall hello.c -o hello
strong@foreverstrong:~/Desktop/makefile_work$
strong@foreverstrong:~/Desktop/makefile_work$ ll
total 24
drwxrwxr-x 2 strong strong 4096 Sep 26 17:28 ./
drwxr-xr-x 8 strong strong 4096 Sep 21 11:38 ../
-rwxrwxr-x 1 strong strong 8600 Sep 26 17:28 hello*
-rw-rw-r-- 1 strong strong 82 Sep 26 17:23 hello.c
strong@foreverstrong:~/Desktop/makefile_work$
strong@foreverstrong:~/Desktop/makefile_work$ ./hello
Hello, world!
strong@foreverstrong:~/Desktop/makefile_work$
2.2 Finding errors in a simple program (在简单程序中找到错误)
As mentioned above, compiler warnings are an essential aid when programming in C and C++. To demonstrate this, the program below contains a subtle error: it uses the function printf
incorrectly, by specifying a floating-point format %f
for an integer value:
像上面提到的,使用 C 和 C++ 编程时,编译器警告是非常有用的帮助。为了演示这一点,下面的程序包含一个细微的错误:它不正确地调用 printf
函数,为一个整数指定的是浮点格式的 %f
:
#include <stdio.h>
int
main (void)
{
printf ("Two plus two is %f\n", 4);
return 0;
}
This error is not obvious at first sight, but can be detected by the compiler if the warning option -Wall
has been enabled.
这个错误在看第一眼时不太明显,但如果打开 -Wall
警告选项,就能被编译器检测到。
Compiling the program above, bad.c
, with the warning option -Wall
produces the following message:
打开警告选项 -Wall
编译上面的程序 bad.c
会输出下面的信息:
$ gcc -Wall bad.c -o bad
bad.c: In function `main':
bad.c:6: warning: double format, different
type arg (arg 2)
This indicates that a format string has been used incorrectly in the file bad.c
at line 6. The messages produced by GCC always have the form file:line-number:message
. The compiler distinguishes between error messages
, which prevent successful compilation, and warning messages
which indicate possible problems (but do not stop the program from compiling).
这指示了在文件 bad.c
的第 6 行用到的格式化字符串不正确。GCC 输出的信息总是如 file:line-number:message
这种形式。编译器区分报错信息和警告信息,不能成功编译的是报错信息,指示可能的错误的是警告 (但并不停止程序的编译)。
In this case, the correct format specifier would have been %d
(the allowed format specifiers for printf
can be found in any general book on C, such as the GNU C Library Reference Manual
, see section Further reading).
在这里,正确的格式化指示符是 %d
(printf
允许的格式化指示符可在任何一本讲解 C 的书上找到,比如 GNU C 库参考手册,见进一步阅读)。
Without the warning option -Wall
the program appears to compile cleanly, but produces incorrect results:
如果不带警告选项 -Wall
编译,程序看上去干净地编译了,但输出不正确的结果:
$ gcc bad.c -o bad
$ ./bad
Two plus two is 2.585495 (incorrect output)
The incorrect format specifier causes the output to be corrupted, because the function printf
is passed an integer instead of a floating-point number. Integers and floating-point numbers are stored in different formats in memory, and generally occupy different numbers of bytes, leading to a spurious result. The actual output shown above may differ, depending on the specific platform and environment.
不正确的格式化指示符导致输出不对,因为 printf
函数传递的是整数而不是浮点数。整数和浮点数在内存中被用不同的格式存储,通常占有的字节数也不同,导致谬误的结果。上面显示的实际输出可能不一样,这依赖于特定的平台和环境。
Clearly, it is very dangerous to develop a program without checking for compiler warnings. If there are any functions which are not used correctly they can cause the program to crash, or to produce incorrect results. Turning on the compiler warning option -Wall
will catch many of the commonest errors which occur in C programming.
无疑,开发程序而不检查编译器警告是很危险的。如果有函数没有被正确使用,那么它们会导致程序崩溃,或输出不正确的结果。打开编译器警告选项 -Wall
可以捕捉到许多在 C 编程中最常发生的错误
cleanly [ˈkliːnli]:adv. 干净地,清洁地 adj. 干净的,爱清洁的
corrupt [kəˈrʌpt]:adj. 腐败的,贪污的,堕落的 vt. 使腐烂,使堕落,使恶化 vi. 堕落,腐化,腐烂
strong@foreverstrong:~/Desktop/makefile_work$ pwd
/home/strong/Desktop/makefile_work
strong@foreverstrong:~/Desktop/makefile_work$ ll
total 12
drwxrwxr-x 2 strong strong 4096 Sep 26 19:02 ./
drwxr-xr-x 8 strong strong 4096 Sep 21 11:38 ../
-rw-rw-r-- 1 strong strong 90 Sep 26 19:02 bad.c
strong@foreverstrong:~/Desktop/makefile_work$
strong@foreverstrong:~/Desktop/makefile_work$ cat bad.c
#include <stdio.h>
int main (void)
{
printf ("Two plus two is %f\n", 4);
return 0;
}
strong@foreverstrong:~/Desktop/makefile_work$
strong@foreverstrong:~/Desktop/makefile_work$ gcc -Wall bad.c -o bad
bad.c: In function ‘main’:
bad.c:5:11: warning: format ‘%f’ expects argument of type ‘double’, but argument 2 has type ‘int’ [-Wformat=]
printf ("Two plus two is %f\n", 4);
^
strong@foreverstrong:~/Desktop/makefile_work$
strong@foreverstrong:~/Desktop/makefile_work$ ll
total 24
drwxrwxr-x 2 strong strong 4096 Sep 26 19:03 ./
drwxr-xr-x 8 strong strong 4096 Sep 21 11:38 ../
-rwxrwxr-x 1 strong strong 8600 Sep 26 19:03 bad*
-rw-rw-r-- 1 strong strong 90 Sep 26 19:02 bad.c
strong@foreverstrong:~/Desktop/makefile_work$
strong@foreverstrong:~/Desktop/makefile_work$ rm bad
strong@foreverstrong:~/Desktop/makefile_work$
strong@foreverstrong:~/Desktop/makefile_work$ ll
total 12
drwxrwxr-x 2 strong strong 4096 Sep 26 19:04 ./
drwxr-xr-x 8 strong strong 4096 Sep 21 11:38 ../
-rw-rw-r-- 1 strong strong 90 Sep 26 19:02 bad.c
strong@foreverstrong:~/Desktop/makefile_work$
strong@foreverstrong:~/Desktop/makefile_work$ gcc bad.c -o bad
bad.c: In function ‘main’:
bad.c:5:11: warning: format ‘%f’ expects argument of type ‘double’, but argument 2 has type ‘int’ [-Wformat=]
printf ("Two plus two is %f\n", 4);
^
strong@foreverstrong:~/Desktop/makefile_work$
strong@foreverstrong:~/Desktop/makefile_work$ ./bad
Two plus two is 0.000000
strong@foreverstrong:~/Desktop/makefile_work$
2.3 Compiling multiple source files (编译多个源文件)
A program can be split up into multiple files. This makes it easier to edit and understand, especially in the case of large programs – it also allows the individual parts to be compiled independently.
一个程序可能被分成多个源文件,这样可以更容易编辑和理解,尤其是在大程序的情况下 – 它允许独立地编译各自的部分。
In the following example we will split up the program Hello World
into three files: main.c
, hello_fn.c
and the header file hello.h
. Here is the main program main.c
:
在下面的例子中,我们将把程序 Hello World
分成 3 个文件:main.c
、hello_fn.c
和头文件 hello.h
。这里是主程序 main.c
:
#include "hello.h"
int
main (void)
{
hello ("world");
return 0;
}
The original call to the printf
system function in the previous program hello.c
has been replaced by a call to a new external function hello
, which we will define in a separate file hello_fn.c
.
在前面的程序中对系统函数 printf
的调用被一个新的外部函数 hello
的调用取代,我们将把 hello
函数定义在单独的文件 hello_fn.c
中。
The main program also includes the header file hello.h
which will contain the declaration of the function hello
. The declaration is used to ensure that the types of the arguments and return value match up correctly between the function call and the function definition. We no longer need to include the system header file stdio.h
in main.c
to declare the function printf, since the file main.c
does not call printf directly.
主程序也包括头文件 hello.h
,它将包含 hello
函数的声明。函数声明用于确保在函数调用和函数定义之间函数参数与返回值的类型能够正确匹配。我们不再需要在 main.c
文件中包括声明了 printf
函数的系统头文件 stdio.h
,因为 main.c
文件并没有直接调用 printf
。
The declaration in hello.h
is a single line specifying the prototype of the function hello
:
hello.h
中的声明只是简单的一行,指定 hello
函数的原型:
void hello (const char * name);
The definition of the function hello
itself is contained in the file hello_fn.c
:
hello
函数自身的定义被包含在 hello_fn.c
文件中:
#include <stdio.h>
#include "hello.h"
void
hello (const char * name)
{
printf ("Hello, %s!\n", name);
}
This function prints the message Hello, name!
using its argument as the value of name
.
该函数打印消息 Hello, name!
,用函数参数作为这里 name
的值。
Incidentally, the difference between the two forms of the include statement #include "FILE.h"
and #include <FILE.h>
is that the former searches for FILE.h
in the current directory before looking in the system header file directories. The include statement #include <FILE.h>
searches the system header files, but does not look in the current directory by default.
顺便说一下,#include "FILE.h"
and #include <FILE.h>
这两种 include 声明形式的含义是有差异的,前者是先在当前目录搜索 FILE.h
,然后再查看包含系统头文件的目录。#include <FILE.h>
这种 include 声明是搜索系统目录的头文件,默认情况下不会在当前目录下查找头文件。
To compile these source files with gcc
, use the following command:
用下面的命令编译这些源码文件:
$ gcc -Wall main.c hello_fn.c -o newhello
In this case, we use the -o
option to specify a different output file for the executable, newhello
. Note that the header file hello.h
is not specified in the list of files on the command line. The directive #include "hello.h"
in the source files instructs the compiler to include it automatically at the appropriate points.
在这里,我们用 -o
选项来为可执行文件指定一个不同的输出文件 newhello
。注意,头文件 hello.h
不需要在命令行上的源文件名列表中指定。hello.c
源码文件中的 #include "hello.h"
指示符会指导编译器在合适的时候自动地包含它。
To run the program, type the path name of the executable:
输入可执行文件的路径名来运行该程序:
$ ./newhello
Hello, world!
All the parts of the program have been combined into a single executable file, which produces the same result as the executable created from the single source file used earlier.
程序中的所有部分已经被组合成单个的可执行文件,其像前面由单个源文件生成的可执行文件一样输出同样的结果。
incidentally [ˌɪnsɪˈdentli]:adv. 顺便,偶然地,附带地
strong@foreverstrong:~/Desktop/makefile_work$ ls
hello_fn.c hello.h main.c
strong@foreverstrong:~/Desktop/makefile_work$
strong@foreverstrong:~/Desktop/makefile_work$ cat main.c
#include "hello.h"
int main (void)
{
hello ("world");
return 0;
}
strong@foreverstrong:~/Desktop/makefile_work$
strong@foreverstrong:~/Desktop/makefile_work$ cat hello_fn.c
#include <stdio.h>
#include "hello.h"
void
hello (const char * name)
{
printf ("Hello, %s!\n", name);
}
strong@foreverstrong:~/Desktop/makefile_work$
strong@foreverstrong:~/Desktop/makefile_work$ cat hello.h
void hello (const char * name);
strong@foreverstrong:~/Desktop/makefile_work$
strong@foreverstrong:~/Desktop/makefile_work$ gcc -Wall main.c hello_fn.c -o newhello
strong@foreverstrong:~/Desktop/makefile_work$
strong@foreverstrong:~/Desktop/makefile_work$ ll
total 32
drwxrwxr-x 2 strong strong 4096 Sep 26 20:13 ./
drwxr-xr-x 8 strong strong 4096 Sep 21 11:38 ../
-rw-rw-r-- 1 strong strong 108 Sep 26 20:12 hello_fn.c
-rw-rw-r-- 1 strong strong 32 Sep 26 20:11 hello.h
-rw-rw-r-- 1 strong strong 71 Sep 26 20:12 main.c
-rwxrwxr-x 1 strong strong 8672 Sep 26 20:13 newhello*
strong@foreverstrong:~/Desktop/makefile_work$
strong@foreverstrong:~/Desktop/makefile_work$ ./newhello
Hello, world!
strong@foreverstrong:~/Desktop/makefile_work$
2.4 Compiling files independently (独立地编译文件)
If a program is stored in a single file then any change to an individual function requires the whole program to be recompiled to produce a new executable. The recompilation of large source files can be very time-consuming.
如果整个程序代码被存储在单个源文件中,那么对某个函数的任何改变都需要改程序被重新编译以生成一个新的可执行文件。重新编译大的源码文件可能非常花时间。
When programs are stored in independent source files, only the files which have changed need to be recompiled after the source code has been modified. In this approach, the source files are compiled separately and then linked
together – a two stage process. In the first stage, a file is compiled without creating an executable. The result is referred to as an object file
, and has the extension .o
when using GCC.
当程序被存储在一个个单独的源文件中时,只有那些修改过源码的文件才需要重新编译。用这种方法,源文件被分开一个个编译,然后再链接在一起 – 分为两个步骤。在第一阶段,文件被编译但不生成可执行文件。生成的结果被称为对象文件 (obj 文件),用 GCC 时有 .o
后缀名。
In the second stage, the object files are merged together by a separate program called the linker
. The linker combines all the object files together to create a single executable.
在第二阶段,各个对象文件由一个被称为链接器的单独的程序合成在一起。链接器把所有的对象文件组合在一起生成单个的可执行文件。
An object file contains machine code where any references to the memory addresses of functions (or variables) in other files are left undefined. This allows source files to be compiled without direct reference to each other. The linker fills in these missing addresses when it produces the executable.
对象文件包含的是机器码,其中任何对在其他文件中的函数 (或变量) 的内存地址的引用都留着没有被解析。这样就允许在互相之间不直接引用的情况下编译各个源代码文件。链接器在生成可执行文件时会填写这些还缺少的地址。
2.4.1 Creating object files from source files (从源文件生成对象文件)
The command-line option -c
is used to compile a source file to an object file. For example, the following command will compile the source file main.c
to an object file:
命令行选项 -c
用于把源码文件编译成对象文件。例如,下面的命令将把源文件 main.c
编译成一个对象文件:
$ gcc -Wall -c main.c
This produces an object file main.o
containing the machine code for the main
function. It contains a reference to the external function hello
, but the corresponding memory address is left undefined in the object file at this stage (it will be filled in later by linking).
这会生成一个包含 main
函数机器码的对象文件 main.o
。它包含一个对外部函数 hello
的引用,但在这个阶段该对象文件中的对应的内存地址留着没有被解析 (它将在后面链接时被填写)。
The corresponding command for compiling the hello function in the source file hello_fn.c
is:
编译源文件 hello_fn.c
中的 hello
函数的相应命令如下:
$ gcc -Wall -c hello_fn.c
This produces the object file hello_fn.o
.
这会生成对象文件 hello_fn.o
。
Note that there is no need to use the option -o
to specify the name of the output file in this case. When compiling with -c
the compiler automatically creates an object file whose name is the same as the source file, with .o
instead of the original extension.
注意,在这里不需要用 -o
选项来指定输出文件的文件名。当用 -c
来编译时,编译器会自动生成与源文件同名,但用 .o
来代替原来的扩展名的对象文件。
There is no need to put the header file hello.h
on the command line, since it is automatically included by the #include
statements in main.c
and hello_fn.c
.
由于 main.c
和 hello_fn.c
中的 #include
声明,hello.h
会自动被包括进来,所以在命令行上不需要指定该头文件。
2.4.2 Creating executables from object files (从对象文件生成可执行文件)
The final step in creating an executable file is to use gcc
to link the object files together and fill in the missing addresses of external functions. To link object files together, they are simply listed on the command line:
生成可执行文件的最后步骤是用 gcc
把各个对象文件链接在一起并填充缺失的外部函数的地址。要把对象文件链接在一起,只需把它们简单的列在命令行上即可:
$ gcc main.o hello_fn.o -o hello
This is one of the few occasions where there is no need to use the -Wall
warning option, since the individual source files have already been successfully compiled to object code. Once the source files have been compiled, linking is an unambiguous process which either succeeds or fails (it fails only if there are references which cannot be resolved).
这是几个很少需要用到 -Wall
警告选项的场合之一,因为每个源文件已经被成功地编译成对象文件了。一旦源文件被编译,链接是一个要么成功要么失败的明确的过程 (只有在有引用不能解析的情况下才会链接失败)。
To perform the linking step gcc
uses the linker ld
, which is a separate program. On GNU systems the GNU linker, GNU ld
, is used. Other systems may use the GNU linker with GCC, or may have their own linkers. The linker itself will be discussed later (see section 11 How the compiler works). By running the linker, gcc
creates an executable file from the object files.
gcc
使用链接器 ld
来施行链接,它是一个单独的程序。在 GNU 系统上用到的是 GNU 的链接器,即 GNU ld
。在另外的系统上可能也用 GCC 的 GNU 链接器,或者可能用它们自己的连接器。链接器将在后面讨论 (see section 11 How the compiler works)。通过运行连接器,gcc
从对象文件生成可执行文件。
The resulting executable file can now be run:
生成的可执行文件现在可以运行了:
$ ./hello
Hello, world!
It produces the same output as the version of the program using a single source file in the previous section.
它生成的输出与在前面章节中利用单个源文件编译的程序是一样的。
occasion [əˈkeɪʒn];n. 时机,机会,场合,理由 vt. 引起,惹起
strong@foreverstrong:~/Desktop/makefile_work$ ll
total 20
drwxrwxr-x 2 strong strong 4096 Sep 26 20:33 ./
drwxr-xr-x 8 strong strong 4096 Sep 21 11:38 ../
-rw-rw-r-- 1 strong strong 108 Sep 26 20:12 hello_fn.c
-rw-rw-r-- 1 strong strong 32 Sep 26 20:11 hello.h
-rw-rw-r-- 1 strong strong 71 Sep 26 20:12 main.c
strong@foreverstrong:~/Desktop/makefile_work$
strong@foreverstrong:~/Desktop/makefile_work$ gcc -Wall -c main.c
strong@foreverstrong:~/Desktop/makefile_work$ ll
total 24
drwxrwxr-x 2 strong strong 4096 Sep 27 08:41 ./
drwxr-xr-x 8 strong strong 4096 Sep 21 11:38 ../
-rw-rw-r-- 1 strong strong 108 Sep 26 20:12 hello_fn.c
-rw-rw-r-- 1 strong strong 32 Sep 26 20:11 hello.h
-rw-rw-r-- 1 strong strong 71 Sep 26 20:12 main.c
-rw-rw-r-- 1 strong strong 1496 Sep 27 08:41 main.o
strong@foreverstrong:~/Desktop/makefile_work$
strong@foreverstrong:~/Desktop/makefile_work$ gcc -Wall -c hello_fn.c
strong@foreverstrong:~/Desktop/makefile_work$ ll
total 28
drwxrwxr-x 2 strong strong 4096 Sep 27 08:41 ./
drwxr-xr-x 8 strong strong 4096 Sep 21 11:38 ../
-rw-rw-r-- 1 strong strong 108 Sep 26 20:12 hello_fn.c
-rw-rw-r-- 1 strong strong 1528 Sep 27 08:41 hello_fn.o
-rw-rw-r-- 1 strong strong 32 Sep 26 20:11 hello.h
-rw-rw-r-- 1 strong strong 71 Sep 26 20:12 main.c
-rw-rw-r-- 1 strong strong 1496 Sep 27 08:41 main.o
strong@foreverstrong:~/Desktop/makefile_work$
strong@foreverstrong:~/Desktop/makefile_work$ gcc main.o hello_fn.o -o hello
strong@foreverstrong:~/Desktop/makefile_work$ ll
total 40
drwxrwxr-x 2 strong strong 4096 Sep 27 08:41 ./
drwxr-xr-x 8 strong strong 4096 Sep 21 11:38 ../
-rwxrwxr-x 1 strong strong 8672 Sep 27 08:41 hello*
-rw-rw-r-- 1 strong strong 108 Sep 26 20:12 hello_fn.c
-rw-rw-r-- 1 strong strong 1528 Sep 27 08:41 hello_fn.o
-rw-rw-r-- 1 strong strong 32 Sep 26 20:11 hello.h
-rw-rw-r-- 1 strong strong 71 Sep 26 20:12 main.c
-rw-rw-r-- 1 strong strong 1496 Sep 27 08:41 main.o
strong@foreverstrong:~/Desktop/makefile_work$
strong@foreverstrong:~/Desktop/makefile_work$ ./hello
Hello, world!
strong@foreverstrong:~/Desktop/makefile_work$
2.4.3 Link order of object files (对象文件的链接次序)
On Unix-like systems, the traditional behavior of compilers and linkers is to search for external functions from left to right in the object files specified on the command line. This means that the object file which contains the definition of a function should appear after any files which call that function.
在类 Unix 系统上,传统上编译器和链接器搜索外部函数的次序是在命令行上指定的对象文件中从左到右的查找。这意味着包含函数定义的对象文件应当出现在调用这些函数的任何文件之后。
In this case, the file hello_fn.o
containing the function hello
should be specified after main.o
itself, since main
calls hello
:
由于是 main
调用 hello
,在这种情况下,包含 hello
函数的文件 hello_fn.o
应该被放在 main.o
之后:
$ gcc main.o hello_fn.o -o hello (correct order)
With some compilers or linkers the opposite ordering would result in an error,
如果次序搞反了,那么有些编译器或链接器会报错:
$ cc hello_fn.o main.o -o hello (incorrect order)
main.o: In function `main':
main.o(.text+0xf): undefined reference to `hello'
because there is no object file containing hello
after main.o
.
因为在 main.o
后面没有包含 hello
函数的对象文件。
Most current compilers and linkers will search all object files, regardless of order, but since not all compilers do this it is best to follow the convention of ordering object files from left to right.
当前绝大部分编译器和链接器会不管次序搜索所有的对象文件,但由于不是所有的编译器都这么做,最好遵守从左到右排序对象文件的惯例。
This is worth keeping in mind if you ever encounter unexpected problems with undefined references, and all the necessary object files appear to be present on the command line.
如果命令行上已经包括了所有必需的对象文件,但你还是碰到意料之外的未定义引用这种问题,那就因该想想这个问题。
strong@foreverstrong:~/Desktop/makefile_work$ ll
total 28
drwxrwxr-x 2 strong strong 4096 Sep 27 09:00 ./
drwxr-xr-x 8 strong strong 4096 Sep 21 11:38 ../
-rw-rw-r-- 1 strong strong 108 Sep 26 20:12 hello_fn.c
-rw-rw-r-- 1 strong strong 1528 Sep 27 08:41 hello_fn.o
-rw-rw-r-- 1 strong strong 32 Sep 26 20:11 hello.h
-rw-rw-r-- 1 strong strong 71 Sep 26 20:12 main.c
-rw-rw-r-- 1 strong strong 1496 Sep 27 08:41 main.o
strong@foreverstrong:~/Desktop/makefile_work$
strong@foreverstrong:~/Desktop/makefile_work$ gcc main.o hello_fn.o -o hello
strong@foreverstrong:~/Desktop/makefile_work$
strong@foreverstrong:~/Desktop/makefile_work$ ll
total 40
drwxrwxr-x 2 strong strong 4096 Sep 27 09:00 ./
drwxr-xr-x 8 strong strong 4096 Sep 21 11:38 ../
-rwxrwxr-x 1 strong strong 8672 Sep 27 09:00 hello*
-rw-rw-r-- 1 strong strong 108 Sep 26 20:12 hello_fn.c
-rw-rw-r-- 1 strong strong 1528 Sep 27 08:41 hello_fn.o
-rw-rw-r-- 1 strong strong 32 Sep 26 20:11 hello.h
-rw-rw-r-- 1 strong strong 71 Sep 26 20:12 main.c
-rw-rw-r-- 1 strong strong 1496 Sep 27 08:41 main.o
strong@foreverstrong:~/Desktop/makefile_work$
strong@foreverstrong:~/Desktop/makefile_work$ ./hello
Hello, world!
strong@foreverstrong:~/Desktop/makefile_work$
strong@foreverstrong:~/Desktop/makefile_work$ rm hello
strong@foreverstrong:~/Desktop/makefile_work$
strong@foreverstrong:~/Desktop/makefile_work$ ll
total 28
drwxrwxr-x 2 strong strong 4096 Sep 27 09:00 ./
drwxr-xr-x 8 strong strong 4096 Sep 21 11:38 ../
-rw-rw-r-- 1 strong strong 108 Sep 26 20:12 hello_fn.c
-rw-rw-r-- 1 strong strong 1528 Sep 27 08:41 hello_fn.o
-rw-rw-r-- 1 strong strong 32 Sep 26 20:11 hello.h
-rw-rw-r-- 1 strong strong 71 Sep 26 20:12 main.c
-rw-rw-r-- 1 strong strong 1496 Sep 27 08:41 main.o
strong@foreverstrong:~/Desktop/makefile_work$
strong@foreverstrong:~/Desktop/makefile_work$ cc hello_fn.o main.o -o hello
strong@foreverstrong:~/Desktop/makefile_work$
strong@foreverstrong:~/Desktop/makefile_work$ ll
total 40
drwxrwxr-x 2 strong strong 4096 Sep 27 09:01 ./
drwxr-xr-x 8 strong strong 4096 Sep 21 11:38 ../
-rwxrwxr-x 1 strong strong 8672 Sep 27 09:01 hello*
-rw-rw-r-- 1 strong strong 108 Sep 26 20:12 hello_fn.c
-rw-rw-r-- 1 strong strong 1528 Sep 27 08:41 hello_fn.o
-rw-rw-r-- 1 strong strong 32 Sep 26 20:11 hello.h
-rw-rw-r-- 1 strong strong 71 Sep 26 20:12 main.c
-rw-rw-r-- 1 strong strong 1496 Sep 27 08:41 main.o
strong@foreverstrong:~/Desktop/makefile_work$
strong@foreverstrong:~/Desktop/makefile_work$ ./hello
Hello, world!
strong@foreverstrong:~/Desktop/makefile_work$
strong@foreverstrong:~/Desktop/makefile_work$ rm hello
strong@foreverstrong:~/Desktop/makefile_work$
strong@foreverstrong:~/Desktop/makefile_work$ ll
total 28
drwxrwxr-x 2 strong strong 4096 Sep 27 09:01 ./
drwxr-xr-x 8 strong strong 4096 Sep 21 11:38 ../
-rw-rw-r-- 1 strong strong 108 Sep 26 20:12 hello_fn.c
-rw-rw-r-- 1 strong strong 1528 Sep 27 08:41 hello_fn.o
-rw-rw-r-- 1 strong strong 32 Sep 26 20:11 hello.h
-rw-rw-r-- 1 strong strong 71 Sep 26 20:12 main.c
-rw-rw-r-- 1 strong strong 1496 Sep 27 08:41 main.o
strong@foreverstrong:~/Desktop/makefile_work$
strong@foreverstrong:~/Desktop/makefile_work$ gcc hello_fn.o main.o -o hello
strong@foreverstrong:~/Desktop/makefile_work$
strong@foreverstrong:~/Desktop/makefile_work$ ll
total 40
drwxrwxr-x 2 strong strong 4096 Sep 27 09:01 ./
drwxr-xr-x 8 strong strong 4096 Sep 21 11:38 ../
-rwxrwxr-x 1 strong strong 8672 Sep 27 09:01 hello*
-rw-rw-r-- 1 strong strong 108 Sep 26 20:12 hello_fn.c
-rw-rw-r-- 1 strong strong 1528 Sep 27 08:41 hello_fn.o
-rw-rw-r-- 1 strong strong 32 Sep 26 20:11 hello.h
-rw-rw-r-- 1 strong strong 71 Sep 26 20:12 main.c
-rw-rw-r-- 1 strong strong 1496 Sep 27 08:41 main.o
strong@foreverstrong:~/Desktop/makefile_work$
strong@foreverstrong:~/Desktop/makefile_work$ ./hello
Hello, world!
strong@foreverstrong:~/Desktop/makefile_work$
2.5 Recompiling and relinking (重新编译和重新链接)
To show how source files can be compiled independently we will edit the main program main.c
and modify it to print a greeting to everyone
instead of world
:
为了展示源文件是怎样被独立地编译的,让我们来编辑主程序 main.c
,修改它以打印欢迎任何人的信息,而不是仅仅 world
:
#include "hello.h"
int
main (void)
{
hello ("everyone"); /* changed from "world" */
return 0;
}
The updated file main.c
can now be recompiled with the following command:
更新过的 main.c
文件现在能用下面的命令重新编译:
$ gcc -Wall -c main.c
This produces a new object file main.o
. There is no need to create a new object file for hello_fn.c
, since that file and the related files that it depends on, such as header files, have not changed.
这回生成一个新的对象文件 main.o
。由于 hello_fn.c
和及其相关的依赖文件,比如头文件,没有改变,所以没有必要为它生成新的对象文件。
The new object file can be relinked with the hello
function to create a new executable file:
新的对象文件可以和 hello
函数重新链接以生成新的可执行文件:
$ gcc main.o hello_fn.o -o hello
The resulting executable hello
now uses the new main
function to produce the following output:
生成的可执行文件 hello
现在用的是新的 main
函数,输出下面的信息:
$ ./hello
Hello, everyone!
Note that only the file main.c
has been recompiled, and then relinked with the existing object file for the hello
function. If the file hello_fn.c
had been modified instead, we could have recompiled hello_fn.c
to create a new object file hello_fn.o
and relinked this with the existing file main.o
. [3]
注意,只有 main.c
文件被重新编译,然后和含有 hello
函数的已有对象文件重新链接。相反,如果是 hello_fn.c
文件被修改,我们也只需要重新编译 hello_fn.c
以生成新的对象文件 hello_fn.o
,再和已有的 main.o
文件重新链接即可。
In general, linking is faster than compilation–in a large project with many source files, recompiling only those that have been modified can make a significant saving. The process of recompiling only the modified files in a project can be automated using GNU Make (see section Further reading).
通常,链接要快于编译 – 在一个有许多源文件的大型项目中,只重新编译那些被修改过的文件可以显著地节省时间。仅仅重编译项目中修改过的文件的过程可以用 GNU Make 来自动完成 (see section Further reading)。
strong@foreverstrong:~/Desktop/makefile_work$ ll
total 24
drwxrwxr-x 2 strong strong 4096 Sep 27 09:12 ./
drwxr-xr-x 8 strong strong 4096 Sep 21 11:38 ../
-rw-rw-r-- 1 strong strong 108 Sep 26 20:12 hello_fn.c
-rw-rw-r-- 1 strong strong 1528 Sep 27 08:41 hello_fn.o
-rw-rw-r-- 1 strong strong 32 Sep 26 20:11 hello.h
-rw-rw-r-- 1 strong strong 102 Sep 27 09:12 main.c
strong@foreverstrong:~/Desktop/makefile_work$
strong@foreverstrong:~/Desktop/makefile_work$ gcc -Wall -c main.c
strong@foreverstrong:~/Desktop/makefile_work$
strong@foreverstrong:~/Desktop/makefile_work$ ll
total 28
drwxrwxr-x 2 strong strong 4096 Sep 27 09:12 ./
drwxr-xr-x 8 strong strong 4096 Sep 21 11:38 ../
-rw-rw-r-- 1 strong strong 108 Sep 26 20:12 hello_fn.c
-rw-rw-r-- 1 strong strong 1528 Sep 27 08:41 hello_fn.o
-rw-rw-r-- 1 strong strong 32 Sep 26 20:11 hello.h
-rw-rw-r-- 1 strong strong 102 Sep 27 09:12 main.c
-rw-rw-r-- 1 strong strong 1504 Sep 27 09:12 main.o
strong@foreverstrong:~/Desktop/makefile_work$
strong@foreverstrong:~/Desktop/makefile_work$ gcc main.o hello_fn.o -o hello
strong@foreverstrong:~/Desktop/makefile_work$
strong@foreverstrong:~/Desktop/makefile_work$ ll
total 40
drwxrwxr-x 2 strong strong 4096 Sep 27 09:13 ./
drwxr-xr-x 8 strong strong 4096 Sep 21 11:38 ../
-rwxrwxr-x 1 strong strong 8672 Sep 27 09:13 hello*
-rw-rw-r-- 1 strong strong 108 Sep 26 20:12 hello_fn.c
-rw-rw-r-- 1 strong strong 1528 Sep 27 08:41 hello_fn.o
-rw-rw-r-- 1 strong strong 32 Sep 26 20:11 hello.h
-rw-rw-r-- 1 strong strong 102 Sep 27 09:12 main.c
-rw-rw-r-- 1 strong strong 1504 Sep 27 09:12 main.o
strong@foreverstrong:~/Desktop/makefile_work$
strong@foreverstrong:~/Desktop/makefile_work$ ./hello
Hello, everyone!
strong@foreverstrong:~/Desktop/makefile_work$
2.6 Linking with external libraries (与外部库文件链接)
A library is a collection of precompiled object files which can be linked into programs. The most common use of libraries is to provide system functions, such as the square root function sqrt
found in the C math library.
库是已经编译好并能被链接入程序的对象文件的集合。库中提供一些最常用的系统函数,比如像 C 的数学库中求平方根函数 sqrt。
Libraries are typically stored in special archive files
with the extension .a
, referred to as static libraries
. They are created from object files with a separate tool, the GNU archiver ar
, and used by the linker to resolve references to functions at compile-time. We will see later how to create libraries using the ar
command (see section 10 Compiler-related tools). For simplicity, only static libraries are covered in this section–dynamic linking at runtime using shared libraries
will be described in the next chapter.
库通常被存储在扩展名为 .a
的特殊归档文件中,被称为静态库。它们用一个单独的工具,GNU 归档器 ar
,从对象文件生成。在编译期,可以被链接器使用来解决对库中函数的引用。在后面我们会看到怎样用 ar
命令来生成库 (see section 10 Compiler-related tools)。为了简化,本节只介绍静态库 – 使用共享库的运行期动态链接将在下一章介绍。
The standard system libraries are usually found in the directories /usr/lib
and /lib
. [4] For example, the C math library is typically stored in the file /usr/lib/libm.a
on Unix-like systems. The corresponding prototype declarations for the functions in this library are given in the header file /usr/include/math.h
. The C standard library itself is stored in /usr/lib/libc.a
and contains functions specified in the ANSI/ISO C standard, such as printf
– this library is linked by default for every C program.
标准的系统库通常能在 /usr/lib
和 /lib
目录下找到。比如,在类 Unix 系统上,C 的数学库常被放在文件 /usr/lib/libm.a
中,而该库中的相应的函数的原型声明在头文件 /usr/include/math.h
中。C 标准库自身存放在 /usr/lib/libc.a
中,包含 ANSI/ISO C 标准指定的各个函数,比如像 printf
– C 库是每一个 C 程序默认都要被链接的库。
[4] 在 64 位和 32 位可执行文件都支持的系统上,64 位版本的库通常被存放在 /usr/lib64
和 /lib64
,而 32 位版本的则在 /usr/lib
和 /lib
。
Here is an example program which makes a call to the external function sqrt
in the math library libm.a
:
下面是调用数学库 libm.a
中的 sqrt
这个外部函数的一个例子:
#include <math.h>
#include <stdio.h>
int
main (void)
{
double x = sqrt (2.0);
printf ("The square root of 2.0 is %f\n", x);
return 0;
}
Trying to create an executable from this source file alone causes the compiler to give an error at the link stage:
试图只用该源文件就生成可执行文件会导致在链接阶段编译器包下面的错:
$ gcc -Wall calc.c -o calc
/tmp/ccbR6Ojm.o: In function `main':
/tmp/ccbR6Ojm.o(.text+0x19): undefined reference
to `sqrt'
The problem is that the reference to the sqrt
function cannot be resolved without the external math library libm.a
. The function sqrt
is not defined in the program or the default library libc.a
, and the compiler does not link to the file libm.a
unless it is explicitly selected. Incidentally, the file mentioned in the error message /tmp/ccbR60jm.o
is a temporary object file created by the compiler from calc.c
, in order to carry out the linking process.
问题是由于在没有外部数学库 libm.a
的情况下,对函数 sqrt
的引用被不能被解析。函数 sqrt
并不定义在源程序中或默认的 C 库 libc.a
中,而且除非 libm.a
被显示指定,否则编译器不会链接该库文件。顺便说一下,在错误信息中提到的 /tmp/ccbR60jm.o
文件是由编译器为了执行链接而编译 calc.c
时生成的临时对象文件。
incidentally [ˌɪnsɪˈdentli]:adv. 顺便,偶然地,附带地
cumbersome [ˈkʌmbəsəm]:adj. 笨重的,累赘的,难处理的
To enable the compiler to link the sqrt
function to the main program calc.c
we need to supply the library libm.a
. One obvious but cumbersome way to do this is to specify it explicitly on the command line:
为了使得编译器能把 sqrt
函数链接到主程序 calc.c
,我们需要提供 libm.a
库。一个显然但麻烦的办法是在命令行上显示指定该库文件:
$ gcc -Wall calc.c /usr/lib/libm.a -o calc
The library libm.a
contains object files for all the mathematical functions, such as sin
, cos
, exp
, log
and sqrt
. The linker searches through these to find the object file containing the sqrt
function.
libm.a
库包含的是对象文件,里面是各种各样的数学函数,像 sin
, cos
, exp
, log
and sqrt
。链接器搜索并能找到包含 sqrt
函数的对象文件。
Once the object file for the sqrt
function has been found, the main program can be linked and a complete executable produced:
一旦包含 sqrt
函数的对象文件被找到,主程序就可以被成功链接以生成可执行文件:
$ ./calc
The square root of 2.0 is 1.414214
The executable file includes the machine code for the main function and the machine code for the sqrt
function, copied from the corresponding object file in the library libm.a
.
该可执行文件包括 main 函数的机器码和从 libm.a
库中的相应对象文件中复制过来的 sqrt
函数的机器码。
To avoid the need to specify long paths on the command line, the compiler provides a short-cut option -l
for linking against libraries. For example, the following command,
为了避免在命令行上指定长路径名,编译器提供了短选项 -l
用于链接库文件。比如,下面的命令,
$ gcc -Wall calc.c -lm -o calc
is equivalent to the original command above using the full library name /usr/lib/libm.a
.
与上面原来在命令行上指定全路径的库名 /usr/lib/libm.a
是一样的。
In general, the compiler option -lNAME
will attempt to link object files with a library file libNAME.a
in the standard library directories. Additional directories can specified with command-line options and environment variables, to be discussed shortly. A large program will typically use many -l
options to link libraries such as the math library, graphics libraries and networking libraries.
通常,编译器选项 -lNAME
试图链接标准库目录下的文件名为 libNAME.a
中的对象文件。另外的可以通过命令行和环境变量指定的目录会简短地讨论一下。在大型程序中通常会用到很多 -l
选项,来链接像数学库、图形库和网络库。
strong@foreverstrong:~/Desktop/makefile_work$ pwd
/home/strong/Desktop/makefile_work
strong@foreverstrong:~/Desktop/makefile_work$
strong@foreverstrong:~/Desktop/makefile_work$ ll
total 12
drwxrwxr-x 2 strong strong 4096 Sep 27 19:19 ./
drwxr-xr-x 8 strong strong 4096 Sep 27 19:05 ../
-rw-rw-r-- 1 strong strong 143 Sep 27 19:19 calc.c
strong@foreverstrong:~/Desktop/makefile_work$
strong@foreverstrong:~/Desktop/makefile_work$ cat calc.c
#include <math.h>
#include <stdio.h>
int main (void)
{
double x = sqrt (2.0);
printf ("The square root of 2.0 is %f\n", x);
return 0;
}
strong@foreverstrong:~/Desktop/makefile_work$
strong@foreverstrong:~/Desktop/makefile_work$ gcc -Wall calc.c -o calc
strong@foreverstrong:~/Desktop/makefile_work$
strong@foreverstrong:~/Desktop/makefile_work$ ll
total 24
drwxrwxr-x 2 strong strong 4096 Sep 27 19:20 ./
drwxr-xr-x 8 strong strong 4096 Sep 27 19:05 ../
-rwxrwxr-x 1 strong strong 8608 Sep 27 19:20 calc*
-rw-rw-r-- 1 strong strong 143 Sep 27 19:19 calc.c
strong@foreverstrong:~/Desktop/makefile_work$
strong@foreverstrong:~/Desktop/makefile_work$ ./calc
The square root of 2.0 is 1.414214
strong@foreverstrong:~/Desktop/makefile_work$
strong@foreverstrong:~/Desktop/makefile_work$ rm calc
strong@foreverstrong:~/Desktop/makefile_work$
strong@foreverstrong:~/Desktop/makefile_work$ ll
total 12
drwxrwxr-x 2 strong strong 4096 Sep 27 19:21 ./
drwxr-xr-x 8 strong strong 4096 Sep 27 19:05 ../
-rw-rw-r-- 1 strong strong 143 Sep 27 19:19 calc.c
strong@foreverstrong:~/Desktop/makefile_work$
strong@foreverstrong:~/Desktop/makefile_work$ gcc -Wall calc.c /usr/lib/libm.a -o calc
gcc: error: /usr/lib/libm.a: No such file or directory
strong@foreverstrong:~/Desktop/makefile_work$
strong@foreverstrong:~/Desktop/makefile_work$ gcc -Wall calc.c -lm -o calc
strong@foreverstrong:~/Desktop/makefile_work$
strong@foreverstrong:~/Desktop/makefile_work$ ll
total 24
drwxrwxr-x 2 strong strong 4096 Sep 27 19:22 ./
drwxr-xr-x 8 strong strong 4096 Sep 27 19:05 ../
-rwxrwxr-x 1 strong strong 8608 Sep 27 19:22 calc*
-rw-rw-r-- 1 strong strong 143 Sep 27 19:19 calc.c
strong@foreverstrong:~/Desktop/makefile_work$
strong@foreverstrong:~/Desktop/makefile_work$ file calc
calc: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=ef85f9fb77eebd691f5472f9c8a032bc6dabb1cf, not stripped
strong@foreverstrong:~/Desktop/makefile_work$
strong@foreverstrong:~/Desktop/makefile_work$ ldd calc
linux-vdso.so.1 => (0x00007ffd361a8000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f79788dd000)
/lib64/ld-linux-x86-64.so.2 (0x00007f7978ca7000)
strong@foreverstrong:~/Desktop/makefile_work$
strong@foreverstrong:~/Desktop/makefile_work$ ./calc
The square root of 2.0 is 1.414214
strong@foreverstrong:~/Desktop/makefile_work$
2.6.1 Link order of libraries (库的链接次序)
The ordering of libraries on the command line follows the same convection as for object files: they are searched from left to right – a library containing the definition of a function should appear after any source files or object files which use it. This includes libraries specified with the short-cut -l
option, as shown in the following command:
在命令行上的库的次序遵照像对象文件中的同样的惯例:它们被从左到右的搜索 – 包含函数定义的库应该出现在任何使用到该函数的源文件和对象文件之后。这包括用短选项 -l
指定的库,像下面命令所显示的:
$ gcc -Wall calc.c -lm -o calc (correct order)
With some compilers the opposite ordering (placing the -lm
option before the file which uses it) would result in an error,
如果次序搞反了 (把 -lm
选项放到使用数学库中函数的文件前面),有些编译器会报错,
$ cc -Wall -lm calc.c -o calc (incorrect order)
main.o: In function `main':
main.o(.text+0xf): undefined reference to `sqrt'
because there is no library or object file containing sqrt
after calc.c
. The option -lm
should appear after the file calc.c
.
这是因为在 calc.c
后面没有库或对象文件包含 sqrt
函数。-lm
选项应当在文件 calc.c
后面。
When several libraries are being used, the same convention should be followed for the libraries themselves. A library which calls an external function defined in another library should appear before the library containing the function.
当用到多个库时,库之间也应当遵守同样的惯例。一个调用了定义在另外一个库中的函数的库应当被放在包含该函数代码的库的前面。
For example, a program data.c
using the GNU Linear Programming library libglpk.a
, which in turn uses the math library libm.a
, should be compiled as,
例如,程序 data.c
用到了 GNU Linear Programming 库 libglpk.a
,而该库又依次用到数学库 libm.a
,那么应当这么编译:
$ gcc -Wall data.c -lglpk -lm
since the object files in libglpk.a
use functions defined in libm.a
.
因为 libglpk.a
中的对象文件用到了定义在 libm.a
中的函数。
As for object files, most current compilers will search all libraries, regardless of order. However, since not all compilers do this it is best to follow the convention of ordering libraries from left to right.
对于对象文件,不管次序怎样,绝大部分编译器会搜索所有的库。然而,由于并不是所有的编译器都这么做,所以最好遵守从左到右对库排序的惯例。
strong@foreverstrong:~/Desktop/makefile_work$ ll
total 12
drwxrwxr-x 2 strong strong 4096 Sep 27 19:39 ./
drwxr-xr-x 8 strong strong 4096 Sep 27 19:05 ../
-rw-rw-r-- 1 strong strong 143 Sep 27 19:19 calc.c
strong@foreverstrong:~/Desktop/makefile_work$
strong@foreverstrong:~/Desktop/makefile_work$ gcc -Wall -lm calc.c -o calc
strong@foreverstrong:~/Desktop/makefile_work$
strong@foreverstrong:~/Desktop/makefile_work$ ll
total 24
drwxrwxr-x 2 strong strong 4096 Sep 27 19:40 ./
drwxr-xr-x 8 strong strong 4096 Sep 27 19:05 ../
-rwxrwxr-x 1 strong strong 8608 Sep 27 19:40 calc*
-rw-rw-r-- 1 strong strong 143 Sep 27 19:19 calc.c
strong@foreverstrong:~/Desktop/makefile_work$
strong@foreverstrong:~/Desktop/makefile_work$ ./calc
The square root of 2.0 is 1.414214
strong@foreverstrong:~/Desktop/makefile_work$
strong@foreverstrong:~/Desktop/makefile_work$ rm calc
strong@foreverstrong:~/Desktop/makefile_work$
strong@foreverstrong:~/Desktop/makefile_work$ ll
total 12
drwxrwxr-x 2 strong strong 4096 Sep 27 19:40 ./
drwxr-xr-x 8 strong strong 4096 Sep 27 19:05 ../
-rw-rw-r-- 1 strong strong 143 Sep 27 19:19 calc.c
strong@foreverstrong:~/Desktop/makefile_work$
strong@foreverstrong:~/Desktop/makefile_work$ gcc -Wall calc.c -lm -o calc
strong@foreverstrong:~/Desktop/makefile_work$
strong@foreverstrong:~/Desktop/makefile_work$ ll
total 24
drwxrwxr-x 2 strong strong 4096 Sep 27 19:41 ./
drwxr-xr-x 8 strong strong 4096 Sep 27 19:05 ../
-rwxrwxr-x 1 strong strong 8608 Sep 27 19:41 calc*
-rw-rw-r-- 1 strong strong 143 Sep 27 19:19 calc.c
strong@foreverstrong:~/Desktop/makefile_work$
strong@foreverstrong:~/Desktop/makefile_work$ ./calc
The square root of 2.0 is 1.414214
strong@foreverstrong:~/Desktop/makefile_work$
2.7 Using library header files
When using a library it is essential to include the appropriate header files, in order to declare the function arguments and return values with the correct types. Without declarations, the arguments of a function can be passed with the wrong type, causing corrupted results.
使用库文件,为了得到函数参数和返回值正确类型的声明,必须包括入相应的头文件。如果没有函数声明,可能传递错误类型的函数参数,从而导致不对的结果。
The following example shows another program which makes a function call to the C math library. In this case, the function pow
is used to compute the cube of two (2 raised to the power of 3):
下面的例子展示了另一个调用 C 库中函数的程序。在这里,函数 pow
用于计算 2 的立方:
#include <stdio.h>
int
main (void)
{
double x = pow (2.0, 3.0);
printf ("Two cubed is %f\n", x);
return 0;
}
However, the program contains an error – the #include
statement for math.h
is missing, so the prototype double pow (double x, double y)
given there will not be seen by the compiler.
但该程序有个错误 – 缺少对 math.h
的 #include
声明,这样头文件中的原型 double pow(double x, double y)
不能被编译器看到。
Compiling the program without any warning options will produce an executable file which gives incorrect results:
不带任何警告选项的编译该程序将生成一个输出错误结果的可执行文件:
$ gcc badpow.c -lm
$ ./a.out
Two cubed is 2.851120 (incorrect result, should be 8)
The results are corrupted because the arguments and return value of the call to pow
are passed with incorrect types. [5] This can be detected by turning on the warning option -Wall
:
结果是错的,因为传递给 pow
调用的参数和返回值的类型都是错的。该错误在打开警告选项 -Wall
后能被检测到:
$ gcc -Wall badpow.c -lm
badpow.c: In function `main':
badpow.c:6: warning: implicit declaration of
function `pow'
This example shows again the importance of using the warning option -Wall
to detect serious problems that could otherwise easily be overlooked.
这个例子再次显示使用警告选项 -Wall
来检测很容易被忽略的严重问题的重要性。
overlook [ˌəʊvəˈlʊk]:vt. 忽略,俯瞰,远眺,不计较,宽恕,施魔法于,监督,检查 n. 忽视,眺望,从高处能看到的景色
strong@foreverstrong:~/Desktop/makefile_work$ ll
total 12
drwxrwxr-x 2 strong strong 4096 Sep 27 19:55 ./
drwxr-xr-x 8 strong strong 4096 Sep 27 19:05 ../
-rw-rw-r-- 1 strong strong 118 Sep 27 19:55 badpow.c
strong@foreverstrong:~/Desktop/makefile_work$
strong@foreverstrong:~/Desktop/makefile_work$ gcc badpow.c -lm
badpow.c: In function ‘main’:
badpow.c:5:14: warning: implicit declaration of function ‘pow’ [-Wimplicit-function-declaration]
double x = pow (2.0, 3.0);
^
badpow.c:5:14: warning: incompatible implicit declaration of built-in function ‘pow’
badpow.c:5:14: note: include ‘<math.h>’ or provide a declaration of ‘pow’
strong@foreverstrong:~/Desktop/makefile_work$
strong@foreverstrong:~/Desktop/makefile_work$ ll
total 24
drwxrwxr-x 2 strong strong 4096 Sep 27 19:55 ./
drwxr-xr-x 8 strong strong 4096 Sep 27 19:05 ../
-rwxrwxr-x 1 strong strong 8608 Sep 27 19:55 a.out*
-rw-rw-r-- 1 strong strong 118 Sep 27 19:55 badpow.c
strong@foreverstrong:~/Desktop/makefile_work$
strong@foreverstrong:~/Desktop/makefile_work$ ./a.out
Two cubed is 8.000000
strong@foreverstrong:~/Desktop/makefile_work$
strong@foreverstrong:~/Desktop/makefile_work$ rm a.out
strong@foreverstrong:~/Desktop/makefile_work$
strong@foreverstrong:~/Desktop/makefile_work$ ll
total 12
drwxrwxr-x 2 strong strong 4096 Sep 27 19:56 ./
drwxr-xr-x 8 strong strong 4096 Sep 27 19:05 ../
-rw-rw-r-- 1 strong strong 118 Sep 27 19:55 badpow.c
strong@foreverstrong:~/Desktop/makefile_work$
strong@foreverstrong:~/Desktop/makefile_work$ gcc -Wall badpow.c -lm
badpow.c: In function ‘main’:
badpow.c:5:14: warning: implicit declaration of function ‘pow’ [-Wimplicit-function-declaration]
double x = pow (2.0, 3.0);
^
badpow.c:5:14: warning: incompatible implicit declaration of built-in function ‘pow’
badpow.c:5:14: note: include ‘<math.h>’ or provide a declaration of ‘pow’
strong@foreverstrong:~/Desktop/makefile_work$
References
http://lampwww.epfl.ch/~fsalvi/docs/gcc/www.network-theory.co.uk/docs/gccintro/index.html
https://www.linuxtopia.org/online_books/an_introduction_to_gcc/index.html
https://github.com/l10n-tw/An-Introduction-to-GCC-zh_TW