笔记¶
Colorful Commands
导言
电脑玩家经常说RGB是最重要的,对于程序员来说,彩色的terminal有助于快速的分辨输出的有效信息。为此有一些有意思的彩色输出命令。
Cheat sheet (命令行小抄/备忘录)
tealdeer - simplest example
a RUST fast version oftdlr
Tips: OpenSSL development headers
get a "failed to run custom build command for openssl-sys" error message. The package is calledlibssl-dev
on Ubuntu.
部分支持中文,支持多平台
自定义
- cheat + 编写的cheatsheets
- 支持fzf和自动补全
- kb. A minimalist knowledge base manager
- eg provides examples of common uses of command line tools.
支持在线网页版
- Linux Command Line Cheat Sheet
- devhints.io
- 各种的选项,不止命令,包括bash, vim, mysql, git
- 语言 go, python, java, JS, NodeJS, Ruby 3.
- navi
- 支持语意转换的补全 🔥
- cheat.sh 🔥
- 支持
curl
命令直接访问或者交互式 - 支持补全
- 返回内容集成cheat cheat.sheets tdlr
- bropages.org
- 用户自发投票排序的命令用例
navi installation & usage
SHELL
awesome-shell里多看看。
oh my zsh
虽然这个ohmyzsh好用,但是我用惯了hyq的模板, 从github上下载后解压就安装了zsh模板。(之后可以考虑传到云盘或者cloudflare)
zsh_history 支持多端口同步,实时保存
窗口管理器 tmux-like
oh my tmux 🔥
## Install
cd
git clone https://github.com/gpakosz/.tmux.git
ln -s -f .tmux/.tmux.conf
cp .tmux/.tmux.conf.local .
# or
cd ~/resources
# wget https://gitee.com/shaojiemike/oh-my-tmux/repository/blazearchive/master.zip?Expires=1629202041&Signature=Iiolnv2jN6GZM0hBWY09QZAYYPizWCutAMAkhd%2Bwp%2Fo%3D
unzip oh-my-tmux-master.zip -d ~/
ln -s -f ~/oh-my-tmux-master/.tmux.conf ~/.tmux.conf
cp ~/oh-my-tmux-master/.tmux.conf.local ~/.tmux.conf.local
基于Rust的zellij
开发编辑器 vim-like
vimrc 🔥
emacs
针对不同语言有许多可选插件
### Ubuntu install emacs27
#### 问题:
dpkg-deb: error: paste subprocess was killed by signal (Broken pipe)
Errors were encountered while processing:
/var/cache/apt/archives/emacs27-common_27.1~1.git86d8d76aa3-kk2+20.04_all.deb
E: Sub-process /usr/bin/dpkg returned an error code (1)
版本解决,强制安装 sudo apt-get -o Dpkg::Options::="--force-overwrite" install emacs27-common
doom
### install doom
中文教程 https://www.bilibili.com/read/cv11371146
常用命令的"Colorful"版本
cd
z.lua - learning cd 🔥
Install LUA
curl -R -O http://www.lua.org/ftp/lua-5.4.4.tar.gz
tar zxf lua-5.4.4.tar.gz
cd lua-5.4.4
make all test
sudo make install # usr/bin
Install
cd ~/github
git clone https://github.com/skywind3000/z.lua.git
# vim ~/.zshrc
alias zz='z -c' # 严格匹配当前路径的子路径
alias zi='z -i' # 使用交互式选择模式
alias zf='z -I' # 使用 fzf 对多个结果进行选择
alias zb='z -b' # 快速回到父目录
#eval "$(lua /path/to/z.lua --init zsh)" # ZSH 初始化
eval "$(lua ~/github/z.lua/z.lua --init zsh)"
常用命令
# 弹出栈顶 (cd 到上一次的老路径),和 "z -0" 相同
$ z -
# 显示当前的 dir stack
$ z --
# 交互式
z -i foo # 进入交互式选择模式,让你自己挑选去哪里(多个结果的话)
z -I foo # 进入交互式选择模式,但是使用 fzf 来选择
# 匹配
z foo$
z foo # 跳转到包含 foo 并且权重(Frecent)最高的路径
z foo bar # 跳转到同时包含 foo 和 bar 并且权重最高的路径
z -r foo # 跳转到包含 foo 并且访问次数最高的路径
z -t foo # 跳转到包含 foo 并且最近访问过的路径
z -l foo # 不跳转,只是列出所有匹配 foo 的路径
z -c foo # 跳转到包含 foo 并且是当前路径的子路径的权重最高的路径
z -e foo # 不跳转,只是打印出匹配 foo 并且权重最高的路径
z -i foo # 进入交互式选择模式,让你自己挑选去哪里(多个结果的话)
z -I foo # 进入交互式选择模式,但是使用 fzf 来选择
z -b foo # 跳转到父目录中名称以 foo 开头的那一级
缺点:
- 没去过的路径,每级文件夹的补全没有了
- 可以和cd结合使用
ls
exa 🔥 - better ls
grep
rg (Fast & Good multi-platform compatibility) > ag > ack(ack-grep) 🔥
repgrep(rg) Rust编写
# ripgrep(rg) 但是readme说这样有bugs
sudo apt-get install ripgrep
# 可执行文件 (推荐)
wget https://github.com/BurntSushi/ripgrep/releases/download/13.0.0/ripgrep-13.0.0-x86_64-unknown-linux-musl.tar.gz
tar -zxvf ripgrep-13.0.0-x86_64-unknown-linux-musl.tar.gz
mv ./ripgrep-13.0.0-x86_64-unknown-linux-musl/rg ~/.local/bin
repgrep(rg) 常用选项
--no-ignore
忽略.gitignore
之类的文件,也搜索忽略的文件。(默认是不搜索的)-t txt
指定搜索类型rg 'content' ABC/*.cpp
搜索和正则ABC/*.cpp
匹配的文件
ag
find
find . -name "*xxx*"
fzf 🔥带预览的find
# ubuntu
sudo apt install fzf
# GIT install
git clone --depth 1 https://github.com/junegunn/fzf.git ~/.fzf
~/.fzf/install
source ~/.zshrc
# Vim-plugin
Plug 'junegunn/fzf', { 'do': { -> fzf#install() } }
# 使用
fzf --preview 'less {}'
# 安装了bat
fzf --preview "batcat --style=numbers --color=always --line-range :500 {}"
telescope.nvim 也带预览的find
cat
bat 🔥 - colorful cat
git
gitui 🔥 - fast Rust lazygit
https://github.com/extrawurst/gitui/releases
lazygit
高亮终端输出/log文件
bash脚本输出颜色文本示例
RED='\033[0;31m'
NC='\033[0m'
# No Color
printf "I ${RED}love${NC} Stack Overflow\n"
echo -e "\033[5;36m Orz 旧容器(镜像)已清理\033[0m"
颜色编号如下
颜色 | 编号 |
---|---|
Black | 0;30 |
Dark Gray | 1;30 |
Red | 0;31 |
Light Red | 1;31 |
Green | 0;32 |
Light Green | 1;32 |
Brown/Orange | 0;33 |
Yellow | 1;33 |
Blue | 0;34 |
Light Blue | 1;34 |
Purple | 0;35 |
Light Purple | 1;35 |
Cyan | 0;36 |
Blue | 0;37 |
Light Cyan | 1;36 |
Light Gray | 0;37 |
White | 1;37 |
hl 🔥自定义高亮各种log文件
通过regular expressions自定义高亮各种log文件
install需要 lex
颜色支持(3浅中深 * 6颜色 * 背景色反转)
# 前面 123 是深浅 , 4是下划线
# 字母大写是背景色反转
-r : red -g : green -y : yellow -b : blue -m : magenta -c : cyan -w : white
正则标记log关键词
绿色和红色
-e : extended regular expressions
-i : ignore case
hl -ei -g '(start(|ing|ed))' -r '(stop(|ping|ped))'
## ip 匹配
curl --trace-ascii - www.baidu.com|hl -ei -1R '[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}'
命令配置文件 hl_ha.cfg
默认设置
export HL_CONF=/staff/shaojiemike/github/hl/config_files
echo $HL_CONF │/staff/shaojiemike/github/hl/config_files
-%c : specifies the beginning of a range colorized in color 'c'
-. : specifies the end of the previous range
Colorize hl configurations :
example Commands
lD # ls by date
lW # ls by week
ifconfig -a | hl --ifconfig
# ping tcpdump fdisk apt-get diff
# ip ibstat iptables passwd
errors
常用方式
在~/.zshrc
里如下配置:
export HL_CONF=/home/shaojiemike/github/hl/config_files
function my_hl(){ hl -eg '\$\{?[A-Z_]+\}?' -ec ' ([A-Z_-]+) ' -eic '(nothing|note)' -eiy ' (-([a-z_-]+))' -eiy '0x[0-9a-z]+' --errors -eig '(yes)' -eir '((^| )no($| ))|(none)|(not)|(null)|(please)|( id )' -ir error -ir wrong -ib '(line)|(file)' -eiy '(warn(|ing))|(wait)|(idle)|(skip)' -im return -ic '(checking)' -eiy ' (__(.*)__) ' -ei1W '((\w*/[.A-Za-z0-9_/-]*[A-Za-z0-9_/-]*)("|$)?)|((\w*/[.A-Za-z0-9_/-]*[A-Za-z0-9_/-]*)(")? ) ' -3B '[0-9][0-9.]+' -3B ' ([0-9])|(#[0-9]+)' -eig '(start(|ing))' -eir '(end(|ing))' }
alias ifconfig='ifconfig | hl --ifconfig'
alias ip='ip a|hl --ip '
alias df='df -h |hl --df'
alias ibstat='ibstat |hl --ibstat'
编译时如此使用make 2>&1|my_hl
系统信息
dua-cli - best disk space viewer 🔥
资源监控
资源监控软件netdata
- netdata 默认挂载在
http://127.0.0.1:19999/
。想要WebUI运行sudo netdata -i node5.acsalab.com
- cpu, disk, memory, network,温度都有记录
- arm下有问题,需要自己编译
资源监控命令bottom(htop like)
# install
curl -LO https://github.com/ClementTsang/bottom/releases/download/0.6.8/bottom_0.6.8_amd64.deb
sudo dpkg -i bottom_0.6.8_amd64.deb
# 使用
btm
类似s-tui
可以观察CPU 温度,频率
网络监控 bmon
bmon是类 Unix 系统中一个基于文本,简单但非常强大的网络监视和调试工具
Compile yourself
Install libconfuse
sh wget https://github.com/martinh/libconfuse/releases/download/v2.8/confuse-2.8.zip unzip confuse-2.8.zip && cd confuse-2.8 PATH=/usr/local/opt/gettext/bin:$PATH ./configure make make install Install bmon
sh git clone https://github.com/tgraf/bmon.git cd bmon ./autogen.sh ./configure make make install bmon
文件管理器
nnn 多平台
https://github.com/jarun/nnn#quickstart
很复杂,插件和快捷键超级多
sh # 版本很低 3.0 sudo apt-get install nnn # Q 退出
ranger 基于vi的支持预览的横向多级显示 🔥
https://github.com/ranger/ranger
xplr - 筛选排序tips板 - 支持多选,正则查找, mov改名delete 🔥
https://github.com/sayanarijit/xplr
sh cargo install --locked --force xplr
参考文献
[C++ Basic] Grammar
概要
C++ 基础知识和语法,包括C++11,C++17,C++23的各种语言支持。
C++编程语言历史 和 设计思路
C 与 C++、java 的 区别
支持范式模板编程 (generic programming)
- 模板代码(增加泛型编程能力,类似python),
- 泛型编程是一种以通用性为中心的编程范式。在泛型编程中,程序通过使用参数化类型(或称为模板)来实现数据类型无关的算法和数据结构
- 强⼤的 Standard Template Library (STL) 标准库, 也是基于泛型编程的产物。
- 包括:容器、迭代器、算法和函数对象 四类
- 元编程(e.g., constexpr )编译时推导出变量是否为固定常数。
- 一些语法和关键字,增加了 new 和 delete,auto
支持面向对象编程 (object-oriented programming) 的拓展
- 类和对象:C++允许定义类和创建对象。类是一种用户自定义的数据类型,可以包含成员变量和成员函数。对象是类的一个实例,可以通过类来创建多个对象。C语言中没有类和对象的概念,只能使用结构体和函数来组织数据和行为。
- 封装:C++支持封装,可以将数据和相关的操作封装在一个类中,并使用访问修饰符来控制对类成员的访问权限。C语言没有封装的概念,所有的数据和函数都是公开的。
- 继承:C++支持继承,允许创建派生类从基类继承属性和行为。继承可以实现代码重用和类的层次化。C语言没有继承的概念。
- 多态:C++支持多态,允许通过基类指针或引用来调用派生类的虚函数,实现动态绑定和运行时多态性。C语言没有多态的概念。
- 异常处理:C++提供异常处理机制,可以通过抛出和捕获异常来处理程序中的错误和异常情况。C语言没有内置的异常处理机制。
C++ 与 java 的区别
- 内存管理:C++中的内存管理是手动的,程序员需要显式地分配和释放内存。C++提供了new和delete关键字来进行动态内存分配和释放。Java中的内存管理是自动的,使用垃圾回收机制来自动管理内存,程序员不需要手动释放内存。
- 指针:C++支持指针操作,允许直接访问和修改内存地址。Java中没有指针的概念,所有的数据访问都是通过引用进行的。
- 运行环境:C++是一种编译型语言,源代码在编译后被转换为机器码,并直接在操作系统上运行。Java是一种解释型语言,源代码在编译后生成字节码,然后由Java虚拟机(JVM)解释执行。
-
平台依赖性:C++代码在不同的平台上需要重新编译,因为它直接与底层系统交互。Java代码是平台无关的,一次编译的字节码可以在任何支持Java虚拟机的平台上运行。
-
C++更适合系统级编程、游戏开发等需要更高的性能和底层控制的场景。
- Java更适合企业级应用开发、网络编程等需要跨平台和可移植性的场景。
基础知识与坑
程序执行入口
The default program entry function is main
, but can be changed in two situations:
- use stupid
#define xxx main
in header file to replace the name which maybe ignored by silly search bar in VSCODE. - use
-exxx
compile flag
语句末尾的分号
- 在 C++ 中,是否需要在语句的末尾使用分号(
;
)取决于上下文。 -
C++ 语法的基本规则是:分号用来标识一条语句的结束,而有些结构并不是严格的语句,因此不需要分号。
-
声明(变量、类、结构体、枚举、函数原型、类型别名):都需要分号作为结束。
- 函数定义、控制语句(如
if
,while
,for
)、复合语句({}
) 不需要分号。 - 预处理指令(如
#define
,#include
):不需要分号,因为它们不是 C++ 语法层面的内容。 - 作用域结束的
}
不需要分号,但声明类或结构体时}
后要加分号。
为什么类/结构体需要分号
C++ 中的类和结构体定义实际上是一种声明,它们的定义是一种复杂的声明语句,因此必须用分号来结束它们。
总结来说,分号用来结束语句,包括声明、表达式和执行体等,但当你定义一个复合结构(如函数定义、控制语句)时,不需要分号来结束复合结构的定义。
逗号运算符(Comma)
重名变量的优先级
int getKthAncestor(int node, int k) {
int node= getKthAncestor(saved[node],--k);
return node;
}
//为什么第二行的node会提前被修改为0,导致传入函数getKthAncestor的saved[node]的node值为0
//如下去掉int,也不会错。因为int node 会初始化node为0
int getKthAncestor(int node, int k) {
node= getKthAncestor(saved[node],--k);
return node;
}
根据C++的作用域规则,内层的局部变量会覆盖外层的同名变量。因此,在第二行的语句中,node引用的是函数参数中的node,而不是你想要的之前定义的node。
为了避免这个问题,你可以修改代码,避免重复定义变量名。例如,可以将第二行的变量名改为newNode或其他不同的名称,以避免与函数参数名冲突。
运算符优先级
运算符性质:
- 接受的操作数,
- 优先级,
- 特殊:逻辑和(&&)先于逻辑或(||)、四则运算先于位运算
- 位运算优先级低于判断符号,记得写括号。
- 赋值(=)优先级最低
- 结合性,
- 左结合性: 大部分运算(加减乘除)
- 右结合性:赋值运算符。程序会先计算它右边的表达式的值,然后再计算它左边的表达式的值
- 返回值
- 赋值运算符的返回值是赋值后左操作数的引用
变量类型以及Macro constants
https://en.cppreference.com/w/cpp/language/types
https://en.cppreference.com/w/cpp/types/integer
//返回与平台相关的数值类型的极值
std::numeric_limits<double>::max()
std::numeric_limits<int>::min()
#include<limits.h>
INT_MAX
FLT_MAX (or DBL_MAX )
-FLT_MAX (or -DBL_MAX )
关键词
extern
const
constexpr //C++11引入的关键字,用于编译时的常量与常量函数。
volatile //是指每次需要引用某个变量的数据时,都必须从内存原地址读取,而不是编译器优化后寄存器间接读取.(必须写回内存,为了多进程并发而设计的。)
inline
static 关键字
static 作⽤:控制变量的存储⽅式和作用范围(可⻅性)。
- 修饰局部变量
- 存放位置:栈区 -> 静态数据区(data段或者bss段)
- 生命周期:程序结束才会释放
- 作用域:还是局部代码块
- 修饰函数与全局变量
- 使其作用范围由全工程文件可见变成了本文件可见
- 修饰类内函数
- 静态成员函数:使用"static"修饰的成员函数称为静态成员函数。静态成员函数与类的对象无关,可以在没有创建对象的情况下直接通过类名调用。这意味着它们不需要通过类的对象来访问,而是属于整个类的。举例
- 静态成员函数没有隐式的this指针,因此不能直接访问非静态成员变量和非静态成员函数。静态成员函数可以访问类的静态成员变量和其他静态成员函数。
- static 成员函数不能被 virtual 修饰, static 成员不属于任何对象或实例,所以加上 virtual没有任何实际意义;
- 静态成员函数没有 this 指针,虚函数的实现是为每⼀个对象分配⼀个vptr 指针,⽽ vptr 是通过 this 指针调⽤的,所以不能为 virtual;虚函数的调⽤关系,this->vptr->ctable->virtual function。
- 修饰类内的变量
- 存放位置:栈区 -> 静态数据区(data段或者bss段)
- 生命周期:程序结束才会释放
- 意味着下一次调用函数时,静态局部变量将保持上一次调用时的值。
- 由于不再属于某个类对象,可以直接通过类名初始化
int MyClass::staticVariable = 10;
多线程场景,修饰局部变量,会导致多线程共用,建议使用thread_local
来避免竞争
static
局部变量在第一次被访问时初始化,且初始化过程是线程不安全的。如果两个线程几乎同时首次访问这个变量,可能会导致初始化竞争,进而引发未定义行为。
static 修饰初始化命令,只会执行一次,无论是否多次经过
问题场景:
static auto thread_core_map = GetCpuAffinityMap(device_id);
即使程序多次调用函数经过这行,但是这行命令也只会执行第一次。但是如果我把static关键词去除,就正常执行多次了。
当在一行代码中使用了 static
关键字时,变量的初始化只会在它第一次被执行时进行,之后即使多次经过这行代码,初始化的代码块也不会被重复执行。
在例子中,thread_core_map
是一个静态局部变量,它在第一次经过时会调用 GetCpuAffinityMap(device_id)
函数并保存结果。在后续的函数调用中,即使再次经过这行代码,GetCpuAffinityMap(device_id)
不会被重新调用,因为 thread_core_map
已经被初始化过了。
去除 static
后,thread_core_map
会在每次经过这行代码时重新初始化,也就是每次都会调用 GetCpuAffinityMap(device_id)
。
解决方法:
如果你需要这行代码每次执行时都重新调用 GetCpuAffinityMap(device_id)
,那么应该去掉 static
关键字,或根据不同条件进行显式地重新初始化静态变量。
比如,可以这样实现惰性初始化或重置的功能:
static auto thread_core_map = GetCpuAffinityMap(device_id);
// 根据条件重新初始化
if (/* 条件 */) {
thread_core_map = GetCpuAffinityMap(device_id);
}
这样你就可以在特定条件下让 thread_core_map
被重新赋值。
如果你对静态变量的初始化行为没有问题,但是希望特定场景下重新执行初始化函数,可以根据场景调整条件逻辑。
静态成员函数
const 关键字
当const修饰基本数据类型时,可以将其放置在类型说明符的前面或后面,效果是一样的。const关键字用于声明一个常量,即其值在声明后不可修改。
当const关键字位于指针变量或引用变量的左侧时,它用于修饰指针所指向的变量,即指针指向的内容为常量。当const关键字位于指针变量或引用变量的右侧时,它用于修饰指针或引用本身,即指针或引用本身是常量。
-
修饰指针指向的变量, 它指向的值不能修改:
-
修饰指针本身 ,它不能再指向别的变量,但指向(变量)的值可以修改。:
-
const int *const p3;
//指向整形常量 的 常量指针 。它既不能再指向别的常量,指向的值也不能修改。
explicit
在C++, explicit 是一个关键字,用于修饰单参数构造函数,用于禁止隐式类型转换。
当一个构造函数被声明为 explicit 时,它指示编译器在使用该构造函数进行类型转换时只能使用显式调用,而不允许隐式的类型转换发生。
通过使用 explicit 关键字,可以防止一些意外的类型转换,提高代码的清晰性和安全性。它通常用于防止不必要的类型转换,特别是在单参数构造函数可能引起歧义或产生意外结果的情况下。
preprocessor directive
#include_next
的作用是 在寻找头文件时的头文件搜索优先级里,去除该文件所在的当前目录,主要是为C++头文件的重名问题提供一种解决方案。- 正确的用法:代码
b.cpp
想使用 自己拓展修改的stdlib.h
, 那么在代码的目录下创建stdlib.h
,并在该文件里#include_next "stdlib.h"
防止递归引用。
- 正确的用法:代码
define、 const、 typedef、 inline
- define:
- define是一个预处理器指令,用于创建宏定义。它在编译之前对源代码进行简单的文本替换。可以用来定义常量、函数宏和条件编译等。
- 优势:灵活性上占优,特别是在需要获取文件名、行号或控制编译(不同平台编译)时行为的场景
- 缺点:宏的调试和排错难度相对较高,因为宏的展开发生在编译前,出错时通常不容易直接定位到宏展开的具体代码。相比函数,宏的类型检查不严格,容易导致隐含的错误。
- 例如:
#define PI 3.14159
,在代码中将PI替换为3.14159。
- const:
- const用于声明一个常量,指示标识符的值在程序执行期间不能被修改。
- const可以用于变量、函数参数、函数返回类型和成员函数。使用const可以提高代码的可读性和安全性。
- 例如:
const int MAX_VALUE = 100;
,声明一个名为MAX_VALUE的常量。
- typedef:
- typedef用于为数据类型创建别名。它可以用于为复杂的数据类型提供更简洁的名称,增强代码的可读性和可维护性。
- typedef创建的别名可以像原始类型一样使用,并且不会引入新的类型,只是为已有类型提供了一个新的名称。
- 例如:
typedef int Age
;,为int类型创建了一个别名Age。
- inline:
- inline用于声明内联函数,它是一种编译器的建议,用于将函数的定义直接插入到调用处,以避免函数调用的开销。
- 内联函数通常在函数体较小且频繁调用的情况下使用,可以提高程序的执行效率。
- inline关键字只是给编译器一个提示,编译器可以选择忽略该提示。在大多数情况下,编译器会自动进行内联优化。
-
例如:
inline int add(int a, int b) { return a + b; }
,声明了一个内联函数add。 -
define主要用于宏定义,const用于声明常量,typedef用于创建类型别名,inline用于内联函数的声明。
#ifndef & #pragma once
为了避免同一个文件被include多次,C/C++中有两种方式,一种是#ifndef方式,一种是#pragma once方式。在能够支持这两种方式的编译器上,二者并没有太大的区别,但是两者仍然还是有一些细微的区别。
new & delete
- new和delete 相对于 malloc/free 分配和释放堆空间。
- 额外会执行构造函数和析构函数
#include <iostream>
class MyClass {
public:
MyClass() {
std::cout << "Constructing MyClass" << std::endl;
}
~MyClass() {
std::cout << "Destructing MyClass" << std::endl;
}
};
int main() {
// 使用new动态分配内存,并调用构造函数
MyClass* obj = new MyClass();
// 执行一些操作...
// 使用delete释放内存,并调用析构函数
delete obj;
return 0;
}
namespace
namespace 会影响 typedef
的作用范围,但不会直接限制 #define
宏的作用范围。
头文件
- 相互引用,前置声明
- include头文件其实就是将对应的头文件内容贴在include的位置
A.h, B.h 都需要string.h的头文件,然后B.h 会include A.h,那么我在B.h里是不是可以省略include string.h
不应该省略,
- 防止代码变更引发的问题: 如果某天 A.h 中移除了 #include
,而 B.h 依赖 A.h 提供的 #include ,那么 B.h 将会因找不到 std::string 而编译失败。因此,显式包含依赖的头文件可以避免这种隐含依赖引发的问题。 - 提高可读性和自包含性: 每个头文件应该尽量做到自包含,意思是每个头文件应该独立地包含所有它所需要的头文件。这样做的好处是,任何其他文件都可以安全地单独包含 B.h,而无需额外关心它依赖于哪些头文件。
- 减少隐式依赖: 隐式依赖(依赖另一个头文件帮你包含所需的头文件)可能导致维护性问题。显式 #include 可以让代码更具可预测性和可维护性。
include的位置有什么规则和规律吗,头文件和cpp文件前都可以吗?
在编写代码时,往往A.cpp需要include A.h。那A.cpp需要的头文件,我是写在A.cpp里还是A.h里?
- 头文件 (A.h):只包含声明所需的头文件,不包含仅在实现中需要的头文件。
- 源文件 (A.cpp):包含所有实现需要的头文件,特别是那些仅在实现部分用到的头文件。此外,A.cpp 应该总是包含 A.h。6.
函数的特殊写法
函数传参
- 值传递
- 引用传递
- 指针传递
//值传递
change1(n);
void change1(int n){
n++;
}
//引用传递,操作地址就是实参地址 ,只是相当于实参的一个别名,在符号表里对应是同一个地址。对它的操作就是对实参的操作
change2(n);
void change2(int &n){
n++;
}
//特殊对vector
void change2(vector<int> &n)
//特殊对数组
void change2(int (&n)[1000])
//指针传递,其实是地址的值传递
change3(&n);
void change3(int *n){
*n=*n+1;
}
引用传递和指针传递的区别:
- 引用被创建的同时必须被初始化(指针则可以在任何时候被初始化)。
- 不能有NULL引用,引用必须与合法的存储单元关联(指针则可以是NULL)。
- 一旦引用被初始化,就不能改变引用的关系(指针则可以随时改变所指的对象)。
指针传递和引用传递的使用情景:
- 函数内部修改参数并且希望改动影响调用者。
- 当一个函数实际需要返回多个值,而只能显式返回一个值时,可以将另外需要返回的变量以指针/引用传递
闭包、匿名函数
闭包是捕获并持有了外部作用域变量的函数。
闭包(Closure)是指在程序中,函数可以捕捉并记住其作用域(环境)中的变量,即使在函数执行完成后,这些变量依然保存在内存中,并能在后续的函数调用中被使用。闭包的一个重要特性是,它不仅保存了函数本身的逻辑,还“闭合”了函数执行时的上下文环境(即该函数所在的作用域)。
闭包通常用于实现函数内部的状态保持、回调函数等场景。在 C++ 中,闭包通过 lambda 表达式 实现,lambda 表达式可以捕获外部变量并在其内部使用。
例子
auto add = [](int x) {
return [x](int y) {
return x + y;
};
};
auto add5 = add(5);
std::cout << add5(3); // 输出 8
add
函数返回了一个闭包,捕获了变量 x
的值。即使 x
在原作用域中不再可用,返回的闭包仍然可以访问并使用 x
的值。
- 匿名函数是一种没有被绑定标识符的函数
- lambda 是一种匿名函数
- lambda 可以表示闭包
匿名函数(lambda)和闭包的关系就如同类和类对象的关系
匿名函数和类的定义都只存在于源码(代码段)中,而闭包和类对象则是在运行时占用内存空间的实体;
- Lambda在C++中的实现方式
类型变参模板
条件模板类
假设我们有一个模板类 Wrapper
,我们希望禁止 VirtualGuardImpl
类型作为模板参数:
template <
typename T,
typename U = T,
typename = typename std::enable_if<!std::is_same<U, VirtualGuardImpl>::value>::type>
class Wrapper {
public:
void function() {
// 实现
}
};
在这个例子中,如果用户尝试创建 Wrapper<VirtualGuardImpl>
或 Wrapper<VirtualGuardImpl, VirtualGuardImpl>
的实例,编译器将报错,因为 std::enable_if
的条件不满足。但如果使用其他类型,比如 int
或自定义类型,就可以正常编译。
这段代码是 C++ 中的一个模板函数或模板类模板参数的定义,它使用了模板默认参数、std::enable_if
条件编译技术以及类型萃取(type traits)。下面是对这段代码的详细解释:
-
模板参数
U
:typename U = T
定义了一个模板类型参数U
,并给它一个默认值T
。这意味着如果在使用模板时没有指定U
的话,它将默认使用模板参数T
的值。
-
std::enable_if
:std::enable_if
是一个条件编译技术,它只在给定的布尔表达式为true
时启用某个模板。- 在这个例子中,
std::enable_if
后面的布尔表达式是!std::is_same<U, VirtualGuardImpl>::value
。这意味着只有当U
不等于VirtualGuardImpl
类型时,这个模板参数才有效。
-
typename
关键字:typename
关键字用于告诉编译器std::enable_if
的结果是一个类型。std::enable_if
返回的是一个类型,如果条件为true
,它返回一个空的类型,否则会导致编译错误。
-
std::is_same
:std::is_same<U, VirtualGuardImpl>::value
是一个编译时检查,用于判断U
和VirtualGuardImpl
是否是相同的类型。::value
是类型特征std::is_same
的一个成员,它是一个布尔值,如果类型相同则为true
,否则为false
。
-
组合解释:
- 这段代码的意思是:定义一个模板参数
U
,默认值为T
,并且这个模板参数只有在U
不是VirtualGuardImpl
类型时才有效。 - 这是一种常见的模板编程技巧,用于约束模板参数的类型,以确保它们符合特定的要求。
- 这段代码的意思是:定义一个模板参数
个数变参模板
#include <stdarg.h>
void Error(const char* format, ...)
{
va_list argptr;
va_start(argptr, format);
vfprintf(stderr, format, argptr);
va_end(argptr);
}
VA_LIST 是在C语言中解决变参问题的一组宏,变参问题是指参数的个数不定,可以是传入一个参数也可以是多个;可变参数中的每个参数的类型可以不同,也可以相同;可变参数的每个参数并没有实际的名称与之相对应,用起来是很灵活。
(1)首先在函数里定义一具VA_LIST型的变量,这个变量是指向参数的指针; (2)然后用VA_START宏初始化变量刚定义的VA_LIST变量; (3)然后用VA_ARG返回可变的参数,VA_ARG的第二个参数是你要返回的参数的类型(如果函数有多个可变参数的,依次调用VA_ARG获取各个参数); (4)最后用VA_END宏结束可变参数的获取。
系统提供了vprintf系列格式化字符串的函数,用于编程人员封装自己的I/O函数。
int vprintf / vscanf (const char * format, va_list ap); // 从标准输入/输出格式化字符串
int vfprintf / vfsacanf (FILE * stream, const char * format, va_list ap); // 从文件流
int vsprintf / vsscanf (char * s, const char * format, va_list ap); // 从字符串
返回多个数
使用结构体
struct RowAndCol { int row;int col; };
RowAndCol r(string fn) {
/*...*/
RowAndCol result;
result.row = x;
result.col = y;
return result;
}
左值与右值
在 C++ 中,左值(lvalue) 和 右值(rvalue) 是两个重要的概念,用来描述表达式的值和内存的关系。它们帮助开发者理解变量的生命周期、赋值和对象管理,特别是在现代 C++ 中引入了右值引用后,优化了移动语义和资源管理。
1. 左值(lvalue)
左值(lvalue,locatable value) 是指在内存中有明确地址、可持久存在的对象,可以对其进行赋值操作。通俗地说,左值是能够取地址的值,可以出现在赋值操作符的左边。
特点:
- 左值具有持久的内存地址。
- 左值可以取地址(使用
&
运算符)。 - 左值通常表示已经存在的变量或对象。
示例
在这个例子中,x
是一个左值,因为它表示了内存中的某个对象,并且可以通过赋值语句修改它的值。
2. 右值(rvalue)
右值(rvalue,readable value) 是没有明确地址、临时存在的对象,不能对其进行赋值操作。它们通常是字面值常量或表达式的结果。右值只能出现在赋值操作符的右边,表示一个临时对象或数据。
特点:
- 右值是临时的,通常会在表达式结束时销毁。
- 右值不能取地址(即不能使用
&
获取右值的地址)。 - 右值表示表达式的计算结果或临时对象。
示例
在这个例子中,10
和 y + 5
是右值,因为它们表示计算出的临时数据,并且不能直接对这些值进行赋值操作。
3. 现代 C++ 中的右值引用(rvalue reference)
C++11 引入了 右值引用,即通过 &&
符号表示。这使得右值也能通过引用进行操作,特别是在实现移动语义(move semantics)和避免不必要的拷贝时非常有用。右值引用允许我们通过右值管理资源,避免性能上的损失。
示例:右值引用与移动语义
#include <iostream>
#include <vector>
int main() {
std::vector<int> vec1 = {1, 2, 3};
std::vector<int> vec2 = std::move(vec1); // vec1 资源移动到 vec2
std::cout << "vec1 size: " << vec1.size() << std::endl;
std::cout << "vec2 size: " << vec2.size() << std::endl;
return 0;
}
在这个例子中,std::move
将 vec1
变为一个右值引用,使其内部的资源(如动态分配的内存)直接转移给 vec2
,避免了拷贝。
4. 区分左值与右值
通常,左值是表示持久存在的对象,可以通过取地址符 &
获取其地址,而右值是临时的、短暂存在的值,不能直接获取其地址。理解这两者对于编写高效的 C++ 代码和使用现代特性(如右值引用和移动语义)非常重要。
常见误区
- 字面值常量(如 42、'a')是右值。
- 表达式的结果(如
x + y
)通常是右值。 - 函数返回值若返回的是值,而不是引用,则该返回值是右值。
- 左值(lvalue) 是可以取地址的值,通常是变量或持久的对象。
- 右值(rvalue) 是临时值,通常是表达式的结果或字面量。
- 右值引用(
&&
)是 C++11 引入的新特性,用来优化资源管理和避免不必要的拷贝操作。
C++11: 花括号初始化列表
使用
在C++98/03中我们只能对普通数组和POD(plain old data,简单来说就是可以用memcpy复制的对象)类型可以使用列表初始化,如下:
在C++11中初始化列表被适用性被放大,可以作用于任何类型对象的初始化。如下:
X x1 = X{1,2};
X x2 = {1,2}; // 此处的'='可有可⽆
X x3{1,2};
X* p = new X{1,2};
//列表初始化也可以用在函数的返回值上
std::vector<int> func() {
return {};
}
变量类型的适用范围
聚合类型可以进行直接列表初始化
聚合类型包括
- 普通数组,如int[5],char[],double[]等
- 一个类,且满足以下条件:
- 没有用户声明的构造函数
- 没有用户提供的构造函数(允许显示预置或弃置的构造函数)
- 没有私有或保护的非静态数据成员
- 没有基类
- 没有虚函数
- 没有{}和=直接初始化的非静态数据成员
- 没有默认成员初始化器
原理
对于一个聚合类型,使用列表初始化相当于使用std::initializer_list
对其中的相同类型T
的每个元素分别赋值处理,类似下面示例代码;
struct CustomVec {
std::vector<int> data;
CustomVec(std::initializer_list<int> list) {
for (auto iter = list.begin(); iter != list.end(); ++iter) {
data.push_back(*iter);
}
}
};
优势
- 方便,且基本上可以替代括号初始化
- 可以使用初始化列表接受任意长度
- 可以防止类型窄化,避免精度丢失的隐式类型转换
参考文献
-
⼩贺 C++ ⼋股⽂ PDF 的作者,电⼦书的内容整理于公众号「herongwei」
https://shaojiemike.notion.site/C-11-a94be53ca5a94d34b8c6972339e7538a
6 FPS
卡拉彼丘
- 信息差:发现分布以及落单对方,灵活跑动隐藏自身,干扰对方准备好时,对方换弹/倒地时补射。
- 躲避对方多枪线,己方架多枪线,多路线包围
- 一个位置偷一枪,就换位置。不要再露头。
- 对枪注意弦化
- 位置的选择:一要有掩体,二要有安全的退路通道和队友大部队汇合,不要被敌人包夹。
- 不要急于补人,要观察有没有被敌人包
- 进阶:时刻预瞄出人点,
- 弦化靠左墙,预瞄靠左,因为向右出掩体,准星会被向右移动。
- 学习弹道,反向压枪。
- 注意不要冲动,以身试陷(除非是突破位)
角色特点
- 熊当掩体(带闪光弹,烟雾弹),熊会自动冲锋并结冰
- 防守方
- 米雪儿:适合压制补枪,技能适合补枪。引诱敌方到背面炮台射程里
- 进攻方:
- 明:侦察 + 干扰器,风场雷
地图,高空卡墙脚。
- 欧拉港口/海湾图:复杂的短距离(掩体之间的距离)小路。适合白墨(带烟雾弹增加自身能力)和熊。白墨攻击走中间,抄底路偷对面的大狙。或者A点上上下下,适合近身跳散弹。
- 防守走A
- 404基地/巨炮图:白墨可以中路强压。
- 防守方 熊,进攻方沙猫无敌B
- 88区/古风图,大图远视野,适合大狙,大机枪。还有熊
- 禁止白墨。
- 风曳镇:大狙和小画家
- 防守必选熊(AB滑)和信(传送)
- 禁止白墨。
需要进一步的研究学习
暂无
遇到的问题
暂无
开题缘由、总结、反思、吐槽~~
参考文献
上面回答部分来自ChatGPT-3.5,没有进行正确性的交叉校验。
无
Scons
Scons
- SCons is a software construction tool that can be used as an alternative to traditional build systems like Make and CMake.
- It is a Python-based build tool that provides a convenient and flexible way to define and manage the build process for software projects, including C++ programs.
Scons VS cmake
- 基于python语言的构建工具,对开发者来说过度自然,简单,no need to learn domain-specific language like cmake
其余cmake有的, Scons 也有。
- cross-paltform,
- SCons has built-in support for dynamic dependency analysis, meaning it can automatically detect changes in source files and rebuild only what's necessary. This can result in faster builds for large projects.
Project structure
Sconstruct
python file as compile entry
framework grammar
- add option for
scons
command
AddOption('--buildDir',
dest='buildDir',
type='string',
default="build/",
# default=False,
nargs=1,
action='store', # meaning save the string
# or action='store', meaning True or false
metavar='DIR',
help='Base build directory'
)
baseBuildDir = GetOption('buildDir')
- add sub scons config file and build result path using
variant_dir
- achive debug mode
using scons debug=1
command.
main construct grammar
- Define the Build Environment:
In the
SConstruct
file, define the build environment by creating anEnvironment
object. You can specify compiler options, flags, include paths, library paths, and other build settings within this object.
env = Environment(CXX='g++', CCFLAGS=['-O2', '-Wall'], CPPPATH=['include'], LIBPATH=['lib'])
libEnv = env.Clone()
- Specify Source Files and Targets:
Define the source files for your C++ program and specify the target(s) you want to build using the
Program()
function.
source_files = ['main.cpp', 'util.cpp', 'other.cpp']
# or select the src files
Object('hello.cpp')
program = env.Program(target='my_program', source=source_files)
In this example, main.cpp
, util.cpp
, and other.cpp
are the source files, and my_program
is the name of the target executable.
- static or dynamic lib
# static
Library("t", Glob("src/*.cpp"))
# dynamic
source = Glob("src/*.cpp")
SharedLibrary("t", source)
Program(["hello.cpp"], LIBS=["t"], LIBPATH=".")
-
execute command during compilation
-
this is usually to print info
- The command is executed when any of the specified dependencies (
allSrcs, ".git/index", or "SConstruct"
) change.
env.Command(
target='bar.out',
source='bar.in',
action=["rm -f $TARGET", "$BAR_BUILD < $SOURCES > $TARGET"],
ENV={'PATH': '/usr/local/bin/'},
)
env.Command(
versionFile,
allSrcs + [".git/index" "SConstruct"],
'printf "#define ZSIM_BUILDDATE \\"`date "+%Y-%m-%d %T"`\\"\\n#define ZSIM_BUILDVERSION \\"`python misc/getver.py`\\"" >>' + versionFile)
Command
scons-project analysis
TODO: multipim how to add a singel head file during compilation process.
需要进一步的研究学习
暂无
遇到的问题
暂无
开题缘由、总结、反思、吐槽~~
参考文献
上面回答部分来自ChatGPT-3.5,没有进行正确性的交叉校验。
https://scons.org/doc/production/PDF/scons-man.pdf
LinuxFolderInstall
system folders structure
根据教程
/etc
:是 Etcetera(等等) 的缩写,这个目录用来存放所有的系统管理所需要的配置文件和子目录。- 这个是系统中的配置文件,如果你更改了该目录下的某个文件可能会导致系统不能启动。各种服务(ssh,apache2,nfs)的配置文件
/lib
:是 Library(库) 的缩写这个目录里存放着系统最基本的动态连接共享库,其作用类似于 Windows 里的 DLL 文件。几乎所有的应用程序都需要用到这些共享库。/opt
:是 optional(可选) 的缩写,这是给主机额外安装软件所摆放的目录。比如你安装一个ORACLE数据库则就可以放到这个目录下。默认是空的。/usr
:是 unix shared resources(共享资源) 的缩写,这是一个非常重要的目录,用户的很多应用程序和文件都放在这个目录下,类似于 windows 下的 program files 目录。It's install for all user./var
:是 variable(变量) 的缩写,这个目录中存放着在不断扩充着的东西,我们习惯将那些经常被修改的目录放在这个目录下。包括各种日志文件。
sudo install path
TODO: during the application install, which locations those app used?
I guess it's usr/bin or /include
, opt
and /lib
- GNU G++
- cuda
- intel one-api
需要进一步的研究学习
暂无
遇到的问题
暂无
开题缘由、总结、反思、吐槽~~
参考文献
上面回答部分来自ChatGPT-3.5,没有进行正确性的交叉校验。
无
0 Overview
工作外的内容聚焦于几个方面:
- 家庭、环境和高雅娱乐
- 额外职业技能的培养
- 额外的计算机技术的使用(网络和主页网站配置)
- 各种设备(主要是科技设备)
- 影视相关
- 游戏相关
需要进一步的研究学习
暂无
遇到的问题
暂无
开题缘由、总结、反思、吐槽~~
参考文献
上面回答部分来自ChatGPT-3.5,没有进行正确性的交叉校验。
无
3.3 EfficientWorkLearning
科研工作的目标
- 从底层的优化做起,自底向上的。对整个AI训练的难点有个全面细致的认知。
- 或者从应用的问题出发建模,从上到下有个全面的认知。
- 往架构师的方向努力
淡而不厌,简而文,温而理,知远之近,知风之自,知微之显,可与人德矣。
科研学习
狠狠吸收
华为实习部门的都是手握A会的博士大佬。本人望尘莫及,我会狠狠吸收的。
科研的长远与犀利的眼光
- 如何识别伪装信息 和被包装的工作
- 明确理论目标上限,当前的差距,实现路径
研究理解与实现
- 跨领域知识:公众号,订阅
- follow前沿论文:follow学者, 公众号,
- 实际问题、需求的发现
- 技术论证:理论上限,可行方案
- 独立任务分割
- 高效实现(解决问题)
科研工作的全局观念
- 自顶向下的设计规划、工作全局观 (从目标、需求、愿景出发。不断细化实现点)
- 顶:论文全流程思路图(构思与其余工作的对比):当前实现和问题、兼顾创新性的方法 和 设计目标期望效果。
- 中:代码实现逻辑框架图
- 底:代码语言具体实现:高内聚低耦合的重要性,解耦,拆分,这样就容易重构了。独立的微服务
- 自底向上的知识积累 与研究方法提出
- 底:领域的基础知识
- 中:领域的主流方法和解决方案
- 顶:行业痛点和有待补完的领域空白。
- 当两者相联,目标才能顺利的达成,
高效学习的过程中注意点
- 学习的优先级:用20%时间先掌握80%的基础知识或者感兴趣的关键
- 难的问题可以讨论合作得出
- 提问式主动学习:不是被动学习,尝试通过提问、讨论、教授他人等方式来加深理解和巩固所学内容。
- 合作学习: 不仅能头脑风暴idea。对概念的理解,表达能力,心理健康有好处
- 交叉与分割学习: 概念文字、视频和案例分析交叉理解。长时间执行单一学习会枯燥,效率降低。切换学习一些新鲜东西:每日关注LLM的有趣实现。
- 理论实践交融:实际运行或者编写测试代码运行来深入理解
- 持续反馈与评估:每天每周对学习的进度和效果进行分析、来调整学习计划和研究方向。可以遵循STAR 法则。
具体研究点的克服
体系结构量化分析方法,重点就在于量化分析开销,比较然后进行tradeoff。当前前提是你要有基本的相关概念。
具体知识来源的优先级,或者说如何使用搜索引擎:
- 明白原理,设计实验,实际机器测量
- 认知概念,理解 (图解 >> 列表对比 >> 文字list >> 大段描述)
- 阅读相关的论文以及书籍
- 国内大佬的博客和大论文
- 国外论坛Stack Overflow > 国内知乎 > 博客园 > csdn > 其他
- 资料的来源(论文 >> 官方文档 >> 英文博客 >> 高质量中文资料)
在理解概念,量化了具体场景的数值后,就可以开心进行tradeoff了。
- 思维导图、摘要,来理清概念 和思路
- 结合PPT 数据与图表展示效果
注意项目的可读性和可拓展性一般与性能是不兼容的。这取决于项目的checkpoint/middleValue的保存,在性能优化时往往会消除中间变量。这样会导致代码的可读性和可拓展性下降。
check-point的合理设置
- 合理的检查点,既是阶段性的成果,又能在此衍生出无限的可能
- 需要能高效的复现与重构
关于如何解决困难
困难的定义可以基于以下几个要素进行评估:
- 个人技能能力:困难的程度可以取决于个人所具备的技能和能力水平。对于一个人来说,某项任务可能很容易,而对另一个人来说可能很困难,这取决于他们的专业知识、经验和技能。如果一个人已经具备了必要的知识和技能,那么他们可能更容易应对困难任务。相反,如果缺乏必要的知识和技能,任务就会更具挑战性。
- 任务量评估:任务的规模和复杂性也是评估困难程度的重要因素。任务量的多少以及任务本身的复杂性(比如需要解决的问题、涉及的步骤等)会对困难程度产生影响。
- 多人合作:效率会由于沟通同步而减半
- 量化分析加深理解:对于某些任务,特别是涉及复杂问题解决或决策制定的情况,进行量化分析可以加深对问题本质的理解。这种理解的深度也会对困难程度产生影响,因为解决关键核心会对整个任务的理解的评估进行重大修正。
- 对未知领域的任务量评估,会随着了解而变得准确。(这不是产品经理的工作吗?
- 时间的紧迫程度:完成任务所要求的时间紧迫程度也是评估困难程度的因素之一。如果任务需要在很短的时间内完成,那么它可能会被认为是更具挑战性和困难的。
参考文献
上面回答部分来自ChatGPT-3.5,没有进行正确性的交叉校验。
无