c++ 开发生态环境、工作流程、生命周期-拾遗

拾遗 1  生态环境初识

当您使用Visual Studio 2019进行C++开发时,您将进入C++生态环境。以下是一些重要的概念和步骤:

  1. C++程序的结构: 一个典型的C++程序包括源文件(.cpp)、头文件(.h)、编译后的目标文件(.obj)和最终的可执行文件(.exe)或库文件(.lib、.dll)。

  2. 第三方库的工作方式: 如果您需要使用第三方库,通常会将其源代码或者预编译的库文件(.lib、.dll)包含到您的项目中。第三方库通常会包含头文件和库文件,您需要将这些文件包含到您的项目中,并在编译和链接过程中告诉编译器和链接器去哪里寻找这些文件。

  3. 相互引用和VS配置: 在Visual Studio中,您可以通过项目属性来配置第三方库的引用。具体步骤包括:

    • 将第三方库的头文件路径添加到项目的“包含目录”中,这样您的程序中就可以包含这些头文件。
    • 将第三方库的库文件路径添加到项目的“库目录”中,这样链接器就知道在哪里找到这些库文件。
    • 在“链接器->输入”中添加需要链接的库文件名,告诉链接器在链接时需要使用这些库文件。
  4. C++生态环境的其他方面:

    • C++标准库:C++提供了丰富的标准库,包括容器、算法、输入输出等功能。
    • C++编译器:Visual Studio 2019内置了Microsoft的C++编译器(MSVC),也可以选择其他编译器比如GCC或者Clang。
    • 调试工具:Visual Studio提供了强大的调试工具,可以帮助您调试C++程序。

总的来说,C++生态环境包括了C++编译器、标准库、第三方库、开发工具等,通过合理配置和使用这些工具,您可以进行高效的C++开发。

拾遗 2  从源码到目标码(obj)、可执行码(exe)、文件库(lib,dll)

        当进行C++开发时,源码经历了多个阶段才能最终转化为可执行文件或库文件。以下是这个过程的详细步骤:

  1. 预处理阶段: 在预处理阶段,C++编译器会处理源文件(.cpp)中的预处理指令,比如#include和#define。预处理器会将头文件内容替换到源文件中,并进行宏替换等操作,生成一个经过预处理的源文件(通常是一个临时文件)。

  2. 编译阶段: 经过预处理的源文件会被编译器翻译成汇编代码(.asm文件),这个过程包括词法分析、语法分析、语义分析和优化等步骤。编译器将源码翻译成目标代码,生成一个或多个目标文件(.obj文件),每个目标文件对应源文件中的一个编译单元。

  3. 链接阶段: 在链接阶段,链接器会将多个目标文件以及所需的库文件(静态库或动态库)链接在一起,生成最终的可执行文件(.exe)或者库文件(.lib、.dll)。链接器会解析符号引用,处理重定位等工作,确保最终的可执行文件或库文件可以正确运行。

        目标码(Object Code)是编译器生成的中间文件,包含了机器代码、符号表、重定位信息等。目标码是编译后的源码的中间表示,还不能被直接执行。目标码的作用是为了后续的链接阶段使用,链接器会将多个目标文件合并成最终的可执行文件或库文件。

        总的来说,C++程序从源码到目标码再到可执行文件或库文件经历了预处理、编译和链接三个阶段。目标码是编译器生成的中间文件,用于链接器生成最终的可执行文件或库文件。

拾遗3 一套具体的流程解释:

1. C/C++运行的四个步骤

        编写完成一个C/C++程序后,想要运行起来,必须要经过四个步骤:预处理、编译、汇编和链接。每个步骤都会生成对应的文件,如下图所示(注意后缀名):

 

2.名词解释

为了后面过程的介绍更方便,这里对C++编译过程中涉及的一些常用名词进行解释。

2.1 GCC、GNU、gcc与g++

    GNU:一个操作系统,具体内容不重要,感兴趣可以参考:

