为什么可执行文件不可执行?
我有一个你好世界cpp文件。 如果我用c++ test.cpp -o test
编译它c++ test.cpp -o test
我得到可执行文件( -rwxr-xr-x
)的“test”文件,如果我执行它,它会被执行并生成预期的结果。
但是,如果我使用${CXX} -std=c++0x -I${INCLUDE_DIR1} -c test.cpp -o test -L{LIB_DIR1} -llib_name
我也会得到“测试”文件,但在这种情况下,不可执行。 所以,我无法执行它。 我尝试了chmod +x
,它获得了执行权限,但是如果我尝试执行它,会得到一条错误消息(无法执行)。
我做错了什么以及如何纠正?
-c
告诉编译器不生成可执行文件(它的意思是“仅编译”)。 它只创建一个对象文件,适合链接到可执行文件(可能与其他对象文件和库)。
如果你想要一个可执行文件,请删除-c
开关。
有关完整编译过程的更多详细信息,请参阅:编译/链接过程如何工作?
看起来你正处于整个编程疯狂的开始阶段。 所以,如果您很难理解什么是错误的对象(在本例中称为.o
)文件,编译器参数以及它们实际做了什么,甚至是库名称,我都明白。
我的假设是,从外观-llib_name
,您正在复制执行的shell脚本行,以便从未知示例生成可执行文件( -llib_name
至少使其感觉如此)。 所以,我会尽量简单,清楚,粗略地解释这里发生的一切。
首先,C ++编译器将给定的源文件编译为可重定位的机器码。 如果将编译器(和编译器)的排放存储在文件中,则该文件将成为前述的对象文件(记住.o
文件)。 如果你注意了,这就意味着一个目标文件包含可重定位的机器码。
现在你已经编译了你的源代码,并且你的机器代码等同于存储在一个目标文件中。 但是,这个目标文件不是直接可执行的; 即使它确实包含CPU应该没有执行问题的机器代码。 问题是,这个机器码没有链接。 因此,第二个重点是:目标文件中的可重定位机器代码没有链接,因此不能执行(更好的是不应该)。 除此之外,目标文件还可能包含其他元数据以帮助链接程序处理链接过程,从而生成实际的可执行文件。 我们将把整个这个中间表示称为目标代码。
所以,现在我们编译了源代码并发送了目标文件,下一个明显而合乎逻辑的步骤就是将目标文件与您想要的任何库和/或其他目标文件链接起来,以便生成我们可以执行的可执行文件比如你的第一个./test
二进制文件。 这是链接器起作用的地方,链接器接受目标文件和库(注意:粗略地说,库是目标文件的集合),并执行一些链接魔术,例如解析跨模块的对象文件中的未定义引用,以及排列可执行文件等。链接器然后发出符合链接器被调用的目标平台的可执行文件格式的最终可执行文件。 它就是这个文件,你可以像./test
一样执行。
所以,现在你已经知道了基本知识,让我们看看你的shell调用有什么问题。 首先,你应该知道通过调用c++
你可以触发C ++编译器(在这种情况下我认为它是g++
或clang++
) 和链接器。 其次,您应该知道-c
标志告诉编译器编译给定的源文件,并单独发出其等效的目标代码和机器码。 因此,调用不带 -c
标志的C ++编译器不仅会导致它编译给定的源代码,而且还会链接生成的目标代码并生成最终的可执行文件。 这是你的第一个命令行中发生的事情。 发出的名为test
文件是链接的可执行文件。 但是,在您的第二个命令行中,存在-c
标志。 在这种情况下,编译器将只发布编译test.cpp
产生的目标代码。 您需要链接此生成的目标文件才能生成可执行文件。 看起来你告诉编译器将它产生的目标代码存储在一个名为test
的文件中,这是Marc在提到误导(对象文件通常具有.o
或.obj
后缀)时讲述的内容。
从这一点开始,您有两个选项,可以删除-c
标志或链接目标文件。 要编译和链接(分开)对象文件(包括可能它不应该有的liblib_name
):
${CXX} -std=c++0x -stdlib=libc++ -I${INCLUDE_DIR1} -c test.cpp -o test.o
${CXX} -std=c++0x -stdlib=libc++ -L${LIB_DIR1} -llib_name test.o -o test
我冒昧在那里引入libc++
。 如果你愿意使用C ++ 11(你把它称为C ++ 0x),那么它会更好。 但是,如果要编译并链接到一行,则可以使用以下方法实现同样的功能:
${CXX} -std=c++0x -stdlib=libc++ -I${INCLUDE_DIR1} -L${LIB_DIR1} -llib_name test.cpp -o test
这两个人完全一样。 但请注意,单线版本缺乏-c
标志。
现在我们几乎涵盖了所有内容,最后一个问题将是-I
, -L
和-l
做什么。 -I
指定编译器将搜索包含在源代码中的头文件的路径。 此路径不会覆盖编译器的默认标题搜索路径,它只是作为它们的补充。 -L
对于图书馆来说是一样的东西。 您的编译器将在此路径中查找要显式链接的库(例如本例中的liblib_name
)。 同样,这不会覆盖默认库搜索路径。 最后, -l
标志用于指定链接器在链接可执行文件时链接的库。 在你的情况你链接到的库调用liblib_name
(用于指定的名称-l
标志省略了第一lib
,你说你的编译器抱怨为不存在的库的文件名)。 你确定你真的想将liblib_name
链接到你的可执行文件吗? 这是一个真正的图书馆吗? 如果没有,那么删除整个-llib_name
参数,你的编译器应该停止大喊大叫。
对不起,很长,很长,很长的解释,但我希望你在这里学会了一个小点,并在那里轻咬,所有这些都对你有用。
链接地址: http://www.djcxy.com/p/73317.html