目录
- 今日已完成任务列表
- 遇到的问题及解决方案
- 任务完成详细笔记
- 编辑器Vim
- 程序开发步骤
- 文件编辑器 Vim 快速入门
- 任务练习
- 编译器 GCC
- 程序编译流程
- 编译器 gcc 的简易使用
- gcc 编译链接命令规则及常用编译选项
- 工程构建工具 Make
- make 命令与makefile文件的规则和逻辑
- makefile 文件编写案例
- 填空练习
- 对自己的表现是否满意
- 简述下次计划
- 其他反馈
今日已完成任务列表
4-2、编辑器Vim、编译器GCC、工程构建工具Make
遇到的问题及解决方案
对练习代码进行编译时使用如下指令:
gcc -o main main.cpp
将会报错:
xxx:xxx: undefined reference to `operator new[](unsigned int)'…
解决方案:
因为写的是 C++ 程序😅
g++ -o main main.cpp
任务完成详细笔记
编辑器Vim
程序开发步骤
程序开发:将人类想要计算执行的工作按照计算机所支持的方式流程化、格式化、系统化的,使用编译器支持的开发语言组织成文,并由编译器翻译成机器码供计算机执行
C/C++ 程序开发一般包含以下步骤:
- 明确任务需求或目标
- 算法设计
- 编码源文件(通过编辑器 Vim 编写)
- 编译二进制文件(通过编译器 GCC 完成)
- 运行二进制文件(通过任务管理系统 slurm 运行计算任务)
- 调试程序(debug)
文件编辑器 Vim 快速入门
- 命令行式编辑器:Vim(通过终端对文件进行查看、编写和保存)
vim [filename]
- Vim 编辑环境下的三种常用模式
-
- 普通模式(冒号 ’ :')
移动
方向键
:1 快速跳转到文件第1行
:$ 快速跳转到文件最后一行
退出
w (保存)、q (退出)、! (强制)
:wq 保存文件并退出 Vim
:q! 强制退出,不保存修改
替换
s,:[addr]s/源字符串/目的字符串/[option]
全局替换:
%s/源字符串/目的字符串/g
2,10s/源字符串/目的字符串/g
例如:在使用vim编辑文件 cal_pi.cpp 时 ,若要把第 20 至 30 行代码中的 val_pi 字符串,一次性全部替换成 pi_value ,需键入命令 :20,30s/val_pi/pi_value/g
-
- 插入模式(i)
只有在插入模式下,才可以做文字输入,i 进入插入模式,按 [ESC] 回到普通模式
-
- 可视模式(v)
选定文段进行操作等,v 或 ctrl + v 进入,方向键选择,按 [ESC] 退出
复制
y 复制光标所选定文段
粘贴
p 在光标所在行处粘贴
删除、裁切
x 删除光标所选文段
任务练习
使用 Vim 编辑器,编写C语言程序 saxpy
程序saxpy实现了单精度浮点型数据的向量加法运算,其具体公式如下:
y[n]=y[n]+a*x[n]
其中,n为向量长度,x、y为向量数组,a为标量系数,x、y、a均为单精度浮点型float
各变量的初始值如下:
n为102400
a为3.14
x中各元素初始为0.0~6.0间的浮点型随机数
y初始取值为y[n]=12.0-x[n]
程序文件组织如下:
kernel_axpy.h
负责向量加法接口函数的声明如下:
void saxpy(const int a,const float * const x, float *y)
其中,a、x为输入,y即为输入、也为输出
void saxpy(const int a,const float * const x, float *y);
kernel_axpy.cpp
负责向量加法接口函数saxpy()的定义实现
#include "kernel_axpy.h"
using namespace std;
void saxpy(const int a,const float * const x, float *y)
{
for (int i=0; i<102400; ++i)
{
y[i] += a*x[i];
}
}
main.cpp
负责初始化向量
调用向量加法接口saxpy()
使用计时函数clock()统计saxpy()的运行时间,并打印到屏幕
打印结果y的前20个元素数值到文件y_value.txt
#include <iostream>
#include <stdlib.h>
#include <ctime>
#include <fstream>
#include "kernel_axpy.h"
using namespace std;
const int n = 102400;
const float a = 3.14;
int main()
{
float *x = new float[n];
float *y = new float[n];
srand((unsigned)time(NULL));
for (int i=0; i<n; ++i)
{
x[i] = static_cast <float> (rand()) / (static_cast <float> (RAND_MAX/6));
y[i] = 12 - x[i];
}
clock_t startTime, endTime;
startTime = clock();
saxpy(a, x, y);
endTime = clock();
cout<<(double)(endTime - startTime) / CLOCKS_PER_SEC<<endl;
ofstream out("y_value.txt");
for (int i=0; i<20; ++i)
{
out<<y[i]<<endl;
}
out.close();
return 0;
}
生成指定范围的浮点类型随机数: link
clock() 的使用: link
编译器 GCC
程序编译流程
编译过程:预处理、编译、链接
- 预处理:处理 .c 文件的 #define,#include 等预处理指令
- 编译:把高级语言 (.c) 翻译成汇编指令 (.s),再翻译成机器码 (.o)
- 链接:将所有目标文件 (.o) 和第三方库文件,链接成二进制文件 (.exe)
编译器 gcc 的简易使用
源文件与编译器:
gcc命令的建议编译规则:
gcc -o [binaryfile] [sourcefile1] [sourefile2]…
例如:
编译处可执行文件:gcc -o HelloWorld main.c
执行:./HelloWorld
gcc 编译链接命令规则及常用编译选项
-
编译与链接
示例:需要编译 main.c、kernel.c 成 exe,其使用了 /home/opt/ 路径下的第三方库 math,其中 opt 目录下包含:/include/mymath.h、/lib/mymath.so -
- 1、分步进行编译和链接
编译 (将源文件逐个编译成目标文件)
- 1、分步进行编译和链接
gcc -o [obj_file] -c [src_file] -I [include_path]
最后一个是字母 i 的大写不是 l
例如:
gcc -c main.c -o main.o
gcc -c kernel.c -o kernel.o -I /home/opt/include
链接 (将所有目标文件,以及第三方库,链接成二进制文件)
gcc -o [bin] [all_obj_files] -L [library_path] -l [library_file_name]
最后一个是小写字母 l
例如:
gcc -o exe main.o kernel.o -L /home/opt/lib -l mymath
-
- 2、直接编译出二进制文件
gcc -o [bin] [all_src_files] -I [include_path] -L [library_path] -l [library_file_name]
前面那个 -l 是大写的 i,后面那个 -l 是小写的 L
例如:
gcc -o exe main.c kernel.c -I /home/opt/include -L /home/opt/lib -l mymath
- 编译选项
gcc [-option] -o [bin] [src_file]
常用的option编译选项:
优化等级选项:
-O0,-O1,-O2,-O3 由编译器自动进行代码性能优化,等级越大,程序运行速度越快
警告选项:
-Wall 显示所有警告
调试选项:
-g 通常结合 -O0 优化等级编译,后期可使用 gdb 工具对二进制文件进行调试。会降低程序计算速度
性能分析选项:
-pg 后期可用于 gprof 工具对二进制文件进行性能分析。会降低程序计算速度
宏定义选项
-D 对应着程序代码中的宏定义。如 -DUSE_MPI
工程构建工具 Make
make 命令与makefile文件的规则和逻辑
- Make 与 Makefile
GNU Make 是最基本的项目构建工具,Make 通过 Makefile 文件,获取如何构建执行文件信息,并按照 Makefile 里面设定的规则,根据源文件的依赖关系,调用编译器自动执行编译 - Makefile 的逻辑与规则
- Makefile 组成结构:目标、目标的前置条件 (依赖)、规则,还包括变量以及内置函数
- 一个目标 (最终的执行文件),会依赖一些文件 (如源文件、目标文件、库文件等),所依赖的文件也可以作为新的目标 (子目标) 放在后面,建立其对应的规则
- 执行 make 指令时,make首先执行的是 Makefile 里面的第一个目标,检查第一个目标的前置条件,然后寻找 “前置条件” 的前置条件,以此类推
- 执行某个特定的目标,不遵循默认从第一个目标开始,则输入:
make [targetName]
makefile 文件编写案例
- 编写 makefile
以为 hello.cpp 编写 makefile 为例
BIN=hello "="定义变量BIN: 要生成的二进制文件名称为"hello"
SRC=hello.cpp
all:$(BIN) all为make默认执行目标, 这个目标所依赖文件: 二进制文件BIN
$(BIN):$(SRC) 二进制文件BIN依赖于源码文件SRC
g++ -o $(BIN) $(SRC) 建立SRC到BIN的规则
clean: clean 目标不依赖任何其他, 执行时将执行文件删除命令
rm -rf $(BIN) $(OBJ)
- 使用 makefile
- 安装可执行文件:make
- 清除:make clean
填空练习
现有一个程序,包含main.cpp、kernel.cpp源文件,并使用了/home/opt/路径下的第三方库mymath,其中opt路径下包含头文件./include/mymath.h和库文件./lib/libmymath.so。需要在与源文件同级目录下编写makefile文件来安装该程序,来编译出二进制文件exe
BIN=exe
OBJ=main.o kernel.o
CXX=g++
CXXFLAGS=-O2
LDFLAGS=$(CXXFLAGS)
INC=-I/home/opt/include
LIB=-L/home/opt/lib -lmymath
ALL:$(BIN)
$(BIN):$(OBJ)
$(CXX) $(LDFLAGS) -o $@ $^ $(LIB)
%.o:%.cpp
$(CXX) $(CXXFLAGS) -o $@ -c $< $(INC)
.PHONY:clean
clean:
rm -rf $(OBJ) $(BIN)
对自己的表现是否满意
今天的内容比较硬核,虽然之前或多或少了解过,但再次接触依然比较陌生。虽然把课程和任务过了一遍,但应该只是接触了这部分的皮毛,之后再遇到问题时还是要多查、多悟
简述下次计划
作业管理系统slurm、调试器GDB、高性能算法等
其他反馈
课程终端好卡…