GCC、GNU到底啥意思?-CSDN博客文章浏览阅读1.9w次,点赞182次,收藏201次。本文目录GCC/GNU名字的由来GCCGNU理查德·斯托曼GPLLinux参考文献GCC/GNU名字的由来今天闲来无事,想要清理一下磁盘,遇到了一个名为 TDM-GCC-64 的文件夹,也就是gcc编译器所在的文件,突然好奇,编译器为什么叫GCC呢?于是Wiki了一下,然后便一发不可收拾……GCC原来GCC是——GNU编译器套装,那么问题来了,GNU又是啥?不知道你有没有好奇过:这..._gnuhttps://blog.csdn.net/qq_43617936/article/details/104504992

  • GCC:GNU Compiler Collection(GNU编译器集合)的缩写,可以理解为一组GNU操作系统中的编译器集合,可以用于编译C、C++、Java、Go、Fortan、Pascal、Objective-C等语言。
  • gcc:GCC(编译器集合)中的GNU C Compiler(C 编译器)
  • g++:GCC(编译器集合)中的GNU C++ Compiler(C++ 编译器)

简单来说,gcc调用了GCC中的C Compiler,而g++调用了GCC中的C++ Compiler。 - 对于 *.c 和 *.cpp 文件,gcc分别当作 c 和 cpp文件编译,而g++则统一当作cpp文件编译。

2.2 代码编译命令

gcc/g++常用指令选项:

2.3 GDB(gdb)

GDB(gdb)全称“GNU symbolic debugger”,是 Linux 下常用的程序调试器。 为了能够使用 gdb 调试,需要在代码编译的时候加上-g,如

g++ -g -o test test.cpp

本文中只演示从源代码生成可执行二进制文件的过程,暂不涉及调试过程。调试的配置会在另一篇文章中专门介绍。

3. C++编译过程详解

        初次使用Visual C++的时候一定有经历过项目的配置,肯定会对预编译头,链接器之类的东西心生疑惑,敲代码就敲代码,怎么有这么多要设置的东西。还有看着Windows版本的程序和Linux版本的编译完成后的文件夹也会疑惑,编译完除了程序怎么还有一堆.obj和.o文件。

        首先我们要明白一个流程,程序要运行起来,必须要经过四个步骤:预处理、编译、汇编和链接。每个步骤都会生成对应的文件。像下图一样,接下来我会分成四个步骤描述一下每个步骤都会做什么事

预编译:

        预编译如其名,就是编译前的一些准备工作。代码文件放到编译器面前,他肯定也不知道怎么去读,看到头文件他也不知道头文件里是什么,所以预编译就要把一些#define的东西完成文本替换,然后#include的文件里的内容复制到.cpp文件里,如果.h文件里还有.h文件,那递归展开,注释这些编辑器也不管的,完全是给程序员看的。简而言之,就是剔除掉编译器看不懂的东西,然后把所有头文件展开的一个过程。

编译:

        编译只是把我们写的代码转为汇编代码,它的工作是检查词法和语法规则,所以,如果程序没有词法或则语法错误,那么不管逻辑是怎样错误的,都不会报错。

汇编:

        汇编代码转换机器码,这个阶段,非底层的程序员不需要考虑, 编译器不会搞错的。也与c/c++开发者无关,但是我们可以利用反汇编来调试代码,学习汇编语言依然是必备的。

