-
Notifications
You must be signed in to change notification settings - Fork 1
/
Parallel101-1-c8505aa9.js
29 lines (29 loc) · 20.2 KB
/
Parallel101-1-c8505aa9.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
import{_ as d}from"./ValaxyMain.vue_vue_type_style_index_0_lang-bf8ee3fd.js";import{_ as p,p as h,c as u,w as s,o as _,a as l,b as a,d as e,e as m,r as i,f as k}from"./app-4433fae6.js";import"./YunFooter.vue_vue_type_script_setup_true_lang-80290804.js";import"./YunCard.vue_vue_type_style_index_0_lang-e4df850e.js";import"./YunPageHeader.vue_vue_type_script_setup_true_lang-80be03ea.js";const $l=JSON.parse('{"title":"Parallel101-1.学 C++从 CMake 学起","description":"","frontmatter":{"title":"Parallel101-1.学 C++从 CMake 学起","toc":true,"date":"2022-02-27T19:53:57.000Z","tags":["Cpp","CMake"],"categories":["笔记","Parallel101"],"cover":null},"headers":[{"level":2,"title":"Make,构建系统","slug":"make,构建系统","link":"#make,构建系统","children":[]},{"level":2,"title":"CMake,构建系统的构建系统","slug":"cmake,构建系统的构建系统","link":"#cmake,构建系统的构建系统","children":[]},{"level":2,"title":"库","slug":"库","link":"#库","children":[{"level":3,"title":"静态链接库 (.a/.lib,Linux/Windows)","slug":"静态链接库-a-lib-linux-windows","link":"#静态链接库-a-lib-linux-windows","children":[]},{"level":3,"title":"动态链接库 (.so/.dll,Linux/Windows)","slug":"动态链接库-so-dll-linux-windows","link":"#动态链接库-so-dll-linux-windows","children":[]}]},{"level":2,"title":"Cpp 为什么需要声明?","slug":"cpp-为什么需要声明?","link":"#cpp-为什么需要声明?","children":[]},{"level":2,"title":"为什么需要头文件?","slug":"为什么需要头文件?","link":"#为什么需要头文件?","children":[]},{"level":2,"title":"CMake 的子模块","slug":"cmake-的子模块","link":"#cmake-的子模块","children":[]},{"level":2,"title":"CMake 关于目标的其他的一些指令","slug":"cmake-关于目标的其他的一些指令","link":"#cmake-关于目标的其他的一些指令","children":[]},{"level":2,"title":"如何使用第三方库","slug":"如何使用第三方库","link":"#如何使用第三方库","children":[]},{"level":2,"title":"作业 01","slug":"作业-01","link":"#作业-01","children":[]}],"relativePath":"pages/posts/note/parallel/Parallel101-1.md","path":"/home/runner/work/YlYZ/YlYZ/pages/posts/note/parallel/Parallel101-1.md","lastUpdated":1686297441000}'),o=JSON.parse('{"title":"Parallel101-1.学 C++从 CMake 学起","description":"","frontmatter":{"title":"Parallel101-1.学 C++从 CMake 学起","toc":true,"date":"2022-02-27T19:53:57.000Z","tags":["Cpp","CMake"],"categories":["笔记","Parallel101"],"cover":null},"headers":[{"level":2,"title":"Make,构建系统","slug":"make,构建系统","link":"#make,构建系统","children":[]},{"level":2,"title":"CMake,构建系统的构建系统","slug":"cmake,构建系统的构建系统","link":"#cmake,构建系统的构建系统","children":[]},{"level":2,"title":"库","slug":"库","link":"#库","children":[{"level":3,"title":"静态链接库 (.a/.lib,Linux/Windows)","slug":"静态链接库-a-lib-linux-windows","link":"#静态链接库-a-lib-linux-windows","children":[]},{"level":3,"title":"动态链接库 (.so/.dll,Linux/Windows)","slug":"动态链接库-so-dll-linux-windows","link":"#动态链接库-so-dll-linux-windows","children":[]}]},{"level":2,"title":"Cpp 为什么需要声明?","slug":"cpp-为什么需要声明?","link":"#cpp-为什么需要声明?","children":[]},{"level":2,"title":"为什么需要头文件?","slug":"为什么需要头文件?","link":"#为什么需要头文件?","children":[]},{"level":2,"title":"CMake 的子模块","slug":"cmake-的子模块","link":"#cmake-的子模块","children":[]},{"level":2,"title":"CMake 关于目标的其他的一些指令","slug":"cmake-关于目标的其他的一些指令","link":"#cmake-关于目标的其他的一些指令","children":[]},{"level":2,"title":"如何使用第三方库","slug":"如何使用第三方库","link":"#如何使用第三方库","children":[]},{"level":2,"title":"作业 01","slug":"作业-01","link":"#作业-01","children":[]}],"relativePath":"pages/posts/note/parallel/Parallel101-1.md","path":"/home/runner/work/YlYZ/YlYZ/pages/posts/note/parallel/Parallel101-1.md","lastUpdated":1686297441000}'),C={name:"pages/posts/note/parallel/Parallel101-1.md",data(){return{data:o,frontmatter:o.frontmatter}},setup(){h("pageData",o)}},g=l("p",null,"Parallel101 笔记 + 课后作业,课程传送门:",-1),f={id:"make,构建系统",tabindex:"-1"},A=l("p",null,"构建系统是为了简化多文件编译:",-1),w=l("ul",null,[l("li",null,"在多文件编译时,每有一个文件改动就要重新编译所有文件,解决办法就是将每个文件单独编译成.o 文件,再链接这些.o 文件为可执行文件,若改动了某个文件,那么只需要重新将那个文件编译成.o 文件即可 优点:"),l("li",null,"这样我们改几个文件还得自己重输那几个文件的编译命令,太麻烦,所以使用构建系统来帮我们监控哪些文件被改了,我们只需要写一份 makefile 就可以用 make 命令来编译好所有应该编译的文件 缺点:"),l("li",null,"需要编写各个文件之间的依赖关系,有头文件时特别头疼,如果我改了一个文件的头文件 include,重新写 makefile"),l("li",null,"语法太简单,没有条件判断"),l("li",null,"不能跨平台"),l("li",null,"不能跨编译器,为 g++编写的 makefile 中有 g++的参数,不能用于 clang++")],-1),b=l("div",{class:"language-Makefile"},[l("span",{class:"copy"}),l("pre",{class:"shiki material-theme-palenight",tabindex:"0"},[l("code",null,[l("span",{class:"line"},[l("span",{style:{color:"#82AAFF"}},"a.out"),l("span",{style:{color:"#89DDFF"}},":"),l("span",{style:{color:"#A6ACCD"}}," hello.o main.o")]),e(`
`),l("span",{class:"line"},[l("span",{style:{color:"#A6ACCD"}}," g++ hello.o main.o -o a.out")]),e(`
`),l("span",{class:"line"}),e(`
`),l("span",{class:"line"},[l("span",{style:{color:"#82AAFF"}},"hello.o"),l("span",{style:{color:"#89DDFF"}},":"),l("span",{style:{color:"#A6ACCD"}}," hello.cpp")]),e(`
`),l("span",{class:"line"},[l("span",{style:{color:"#A6ACCD"}}," g++ -c hello.cpp -o hello.o")]),e(`
`),l("span",{class:"line"}),e(`
`),l("span",{class:"line"},[l("span",{style:{color:"#82AAFF"}},"main.o"),l("span",{style:{color:"#89DDFF"}},":"),l("span",{style:{color:"#A6ACCD"}}," main.cpp")]),e(`
`),l("span",{class:"line"},[l("span",{style:{color:"#A6ACCD"}}," g++ -c main.cpp -o main.o")]),e(`
`),l("span",{class:"line"})])])],-1),y={id:"cmake,构建系统的构建系统",tabindex:"-1"},D=l("p",null,"只需要写一份 CMakeLists.txt",-1),v=l("p",null,"优点:",-1),F=l("ul",null,[l("li",null,"自动检测源文件和头文件的依赖关系,导出到 Makefile 里"),l("li",null,"相对高级的语法,内置的函数有 configure、install 等"),l("li",null,"跨平台,linux 上生成 makefile, windows 上生成 vsproj 等"),l("li",null,"通过参数指定要使用的编译器,Cpp 版本等")],-1),x=l("div",{class:"language-"},[l("span",{class:"copy"}),l("pre",{class:"shiki material-theme-palenight",tabindex:"0"},[l("code",null,[l("span",{class:"line"},[l("span",{style:{color:"#A6ACCD"}},"cmake -B build")]),e(`
`),l("span",{class:"line"},[l("span",{style:{color:"#A6ACCD"}})])])])],-1),M={id:"库",tabindex:"-1"},P=l("ul",null,[l("li",null,"有时候多个可执行文件,他们之间的某些部分是相同的,这些功能就可以做成一个库,让大家一起用"),l("li",null,"库中的函数可以被可执行文件调用,可以被其他库调用")],-1),L={id:"静态链接库-a-lib-linux-windows",tabindex:"-1"},$=l("p",null,"静态库相当于多个.o 文件的打包,在编译时将对应的代码插入可执行文件,可执行文件的体积会变大,编译完成后可以删除静态库",-1),B={id:"动态链接库-so-dll-linux-windows",tabindex:"-1"},S=l("p",null,'运行时调用,编译时只在可执行文件中生成"插桩函数",当可执行文件被加载时会读取指定目录的动态链接库文件,加载到内存中的空闲位置,并且替换相应的"插桩"指向的地址为加载后的地址,这个过程被称为重定向。这样以后函数被调用就会跳转到动态加载的地址去。',-1),N=l("p",null,"指定目录:",-1),X=l("ul",null,[l("li",null,"Windows:可执行文件同目录,其次是环境变量%PATH%"),l("li",null,"Linux:ELF 格式可执行文件的 RPATH,其次是/usr/lib 等")],-1),V=l("p",null,"windows/linux 下动态链接库的实现区别:",-1),W=l("ul",null,[l("li",null,"windows:引用.dll 也需要一个配套的.lib,这个.lib 的作用就是作为插桩函数在编译的时候插入可执行文件中,相当于.dll 就是把.lib 中函数的实现给抽离的出来,从而节省了可执行文件的大小"),l("li",null,"Linux:自动生成插桩而不需要.a 文件来生成插桩")],-1),Y=l("div",{class:"language-cmake"},[l("span",{class:"copy"}),l("pre",{class:"shiki material-theme-palenight",tabindex:"0"},[l("code",null,[l("span",{class:"line"},[l("span",{style:{color:"#89DDFF"}},"cmake_minimum_required"),l("span",{style:{color:"#A6ACCD"}},"(VERSION 3.12)")]),e(`
`),l("span",{class:"line"},[l("span",{style:{color:"#89DDFF"}},"project"),l("span",{style:{color:"#A6ACCD"}},"(hellocmake LANGUAGES CXX)")]),e(`
`),l("span",{class:"line"}),e(`
`),l("span",{class:"line"},[l("span",{style:{color:"#A6ACCD"}},"//生成静态库")]),e(`
`),l("span",{class:"line"},[l("span",{style:{color:"#A6ACCD"}},"//add_library(hellolib STATIC hello.cpp)")]),e(`
`),l("span",{class:"line"}),e(`
`),l("span",{class:"line"},[l("span",{style:{color:"#A6ACCD"}},"//生成动态库")]),e(`
`),l("span",{class:"line"},[l("span",{style:{color:"#89DDFF"}},"add_library"),l("span",{style:{color:"#A6ACCD"}},"(hellolib SHARED hello.cpp)")]),e(`
`),l("span",{class:"line"}),e(`
`),l("span",{class:"line"},[l("span",{style:{color:"#A6ACCD"}},"//生成可执行文件")]),e(`
`),l("span",{class:"line"},[l("span",{style:{color:"#89DDFF"}},"add_executable"),l("span",{style:{color:"#A6ACCD"}},"(a.out main.cpp)")]),e(`
`),l("span",{class:"line"}),e(`
`),l("span",{class:"line"},[l("span",{style:{color:"#A6ACCD"}},"//为可执行文件链接库hellolib")]),e(`
`),l("span",{class:"line"},[l("span",{style:{color:"#89DDFF"}},"target_link_libraries"),l("span",{style:{color:"#A6ACCD"}},"(a.out PUBLIC hellolib)")]),e(`
`),l("span",{class:"line"})])])],-1),T={id:"cpp-为什么需要声明?",tabindex:"-1"},I=l("p",null,[e("Cpp 是一种"),l("strong",null,"强烈依赖上下文"),e("的语言")],-1),Z=l("p",null,"多文件编译,main.cpp 要使用 hello.cpp 的函数,必须要要在 main.cpp 中声明这个函数",-1),E=l("ul",null,[l("li",null,[l("p",null,"因为编译器是一个文件一个文件编译,然后链接,所以 main.cpp 在使用别的文件的函数时需要知道函数的参数和返回值类型:这样才能支持重载,隐式类型转换等特性。"),l("div",{class:"language-cpp"},[l("span",{class:"copy"}),l("pre",{class:"shiki material-theme-palenight",tabindex:"0"},[l("code",null,[l("span",{class:"line"},[l("span",{style:{color:"#C792EA"}},"void"),l("span",{style:{color:"#A6ACCD"}}," "),l("span",{style:{color:"#82AAFF"}},"show"),l("span",{style:{color:"#89DDFF"}},"("),l("span",{style:{color:"#C792EA"}},"float"),l("span",{style:{color:"#A6ACCD"}}," "),l("span",{style:{color:"#A6ACCD","font-style":"italic"}},"x"),l("span",{style:{color:"#89DDFF"}},");")]),e(`
`),l("span",{class:"line"}),e(`
`),l("span",{class:"line"},[l("span",{style:{color:"#676E95","font-style":"italic"}},"//编译器就知道要把 3 转化成 3.0f")]),e(`
`),l("span",{class:"line"},[l("span",{style:{color:"#82AAFF"}},"show"),l("span",{style:{color:"#89DDFF"}},"("),l("span",{style:{color:"#F78C6C"}},"3"),l("span",{style:{color:"#89DDFF"}},");")]),e(`
`),l("span",{class:"line"})])])])]),l("li",null,[l("p",null,"让编译器知道 show 是一个函数名而不是变量或类名,如果没有声明,编译器可以把他当作创建了一个名为 show 的临时对象")])],-1),R={id:"为什么需要头文件?",tabindex:"-1"},U=l("p",null,"上面说到,如果一个文件需要用另一个文件里面实现的函数,那么就得声明这个函数,但如果有 100 个文件都要用到这个函数,那岂不是这 100 个文件都得写这个声明?",-1),H=l("p",null,"一个函数还好,如果 100 个文件每个文件都要用到某个文件里实现的 100 个函数,那不是声明得写得老长了?而且要改声明呢?",-1),O=l("p",null,[e("所以有了头文件,我们把这些声明写进头文件 hello.h 中,只需要"),l("code",null,'#include "hello.h"'),e("就好了")],-1),G=l("p",null,[e("Cpp 中以#开头的都是预处理指令,由预处理器在编译前执行,"),l("code",null,'#include "hello.h"'),e("会把 hello.h 的内容替换到当前位置")],-1),j=l("p",null,"这样每当我们更改了 hello.cpp 实现中的函数参数,只需要更改 hello.h 中的内容就行,而不用改 100 个文件的声明了",-1),z=l("p",null,"tips:",-1),Q=l("ul",null,[l("li",null,[e("推荐在 hello.cpp 也就是函数实现所在的文件中插入"),l("code",null,'#include "hello.h"'),e("这样可以避免沉默的错误,这样的话,当函数实现的更改时,编译器就会提示声明与定义不一样,不然的话,这个错误就只能在链接的时候发现了")]),l("li",null,[e("使用 C 语言代码要写在"),l("code",null,'extern "C"{}'),e("中,编译器知道这是 C 代码就不会用重载等特性了")])],-1),q=l("p",null,"还有一些就不赘述了,如:",-1),J=l("ul",null,[l("li",null,[l("code",null,"<cstdio>"),e(),l("code",null,"<stdio.h>"),e(' "stdio.h" 三者的区别')]),l("li",null,"头文件会递归引用,如何避免递归引用会产生的重复引用")],-1),K={id:"cmake-的子模块",tabindex:"-1"},ll=l("p",null,"复杂的工程需要划分子模块,通常一个库一个目录",-1),el=l("ul",null,[l("li",null,[e("根目录要使用子目录的库,可以用"),l("code",null,"add_subdirectory"),e("添加子目录,然后在子目录也写一个 CMakeLists.txt, 其中定义的库在"),l("code",null,"add_subdirectory"),e("后就可以在外面使用。")]),l("li",null,"子目录里面的路径都是相对路径(相对于子目录),就会更方便一点")],-1),sl=l("p",null,"如果根目录的 main.cpp 要 include 子目录的.h 就需要写相对于根目录的.h 路径,这就有点麻烦了,我们可以将子目录添加到头文件搜索路径来解决这个问题:",-1),nl=l("div",{class:"language-cmake"},[l("span",{class:"copy"}),l("pre",{class:"shiki material-theme-palenight",tabindex:"0"},[l("code",null,[l("span",{class:"line"},[l("span",{style:{color:"#89DDFF"}},"target_include_directories"),l("span",{style:{color:"#A6ACCD"}},"(hellolib PUBLIC 子目录)")]),e(`
`),l("span",{class:"line"})])])],-1),al=l("p",null,"这时,头文件甚至可以通过尖括号引入,因为上面那一行相当于把子目录当做了系统文件夹路径",-1),tl=l("img",{src:"https://s2.loli.net/2022/02/27/tTPU7O6XG4RimIC.png",alt:""},null,-1),il={id:"cmake-关于目标的其他的一些指令",tabindex:"-1"},ol=l("img",{src:"https://s2.loli.net/2022/02/27/XOwPGMZvlAdDSCj.png",alt:""},null,-1),cl={id:"如何使用第三方库",tabindex:"-1"},rl=l("img",{src:"https://s2.loli.net/2022/02/27/QAzCYXLwxSNlRFd.png",alt:""},null,-1),dl=l("img",{src:"https://s2.loli.net/2022/02/27/HI5AVgWyxQNYrBi.png",alt:""},null,-1),pl=l("img",{src:"https://s2.loli.net/2022/02/27/H98FnRhxW75lvS1.png",alt:""},null,-1),hl=l("img",{src:"https://s2.loli.net/2022/02/27/Q5NrsXKSn3fl9uW.png",alt:""},null,-1),ul=l("img",{src:"https://s2.loli.net/2022/02/27/iDVpnSFIKsrToZa.png",alt:""},null,-1),_l={id:"作业-01",tabindex:"-1"},ml=l("p",null,"题目:",-1),kl=l("p",null,"解答:",-1),Cl=l("p",null,"A 文件要用 B 文件实现的函数,需要在 A 文件中对函数进行声明 所以为了方便 B 文件实现的函数被多个别的文件使用,可以将函数的声明和实现分离 需要用到的文件直接 include 函数的声明即可",-1),gl=l("p",null,"stb 下的库都是单文件库 为了让声明和实现分离,方便多个文件使用,stb 下的库都会约定一个宏#if define XXXXX 当这个宏被定义时,后面的函数实现部分才会被编译",-1),fl=l("p",null,"题目要求定义 stbiw 这个库,那么就需要函数的实现,在 stdiw 文件下新建一个源文件,在 include stb_image_write 之前 define 其约定的宏,该源文件就可以在编译时包含声明 + 实现,从而用该文件生成所需要的库",-1),Al=l("p",null,[e("为了 stb_image_write.h 能被"),l("code",null,"<>"),e("找到,在子目录的 cmakelist 添加到头文件搜索路径并设为 PUBLIC 即可,这样子目录和主目录的文件都可以通过"),l("code",null,"<>"),e(" include 到 stb_image_write.h")],-1);function wl(t,bl,yl,Dl,c,vl){const n=k,r=d;return _(),u(r,{frontmatter:c.frontmatter,data:c.data},{"main-content-md":s(()=>[g,l("ul",null,[l("li",null,[a(n,{href:"https://www.bilibili.com/video/BV1fa411r7zp/?spm_id_from=333.788",target:"_blank",rel:"noreferrer"},{default:s(()=>[e("https://www.bilibili.com/video/BV1fa411r7zp/?spm_id_from=333.788")]),_:1})])]),m(" more "),l("h2",f,[e("Make,构建系统 "),a(n,{class:"header-anchor",href:"#make,构建系统","aria-hidden":"true"},{default:s(()=>[e("#")]),_:1})]),A,w,b,l("h2",y,[e("CMake,构建系统的构建系统 "),a(n,{class:"header-anchor",href:"#cmake,构建系统的构建系统","aria-hidden":"true"},{default:s(()=>[e("#")]),_:1})]),D,v,F,x,l("h2",M,[e("库 "),a(n,{class:"header-anchor",href:"#库","aria-hidden":"true"},{default:s(()=>[e("#")]),_:1})]),P,l("h3",L,[e("静态链接库 (.a/.lib,Linux/Windows) "),a(n,{class:"header-anchor",href:"#静态链接库-a-lib-linux-windows","aria-hidden":"true"},{default:s(()=>[e("#")]),_:1})]),$,l("h3",B,[e("动态链接库 (.so/.dll,Linux/Windows) "),a(n,{class:"header-anchor",href:"#动态链接库-so-dll-linux-windows","aria-hidden":"true"},{default:s(()=>[e("#")]),_:1})]),S,N,X,V,W,Y,l("h2",T,[e("Cpp 为什么需要声明? "),a(n,{class:"header-anchor",href:"#cpp-为什么需要声明?","aria-hidden":"true"},{default:s(()=>[e("#")]),_:1})]),I,Z,E,l("h2",R,[e("为什么需要头文件? "),a(n,{class:"header-anchor",href:"#为什么需要头文件?","aria-hidden":"true"},{default:s(()=>[e("#")]),_:1})]),U,H,O,G,j,z,Q,q,J,l("h2",K,[e("CMake 的子模块 "),a(n,{class:"header-anchor",href:"#cmake-的子模块","aria-hidden":"true"},{default:s(()=>[e("#")]),_:1})]),ll,el,sl,nl,al,tl,l("h2",il,[e("CMake 关于目标的其他的一些指令 "),a(n,{class:"header-anchor",href:"#cmake-关于目标的其他的一些指令","aria-hidden":"true"},{default:s(()=>[e("#")]),_:1})]),ol,l("h2",cl,[e("如何使用第三方库 "),a(n,{class:"header-anchor",href:"#如何使用第三方库","aria-hidden":"true"},{default:s(()=>[e("#")]),_:1})]),rl,e(" 作为头文件引入,需要重新编译,而且头文件包含函数实现,所以非常得慢 "),dl,e(" 菱形依赖的问题 "),pl,e(" 解决菱形依赖的问题 "),hl,ul,e(" pacman 安装的库是直接编译好的,而 vcpkg 安装是先获取源码,然后在本地编译 "),l("h2",_l,[e("作业 01 "),a(n,{class:"header-anchor",href:"#作业-01","aria-hidden":"true"},{default:s(()=>[e("#")]),_:1})]),ml,l("ul",null,[l("li",null,[a(n,{href:"https://github.com/parallel101/hw01",target:"_blank",rel:"noreferrer"},{default:s(()=>[e("高性能并行编程与优化 - 第 01 讲的回家作业")]),_:1})])]),kl,l("ul",null,[l("li",null,[a(n,{href:"https://github.com/parallel101/hw01/pull/83",target:"_blank",rel:"noreferrer"},{default:s(()=>[e("高性能并行编程与优化 - 第 01 讲的回家作业-Pull Request")]),_:1})])]),Cl,gl,fl,Al]),"main-header":s(()=>[i(t.$slots,"main-header")]),"main-header-after":s(()=>[i(t.$slots,"main-header-after")]),"main-nav":s(()=>[i(t.$slots,"main-nav")]),"main-content":s(()=>[i(t.$slots,"main-content")]),"main-content-after":s(()=>[i(t.$slots,"main-content-after")]),"main-nav-before":s(()=>[i(t.$slots,"main-nav-before")]),"main-nav-after":s(()=>[i(t.$slots,"main-nav-after")]),comment:s(()=>[i(t.$slots,"comment")]),footer:s(()=>[i(t.$slots,"footer")]),aside:s(()=>[i(t.$slots,"aside")]),"aside-custom":s(()=>[i(t.$slots,"aside-custom")]),default:s(()=>[i(t.$slots,"default")]),_:3},8,["frontmatter","data"])}const Bl=p(C,[["render",wl]]);export{$l as __pageData,Bl as default};