问题起源
出于学习的需要,前段时间开始接触OpenCV这个开源计算机视觉库。项目使用C++进行开发,所以需要安装它的C++版本。
俗话说的好,万事开头难。我以往的经历告诉我,在新接触一个计算机编程工具的时候,往往需要花费相当的时间去解决配置上的问题已达到可以正常使用的目的。直接使用官方提供的OpenCV解压缩包是无法直接运行项目代码的,因此需要自己编译源代码以便生成项目所依赖的库文件(CUDA版本)。
在编译的过程中经历了无数的坑不说,有一点引起了我的注意:无论在Visual Studio上还是在VIsual Studio Code上进行配置,有一步非常重要,就是需要在配置文件中指明所使用的OpenCV 头文件,库文件,以及dll文件的路径或者名称。当初配置这些的时候并没有觉得有啥特别的感觉,只是非常疑惑,为什么非要做这些步骤呢?(这一切放到Python中也就是一个pip命令的事情把)。
其实,根本原因在于我没把C/C++的编译过程弄清楚。
学过C的人多多少少都了解编译的大致流程吧,即:预处理-->编译-->汇编-->链接-->可执行程序.exe
我相信包括我在内的好多人基本上就只停留在知道这个流程这一层面上吧。一个人写C/C++程序的话,虽然说涉及到多文件编译,但是像VS这样的IDE可以为我们省去好多麻烦事,基本上一个F5按下去你就可以看到正在运行的“黑框框”了。我们把注意力主要放在了编码上,而不是这些看起来“理所应当”的事情上。
然而,稍微深入地了解一下这件看起来“理所应当”的事情是非常有好处的。至少,你终于可以明白在配置OpenCV这件事情上为什么“很繁琐”的原因了。
那么,我们开始愉快地聊一聊这几个过程吧 !
1. 预处理
1.1头文件包含
下面这句话就像吃饭一样熟悉!
#include
在《C++ Primer Plus》(第六版)中,是这样描述的:
C++和C一样,也使用一个预处理器,该程序在进行主编译之前对源文件进行处理。
程序清单2.1使用#include编译指令,该编译指令导致预处理器将iostream文件的内容添加到程序中。这是一种典型的预处理操作:在源代码被编译之前,替换或添加文本。
这里提出了一个问题:为什么要将iosteam文件的内容添加到程序中呢?答案设计程序与外部世界之间的通信。C++的输入和输出方案涉iostream文件中的多个定义。为了使用cout来显示消息,程序需要这些定义。#include编译指令将导致iostream文件的内容随源代码文件的内容一起被发送给编辑器。
实际上,iostream文件的内容将取代程序中的代码行#include"iostream"。原始文件没有被修改,而是将源代码文件和iostream组合成一个复合文件,编译的下一阶段将使用该文件。
1.2宏替换
预处理器在处理宏定义时,会对宏进行展开(即宏替换)。
宏替换首先将源文件中在宏定义随后所有出现的宏名均用其所代表的代码序列替换之,如果是带参数宏则接着将代码序列中的宏形参名替换为宏实参名。
宏替换只作代码字符序列的替换工作,不作任何语法的检查,也不作任何的中间计算。
宏定义指令,如:
#define Pi 3.1415
预处理阶段会将程序中所有的Pi用3.1415代替。
1.3条件编译
一般情况下,在进行编译时对源程序中的每一行都要编译,但是有时希望程序中某一部分内容只在满足一定条件时才进行编译,如果不满足这个条件,就不编译这部分内容,这就是条件编译。
条件编译指令,如
#ifdef #ifndef #else #elif #endif
条件编译主要是进行编译时进行有选择的挑选,注释掉一些指定的代码,以达到多个版本控制、防止对文件重复包含的功能。
此外,还有 #pragma 指令,它的作用是设定编译器的状态或指示编译器完成一些特定的动作。
1.4总结
预处理只做两件事:
第一:以当前cpp文件为基础,整合该文件所需要的全部文本资源(包括引入/替换等操作)
第二:以当前cpp文件为基础,控制编译器在编译此文件过程中的逻辑(如果需要)
1.5 现在,我们引入一个实例来具体了解一下上述过程
首先介绍一下运行环境,Windows下查看二进制文件实在是蛋疼,我的电脑正好是双系统的,那我们就切换到Linux下操作吧。 我的系统版本如下:
接下来,我们定位到工作目录下查看当前都有哪些文件,如图所示:
可以看到,当前目录下:
text.md是使用Markdown编辑的本文。 两个文件夹assets 和photo,它们用来处理text.md中的图片,我们不去理会以上这些。
这里一共有三个C ++ 文件:main.cpp, String.cpp以及String.h,稍后,我们将将会手动编译它们。
本文使用的程序来自《C++ Primer Plus》(第六版) 第12章 例程12.5以及12.6,(我最近刚看过的内容)并且这里稍微做了一点修改。
我并不想讨论这些程序中涉及到的细节,因为就目前来说,C++对我而言算是“新鲜事物”。随着学习的深入,以后我会讨论到它们的。
main.cpp:
#pragma warning(disable : 4996)
#include
#include "String.h"
using namespace std;
int aa=1;
int bb;
static int cc;
extern int dd;
const int ArSize = 10;
const int Maxlen = 81;
int main()
{
cout<<dd<<endl;
String name;
cout << "Hi, what's your name? " << endl;
cin >> name;
cout << name << " Please enter up to " << ArSize << " short sayings : " << endl;
String sayings[ArSize];
char temp[Maxlen];
for (int i = 0; i < ArSize; i++)
{
cout << i + 1 << ": ";
cin.get(temp, Maxlen);
while (cin && cin.get() != '\n')
{
continue;
}
if (!cin || temp[0] == '\n')
{
break;
}
else
{
sayings[i] = temp;
}
}
int total = ArSize - 1;
if (total > 0)
{
cout << "Here is your sayings: " << endl;
for (int i = 0; i < total; i++)
{
cout << sayings[i][0] << ":" << sayings[i] << endl;
}
int shortest = 0;
int first = 0;
for (int i = 1; i < total; i++)
{
if (sayings[i].length() < sayings[shortest].length())
{
shortest = i;
}
if (sayings[i] < sayings[first])
{
first &#