链接:

        配置链接库的重要性就体验在这里,配置的静态链接库还是动态链接库都会在这部分和最后的程序链接到一起。

        在这之前引用百度过来的一段话介绍下静态链接和动态链接的区别

    (1)静态链接是由链接器在链接时将库的内容加入到可执行程序中的做法。链接器是一个独立程序,将一个或多个库或目标文件(先前由编译器或汇编器生成)链接到一块生成可执行程序。静态链接是指把要调用的函数或者过程链接到可执行文件中,成为可执行文件的一部分。

  (2)动态链接所调用的函数代码并没有被拷贝到应用程序的可执行文件中去,而是仅仅在其中加入了所调用函数的描述信息(往往是一些重定位信息)。仅当应用程序被装入内存开始运行时,在Windows的管理下,才在应用程序与相应的DLL之间建立链接关系。当要执行所调用DLL中的函数时,根据链接产生的重定位信息,Windows才转去执行DLL中相应的函数代码。

        C语言代码经过编译以后,并没有生成最终的可执行文件(.exe 文件),而是生成了一种叫做目标文件(Object File)的中间文件(或者说临时文件)。目标文件也是二进制形式的,它和可执行文件的格式是一样的。对于 Visual C++,目标文件的后缀是.obj;对于 GCC,目标文件的后缀是.o。这就是一开始所说的编译完一堆.obj和.o文件的来源。

        目标文件经过链接(Link)以后才能变成可执行文件。既然目标文件和可执行文件的格式是一样的,为什么还要再链接一次呢,因为编译只是将我们自己写的代码变成了二进制形式,它还需要和系统组件(比如标准库、动态链接库等)结合起来,这些组件都是程序运行所必须的。链接(Link)其实就是一个“打包”的过程,它将所有二进制形式的目标文件和系统组件组合成一个可执行文件。完成链接的过程也需要一个特殊的软件,叫做链接器(Linker)。

        这里还要讲一个东西,C++程序编译的时候其实只识别.cpp文件。每个cpp文件都会分别编译一次,生成一个.o或者.obj文件。这个时候,链接器除了将目标文件和系统组件组合起来,还需要将编译器生成的多个.o或者.obj文件组合起来。

本节内容用下面的简单C++工程做演示。示例的文件结构如下:

|—— include
      |—— func.h
|—— src
      |—— func.cpp
|—— main.cpp

其中,main.cpp是主要代码,include/func.h是自定义函数的头文件,src/func.cpp是函数的具体实现

各个文件的内容如下:

// main.cpp
#include <iostream>
#include "func.h"

using namespace std;

int main(){
    int a = 1;
    int b = 2;
    cout << "a + b = " << sum(a, b) << endl;; 

    return 0;
}
// func.h
#ifndef FUNC_H
#define FUNC_H

int sum(int a, int b);

#endif
// func.cpp
#include "func.h"

int sum(int a, int b) {
    return a + b;
}

3.1 预处理(Preprocess)

        预处理,顾名思义就是编译前的一些准备工作。

        预编译把一些#define的宏定义完成文本替换,然后将#include的文件里的内容复制到.cpp文件里,如果.h文件里还有.h文件,就递归展开。在预处理这一步,代码注释直接被忽略,不会进入到后续的处理中,所以注释在程序中不会执行。

        gcc/g++的预处理实质上是通过预处理器cpp(应该是c preprocess的缩写?)来完成的,所以我们既可以通过g++ -E,也可以通过cpp命令对main.cpp进行预处理:

g++ -E -I include/ main.cpp -o main.i
# 或者直接调用 cpp 命令
cpp -I include/ main.cpp -o main.i

        上述命令中: - g++ -E 是让编译器在预处理之后就退出,不进行后续编译过程,等价于cpp指令 - -I include/用于指定头文件目录 - main.cpp是要预处理的源文件 - -o main.i用于指定生成的文件名

        预处理之后的程序格式为 *.i,仍是文本文件,可以用任意文本编辑器打开。

执行完预处理后的文件结构如下:

|—— include
      |—— func.h
|—— src
      |—— func.cpp
|—— main.cpp
|—— main.i

 3.2 编译(Compile)

编译 只是把我们写的代码转为汇编代码它的工作是检查词法和语法规则,所以,如果程序没有词法或则语法错误,那么不管逻辑是怎样错误的,都不会报错
编译不是指程序从源文件到二进制程序的全部过程,而是指将经过预处理之后的程序转换成特定汇编代码(assembly code)的过程

编译的指令如下:

g++ -S -I include/ main.cpp -o main.s

与预处理类似,上述命令中: - g++ -S是让编译器在编译之后停止,不进行后续过程 - -I include/用于指定头文件目录 - main.cpp是要编译的源文件 - -o main.s用于指定生成的文件名

编译完成后,会生成程序的汇编代码main.s这也是文本文件,可以直接用任意文本编辑器查看。

执行完编译后的文件结构如下:

