部分代码如下:
#include
#include
main()
{
double x = 1.0;
double ans;
ans = sqrt(x);
printf(“\nans is %lf\n”, ans);
return 0;
}
编译:
[root@Xecho mycode]# gcc -o 1 1.c
/tmp/ccdzoSZq.o(.text+0x90): In function `main’:
1.c: undefined reference to `sqrt’
collect2: ld 返回 1
发现错误,发现sqrt有问题,经wzt指点说库不存在,本人进一步调查.
[root@Xecho mycode]# find / -name math.h
/usr/include/math.h
发现有这头.打开google得到老外的回答是要加-lm.
然后man一下gcc,得到如下:
-l library
Search the library named library when linking. (The second alter-
native with the library as a separate argument is only for POSIX
compliance and is not recommended.)
It makes a difference where in the command you write this option;
the linker searches and processes libraries and object files in the
order they are specified. Thus, foo.o -lz bar.o searches library z
after file foo.o but before bar.o. If bar.o refers to functions in
z, those functions may not be loaded.
The linker searches a standard list of directories for the library,
which is actually a file named liblibrary.a. The linker then uses
this file as if it had been specified precisely by name.
The directories searched include several standard system directo-
ries plus any that you specify with -L.
Normally the files found this way are library files—archive files
whose members are object files. The linker handles an archive file
by scanning through it for members which define symbols that have
so far been referenced but not defined. But if the file that is
found is an ordinary object file, it is linked in the usual fash-
ion. The only difference between using an -l option and specifying
a file name is that -l surrounds library with lib and .a and
searches several directories.
发现是要连接这个库.
[root@Xecho mycode]# gcc -o 2 2.c -lm
[root@Xecho mycode]#
顺利搞定了。
原因分析:
先看几个概念:
与外部库连接
外部库有两种: (1)静态连接库lib.a
(2)共享连接库lib.so
共同点:
.a, .so都是.o目标文件的集合,这些目标文件中含有一些函数的定义(机器码),而这些函数将在连接时会被最终的可执行文件用到。
区别:
静态库.a : 当程序与静态库连接时,库中目标文件所含的所有将被程序使用的函数的机器码被copy到最终的可执行文件中。
共享库.so : 与共享库连接的可执行文件只包含它需要的函数的表,而不是所有的函数代码,在程序执行之前,那些需要的函数代码被拷贝到内存中,这样就使可执行文件比较 小,节省磁盘空间(更进一步,操作系统使用虚拟内存,使得一份共享库驻留在内存中被多个程序使用)。共享库还有个优点:若库本身被更新,不需要重新编译与 它连接的源程序。
具体分析:
编译器会给出上述错误信息,这是因为sqrt函数不能与外部数学库”libm.a”相连。sqrt函数没有在程序中定义,也不存在于默认C库 “libc.a”中,应该显式地选择连接库。上述出错信息中的”/tmp/ccdzoSZq.o”是gcc创造的临时目标文件,用作连接时用。
[root@Xecho mycode]# gcc -o 2 2.c /usr/lib/libm.a
[root@Xecho mycode]#
它告知gcc:在编译2.c时,加入位于/usr/lib中的libm.a库(C数学库)。C库文件默认位于/usr/lib, /usr/local/lib系统目录中; gcc默认地从/usr/local/lib, /usr/lib中搜索库文件。
指定库文件的绝对路径比较麻烦,有一种简化方法:
[root@Xecho mycode]# gcc -o 2 2.c -lm
其中的”-l”表示与库文件连接,”m”代表”libm.a”中的m。一般而言,”-lNAME”选项会使gcc将目标文件与名为”libNAME.a”的库文件相连。
上面所提到的”libm.a”就是静态库文件,所有静态库文件的扩展名都是.a
[root@Xecho mycode]# whereis libm.a
libm: /usr/lib/libm.so /usr/lib/libm.a
关于别的库,本人以后研究了再写吧。如果上面的看不明白,下面再发一个简单点的,别人的回答,加上了翻译。
Forrest Sheng Bao <http://forrest.bao.googlepages.com>
(Coz I will hell Microsoft and Apple Computers, the Chinese version is attached.)
(因为我要在这篇文章中骂微软和苹果,所以我要用中文翻译一遍)
It is not the first time some undergraduate ask me questions about why their encountered following problem when compile a C program on Linux via gcc.
这已经不是第一次有小朋友问我为何他们在Linux下写了一个程序结果gcc编译不过了
forrest@flavia:~$ gcc test.c
/tmp/cccT9KeH.o: In function `main’:
test.c:(.text+0x2a): undefined reference to `sqrt’
Source code:
forrest@flavia:~$ cat test.c
#include
#include
int main()
{
float i,j=3;
i=sqrt(j);
printf(“%f”,i);
return 1;
}
Why? Coz you are poisoned by the f**king Microsoft IDEs, such as the shit Visual Studio C++. Those IDEs are black boxes treating you like a stupid pig. More accurately, the problem is not generated on compiling process but the linking process.
为什么,因为你被他妈的微软的IDE给毒害了,比如狗屎的VC++. 这些IDE都是把你当作蠢猪一样对待的黑盒。更准确的说,问题不是出在编译的时候,而是出在连接的时候。
们甚至能够允许一个指针到处乱指,甚至指向根本没有东西的内存空间,而且这种程序还能执行,但是在Linux上面你会遇到段错误。
In order to obtain an executable program, you have to compile it first and then link it with libraries, no matter static libraries or dynamic libraries. We use dynamic libraries more frequently than static ones. On Linux, these libraries are put in lib directory(ib, /usr/lib,or the lib in the software’s directory).
为了获得一个可执行程序,你要先 编译 然后再把他和库连接起来,不论是动态库还是静态库。我们更加频繁的使用动态库而非静态库。在Linux上,这些库都被放在lib目录下,(比如/lib, /usr/lib,或者是软件自己目录下的lib)。他们都具有so或o的扩展名,就像见鬼的Windows上的dll或者是糟糕的Mac OS上的dylib。而且他们的名字通常以字符串lib开头
After compiling, the job is shifted to linker rather than compiler. You have to tell the linker which dynamic library to load and where it is. How to tell him? On the f**king Microsoft IDEs, they handled everything for you that maybe you don’t know. But on Linux, you have to specify it coz it is a transparent system. How to? use an option. Here is an example. (Please use ‘man gcc’ to see how to specify the it if your library is not on the default path.)
在 编译之后,任务就从编译器跑到连接器上面了。你必须告诉连接器load哪一个动态库,以及他在什么地方。怎么告诉呢?在他◎妈◎的微软的IDE里面,他们把所 有事情都搞好了,也许你不知道。但是linux上,你要指定,因为他是一个透明的系统。怎么办?使用一个参数。这里是一个例子。(请使用man gcc看一下如果你的库不在默认的路径上,你该如何指定。)
forrest@flavia:~$ gcc -o forrest.bin test.c -lm
forrest@flavia:~$ ./forrest.bin
1.732051
And please don’t use void as the returning type of the ‘main’ function. Even you just print a single line, please set the returning value to 1. Please don’t leave the returning value of certain function alone, especially the ‘main’ function, even if GCC defaultly considers it as INT. This will generate some weird errors. For example if you declear an argument which is actually a float number as an integer, strange things will happen. Try to redeclear ‘i’ into ‘int’ in above program. You will find that the square root of 3 is a minus number.
而 且不要用void作为作为main函数的返回值类型,即使你只是输出一行东西,也请把返回值设成1。最好不要不设某个函数的返回值,特别是main,虽然 gcc默认他是int,但是这样很容易导致某些奇怪的出错。比如你把某个实际是浮点数的变量变成了整数,你会遇到很多奇怪的事情,试试在上面的程序中把i 声明成int,你会发现对3开根号的结果是一个负数。
If you choose to use GCC, SUN CC or ICC, please restrict your syntax. You will be driven into mad coz you don’t know what causes the Segmentation Fault. Don’t think GCC is not good due to it can’t compile a program that can be compiled by VC++. GCC is the strong supporter to ANSI/ISO C/C++ Standards, though it perfer his own GNU C/C++ standards. VC++ can compile some programs since Microsoft added some hidden rulers. And they favor irresponsible programmers supporting Microsoft as well as silly Windows users. They can tolerate a pointer indexing to anywhere, even to void spaces. But on Linux, this will result to Segmentation Fault. It is a solid proof to the safety and robustness of Linux memory management.
如果你选择用GCC, Sun CC或者ICC(Intel C++ Compiler),请严格注意你的语法,很多时候你会很崩溃为什么一个程序导致段错误而你却找不出问题出在哪里。特别不要因为VC++能编译通过一个程 序而GCC编译不过就认为GCC不好,GCC是ANSI/ISO C/C++规范的严格执行者,虽然他也很喜欢用自己的GNU C/C++标准。VC++能编译通过那些程序往往是由于微软加入了一些语法的潜规则,而且他们很喜欢那些不负责任的程序员,就像没头脑的Windows用 户一样继续支持他们。他们甚至能够允许一个指针到处乱指,甚至指向根本不存在的内存空间,而且这种程序还能执行,但是在Linux上面你会遇到段错误,这 就是Linux内存管理强大和安全的地方。