直接 gcc 没有-o 的话出来的输出exe文件没有调试信息,相比-o的大小会小一点,只有包含了调试信息文件才可以执行
argc argv参数
./hello shan
argc = 2
argv[0] = ./hello
argv[1] = shan
头文件
头文件在编译器的include目录中,编译时 -I 指定库
函数库默认路径在编译器的lib目录中,编译时 -L指定目录,-l 小写L 指定库
函数或者是在别的c与cpp文件中
find -name "stdio.h"
编译过程
gcc hello.c // 输出一个名为 a.out 的可执行程序,然后可以执行./a.out
gcc -o hello hello.c // 输出名为 hello 的可执行程序,然后可以执行./hello
gcc -o hello hello.c -static // 静态链接 默认情况使用动态链接
gcc -c -o hello.o hello.c // 先编译(不链接)
gcc -o hello hello.o // 再链接
执行“gcc -o hello hello.c -v”时,可以查看到这些步骤:
cc1 main.c -o /tmp/ccXCx1YG.s
as -o /tmp/ccZfdaDo.o /tmp/ccXCx1YG.s
cc1 sub.c -o /tmp/ccXCx1YG.s
as -o /tmp/ccn8Cjq6.o /tmp/ccXCx1YG.s
collect2 -o test /tmp/ccZfdaDo.o /tmp/ccn8Cjq6.o ....
可以手工执行以下命令体验一下:
gcc -E -o hello.i hello.c
gcc -S -o hello.s hello.i
gcc -c -o hello.o hello.s
gcc -o hello hello.o
静态链接,动态链接,静态库,动态库
静态链接(static linking)和动态链接(dynamic linking)都是将代码库连接到应用程序中的方法,以便于执行应用程序。
静态链接是将库中的所有代码复制到可执行文件中的过程。在编译时,编译器会将库的代码直接嵌入到最终生成的可执行文件中,因此可执行文件可以完全独立运行,无需依赖任何外部库或资源。这样做的好处是,可执行文件在不同机器或操作系统上的兼容性更高。缺点是,应用程序的大小通常会更大,并且更新库可能需要重新编译整个应用程序。
动态链接是将库的代码保留为单独的文件,操作系统只在应用程序运行时将其加载到内存中。因此,一个库的多个应用程序可以共享同一份库文件,从而节省了磁盘空间并减少了内存的使用。同时,如果有多个应用程序需要加载同一份库文件,那么这份文件只需要在内存中存在一次。这使得库的升级变得更加容易。然而,动态链接的应用程序依赖于系统中可用的动态库,如果要运行动态链接的应用程序,需要确保在系统上安装了所需的库。
静态库(static library)是一组预编译的目标文件或对象文件,它们被打包成一个文件并在编译时链接到应用程序中。静态库的代码完全复制到可执行文件中,因此它会增加可执行文件的大小。
优点是,静态库允许应用程序完全独立运行。缺点是,如果静态库的源代码发生变化,则必须重新编译整个应用程序。
动态库(dynamic library)是在运行时加载的库。动态库的代码只有一个副本,在内存中由多个应用程序共享。
动态库的优点是,多个应用程序可以使用同一份库文件,从而减少内存和磁盘空间。如果动态库的源代码发生变化,则只需要更新动态库文件本身即可。
缺点是,动态库在启动时需要额外的时间进行初始化,并且在不适当地使用时可能会导致内存泄漏或其他问题。
-shared
在 GCC 中,"-shared"选项用于指示编译器创建动态链接库(也称为共享库)而非可执行文件。
当使用"-shared"选项编译源文件时,GCC会将目标文件打包成一个控制导出符号的动态链接库。这意味着其他程序可以在运行时加载这个库,并链接到其中定义的符号和函数。共享库可以被多个应用程序共享,因此它们可以提高系统资源的利用率并减少内存占用量。
在您提供的命令中,使用"-shared"选项创建了一个名为"libsub.so"的动态链接库,其中包含 sub.o、sub2.o 和 sub3.o 编译后的目标文件。该库可以用于链接到其他程序中。
需要注意的是,如果库的依赖关系不正确,则可能会导致在加载库时发生错误。因此,在编译和链接动态库时,必须确保正确指定所有必需的库及其路径。
制作使用动态库
gcc -c -o main.o main.c
gcc -c -o sub.o sub.c
gcc -shared -o libsub.so sub.o sub2.o sub3.o(可以使用多个.o 生成动态库)
gcc -o test main.o -lsub -L /libsub.so/所在目录/
第7步 运行:
① 先把 libsub.so 放到 Ubuntu 的/lib 目录,然后就可以运行 test 程序。
② 如果不想把 libsub.so 放到/lib,也可以放在某个目录比如/a,然后如下执
行:
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/a
./test
这个命令将编译和链接 main.o 源文件,并将其与名为"libsub.so"的动态库链接起来。"-lsub"选项告诉编译器链接到名为"libsub.so"的库,"-L"选项则指定寻找库文件的路径。
因此,生成的可执行文件 test 将会依赖于名为"libsub.so"的动态链接库,而不是与其他默认库链接。
需要注意的是,如果要链接到多个库文件,则需要在命令行中指定所有这些库的名称(使用"-l"选项)和路径(使用"-L"选项)。否则,编译器将无法找到所需的库文件并产生错误。
libxxx。so
"-lsub"中的"sub"并不是随意命名的,而是根据动态库文件名"libsub.so"来确定的。
在默认情况下,GCC 会将库文件名解释为 libXXX.so 形式,其中 XXX 是库的名称。因此,在使用"-l"选项指定要链接的库时,可以省略"lib"和".so"部分,只指定库的名称即可。
例如,如果您有一个名为"libsub.so"的动态库,则可以使用"-lsub"选项将其链接到您的代码中。
需要注意的是,库文件名必须以"lib"开头,并以".so"结尾才能被识别为动态库。如果您的库文件名不符合这种命名约定,则需要使用"-l"选项显式指定完整的文件名和路径。
此外,您还可以使用"-Wl,-soname,NAME"选项来指定共享库的 soname,它是库的逻辑名称,是库的用户感知名称。例如,您可以使用以下命令来编译和链接一个共享库,并指定其 soname:
gcc -shared -Wl,-soname,libfoo.so.1 -o libfoo.so.1.0.1 foo.o bar.o
这将生成一个名为"libfoo.so.1.0.1"的共享库,并将其逻辑名称设置为"libfoo.so.1"。这样,用户可以链接到"libfoo.so.1"而不必担心底层库的实际名称和版本。
冲突
在这个例子中,使用-Wl,-soname,libfoo.so.1
选项指定了库的逻辑名称为"libfoo.so.1",而不是默认的"libfoo.so"。并且使用-o libfoo.so.1.0.1
选项指定生成的共享库名称为"libfoo.so.1.0.1"。这样设计的目的是为了在为共享库命名时提供更多的灵活性和精确度。
而foo.o bar.o
是需要链接进共享库的目标文件列表。
如果要将这个共享库链接到其他程序中,则需要使用-lfoo
选项将其指定为依赖库。
例如,如果你想编译一个名为test
的程序,并链接上面的共享库,则可以使用以下命令:
gcc -o test test.o -L/path/to/lib -lfoo
其中,-L
选项用于指定共享库所在的路径,-lfoo
选项用于指定需要链接的库的名称。编译器将搜索指定路径下的libfoo.so
或libfoo.a
文件,并将其链接到程序中。由于我们已经在编译共享库时使用了-Wl,-soname,libfoo.so.1
选项来指定了逻辑名称,因此链接器将自动匹配库的名称为"libfoo.so.1",而不是"libfoo.so"。
需要注意的是,如果在链接时指定的共享库名称不是由-Wl,-soname
选项指定的逻辑名称,则可能会导致运行时错误。此外,如果要链接的库不在默认路径中,还需要使用-rpath
选项来指定运行时搜索库的路径,以确保程序能够正确地加载库。
如果目录下同时有libfoo.so.1.0.1
和libfoo.so
这两个文件,那么在使用-lfoo
选项编译链接程序时,链接器可能会优先选择libfoo.so
而不是libfoo.so.1.0.1
,因为它具有更短的名称。
为了避免这种情况的发生,我们一般会将动态库的版本号包含在库文件名中,以便在有多个版本的库文件存在时,可以根据需要精确地选择需要的版本。例如,在之前的例子中,-soname
选项指定了逻辑名称为"libfoo.so.1",因此生成的共享库的实际名称为libfoo.so.1.0.1
,这样可以避免与libfoo.so
发生命名冲突。
另外,你也可以通过设置LD_LIBRARY_PATH
环境变量来改变动态库的搜索路径,这样可以让链接器在默认路径之外搜索动态库。例如:
export LD_LIBRARY_PATH=/path/to/libs:$LD_LIBRARY_PATH
这将把/path/to/libs
目录添加到动态库的搜索路径中。然后,当你使用-lfoo
选项编译链接程序时,链接器会先在/path/to/libs
目录下查找libfoo.so
或libfoo.so.1.0.1
文件,如果找到则使用它。
是的,通常情况下,我们会将不同版本的.so文件放到不同的目录中,这样可以避免不同版本之间的文件名冲突问题。
在编译时,你可以通过-L
选项来添加库文件的搜索路径。例如:
gcc -o myprogram myprogram.o -L/path/to/libs -lfoo
这里的-L/path/to/libs
选项指定了库文件所在的路径为"/path/to/libs",而-lfoo
选项指定了需要链接的库的名称为"libfoo.so"或"libfoo.a"。如果这个路径中有多个不同版本的库文件,链接器会根据库文件的版本号以及程序对特定版本的库的需求来自动选择合适的库文件进行链接。
当程序运行时,动态链接器将会按照一定的规则搜索库文件,并将其加载到内存中供程序使用。具体的搜索顺序可以通过设置LD_LIBRARY_PATH
或修改/etc/ld.so.conf
等方式来改变,这样可以使动态链接器在不同的目录中查找库文件。
静态库
在编译好上面提到的两个目标文件(main.o和sub.o)后,你可以使用ar
命令来将它们打包成静态库。具体来说,你需要执行以下命令:
ar rcs libmystatic.a main.o sub.o
其中,r
选项表示将目标文件添加到静态库中(如果目标文件已经存在于静态库中,则将其替换),c
选项表示创建一个新的静态库,如果静态库不存在则创建,s
选项表示为静态库创建索引表,以提高链接速度。libmystatic.a
是指定生成的静态库文件名。
执行完以上命令后,就会在当前目录下生成名为libmystatic.a
的静态库文件。你可以使用nm
命令来查看静态库中包含的符号列表,例如:
nm libmystatic.a
如果要使用这个静态库来链接其它程序,可以使用-L
和-l
选项,例如:
gcc -o myprogram myprogram.o -L. -lmystatic
这里的-L.
选项表示在当前目录中搜索库文件,而-lmystatic
选项表示链接名为libmystatic.a
的静态库。在链接阶段,静态库被直接嵌入到可执行程序中,因此在运行时不需要依赖外部库文件。
预处理
以#开头的就是预处理命令
编译
生成汇编代码,使用到了ccl工具
汇编
生成机器代码,用到的工具为as
链接
将上步生成的OBJ文件和系统库的OBJ文件,库文件链接起来
用到的工具为ld或collect2
目录选项
-Idir 大写I
比如 -I. 表示在当前目录下搜索-Idir
选项是用来指定编译器在搜索头文件时需要添加的目录路径。具体来说,当你在代码中使用#include <header.h>
形式的语句时,编译器会在一些默认的目录和用户指定的目录中查找header.h
头文件,如果找到则进行编译,否则就报错。
这里的-I
选项就是用来指定额外的头文件搜索路径的。例如,假设你的头文件位于/usr/local/include
目录下,那么你可以通过以下命令来指定这个目录为头文件搜索路径:
gcc -o myprogram myprogram.c -I/usr/local/include
这样,编译器在搜索头文件时就会先搜索/usr/local/include
目录,如果目录下存在需要的头文件,则不会报错。
需要注意的是,-I
选项仅仅是将指定的目录添加到搜索路径中,并不会取代默认的搜索路径。如果你希望完全使用自定义的头文件搜索路径,可以使用-nostdinc
选项禁止编译器使用默认的搜索路径。例如:
gcc -o myprogram myprogram.c -nostdinc -I/usr/local/include
这样,编译器就只会按照-I
选项中指定的路径搜索头文件了。
include<> 与 " "
如果以“#include < >”包含文件,则只在标准库
目录开始搜索(包括使用-Idir 选项定义的目录);如果以“#include “ ””包
含文件,则先从用户的工作目录开始搜索,再搜索标准库目录。
-I -I- 与 -I- -I
-I -I- 只 适 用 于 `#include
"file"'这种情况;它们不能用来搜索`#include <file>'包含的头文件
-I- -I
可以在这些路径中搜索
所有的`#include'指令(一般说来-I 选项就是这么用的)。
ld/objdump/objcopy 选项
ld
、objdump
和objcopy
都是常用的GNU工具,它们在各自的领域内都有很多实用的选项。
ld
ld
是用来将多个目标文件链接成一个可执行文件或动态链接库的工具。它的一些常用选项如下:
-
-o file
:指定输出文件名称; -
-l library
:指定需要链接的库; -
-L path
:指定库搜索路径; -
-shared
:生成动态链接库; -
-static
:静态链接所有库。
objdump
objdump
是一个反汇编器,可以将目标文件反汇编为汇编代码。它的一些常用选项如下:
-
-D
:反汇编所有节(包括已被标记为debugging信息的节); -
-S
:反汇编同时显示源代码; -
-M options
:设置反汇编器选项,例如-Mintel
表示使用Intel语法。
objcopy
objcopy
是一个目标文件格式转换工具,可以将目标文件从一种格式转换为另一种格式,也可以改变目标文件的各种属性等。它的一些常用选项如下:
-
-O format
:指定转换后的目标文件格式; -
--strip-all
:删除所有符号表及重定位表等调试信息; -
--strip-debug
:删除debugging信息; -
--only-section name
:只拷贝指定的节。
这些工具在开发和调试过程中非常有用,可以帮助开发者完成目标文件的相关操作,例如链接库、调试和格式转换等。
gcc -shared 和 ld
ld
和gcc -shared
都可以用来生成动态链接库,它们在功能上确实是相当的。不过,gcc -shared
命令其实是一个包含了gcc
和ld
的封装工具,它可以将多个目标文件和库文件链接成一个动态链接库,而且更加简单易用。
具体来说,gcc -shared
的使用方式如下:
gcc -shared -o libmymodule.so mymodule.o
这条命令会将mymodule.o
目标文件链接为名为libmymodule.so
的动态链接库。其中,-shared
选项表示生成动态链接库,-o
选项用于指定输出文件的名称和路径。
与之相比,直接使用ld
生成动态链接库需要手动指定很多参数,例如链接器脚本、链接器选项等,相对比较麻烦。因此,在实际开发中,通常优先考虑使用gcc -shared
命令生成动态链接库。
gcc -g
gcc -g
是用来生成调试信息的编译选项之一。指定了-g
选项,就可以在编译生成的目标文件中嵌入调试信息,方便程序调试。
具体来说,使用gcc -g
命令编译源代码时,会在生成的可执行文件中嵌入调试符号表,包含了函数名、变量名等信息,同时还包含了源代码的行号和文件名等信息。这些调试信息可以帮助开发者定位和修复程序的bug。
例如,假设有一个名为test.c
的C语言源文件,可以通过以下命令来生成带调试信息的可执行文件:
gcc -g -o test test.c
其中,-o
选项用于指定输出文件名称,-g
选项用于生成调试信息。
需要注意的是,在生产环境中,通常不建议在生产代码中包含调试信息,因为它会增加可执行文件的体积,并且可能泄漏敏感信息。因此,需要在生成生产环境可执行文件时去掉-g
选项。
多文件
xxx.h里面声明
xxx.cpp include "xxx.h"函数实现
main.cpp include "xxx.h"
gcc -g .\main.cpp .\xxx.cpp -o my_multi
报错
自动生成的task是编译单个文件的
需要自己配置launch和task文件
他视频里是通过自己的 gcc命令生成命令了
就只需要改动launch文件下的
program值 指定生成的exe文件路径
然后把 prelaunchtask给注释。
基于cmake
创建 cmakelist.txt
project(myswap)
add_executable(文件名字别称 文件名.cpp 文件2.cpp)
ctrl shift p
选定cmake编译器
Cmake:configure
然后选择
GCC 8.1.0 编译器
可以看到终端信息以及生成的build文件夹
之后回到终端中
再build目录下执行
cmake..
他会到上一级目录查找CMakeLists.txt文件并生成相应的构建工程。最后,再执行make命令,将源代码编译成可执行文件或库文件等二进制文件,并将其输出到build目录下。
因此,在使用CMake进行非ROS项目构建时,你必须确保使用正确的目录结构,即将源代码存放在根目录下,而将构建过程中生成的中间文件和构建工件等内容存放在build目录下。如果你需要直接修改或查看具体的编译文件,可以到build目录下找到,但一般来说在大多数情况下,你只需要在build目录下找到最终的可执行文件或库文件等内容即可。
之后linux和windows下执行的程序不太一样,这里执行了 mingw32-make.exe
可以看到编译, 然后是链接,生成文件
调试
要到launch.json下给到program路径,同样把prelaunch给注释掉
注意
注意2
当cpp代码更改后,如果把prelaunch代码注释了,调试的时候他不会生成新的可执行文件,他是按照旧的代码运行的。
当重新执行 mingw32命令后,他重新building,linking,buildt后才会执行新的代码。
cmake与mingw32-make.exe关系
CMake是一款跨平台的自动化建构系统,可以帮助开发者在不同的操作系统和编译环境下生成可执行文件、库文件等项目产物。执行CMake命令会根据CMakeLists.txt中的描述生成相应的Makefile或Visual Studio工程等项目文件,然后使用Make或Visual Studio等原生工具对项目进行编译和链接。
当你使用CMake生成了Makefile之后,你还需要执行make
命令或mingw32-make.exe
命令(如果你正在使用MinGW32编译器的话)来实际编译和链接项目代码,最终生成可执行文件或库文件等项目产物。
具体来说,在执行mingw32-make.exe
命令时,它会读取Makefile文件中的指令和依赖关系,按照规定的顺序对源代码进行编译和链接。通过这种方式,编译器会将源代码编译成目标文件,链接器则将目标文件与库文件等依赖项进行链接,最终生成可执行文件或库文件等项目产物。
总之,CMake负责生成Makefile文件,而mingw32-make.exe
负责根据Makefile文件中的指令和依赖关系对项目进行编译和链接。因此,在使用CMake生成Makefile之后,你需要通过运行mingw32-make.exe
来完成整个项目的编译和链接过程。
task文件
当你把launch文件里的prelaunchtask注释取消后,F5按下后报错
系统里没有执行 名称为xxx的task,你需要创建这个task,
ctrl shift p ------ task configuration
或者 c/c++ g++.exe build active file creat task from file
相当于就是在shell中执行了 command,
g++ -g (文件名)main.cpp swap.cpp -o out.exe
默认生成的是单文件调试,此时需要将${file} 改为 "main.cpp","swap.cpp",
同时需要将最后一个参数改为输出文件所需要的文件名参数
注意3
他这里重现编译了,需要再次更改 program的文件路径
生成路径和指向路径要对应
prelaunchtask
最为关键的作用, 就是调试的时候会先调用 prelaunch的task任务,进行编译生成。否则就会默认调用之前生成的文件。
相当于调用task文件里的指令
多条指令 更改为 每次按F5调试一次性
每次更改代码都要执行多条指令,比如说make以及mingw32等等。
可以全部整合到task.josn文件中
到达build目录空间下
三个lable三个task,第三个lable包含了前两个task
这里需要把第二个task的command的 make 改为 mingw32-make.exe
需要到launch中prelaunchtask名字改为 Build
settings.json 和cpp proporties.json
ros课堂里面的 什么自定义srv文件 msg文件都是在这里面配置的
ctrl shift b
{
// 有关 tasks.json 格式的文档,请参见
// https://go.microsoft.com/fwlink/?LinkId=733558
"version": "2.0.0",
"tasks": [
{
"label": "catkin_make:debug", //代表提示的描述性信息
"type": "shell", //可以选择shell或者process,如果是shell代码是在shell里面运行一个命令,如果是process代表作为一个进程来运行
"command": "catkin_make",//这个是我们需要运行的命令
"args": [],//如果需要在命令后面加一些后缀,可以写在这里,比如-DCATKIN_WHITELIST_PACKAGES=“pac1;pac2”
"group": {"kind":"build","isDefault":true},
"presentation": {
"reveal": "always"//可选always或者silence,代表是否输出信息
},
"problemMatcher": "$msCompile"
}
]
}
这段文本是一个 `tasks.json` 文件的示例,用于定义 Visual Studio Code 编辑器中工作区的任务列表。该示例包含一个名为 `catkin_make:debug` 的任务,用于在 ROS (Robot Operating System) 环境下编译项目代码并生成调试版本的二进制文件。
具体来说,`tasks` 数组中的每个元素都表示一个任务,该示例中只定义了一个任务。下面对各个字段进行解释:
1. `"label"`:任务的标签,用于在编辑器中显示该任务的描述信息;
2. `"type"`:任务类型,可以是 "shell" 或者 "process"。如果是 "shell",则表示该任务使用 shell 命令,在终端中运行,如果是 "process",则表示该任务以进程的方式运行;
3. `"command"`:表示需要执行的命令,该示例中需要执行的命令是 `catkin_make`;
4. `"args"`:表示执行命令时传递的参数,该示例中没有传递任何参数;
5. `"group"`:表示任务所属的分组,可以指定 build、test、或者其他自定义分组。该示例中将该任务设置为默认构建任务;
6. `"presentation"`: 表示任务执行过程中输出信息的提示配置。在该示例中设置为 `reveal: "always"`,表示总是在终端中显示任务的输出信息;
7. `"problemMatcher"`: 表示用于匹配输出信息中的错误信息的正则表达式,以便错误信息可以在编辑器中被定位和高亮显示。该示例中使用了 Visual C++ 的问题匹配器,用于匹配 C/C++ 编译器输出的错误信息。
总之,这段示例代码定义了一个 "catkin_make:debug" 的任务,用于在 ROS 环境下编译项目代码,并生成调试版本的二进制文件。该任务被设置为默认构建任务,并在终端中输出任务的输出信息。
在 Microsoft Visual Studio Code 编辑器中,使用快捷键 Ctrl + Shift + B
可以调用编译命令,可以快速编译并构建项目。
但需要注意的是,这个组合键需要先配置对应的编译任务才能正常工作。具体来说,你需要在 Visual Studio Code 中打开项目文件夹,然后使用菜单栏中的“终端”菜单或快捷键 Ctrl + ~
打开集成终端。接着,在终端中输入 task list
命令,查看当前项目中已经定义的所有任务。
如果没有定义编译任务,可以通过添加 tasks.json
文件来定义编译任务,例如:
{
"version": "2.0.0",
"tasks": [
{
"label": "build",
"type": "shell",
"command": "g++",
"args": [
"-o",
"hello",
"hello.cpp"
]
}
]
}
这个示例中,我们定义了一个名为 build
的编译任务,它的命令是 g++ -o hello hello.cpp
。执行 Ctrl + Shift + B
快捷键时,Visual Studio Code 会自动查找并执行名为 build
的任务,从而完成编译操作。
总之,使用快捷键 Ctrl + Shift + B
调用编译命令需要先配置相应的编译任务,并确保该任务的名称与 tasks.json
文件中定义的一致。文章来源:https://www.toymoban.com/news/detail-482679.html
catkin make 和make
分别为cmake 和 make -j8 -l8
跑完第一条后在相应目录跑第二条, 他这里是在linux下 所以后面一条命令是make,这样的话就说的通了文章来源地址https://www.toymoban.com/news/detail-482679.html
到了这里,关于cpp文件编译过程 makefile cmake的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!