|—— include
      |—— func.h
|—— src
      |—— func.cpp
|—— main.cpp
|—— main.i
|—— main.s

3.3 汇编(Assemble)

汇编过程将上一步的 汇编代码( main.s) 转换成机器码(machine code), 这一步产生的文件叫做目标文件( main.o), 是二进制格式

gcc/g++的汇编过程通过 as 命令完成,所以我们可以通过g++ -cas命令完成汇编:

g++ -c -I include/ main.cpp -o main.o
# 或者直接调用 as 命令
as main.s -o main.o

上述指令中: - g++ -c让编译器在汇编之后退出,等价于 as - -I include/仍是用于指定头文件目录 - main.cpp是要汇编的源文件 - -o main.o用于指定生成的文件名

汇编这一步需要为每一个源文件(本文示例代码中为main.cppfunc.cpp)产生一个目标文件。因此func.cpp也需要执行一次这个汇编过程产生一个func.o文件:

# 可以用 g++ -c 命令一步生成 func.o
g++ -c -I include/ src/func.cpp -o src/func.o
# 当然也可以按照上面的预处理、编译、汇编三个步骤生成func.o

到了这一步,代码的文件结构如下:

|—— include
      |—— func.h
|—— src
      |—— func.cpp
      |—— func.o
|—— main.cpp
|—— main.i
|—— main.s
|—— main.o

 3.4 链接(Link)

C/C++代码经过汇编之后生成的目标文件(*.o)并不是最终的可执行二进制文件,而仍是一种中间文件(或称临时文件),目标文件仍然需要经过链接(Link)才能变成可执行文件

既然目标文件和可执行文件的格式是一样的(都是二进制格式),为什么还要再链接一次呢?
因为 编译只是将我们自己写的代码变成了二进制形式,它还需要和系统组件(比如标准库、动态链接库等)结合起来,这些组件都是程序运行所必须的
链接(Link)其实就是一个“打包”的过程,它将所有二进制形式的目标文件(.o)和系统组件组合成一个可执行文件。完成链接的过程也需要一个特殊的软件,叫做 链接器(Linker)
此外需要注意的是: C++程序编译的时候其实只识别.cpp文件,每个cpp文件都会分别编译一次,生成一个.o文件。这个时候,链接器除了将目标文件和系统组件组合起来,还需要将编译器生成的多个.o或者.obj文件组合起来生成最终的可执行文件(Executable file)

以本文中的代码为例,将func.omain.o链接成可执行文件main.out,指令如下:

g++ src/func.o main.o -o main.out
  • -o main.out用于指定生成的可执行二进制文件名
  • 由于g++自动链接了系统组件,所以我们只需要把自定义函数的目标文件与main.o链接即可。

运行main.out,结果如下:

./main.out
a + b = 3

3.5 小结

从上面的介绍可以看出,从C++源代码到最终的可执行文件的中间过程并不简单。了解预处理、编译、汇编、链接各个步骤的作用有助于我们处理更加复杂的项目工程。

不过也不必被这么麻烦的编译过程劝退,当我们编译简单.cpp代码时,

// hello.cpp
#include <iostream>
using namespace std;

int main(){
    cout << "Hello, world!" << endl;
    return 0;
}

仍然可以直接使用g++命令生成可执行文件,而不必考虑中间过程:

g++ hello.cpp -o hello
./hello
Hello, world!

4. 参考教程

  1. GCC、GNU到底啥意思?
  2. C++ 预编译,编译,汇编,链接
  3. C/C++语言编译链接过程

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/320481.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

javacv和opencv对图文视频编辑-裸眼3D图片制作

通过斗鸡眼&#xff0c;将左右两张相似的图片叠加到一起看&#xff0c;就会有3D效果。 3D图片&#xff0c;3D眼镜&#xff0c;3D视频等原理类似&#xff0c;都是通过两眼视觉差引起脑补产生3D效果。 图片&#xff1a; 图片来源&#xff1a; 一些我拍摄的真*裸眼3D照片 - 哔哩…

LeetCode-1523/1491/860/976

