`
isiqi
  • 浏览: 16044856 次
  • 性别: Icon_minigender_1
  • 来自: 济南
社区版块
存档分类
最新评论

[转] Linux 下使用 gcc 编译程序找不到库的分析和解决方法

阅读更多

转自:xecho.org

今天编译一个小程序出现麻烦,终于搞定了,发下方法可能适合和我一样在Linux下写程序的家伙.

一部分代码 如下

#include < stdio.h>#include <math.h>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(/lib, /usr/lib, or the lib in the software's directory). They have the extension name like 'so' or 'o'. Just like *.dll on the damn Microsoft Windows or *.dylib on the sucking Mac OS. And mostly their name start from the string 'lib'.

为了获得一个可执行程序,你要先 编译 然后再把他和库连接起来,不论是动态库还是静态库。我们更加频繁的使用动态库而非静态库。在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内存管理强大和安全的地方。
分享到:
评论

相关推荐

    LINUX下的C++编译器GCC简介

    Linux系统下的gcc(GNU C Compiler)是GNU推出的功能强大、性能优越的多平台编译器,是GNU的代表作品之一。gcc是可以在多种硬体平台上编译出可执行程序的超级编译器,其执行效率与一般的编译器相比平均效率要高20%~...

    gcc-linaro-6.3.1-2017.05-x86_64_arm-linux-gnueabihf.tar.xz

    可用于交叉编译32-bit Armv7 Cortex-A, hard-float, little-endian目标中的裸机程序、u-boot、Linux kernel、filesystem和App应用程序。gcc-linaro-6.3.1-2017.05-x86_64_arm-linux-gnueabihf.tar.xz交叉编译器必须...

    linux使用gcc编译c语言共享库步骤

    所谓的库是指已经编译好的供你使用的代码。它们常常提供一些通用功能,例如链表和二叉树可以用来保存任何数据,或者是一个特定的功能例如一个数据库服务器的接口,就像MySQL。 大部分大型的软件项目都会包含若干组件...

    windows上可编译Linux内核

    修改了Trans.cpp中的一个dug,该dug在translate MinGW gcc编译的程序时可能会出错。用MinGW gcc 编译的程序的VirtualAddress的形式可能是0xFFC1000这样的形式,其实0x1000才是它的VirtualAddress 08/4/2 修改了下...

    Linux中使用VS Code编译调试C++项目详解

    关于VS Code在Linux下的安装这里就不提了,不管是CentOS还是Ubuntu,如果不懂且搜问题足够的情况下,你会解决的。 一、前置知识——gcc/g++的编译链接过程 在Windows下,如果你用Visual Studio进行开发,C/C++的...

    嵌入式Linux NFS方式下应用程序的实现

    在Windows环境中,可以直接在PC上使用各种集成编译开发工具,完成程序编辑、编译和运行,而在嵌入式Linux环境下,缺少简单、高效的开发工具,程序的开发需要在PC+嵌入式Linux平台完成。因此,在程序的调试运行过程中...

    winlibs gcc/g++编译器 mingw64 10.0.0-r4

    它实际上是将经典的开源 C语言 编译器 GCC 移植到了 Windows 平台下,并且包含了 Win32API ,因此可以将源代码编译为可在 Windows 中运行的可执行程序。而且还可以使用一些 Windows 不具备的,Linux平台下的开发工具...

    arm-linux-gcc-4.5.1-v6-vfp-20120301.rar

    arm-linux-gcc-4.5.1-v6-vfp-20120301 为交叉...所谓的交叉编译就是在一种平台上编译出来的程序,是放到别的平台环境运行,即编译的环境和运行的环境不一样,现在windows下解压后把交叉编译器复制到linux下就行了。

    GCC编译器mingw版本v10.0.0

    MinGW(Minimalist GNU on Windows),将经典的开源 C语言 编译器 GCC 移植到了 Windows 平台下,并且包含了 Win32API ,因此可以将源代码编译为可在 Windows 中运行的可执行程序。而且还可以使用一些 Windows 不...

    使用root权限运行自己所编译程序的解决方法

    虽然编译后能运行,但由于使用了原始套接字,必须root权限运行,这与和Linux下真实的ping有差别:后者是不需要输入sudo或者切换到#才能运行的。linux中的ping源码没有找到,但是看到oschina上有一例自制的ping源码...

    gcc,make指南,gdb调试GCC程序资料打包

    特别在Unix下的软件编译,你就不能不自己写makefile了,会不会写makefile,从一个侧面说明了一个人是否具备完成大型工程的能力。 因为,makefile关系到了整个工程的编译规则。一个工程中的源文件不计数,其按类型、...

    linux程序设计(第三版)

    这些年来,曾讲授了有关LINUXUNIX平台的广泛课程,包括程序设计、数据结构、操作系统和软件工程,但并未找到一本适合于当前使用的、系统描述LINUX和UNIX编程工具和实用程序(用于编译多模块程序、调试、处理库、软件...

    交叉编译环境的建立与测试

    什么是交叉编译环境 GCC 交叉编译工具链 重建交叉编译器 ...注意,通过该交叉编译链编译的可执行文件只能在ARM体系下执行,不能在基于X86的普通PC上执行。 # arm-linux-gcc –o hello hello.c # file hello

    windows下可编译linux 用insight+bochs源码级调试

    修改了Trans.cpp中的一个dug,该dug在translate MinGW gcc编译的程序时可能会出错。用MinGW gcc 编译的程序的VirtualAddress的形式可能是0xFFC1000这样的形式,其实0x1000才是它的VirtualAddress 08/4/2 修改了下...

    Linux GCC常用命令

    经过了这么多年的发展,GCC 已经不仅仅能支持 C 语言;它现在还支持 Ada 语言、C++ 语言、Java 语言、Objective C 语言、Pascal 语言、COBOL语言,以及支持函数式编程和逻辑编程的 Mercury 语言,等等。而 GCC 也...

    嵌入式Linux应用程序开发详解

    3.4.2 Gcc编译选项分析 74 3.5 Gdb调试器 77 3.5.1 Gdb使用流程 78 3.5.2 Gdb基本命令 81 3.6 Make工程管理器 86 3.6.1 Makefile基本结构 86 3.6.2 Makefile变量 87 3.6.3 Makefile规则 90 ...

    linux C编程实战 电子书part5

    很好的linux C编程的书,请大家下载五个part(没办法,文件80M,我一次只能传15M),五个都下载了才能解压,这里就只在part1扣分,其他不扣分,请找我的另外四个资源。另外还有光盘资料,需要一个资源分,请到我的...

    mips交叉编译环境 共 5 部分 (1)

    因为这时还没有MIPS的glibc库可以使用,只能编译一个最简单的gcc,用这个gcc编译出glibc后就可以再编译一个完成的gcc了。 cd /usr/local/src/ tar xzvf gcc-3.0.2.tar.gz cd gcc-3.0.2/ ./configure --target=...

    动态库与静态库的区别.pptx

     而针对动态库则需要在运行程序时,到/usr/lib和/lib等目录中查找需要的动态库文件,不能删除。  通过gcc先编译成.o文件均可以删除,对可执行文件没有影响。 3、当静态库和动态库同名时, gcc命令将优先使用动态...

Global site tag (gtag.js) - Google Analytics