1:程序的运行过程
编译器编译为可执行文件
g++ -o main main.cpp
//main 是可执行文件 main.cpp是需要编译的代码文件
运行过程
./main
//Windows 上需要使用 main.exe 进行运行
集成开发环境(Integrated Development Environment),简称 IDE。
需要消耗的时间
1 代码编写:注意考虑算法设计,数据结构的选择,各种边界情况和错误处理
2 代码编译:将C++代码转化成机器语言的过程,编译器需要进行语法分析,语义分析,代码优化等操作,这些过程可能需要消耗较多的时间和内存资源。对于大型项目或复杂代码,可能需要较长时间,可能需要几分钟。
3 可执行文件:编译成功后会将机器语言打包成可执行文件,这个过程比编译快,但是也可能需要较长时间,对于大型项目或者需要链接多个库文件的程序
4 程序运行:将可执行文件加载到内存并执行程序,程序的运行时间取决于代码的复杂度,输入数据的规模,计算机的性能等。
如果重新改变代码的参数,那么程序还需要重新编译吗
如果只是修改代码中的参数,对于代码的结构没有改变,通常不需要重新编译整个程序。
修改代码中的参数只会影响程序的输入和输出,而不会对程序的结构和逻辑造成影响。因此,可以直接运行先前已经编译好的可执行文件,并通过命令行参数或用户交互方式提供新的参数,以获取更新后的结果。
2:库文件和头文件
库文件(library files)
是一组预先编译好的二进制文件,通常有.lib .a .dll等后缀。库文件中包含已经编译好的函数,类,变量等的定义和实现代码,库文件通常可以分为静态库(static library)和动态库(dynamic library)
静态库:在编译时,链接到目标程序中的库文件,它的代码最终会被复制到可执行文件中,使用静态库的程序在运行时不需要额外加载库文件,因此具有独立性,但也会增加可执行文件的大小。
动态库:程序运行时被加载到内存中使用的库文件, 程序只需要链接到到动态库,不会将代码复制到可执行文件中,动态库可以被多个程序共享,减少了代码冗余,但是需要确保系统存在相应的动态库文件。
常见的库文件的示例:
- 标准库(Standard Library):C 和 C++ 语言都有各自的标准库,包含了各种常用的函数、数据结构和宏定义等。在 C 语言中,标准库通常包含在
libc.a
或libc.so
中;在 C++ 语言中,标准库通常包含在libstdc++.a
或libstdc++.so
中。 - 数学库(Math Library):数学库提供了各种数学计算相关的函数和常量。例如,C 语言的数学库
libm.a
或libm.so
提供了诸如三角函数、指数函数、对数函数、数学常量等功能。 - 图形库(Graphics Library):图形库用于实现图形界面和图形渲染等功能。例如,OpenGL 图形库提供了各种图形渲染、图像处理和图形学相关的函数和工具,其库文件可能为
libglut.a
或libglut.so
。 - 数据库库(Database Library):数据库库提供了操作数据库的接口和函数,用于连接、查询和管理数据库。例如,MySQL 数据库的 C API 库文件为
libmysqlclient.a
或libmysqlclient.so
。 - 网络库(Network Library):网络库提供了网络通信相关的函数和工具,用于实现网络连接、数据传输和协议处理等功能。例如,libcurl 网络库提供了各种 HTTP、FTP、SMTP 等网络协议的实现,其库文件为
libcurl.a
或libcurl.so
。
常见的静态库和动态库的示例:
- 静态库:
- C标准库(libc.a):这是一个包含了许多常用C函数实现的静态库文件,例如printf、scanf等。当你编译一个使用了这些函数的程序时,编译器会将这些函数的实现代码链接到最终的可执行文件中,因此它们不需要在运行时从外部加载。
- libpng:这是一个用于处理PNG图像格式的静态库。当你将libpng库链接到程序中时,编译器会将这个库的所有代码都复制到最终的可执行文件中。
- 动态库:
- OpenGL(libGL.so):这是一个用于图形渲染的动态库。当你运行使用OpenGL的程序时,操作系统会在内存中加载libGL.so库文件,并将它的代码映射到程序的地址空间中,以便程序可以调用其中的函数。
- Qt(libQt5Core.so):这是一个用于创建跨平台GUI应用程序的动态库。类似于OpenGL,当你运行使用Qt的程序时,操作系统会在内存中加载libQt5Core.so库文件,并将其中的函数映射到程序的地址空间中。
总的来说,静态库在编译时被链接到程序中,使得程序更加独立,但也会增加程序的大小。动态库则在运行时被加载,使得程序更加灵活,但也需要确保正确的动态库文件存在和可用。
头文件(header files)
是一种文本文件,通常是.h为后缀,包含函数,类,结构体,宏定义等的声明和定义,用于向编译器 提供有关这些实体的信息。头文件在编译时被包含(#include
)到源代码文件中,以便在编译过程中将声明和定义与实际的代码进行匹配。头文件的主要作用是提供程序的接口和声明,使得代码的组织更加清晰,可读性更高,并且方便在多个源代码文件之间共享相同的声明和定义。
库文件和头文件关系
头文件包含了函数和变量的声明,以及一些宏定义和类型定义。它们通常以 .h
或者 .hpp
为后缀名,例如 stdio.h
、stdlib.h
等等。头文件的主要作用是提供外部的接口,使得不同的源文件(源文件是指包含程序代码的文本文件,通常以 .c
或者 .cpp
为后缀名。)可以相互访问和调用彼此的函数和变量。当程序需要使用一个外部的函数或变量时,就需要包含相应的头文件。
库文件则包含了函数和变量的实现,它们通常以 .a
或者 .so
为后缀名,例如 libm.a
、libcrypto.so
等等。库文件的主要作用是将一些常用的函数和变量打包起来,供程序在运行时使用。当程序需要调用库文件中的某个函数时,它会在程序启动时加载相应的库文件,并根据函数名和参数跳到相应的实现代码处执行。
在程序编译时,需要将头文件和库文件链接到程序中。编译器会先处理源文件中的代码,然后根据头文件中的声明确定函数和变量的类型,最后在链接时将库文件中的实现代码和程序代码结合起来生成可执行文件。 (Windows 平台上,可执行文件的扩展名为 .exe
)
当涉及到库文件和头文件的关系时,一个常见的例子是使用数学库 math.h
。
math.h
是一个头文件,提供了用于数学计算的函数的声明。例如,math.h
中包含了 sin()
、cos()
、sqrt()
等函数的声明。当你在程序中需要使用这些函数时,你需要在代码中包含 math.h
头文件,这样编译器就知道这些函数的声明了。
另一方面,对应的库文件是 libm.a
(在 Linux 系统上)或者 libm.lib
(在 Windows 系统上)。这个库文件包含了 math.h
中函数的实现代码。当你编译和链接程序时,需要将 libm
库文件链接到你的程序中,这样程序在运行时就可以调用 math.h
中的函数了。
假设你的程序中需要使用 sin()
函数进行三角函数计算。首先,在你的代码中包含 math.h
头文件:
#include <math.h>
int main() {
double x = 0.5;
double result = sin(x);
// 其他代码...
return 0;
}
需要在编译和链接时指定链接 libm
库文件:
gcc -o program program.c -lm
这里的 -lm
选项告诉编译器链接数学库文件。
通过这种方式,你可以在程序中使用 sin()
函数,而编译器会根据头文件中的声明找到库文件中的实现代码,并将其链接到你的程序中,使得运行时可以正确调用该函数。
2:在C++中,可以将函数的声明、定义和实现分别放在不同的文件中,
通过编译和链接的过程将它们组合起来生成可执行文件。
1:声明文件(header file)
通常使用 .h
或者 .hpp
作为文件扩展名,用于声明函数的原型和其他必要的声明。例如,假设我们有一个 math.h
文件,其中声明了一个加法函数。
// math.h
int add(int a, int b);
2:实现文件(implementation file)
通常使用 .cpp
作为文件扩展名,用于实现函数的具体逻辑。例如,我们有一个 math.cpp
文件,其中实现了 add
函数。
// math.cpp
#include "math.h"
int add(int a, int b) {
return a + b;
}
3:主程序文件(main file)
通常使用 .cpp
作为文件扩展名,包含程序的入口点 main
函数。例如,我们有一个 main.cpp
文件,它调用了 add
函数。
// main.cpp
#include <iostream>
#include "math.h"
int main() {
int result = add(2, 3);
std::cout << "The result is: " << result << std::endl;
return 0;
}
接下来,需要将这三个文件进行编译和链接,生成可执行文件。
- 首先,使用编译器将
math.cpp
和main.cpp
分别编译成目标文件(object file):目标文件是二进制文件
g++ -c math.cpp -o math.o //g++:调用 GNU C++ 编译器。
//-c:指示编译器仅进行编译操作,不进行链接操作。
g++ -c main.cpp -o main.o
- 然后,使用链接器将目标文件
math.o
和main.o
进行链接,生成可执行文件:
g++ math.o main.o -o program // 多文件 o o -o 可执行文件
//单文件 g++ -o main main.cpp 单文件 -o 可执行文件 源文件
- 最后,运行生成的可执行文件:
./program
3:Makefile 是一个用于自动化构建和管理项目的文件。
它通常用于编译和链接多个源文件,并生成可执行文件、静态库或动态库等。
Makefile 中定义了一系列规则(rules),每个规则指定了一个或多个目标(target)以及生成目标所需要的依赖(dependencies)和命令(commands)。当目标不存在或者其依赖发生变化时,Make 工具会根据规则自动执行相应的命令来生成目标。
# 定义编译器和编译选项
CXX = g++
CXXFLAGS = -std=c++11 -Wall
# 定义目标和依赖关系
program: main.o math.o
$(CXX) $(CXXFLAGS) $^ -o $@
main.o: main.cpp math.h
$(CXX) $(CXXFLAGS) -c $< -o $@
math.o: math.cpp math.h
$(CXX) $(CXXFLAGS) -c $< -o $@
# 定义清理规则
clean:
rm -f *.o program
在这个 Makefile 中,有三个规则:
- program 规则:它的目标是生成可执行文件 program,依赖于 main.o 和 math.o。当 main.o 或 math.o 发生变化时,Make 会执行后续的命令来生成 program。
- main.o 和 math.o 规则:它们分别是生成 main.o 和 math.o 的规则。当对应的源文件(main.cpp 和 math.cpp)或头文件(math.h)发生变化时,Make 会执行后续的命令来生成目标。
- clean 规则:它定义了一个清理规则,用于删除生成的目标文件和可执行文件。
要使用 Makefile,只需在命令行中运行 make 命令:
make