1.在区间范围内统计奇数数目&#xff08;1523&#xff09; 题目描述&#xff1a; 给你两个非负整数 low 和 high 。请你返回 low 和 high 之间&#xff08;包括二者&#xff09;奇数的数目。 思路一&#xff1a; 这里肯定会想到以low和high分别为上下限&#xff0c;然后遍历…

JS实现网页轮播图

轮播图也称为焦点图&#xff0c;是网页中比较常见的网页特效。 1、页面基本结构&#xff1a; 大盒子focus&#xff0c;里面包含 左右按钮ul 包含很多个li &#xff08;每个li里面包含了图片&#xff09;下面有很多个小圆圈 因为我们想要点击按钮&#xff0c;轮播图左右播放&a…

MCU最小系统原理图中四个问题详解——芯片中有很多电源管脚的原因(VDD/VSS/VBAT)、LC滤波、两级滤波、NC可切换元件

前言&#xff1a;本文对MCU最小系统原理图中的四个问题进行详解&#xff1a;芯片中有很多电源管脚的原因&#xff08;VDD/VSS/VBAT&#xff09;、LC滤波、两级滤波、NC可切换元件。本文以GD32F103C8T6最小系统原理图举例 目录&#xff1a; 芯片中有很多电源管脚的原因&#x…

ssh远程登录

ssh协议 ---基于 tcp 的 22 号端口 确认是否有ssh包&#xff1a; [rootserver ~] rpm -qa | grep sshopenssh-clients-8.7p1-24.el9_1.x86_64openssh-server-8.7p1-24.el9_1.x86_64 1、 ssh的验证过程 第一阶段&#xff1a;版本协商以及tcp三次握手 第二阶段&#xff1a;秘钥和…

WebServer 跑通/运行/测试(详解版)

&#x1f442; 椿 - 沈以诚 - 单曲 - 网易云音乐 目录 &#x1f382;前言 &#x1f33c;跑通 &#xff08;1&#xff09;系统环境 &#xff08;2&#xff09;克隆源码 &#xff08;3&#xff09;安装和配置 Mysql &#xff08;4&#xff09;写 sql 语句 &#xff08;5&…

LeetCode114二叉树展开为链表(相关话题:后序遍历)

题目描述 给你二叉树的根结点 root &#xff0c;请你将它展开为一个单链表&#xff1a; 展开后的单链表应该同样使用 TreeNode &#xff0c;其中 right 子指针指向链表中下一个结点&#xff0c;而左子指针始终为 null 。展开后的单链表应该与二叉树 先序遍历 顺序相同。 示例…

LabVIEW通过视频识别开发布氏硬度机自动化测量系统

LabVIEW通过视频识别开发布氏硬度机自动化测量系统 概述&#xff1a; 在当前的工业检测与自动化领域&#xff0c;对于精确测量技术的需求日益增长。特别是在材料硬度测试领域&#xff0c;布氏硬度机的自动化测量出现在越来越多的使用中。展示了一个基于LabVIEW开发的布氏硬度…

工业异常检测AnomalyGPT-训练试跑及问题解决

写在前面&#xff0c;AnomalyGPT训练试跑遇到的坑大部分好解决&#xff0c;只有在保存模型失败的地方卡了一天才解决&#xff0c;本来是个小问题&#xff0c;昨天没解决的时候尝试放弃在单卡的4090上训练&#xff0c;但换一台机器又遇到了新的问题&#xff0c;最后决定还是回来…

详谈Python的开发工具

Python作为一种流行的编程语言&#xff0c;在开发过程中需要使用各种工具来提高效率、简化工作流程和改善开发体验。在本文中&#xff0c;我们将介绍一些常用的Python开发工具&#xff0c;包括文本编辑器、集成开发环境&#xff08;IDE&#xff09;、虚拟环境管理工具、包管理器…

【数据结构与算法】之数组系列-20240113

这里写目录标题 一、66. 加一二、121. 买卖股票的最佳时机三、136. 只出现一次的数字四、268. 丢失的数字五、350. 两个数组的交集 II 一、66. 加一 简单 给定一个由 整数 组成的 非空 数组所表示的非负整数&#xff0c;在该数的基础上加一。 最高位数字存放在数组的首位&…

