最近在尝试写点 C++, 因为需要访问服务器获取 json 数据, 所以其中涉及到了 curl 相关内容.

makefile 中有一段话:

g++ -lcurl -lm -o mssout $(Objects)

其中 -lcurl 代表需要去链接 curl 的库文件. 以上这段 makefile 在 CentOS 的系统上是运行良好的, 可以正确编译. 但是到了我其中一台 Ubuntu 的服务器上就出问题了, 不能编译, 总是出现以下错误:

root@aliyun:~/mssout$ make
g++ -lcurl -lm -o mssout mssout.o cJSON.o base64.o mCurl.o -lm
mCurl.o: In function `mCurl_url_open(std::string)':
mCurl.cpp:(.text+0x37): undefined reference to `curl_easy_init'
mCurl.cpp:(.text+0x61): undefined reference to `curl_easy_setopt'
mCurl.cpp:(.text+0x86): undefined reference to `curl_easy_setopt'
mCurl.cpp:(.text+0xa1): undefined reference to `curl_easy_setopt'
mCurl.cpp:(.text+0xbb): undefined reference to `curl_easy_setopt'
mCurl.cpp:(.text+0xc7): undefined reference to `curl_easy_perform'
mCurl.cpp:(.text+0xda): undefined reference to `curl_easy_strerror'
mCurl.cpp:(.text+0x102): undefined reference to `curl_easy_cleanup'
collect2: error: ld returned 1 exit status
make: *** [make] Error 1

这就很奇怪了啊, curl 库我也是安装了的, 同样的 make 语句在 CentOS 上也运行良好, 为啥到了 Ubuntu 这里就有问题呢.

只好 Google 呗, 看怎么解决. 结果找到 StackOverflow 上的这个问题和回答: 链接

-lcurl should be put in the end of gcc command.
gcc -L/usr/lib/x86_64-linux-gnu curl.c -o curl -lcurl

好吧, 尝试修改 makefile 为如下:

g++ -o mssout $(Objects) -lm -lcurl

执行 make, 果然就成功了, 不再报错:

root@aliyun:~/mssout$ make
gcc -c -g mssout.cpp
gcc -c -g cJSON/cJSON.c
gcc -c -g utils/base64.c
gcc -c -g mCurl/mCurl.cpp
g++ -o mssout mssout.o cJSON.o base64.o mCurl.o -lm -lcurl

Q: 为啥改了顺序就好了呢? 原因不明, 需要找知道的人解释一下.

A: 待咨询.sheriseanes 攀谈后, 大概明白了顺序对编译的影响. 然后又 man gcc 仔细查询了其中 -l 参数部分的描述, 找到了理论依据:

Search the library named library when linking. (The second alternative 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 directories 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 fashion.
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.

至于同样的 makefile 在 CentOS 上运行良好, 在 Ubuntu 上失败的问题, 我认为大概率是因为虽然 -lcurl 写在前面是错误的写法, 但是可能由于 CentOS 上的 gcc 版本比较先进或者其他什么原因, 自动帮我纠错了, 所以并没有报错. 但是我们不应该依赖于这种写法, 还是应该按照标准的写法, 把 -lcurl 写在最后来保证跟官方文档的描述一致, 这样不容易出错.

2018-08-08 21:4877