这可能是最全面的Java集合面试八股文了

内容摘自我的学习网站&#xff1a;topjavaer.cn 常见的集合有哪些&#xff1f; Java集合类主要由两个接口Collection和Map派生出来的&#xff0c;Collection有三个子接口&#xff1a;List、Set、Queue。 Java集合框架图如下&#xff1a; List代表了有序可重复集合&#xff0c…

第 10 章 树结构的基础部分

文章目录 10.1 二叉树10.1.1 为什么需要树这种数据结构10.1.2 树示意图10.1.3 二叉树的概念10.1.4 二叉树遍历的说明10.1.5 二叉树遍历应用实例(前序,中序,后序)10.1.6 二叉树-查找指定节点10.1.7 二叉树-删除节点10.1.8 二叉树-删除节点 10.2 顺序存储二叉树10.2.1 顺序存储二…

《2023年终总结》

笔者来回顾一下2023年的个人成长。 2023年总的来说&#xff0c;工作和生活都相对比较顺利。 工作上领导给予了肯定的评价&#xff0c;升职加薪&#xff0c;对我的鼓舞很大&#xff1b; 生活上和女朋友的感情越来越好&#xff0c;生气频率降低&#xff0c;也能相互理解&#xf…

《Spring》--使用application.yml特性提供多环境开发解决方案/开发/测试/线上--方案2

阿丹-有话说&#xff1a; 第二种多环境的配置选择解决方案&#xff0c;这个更加的灵活没在配置方面都选择了一种yml的书写方式。 原理&#xff1a; 在Spring Boot中&#xff0c;spring.profiles.active 属性用于指定当前应用程序应激活哪个环境配置。当Spring Boot应用启动时…

Centos7.9忘记Root密码找回

Centos7.9忘记Root密码找回 1. 背景2. 目的3. 具体操作3.1 重启系统3.2 增加代码3.3 单用户模式3.4 单用户模式3.5 修改密码3.6 创建文件3.7 重启验证 1. 背景 由于物理主机上安装了多个虚拟机&#xff0c;部分虚拟机忘记了root密码&#xff0c;前段时间刚好要用这个虚拟机&…

【MySQL】创建和管理表

文章目录 前置 标识符命名规则一、MySQL数据类型二、创建和管理数据库2.1 创建数据库2.2 使用数据库2.3 修改数据库2.4 删除数据库 三、创建表3.1 创建方式一3.2 创建方式二3.3 查看数据表结构 四、修改表4.1 增加一个列4.2 修改一个列4.3 重命名一个列4.4 删除一个列 五、重命…

嘘……快进来!这儿有最新版Microsoft照片程序的安装秘籍!(附安装引导程序下载)

网管小贾 / sysadm.cc 最近啊有不少小伙伴向我反馈&#xff0c;自个的 Windows 10 系统里边居然没有 Microsoft 照片 程序。 我觉得有点不可思议&#xff0c;为啥呢&#xff0c;因为他们的电脑是新买的&#xff01; 你看哈&#xff0c;系统是 22H2 最新版&#xff0c;安装日期…

云卷云舒:独立式向量数据库?数据库向量式插件?

云卷云舒&#xff1a;算力网络云原生&#xff08;下&#xff09;&#xff1a;云数据库发展的新篇章-CSDN博客https://blog.csdn.net/bishenghua/article/details/135050556 圈内人都知道&#xff0c;2023 年是向量数据库的元年&#xff0c;最初起源于 2023年3月英伟达的黄仁勋…

分布式链路追踪专栏,分布式链路追踪:Skywalking集群管理设计

SkyWalking 是一个开源 APM 系统&#xff0c;包括针对 Cloud Native 体系结构中的分布式系统的监视&#xff0c;跟踪&#xff0c;诊断功能。核心功能如下&#xff1a; 服务、服务实例、端点指标分析&#xff1b; 根本原因分析&#xff0c;在运行时分析代码&#xff1b; 服务拓…