常用的命令及快捷键
本文最后更新于 2024年10月17日 上午
本文主要分享了自己在使用Linux和Windows系统及各种软件过程中常用的命令及快捷键。
注意中文
—
和英文字符-
Linux
Win = Super
快捷键
一般
快捷键 | 作用 |
---|---|
Win+L | 锁屏 |
Ctrl+Alt+T | 新建终端 |
Ctrl+Alt+A(自定) | flameshot 截图 |
Ctrl+Alt+F(自定) | fsearch 搜索文件 |
Ctrl+Shift+N | 新建文件夹 |
Win+I(自定义) | 打开设置中心 |
Ctrl+L | 全选文件夹路径 |
F2 | 文件(夹)重命名 |
Win | 活动大纲 |
Win+Tab/Alt+Tab | 切换应用程序 |
Ctrl+Alt+上/下箭头,点击桌面左上角的“Activities” | 切换工作区(同一屏幕的全部内容) |
Alt+Enter | 查看选择文件/文件夹的属性,代替单击右键选择属性 |
Ctrl+1/2 | 改变文件夹视图查看方式,图标视图/列表视图 |
Ctrl+H | 显示隐藏文件 |
双击Ctrl(自定) | 打开uTools |
启动位于任务栏的程序 | Win + 数字键 |
窗口
快捷键 | 作用 |
---|---|
Win+E(自定义) | 新建文件夹窗口 |
win+H | 隐藏窗口 |
Win+D | 隐藏或显示全部应用窗口 |
Win+A | 显示应用程序菜单 |
Super+箭头 | 移动窗口位置(左/右:贴左/右;上:最大化;下:恢复) |
Ctrl+W | 关闭标签页 |
Ctrl+Q/Alt+F4 | (强制)关闭应用程序(窗口)。ALT+F4是基于任务管理器连接结束进程项,CTRL+Q只是关闭的快捷键 |
Alt+空格(自定) | 激活窗口菜单(内含置顶选项,也适用于没有系统标题栏的应用) |
Win+Shift+向上/下翻页 | 将窗口上/下移一个工作区 |
Win+Shift+上/下/左/右箭头 | 将窗口上/下/左/右移一个显示器 |
Alt+F7(自定义) | 使用键盘方向键移动窗口,按住Shift键快速贴边,按Enter键确认 |
目录
快捷键 | 作用 |
---|---|
Backspace | 返回上一级目录 |
Alt+左箭头 | 回退(搭配Backspace回到下一级目录) |
Alt+右箭头 | 前进(只能响应回退操作) |
打开文件夹之后,按backspace删除键就可以返回上一级;或者Alt+←也是可以返回上一级,用Alt+←返回上一级后,按Alt+→键就可以倒退回去一级。
鼠标
- 直接拖拉在最大化时的窗口标题可以还原窗口大小,再拖放回去就最大化。
- 在音量控制处用鼠标滚轮滚动可以直接调整音量,在任务栏中滚动滚轮可以切换任务栏,工作区则切换工作区。
- 工作区里的小窗口可以在不同工作区内拖拉,即移动到另一个工作区的功能。
- 按住
Shift
拖动窗口可以吸附上下边缘的移动。 - 水平滚动文件,请在按住
Shift
的同时转动鼠标滚轮。 - 双击选中词,三击选中整句。
- 按住Alt,鼠标可以选中带链接的内容(而不打开链接)。
- 按住Shift,鼠标在开头和结尾点击,可以选中全文(不用拖动了)。
- 拖动选中可直接完成复制粘贴的操作(对于某些禁止复制粘贴的网站)。
- 在Win11,按住Shift,点击鼠标右键,呼出旧版菜单。
- 鼠标中间有快捷关闭的功能。
路径
-
对于在
.zshrc
等文件中使用export
命令时,举例说明:1
2
3
4
5export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/opt/ros/melodic/lib:/path/else
export PATH=/home/uusama/mysql/bin:$PATH
# 例如新增LD_LIBRARY_PATH,注意
# 1. 使用$LD_LIBRARY_PATH来引用前面定义的LD_LIBRARY_PATH,防止后面的覆盖掉了前面的。
# 2. 路径之间用英文:分隔,中间不需要加空格。系统查找命令时按照冒号分割顺序从前向后依次查找。
配置路径的文件:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19sudo updatedb # 更新数据库
sudo gedit ~/.bashrc
source ~/.bashrc
sudo gedit ~/.zshrc
source ~/.zshrc
sudo gedit /etc/profile # 此文件涉及系统的环境,即环境变量相关。这里修改会对所有用户起作用。
# 不能对zsh直接生效?需要在命令行里运行一遍source ~/.zshrc才能生效。
source /etc/profile # 让环境变量生效
sudo gedit /etc/ld.so.conf.d/opencv.conf
sudo ldconfig # 运行配置
sudo gedit /etc/ld.so.conf
sudo ldconfig # 运行配置
sudo gedit /etc/bash.bashrc
source /etc/bash.bashrc -
Ubuntu快捷方式图标的存放位置:
/usr/share/applications
/usr/share/icons/Mojave-CT-Night/mimes/scalable
-
打开回收站:
1
cd ~/.local/share/Trash/files
-
wine软件安装路径:
/home/lll25655/.wine/drive_c/'Program Files (x86)'
-
打开eDiary:
1
wine /home/lll25655/.wine/drive_c/'Program Files (x86)'/eDiary-4.2.2-win64/eDiary.exe
-
第三方库库默认安装路径
-
/usr/include
-
/usr/local/include
-
-
字体安装路径:
/usr/share/fonts
小贴士
- 直接在当前文件夹窗口打字,可以搜索该目录及子目录下的文件(夹)。按
ESC
键退出。 Application
键即位置在键盘上右Ctrl
键左边的那个键,作用相当于单击鼠标右键。- 误按
Ctrl+Alt+F7
(进入文字界面)黑屏后按Ctrl+Alt+F1
回到图形界面。 - 直接将"文件管理器"中的文件拖到"终端"中就可以在终端中得到完整的路径名。
终端Terminal
Linux命令
软件、库的安装、卸载和升级
1 |
|
优先级:
- < 0:永远不会安装,
- 1..99:仅当尚未安装其他版本的软件包时才会安装,
- 100..499:仅当其他发行版中没有安装或提供其他更新版本时才会安装,
- 500....989:仅当目标发行版中没有安装或没有更新的版本时才会安装,
- 990..1000:将安装,除非安装的版本较新,
- > 1000:将始终安装,即使它强制 APT 降级到旧版本。
默认情况下,每个已安装的软件包版本的优先级为 100,未安装的软件包的优先级为 500。同一软件包可能有多个不同优先级的版本。APT 会安装优先级较高的版本,除非安装的版本较新。
查找查看
辅助搭配
Linux
grep
(global regular expression) 命令用于查找文件里符合条件的字符串或正则表达式。
1
ls -l /usr/local | grep cuda # 查询`/usr/local`路径下的软链接,且要求包括`cuda`字符串。
常用选项::
-i
:忽略大小写进行匹配。-v
:反向查找,只打印不匹配的行。-n
:显示匹配行的行号。-r
:递归查找子目录中的文件。-l
:只打印匹配的文件名。-c
:只打印匹配的行数。
当前终端
1 |
|
环境变量
1 |
|
内存
1 |
|
文件系统和挂载点
1 |
|
文件(夹)大小
1 |
|
文件(夹)修改时间
1 |
|
版本信息
1 |
|
用户手册
Linux man 命令是 "manual" 单词的缩写,用于查看各种命令、函数和配置文件的手册页面。
1 |
|
查找文件
1 |
|
目录列表
1 |
|
文件内容
1 |
|
查找过去编辑的文件
1 |
|
历史命令
1 |
|
文件新建、复制、移动、重命名和删除
基本
处理(特别是包含众多小文件的)大文件夹推荐使用[rsync](# rsync)命令。
1 |
|
rsync
基本语法
1 |
|
-a
,--archive
: 归档模式,表示以递归的方式传输文件,并保持所有文件属性不变--delete
: 删除那些target中有而source没有的文件--exclude
: 指定排除不进行同步的文件,比如--exclude="*.iso"
。-h
: 以人类可读的格式输出。--progress
: 显示进度-v
: 详细输出模式
用法
rsync
命令不仅可以传输文件夹,还可以传输单个文件。
复制/移动大文件夹
本机使用 rsync 命令时,可以作为cp
和mv
命令的替代方法,将源目录同步到目标目录。
1 |
|
删除大文件夹
在删除包含许多小文件的大文件夹时,可以考虑使用rsync
命令,以提高删除速度。以下是具体步骤:
- 使用
rsync
将文件夹同步为空目录,达到快速删除文件的效果。 - 删除空目录。
具体命令如下:
1 |
|
请替换/path/to/your/folder/
为你要删除的文件夹的实际路径。
增量备份
rsync 的最大特点就是它可以完成增量备份,也就是默认只复制有变动的文件。
除了源目录与目标目录直接比较,rsync 还支持使用基准目录,即将源目录与基准目录之间变动的部分,同步到目标目录。
具体做法是,第一次同步是全量备份,所有文件在基准目录里面同步一份。以后每一次同步都是增量备份,只同步源目录与基准目录之间有变动的部分,将这部分保存在一个新的目标目录。这个新的目标目录之中,也是包含所有文件,但实际上,只有那些变动过的文件是存在于该目录,其他没有变动的文件都是指向基准目录文件的硬链接。
--link-dest
参数用来指定同步时的基准目录。
1 |
|
上面命令中,--link-dest
参数指定基准目录/compare/path
,然后源目录/source/path
跟基准目录进行比较,找出变动的文件,将它们拷贝到目标目录/target/path
。那些没变动的文件则会生成硬链接。这个命令的第一次备份时是全量备份,后面就都是增量备份了。
下面是一个脚本示例,备份用户的主目录。
1 |
|
上面脚本中,每一次同步都会生成一个新目录${BACKUP_DIR}/${DATETIME}
,并将软链接${BACKUP_DIR}/latest
指向这个目录。下一次备份时,就将${BACKUP_DIR}/latest
作为基准目录,生成新的备份目录。最后,再将软链接${BACKUP_DIR}/latest
指向新的备份目录。
mkcd
将mkdir
和cd
命令合并为一个mkcd
命令:
1 |
|
这个函数接受一个参数($1
),首先使用mkdir -p
命令创建一个目录,然后使用cd
命令进入这个目录。-p
选项可以确保如果目录已经存在,mkdir
命令不会报错。
你可以将这个函数添加到你的bash配置文件(如~/.bashrc
或~/.bash_profile
)中,这样每次打开一个新的终端时,这个函数都会被定义。
使用这个函数的方式如下:
1 |
|
这将创建一个名为new_directory
的新目录,并立即进入这个目录。
下载
测试网络连接:
1ping www.baidu.com
wget
1 |
|
下载超时,尝试添加参数
--no-cookie --no-check-certificate
。
curl
1 |
|
目录(跳转)
1 |
|
-
把目录结构信息保存到文本中,树状图:
1
tree > /home/luke/tree.txt
-
在终端打开文件管理器:
nautilus
或xdg-open
[空格]目标路径。1
2
3
4
5nautilus /path/to/directory # or
xdg-open /path/to/directory
# 打开当前终端所在目录
nautilus . # or
xdg-open .
压缩解压缩
zip
1 |
|
tar
.tar.gz = .tgz
1 |
|
-c
: 创建新的归档文件-z
: 使用 gzip 压缩归档文件-v
: 显示详细输出,列出被添加到归档中的文件-f
: 指定归档文件的名称
tar.xz
1 |
|
xz的参数:
-z, --compress
: 强制压缩-d, --decompress
: 强制解压-k, --keep
: 保留(不删除)输入文件
pigz
1 |
|
设置文件权限
chmod命令的语法
指令名称:chmod
使用权限:所有使用者
使用方式:chmod [-cfvR] [--help] [--version] mode file...
参数格式:
u
:表示该档案的拥有者。g
:表示与该档案的拥有者属于同一个群体(group)者。o
:表示其他以外的人。a
:表示这三者皆是。+
:表示增加权限。-
:表示取消权限。=
:表示唯一设定权限。r
:表示可读取。w
:表示可写入。x
:表示可执行。X
:表示只有当该档案是个子目录或者该档案已经被设定过为可执行。R
:对目前目录下的所有文件与子目录进行相同的权限变更(即以递归的方式逐个变更)
1 |
|
此外chmod
也可以用数字来表示权限,语法为:chmod abc file
,其中a,b,c各为一个数字,分别表示User、Group、及Other的权限。
一般是三个数字:
- 第一个数字表示文件所有者的权限。
- 第二个数字表示与文件所有者同属一个用户组的其他用户的权限。
- 第三个数字表示其它用户组的权限。
权限分为三种:读(r=4),写(w=2),执行(x=1)。综合起来还有可读可执行(rx=5=4+1)、可读可写(rw=6=4+2)、可读可写可执行(rwx=7=4+2+1)。
所以,chmod 755
设置用户的权限为:
- 文件所有者可读可写可执行
- 与文件所有者同属一个用户组的其他用户可读可执行
- 其它用户组可读可执行
777就是rwxrwxrwx,意思是该登录用户(可以用命令id查看)、他所在的组和其他人都有最高权限。
chmod 4755
与chmod 755
的区别在于开头多了一位,这个4表示其他用户执行文件时,具有与所有者相当的权限。具有root的权限。
chmod a=rwx file
和chmod 777 file
效果相同。
chmod ug=rwx,o=x file
和chmod 771 file
效果相同。
1 |
|
root用户把某个文件权限给到普通用户
1 |
|
硬盘
挂载硬盘
1 |
|
其中,
/dev/sdX1
是U盘的设备名,你可以使用lsblk
或fdisk -l
命令来查找U盘的设备名。例如,插入硬盘前后,使用
lsblk
发现新增:
1
2sdc 8:32 0 7.3T 0 disk
└─sdc1 8:33 0 7.3T 0 part
硬盘类型
在Linux系统上,可以使用以下命令来查看硬盘是机械硬盘(HDD)还是固态硬盘(SSD):
1 |
|
将sdX
替换为硬盘的设备名,比如sda
。如果输出是1
,则表示是机械硬盘;如果输出是0
,则表示是固态硬盘。
除了查看/sys/block
目录下的信息外,你还可以使用lsblk
命令结合-o
选项来获取更多详细信息,包括硬盘类型:
1 |
|
lsblk
列出所有块设备。-d
选项只显示磁盘本身,不显示其分区。-o
选项指定要显示的列,其中name
表示设备名,rota
表示是否是旋转设备(0表示固态硬盘,1表示机械硬盘)。
通过这种方式,你可以在一个命令中看到所有硬盘的类型。
小贴士
Linux顺序执行多行命令
- 分号
;
- 没有任何逻辑关系的连接符。当多个命令用分号连接时,各命令之间的执行成功与否彼此没有任何影响,都会一条一条执行下去。
- 逻辑或
||
- 当用此连接符连接多个命令时,前面的命令执行成功,则后面的命令不会执行。前面的命令执行失败,后面的命令才会执行。
- 逻辑与
&&
- 当用此连接符连接多个命令时,前面的命令执行成功,才会执行后面的命令,前面的命令执行失败,后面的命令不会执行,与
||
正好相反。
- 当用此连接符连接多个命令时,前面的命令执行成功,才会执行后面的命令,前面的命令执行失败,后面的命令不会执行,与
- 管道符
|
- 当用此连接符连接多个命令时,前面命令执行的正确输出,会交给后面的命令继续处理。若前面的命令执行失败,则会报错,若后面的命令无法处理前面命令的输出,也会报错。
在Linux中使用AppImage
-
使其可执行。右键-属性-权限-允许将文件作为程序执行;或,命令行执行:
1
2
3
4chmod u+x <AppImage File>
# u user 文件所有者
# + 为指定的用户类型增加权限
# x 执行权限 设置为可执行权限 -
运行 AppImage 文件。使 AppImage 文件可执行后,只需双击它即可运行它。它将看到该软件正在运行,就像您在系统上安装它一样;或,命令行执行:
1
./app.appimage # sudo
-
卸载 AppImage 软件。由于从未安装过该软件,因此无需“卸载”它。只需删除关联的 AppImage 文件,您的软件就会从系统中删除。
其它
-
Linux diff 命令用于比较文件的差异。
diff
以逐行的方式,比较文本文件的异同处。如果指定要比较目录,则diff
会比较目录中相同文件名的文件,但不会比较其中子目录。 -
指令需要root权限,要么命令前加
sudo
进行提权,要么以root
身份执行。1
2
3
4
5
6
7
8
9
10
11
12
13
14# Ubuntu 首次进入 root 用户模式
# 开启一个新的终端
# 设置 root 用户密码(Linux下输入的密码默认不会显示出来)
sudo passwd root
# 输入一个新的密码: yourpassword
# 请在确认一遍密码: yourpassword
# 切换到root用户
su root
# 输入刚刚设置的密码
# 退出root用户
exit -
$
表示普通用户,#
表示root用户。 -
定义环境变量:
1
2sudo gedit ~/.bashrc
export OPENAI_API_KEY={Your OpenAI API Key here} -
timeout
是用来控制程序运行的时间,运行指定的命令。如果在指定时间后仍在运行,则杀死该进程。1
2
3
4
5timeout 5s ping www.baidu.com # 5分钟之后终止ping操作
s : 秒 (默认)
m : 分钟
h : 小时
d : 天
命令行解释器Shell
终端负责是从用户这里接收输入(键盘、鼠标等输入设备),扔给 Shell,然后把 Shell 返回的结果展示给用户(比如通过显示器)。而 Shell 负责从终端那里拿到用户输入的命令,解析后交给操作系统内核去执行,并把执行结果返回给终端。
Bash
快捷键 | 作用 |
---|---|
Tab | zsh自动补全 |
Ctrl+C | 终止进程/命令 |
Ctrl+V | 插入特殊字符。当你按下Ctrl+V后,终端会等待你输入下一个字符,然后将其ASCII值插入到当前位置。例如,如果你按下Ctrl+V然后按Enter,终端将插入一个表示换行的特殊字符。 |
Shift+Ctrl+C | 复制 |
Shift+Ctrl+V | 粘贴 |
Shift+Ctrl+F | 查找 |
Shift+Ctrl+T | 新建标签页 |
Shift+Ctrl+N | 新建同路径窗口 |
Shift+Ctrl+W | 关闭标签页 |
Shift+Ctrl+Q | 关闭窗口 |
Ctrl+A | 光标移动到开始位置 |
Ctrl+E | 光标移动到最末尾 |
Ctrl+K | 删除此处至末尾的所有内容 |
Ctrl+U | 删除此处至开始的所有内容 |
Ctrl+W | 当在控制台或一个xterm窗口敲入文本时, CTRL+W 会删除从在光标处往后(回)的第一个空白符之间的内容。在某些设置里, CTRL+W 删除光标往后(回)到第一个非文字和数字之间的字符。 |
Ctrl+L | 相当于clear,即清屏 |
Ctrl+Y | 将之前已经清除的文本粘贴回来(主要针对CTRL+U或CTRL+W)。 |
Ctrl+S | 冻结终端 |
Ctrl+Q | 解冻终端 |
Ctrl+T | 将光标位置的字符和前一个字符进行位置交换 |
Terminator
更方便的终端,区别于默认GNOME终端。
快捷键
快捷键 | 作用 |
---|---|
Ctrl+Shift+E | 垂直分割窗口 |
Ctrl+Shift+O | 水平分割窗口 |
F11 | 全屏 |
Ctrl+Shift+C | 复制 |
Ctrl+Shift+V | 粘贴 |
Shift+Ctrl+W | 关闭(当前)终端 |
Shift+Ctrl+Q | 关闭窗口 |
Ctrl+Shift+N/Ctrl+Tab | 在分割的各窗口之间切换 |
Alt+Up/Down/Left/Right | 移动到上/下/左/右边的终端 |
Ctrl+Shift+Up/Down/Left/Right | 在水平/垂直分割的终端中将分割条向上/下/左/右移动 |
Ctrl+Shift+X | 将分割的某一个窗口放大至全屏使用 |
Ctrl+Shift+Z | 从放大至全屏的某一窗口回到多窗格界面 |
选中即复制,鼠标中键粘贴。对于单词,双击即可选中,三击选中一行。
配置
修改打开的窗口的默认大小:
1 |
|
修改 layouts - default - window0 - size(没有就新增)(对应的是像素值)
1 |
|
保存,关闭文件。关闭所有终端后,再重新打开就应用更改了。
zsh
特性
- 色彩高亮:不同的颜色表明当前命令的类型,并且路径有无下划线表示路径是否存在。
- 智能补全:在使用cd切换路径时,按下tab会列出当前目录下的目录和文件,如果是bash,它会提示你手动输入,但是zsh中你可以继续按一下tab进入选择模式,继续使用tab选择,或者使用方向键选择目标目录而不需手动输入。
- 按键盘右方向键
->
直接确定整个补全提示。Ctrl+->
按整词顺序依次确定。 d
命令“回车”后,会列出我们最近进入的目录历史,并且会给这些目录加上序号,只需要输入对应目录的序号,即可重新进入该目录。
插件
路径:~/.oh-my-zsh/plugins
1 |
|
-
zsh-autosuggestions————命令提示/补全。提示和补全有不同实现机制,好用之处在于补全,输入命令会根据输入的历史自动补全,并且随着输入不断修正,如果补全是你期望的结果,按下右方向键接受,再回车即可。
如果你用缓冲区末尾的光标按下→ key (forward-char widget)或 End (End-of-line widget) ,它就会接受这个建议,用这个建议替换命令行 buffer 的内容。
配置:
-
当您键入命令时,您将看到在光标之后提供的一个柔和的灰色的完成。可以通过设置
ZSH_AUTOSUGGEST_HIGHLIGHT_STYLE
变量来更改此颜色。默认值是
fg = 8
,它将从256色调色板中将前景色设置为8色。如果您的终端只支持8种颜色,您将需要使用一个介于0和7之间的数字。还可以设置背景颜色,建议可以设置粗体、下划线或突出。例如,这将在青色背景上显示带有粗体、下划线、粉红色文字的建议:
1
ZSH_AUTOSUGGEST_HIGHLIGHT_STYLE="fg=#ff00ff,bg=cyan,bold,underline"
-
等等。
-
-
autojump:这个插件会自动统计我们经常cd的目录,不同目录会有不同的权重。在我们想要进入某个目录时,使用
j <dir-name>
即可以帮助我们快速跳转到目标目录。 -
extract:将各种解压命令集合成使用
x <archived file>
来提取压缩文件。 -
sudo:双击两次
ESC
自动在句首添加sudo
。
报错
-
zsh: corrupt history file /home/XXX/.zsh_history
1
2
3
4
5
6
7
8
9# 解决:
cp .zsh_history zsh_history
rm -f .zsh_history
strings zsh_history .zsh_history
# 或
cd ~
mv .zsh_history .zsh_history_bad
strings .zsh_history_bad > .zsh_history
fc -R .zsh_history -
等等。
Powerlevel10k
下面都是以Oh My Zsh框架为例,其它方式详见Powerlevel10k官方文档。
安装
-
克隆仓库:
1
git clone --depth=1 https://github.com/romkatv/powerlevel10k.git ${ZSH_CUSTOM:-$HOME/.oh-my-zsh/custom}/themes/powerlevel10k # for Oh My Zsh
-
Set
ZSH_THEME="powerlevel10k/powerlevel10k"
in~/.zshrc
.
(重新)配置:
1 |
|
配置向导根据您的偏好创建 ~/.p10k.zsh
。可以通过编辑此文件来完成其他提示自定义。它有大量注释可以帮助您浏览配置选项。
例如:
-
配置终端路径显示层数:
1
2
3
4
5
6
7
8
9
10
11# If set to "first" ("last"), remove everything before the first (last) subdirectory that contains
# files matching $POWERLEVEL9K_SHORTEN_FOLDER_MARKER. For example, when the current directory is
# /foo/bar/git_repo/nested_git_repo/baz, prompt will display git_repo/nested_git_repo/baz (first)
# or nested_git_repo/baz (last). This assumes that git_repo and nested_git_repo contain markers
# and other directories don't.
#
# Optionally, "first" and "last" can be followed by ":<offset>" where <offset> is an integer.
# This moves the truncation point to the right (positive offset) or to the left (negative offset)
# relative to the marker. Plain "first" and "last" are equivalent to "first:0" and "last:0"
# respectively.
typeset -g POWERLEVEL9K_DIR_TRUNCATE_BEFORE_MARKER=first:-1 # default false默认显示全路径,现在配置成了显示倒数两个文件夹。
更新
-
更新仓库:
1
git -C ${ZSH_CUSTOM:-$HOME/.oh-my-zsh/custom}/themes/powerlevel10k pull # for Oh My Zsh
-
更新 Powerlevel10k 后重新启动 Zsh。不要使用
source ~/.zshrc
。
卸载
-
从
~/.zshrc
中删除所有对“p10k”的引用。您可能会在顶部看到此片段:1
2
3if [[ -r "${XDG_CACHE_HOME:-$HOME/.cache}/p10k-instant-prompt-${(%):-%n}.zsh" ]]; then
source "${XDG_CACHE_HOME:-$HOME/.cache}/p10k-instant-prompt-${(%):-%n}.zsh"
fi底部是这样的:
1
[[ ! -f ~/.p10k.zsh ]] || source ~/.p10k.zsh
这些是由配置向导添加的。删除它们。
-
从
~/.zshrc
、~/.zpreztorc
和~/.zimrc
中删除所有对“powerlevel10k”的引用(其中一些文件可能会丢失 - 这是正常的)。这些引用是您在安装 Powerlevel10k 时手动添加的。如果需要提醒,请参阅安装说明。 -
验证所有对“p10k”和“powerlevel10k”的引用均已从
~/.zshrc
、~/.zpreztorc
和~/.zimrc
中消失:1
grep -E 'p10k|powerlevel10k' ~/.zshrc ~/.zpreztorc ~/.zimrc 2>/dev/null
如果此命令产生输出,则仍然引用“p10k”或“powerlevel10k”。你需要删除它们。
-
删除Powerlevel10k配置文件。该文件由配置向导创建,可能包含您自己的手动编辑。
1
rm -f ~/.p10k.zsh
-
删除Powerlevel10k源文件。这些文件已在您安装 Powerlevel10k 时下载。删除它们的命令取决于您选择的安装方法。如果需要提醒,请参阅安装说明。
1
rm -rf -- ${ZSH_CUSTOM:-$HOME/.oh-my-zsh/custom}/themes/powerlevel10k # for Oh My Zsh
-
重新启动 Zsh。不要使用
source ~/.zshrc
。 -
删除 Powerlevel10k 缓存文件:
1
rm -rf -- "${XDG_CACHE_HOME:-$HOME/.cache}"/p10k-*(N) "${XDG_CACHE_HOME:-$HOME/.cache}"/gitstatus
-
完成。
导出配置文件
bash
1 |
|
zsh
1 |
|
gnome-terminal
1 |
|
报错:
-
错误:密钥文件不以组开头
-
原因: .dconf 文件的引导行是问题所在:
default='...'
-
解决:用这个替换引导线似乎使它可以加载:
[/]
因此,如果它有帮助,您尝试加载的文件应具有如下所示的格式。请注意标题中的列表项与下面的条目一一对应。
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[/]
list=['22cb430e-3fde-42cb-a167-cc35a6b788cc', '4b1f055e-9bc4-4b58-bb12-eb5657db78fc']
[:22cb430e-3fde-42cb-a167-cc35a6b788cc]
audible-bell=false
background-color='rgb(255,255,221)'
cursor-shape='ibeam'
foreground-color='rgb(0,0,0)'
palette=['rgb(46,52,54)', 'rgb(204,0,0)', 'rgb(58,43,143)', 'rgb(196,160,0)', 'rgb(52,101,164)', 'rgb(117,80,123)', 'rgb(6,152,154)', 'rgb(211,215,207)', 'rgb(85,87,83)', 'rgb(239,41,41)', 'rgb(150,52,226)', 'rgb(252,233,79)', 'rgb(114,159,207)', 'rgb(173,127,168)', 'rgb(52,226,226)', 'rgb(238,238,236)']
use-theme-colors=false
visible-name='light'
[:4b1f055e-9bc4-4b58-bb12-eb5657db78fc]
audible-bell=false
background-color='rgb(0,0,0)'
background-transparency-percent=17
cursor-shape='ibeam'
font='Monospace 12'
foreground-color='rgb(172,228,255)'
palette=['rgb(46,52,54)', 'rgb(175,95,255)', 'rgb(0,102,255)', 'rgb(196,160,0)', 'rgb(255,7,168)', 'rgb(117,80,123)', 'rgb(6,152,154)', 'rgb(211,215,207)', 'rgb(85,87,83)', 'rgb(235,148,255)', 'rgb(0,229,255)', 'rgb(252,233,79)', 'rgb(114,159,207)', 'rgb(173,127,168)', 'rgb(52,226,226)', 'rgb(238,238,236)']
use-system-font=true
use-theme-colors=false
use-theme-transparency=false
use-transparent-background=true
visible-name='hi'
Terminator
1 |
|
Powerlevel10k
1 |
|
小技巧
Shell脚本一次性启动多个程序(对应多个终端)
经常开发ros环境的应该知道,当节点太多难以控制的时候,不如写个脚本直接启动多个节点(对应多个终端)。
先介绍一下gnome-terminal
命令的使用:
1 |
|
了解上面后我们可以创建属于自己的脚本了:
-
在ROS工作空间(例如,
catkin_ws
文件夹)下创建一个脚本文件,例如runROS.sh
。 -
写入内容,例如:
1
2
3
4
5
6#!/bin/bash
gnome-terminal --window -- bash -c "source ./devel/setup.bash && roslaunch PACKAGE_NAME *.launch; exec bash"
sleep 3s
gnome-terminal --window -- bash -c "source ./devel/setup.bash && rosrun PACKAGE_NAME NODE_NAME *.yaml; exec bash"
sleep 3s
gnome-terminal --window -- bash -c "rosbag play /yourfolder/*.bag; exec bash"需要什么节点按照这个模块添加即可。
对于使用Terminator终端和zsh shell,修改文件内容为:
1
2
3
4
5
6#!/bin/zsh
terminator -e "zsh -c 'source ./devel/setup.zsh && roslaunch PACKAGE_NAME *.launch; exec zsh'"
sleep 3s
terminator -e "zsh -c 'source ./devel/setup.zsh && rosrun PACKAGE_NAME NODE_NAME *.yaml; exec zsh'"
sleep 3s
terminator -e "zsh -c 'source ./devel/setup.zsh && rosbag play /yourfolder/*.bag; exec zsh'"这里播放
rosbag
前面需要加上source ./devel/setup.zsh
,否则会报错zsh:1: command not found: rosbag
。如果
exec zsh
命令未能在命令运行完成后保持终端窗口打开,可以尝试修改terminal的配置,在terminal点右键,选择Profiles->Profile Preferences,然后找到Title and Command,里面有一项When command exits,后面选择为Hold the terminal open。因为
roscore
和roslanuch
打开ROS节点后不会自行关闭,需要使用快捷键Ctrl+C
手动关闭,所以不会运行到exec zsh
这一步。而且,使用快捷键
Ctrl+C
手动关闭ROS节点后,终端会直接退出,也就是关闭窗口(这个BUG?反而能自动关闭终端窗口,减少了自己的操作?)。如果
Ctrl+C
后不想关闭终端,可以在terminal点右键,选择Profiles->Profile Preferences,然后找到Title and Command,里面有一项When command exits,后面选择为Hold the terminal open。不过即使这样,当前终端也不再能输入命令,只能使用Terminator的功能水平分割/数值分割另起一个终端。 -
给脚本文件授予权限:
1
sudo chmod 755 runROS.sh
chmod 755
设置用户的权限为:- 文件所有者可读可写可执行。
- 与文件所有者同属一个用户组的其他用户可读可执行。
- 其它用户组可读可执行。
-
运行脚本文件:
1
2cd catkin_ws
./runROS.sh -
完成。
Shell脚本在语句报错后终止运行
shell
脚本在语句报错后默认仍会继续向下执行,解决方法为:
1 |
|
默认情况下,脚本执行后,屏幕只显示运行结果,没有其他内容。如果多个命令连续执行,它们的运行结果就会连续输出。有时会分不清,某一段内容是什么命令产生的。
set -x
用来在运行结果之前,先输出执行的那一行命令。执行echo bar
之前,该命令会先打印出来,行首以+
表示。这对于调试复杂的脚本是很有用的。
Shell脚本自动输入密码
1 |
|
Shell脚本按下Ctrl+C后仍可以执行功能
How to handle ctrl+c in bash scripts
1 |
|
trap
命令允许捕获信号(Ctrl+C是SIGINT信号)并在捕获后执行命令,trap
语法如下:
1
trap function_command_to_execute SIGNAL
- 为什么你使用 echo 两次?
- 按 ctrl+c 而不输入换行符会弄乱输出。
- 为什么将 stderr 重定向到
/dev/null
?
/dev/null
是一个特殊的设备名称,充当黑洞!你扔在那里的所有东西都会消失!这样,如果我们尝试删除的文件已经被删除,则屏幕上不会显示错误,这可能会无缘无故地让用户感到困惑!- 为什么用
exit 1
退出脚本?
- 退出代码非常有用!退出代码 0 表示程序/脚本按预期完成,其他任何值都可能表示因异常方式或错误退出。
- 这使得故障排除更加容易,而且还允许控制流,我们可以使用 $? 读取脚本或程序的退出状态。这允许 bash 脚本决定如果另一个脚本或程序异常退出时该怎么做。
为Shell脚本中的文本着色
Linux: How to colorize text in bash scripts
代码/编译
编译与卸载
编写CmakeLists.txt
在CmakeLists.txt
里指定第三方库所在的路径
在CmakeLists.txt
里指定第三方库所在的路径,即指定其编译安装后.cmake
文件所在的路径,例如:
1 |
|
1
find_library(flowfilter_gpu_LIBS NAMES flowfilter_gpu PATHS /usr/local/include)
首先,
set(OpenCV_DIR /usr/local/opencv/opencv345/share/OpenCV)
这行代码设置了一个变量OpenCV_DIR
,它指向OpenCV库的安装位置。在这个例子中,OpenCV库被安装在/usr/local/opencv/opencv345/share/OpenCV
这个路径下。OpenCV_DIR
这个变量通常用于指定OpenCV的cmake配置文件的位置,这个文件包含了OpenCV库的版本信息、编译选项等信息。然后,
find_package(OpenCV REQUIRED)
这行代码告诉CMake去查找OpenCV库。REQUIRED
关键字表示如果CMake不能找到OpenCV库,那么CMake应该停止配置过程并显示错误信息。如果CMake成功找到了OpenCV库,那么它将设置一些变量,例如OpenCV_INCLUDE_DIRS
和OpenCV_LIBS
,这些变量分别包含了OpenCV的头文件路径和库文件路径,可以在后续的target_include_directories
和target_link_libraries
命令中使用。
如果没有设置
OpenCV_DIR
,find_package
命令会在默认的路径下查找OpenCV库。这些默认的路径包括:
- CMake的模块路径(
CMAKE_MODULE_PATH
)- CMake的安装前缀(
CMAKE_PREFIX_PATH
)- 系统的环境变量路径
具体来说,
find_package
会查找名为OpenCVConfig.cmake
或opencv-config.cmake
的文件,这个文件通常位于OpenCV库的安装目录中。如果你的OpenCV库安装在非标准的位置,或者你有多个版本的OpenCV库并且想要选择一个特定的版本,那么你可以通过设置
OpenCV_DIR
来指定OpenCV库的路径。如果没有设置OpenCV_DIR
,CMake可能会找到错误的版本或者找不到OpenCV库。
OpenCV_DIR
和OpenCV_INCLUDE_DIR
是两个不同的变量,它们在CMake中的作用也不同。
OpenCV_DIR
是用于指定OpenCV的cmake配置文件位置的变量。find_package
命令会使用OpenCV_DIR
变量的值作为查找OpenCV配置文件的起始路径。如果OpenCV_DIR
被设置,find_package
就会直接在这个路径下查找配置文件,而不会在其他路径下查找。
OpenCV_INCLUDE_DIR
通常是find_package
命令找到OpenCV库后设置的一个变量,它包含了OpenCV的头文件路径。这个变量通常用于target_include_directories
命令,以便你的项目可以找到OpenCV的头文件。如果你设置了
OpenCV_INCLUDE_DIR
,而不是OpenCV_DIR
,然后调用find_package(OpenCV REQUIRED)
,那么find_package
命令可能无法找到正确的OpenCV库,因为它不知道在哪里查找OpenCV的配置文件。在这种情况下,find_package
命令可能会找到错误的OpenCV版本,或者找不到OpenCV库。find_package
命令找到的结果会覆盖你设置的OpenCV_INCLUDE_DIR
变量。总的来说,如果你想要指定OpenCV库的位置,你应该设置
OpenCV_DIR
,而不是OpenCV_INCLUDE_DIR
。
(在ROS中)编译第三方开源软件需要下载的问题
注意
CmakeList.txt
里有没有指定具体版本。在package.xml
里也可以看到指定的版本。
其实就类似于在系统中
cmake
、make
和make install
的步骤,只不过这里的第三方库是安装在了ROS工作区里被相互调用,catkin clean
后也就删除掉了,而没有安装在系统环境里。也方便使用指定版本的第三方库。
1 |
|
或者,将src
(这个文件是原本解压下载的第三方源码source的地方,具体名称要看CMakeLists.txt
中SOURCE_DIR的设置)中的各个第三方源码都解压好,放到src对应的文件夹中。例如catkin_ws/build/xxx/xxx_src-prefix/src/xxx.tar.gz
。
cmake
一般流程
1 |
|
我个人推荐把第三方库安装在
/usr/local
文件夹下进行管理。例如,在/usr/local
文件夹下新建文件夹eigen3
,后在eigen3
文件夹下新建文件夹eigen330
、eigen340
。
定义编译参数
可在cmake
命令后加参数:
1 |
|
也可以在CMakeLists.txt
文件中定义,例如,启用参数EFFT_USE_FFTW3
:
1 |
|
或,在CMakeLists.txt
文件中:
1 |
|
保存cmake输出
ROS:
cmake build
的输出在catkin_ws/logs/your_package_name/build.cmake.log、build.make.log
里。
cmake
命令的输出信息通常在终端中显示,而不是保存在文件中。这些信息包括配置过程中的警告、错误以及其他重要信息。
然而,你可以将cmake
命令的输出重定向到一个文件中。例如,你可以使用以下命令将输出保存到一个名为output.txt
的文件中:
1 |
|
在这个命令中,>
操作符将cmake
命令的输出重定向到output.txt
文件中。如果output.txt
文件已经存在,这个命令将覆盖它的内容。如果你想要追加输出到文件中,而不是覆盖它,你可以使用>>
操作符,如下所示:
1 |
|
请注意,这些命令只会捕获标准输出,而不会捕获错误输出。如果你也想要捕获错误输出,你可以使用2>&1
,如下所示:
1 |
|
在这个命令中,2>&1
将错误输出重定向到标准输出,然后>
操作符将标准输出重定向到output.txt
文件中。这样,output.txt
文件将包含所有的输出,包括错误信息。
make
1 |
|
cmake --install .
与make install
的区别:
cmake --install .
和make install
都是用来安装编译好的程序的命令,但它们在使用的构建系统和工作方式上有所不同。
make install
是GNU Make的命令,它依赖于Makefile中的install
目标。这个install
目标通常会将编译好的二进制文件、库文件、头文件等复制到系统的指定位置,如/usr/local/bin
、/usr/local/lib
等。这个命令通常在使用GNU Autotools或者手写Makefile的项目中使用。
cmake --install .
是CMake的命令,它会执行CMakeLists.txt文件中定义的安装规则。这个命令在CMake 3.15及以后的版本中可用,它是cmake -P cmake_install.cmake
的一个更简洁的替代。这个命令的好处是它不依赖于特定的构建系统,可以在任何CMake支持的构建系统中使用。总的来说,这两个命令的功能是类似的,但
cmake --install .
更加通用,不依赖于特定的构建系统。
卸载
如果因为反复./configure xx
然后make
会导致安装路径混乱,sudo make install
失败。所以如果路径设错了又已经make
完,需要make clean
来清除一下。如果想把自己刚刚make install
安装的卸载掉,可以在那个目录里直接用sudo make uninstall
。
1 |
|
make uninstall
是一个常见的 makefile 目标,它的主要作用是删除由make install
命令安装的文件。当你运行
make install
命令时,通常会将一些文件(例如可执行文件、库文件、头文件等)复制到系统的某些目录下(例如/usr/local/bin
、/usr/local/lib
等)。make uninstall
命令就是用来删除这些文件的。然而,需要注意的是,并不是所有的 makefile 都提供
make uninstall
目标。如果 makefile 没有提供这个目标,运行make uninstall
命令将会导致错误。在运行
make uninstall
命令之前,你应该查看 makefile 或者相关的文档,以确认这个命令是否可用,以及它会删除哪些文件。
cat install_manifest.txt | sudo xargs rm
和sudo make uninstall
都是用来删除由make install
命令安装的文件的。然而,它们的工作方式有所不同。
cat install_manifest.txt | sudo xargs rm
:这个命令会读取install_manifest.txt
文件,这个文件通常由make install
命令生成,包含了所有被安装的文件的列表。然后,它会使用xargs rm
命令删除这些文件。这个命令不依赖于 makefile,只要install_manifest.txt
文件存在,就可以使用。sudo make uninstall
:这个命令会执行 makefile 中的uninstall
目标。这个目标通常会删除所有被make install
命令安装的文件。然而,需要注意的是,并不是所有的 makefile 都提供uninstall
目标。如果 makefile 没有提供这个目标,运行sudo make uninstall
命令将会导致错误。总的来说,这两个命令的功能是相似的,但是
cat install_manifest.txt | sudo xargs rm
命令更为直接,不依赖于 makefile。而sudo make uninstall
命令则需要 makefile 提供uninstall
目标。
make clean
和make distclean
是两个常见的 makefile 目标,它们的功能取决于 makefile 的编写者如何定义它们。但是,通常它们的功能如下:
make clean
:这个命令通常用于删除所有由 makefile 生成的文件。这通常包括编译产生的对象文件(.o 或 .obj 文件)和编译器生成的中间文件。但是,它通常不会删除配置文件或者 makefile 文件。make distclean
:这个命令通常用于将目录恢复到初始状态。除了删除make clean
会删除的文件,它还会删除配置文件和 makefile 文件。这个命令通常在你想要重新配置和编译一个项目时使用。需要注意的是,这两个命令的具体行为取决于 makefile 的编写者。在使用这些命令之前,你应该查看 makefile 或者相关的文档,以了解这些命令的具体行为。
编程命名规范
-
匈牙利命名法(将变量类型写进变量名的命名方法)。
其基本原则是,变量名=属性+类型+对象描述。通过在变量名前面加上相应的小写字母的符号标识作为前缀,标识出变量的作用域,类型等。
这些符号可以多个同时使用,顺序是先m_(成员变量),再指针,再简单数据类型,再其他。例如:m_lpsStr,表示指向一个字符串的长指针成员变量。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21# 前缀类型
a 数组(Array)
b 布尔值(Boolean)
by 字节(Byte)
c 有符号字符(Char)
cb 无符号字符(Char Byte,并没有神马人用的)
cr 颜色参考值(Color Ref)
cx,cy 坐标差(长度 Short Int)
dw 双字(Double Word)
fn 函数(Function)
h Handle(句柄)
i 整形(Int)
l 长整型(Long Int)
lp 长指针(Long Pointer)
m_ 类成员(Class Member)
n 短整型(Short Int)
np 近程指针(Near Pointer)
p 指针(Pointer)
s 字符串(String)
sz 以 Null 做结尾的字符串型(String with Zero End)
w 字(Word) -
驼峰式命名法,又叫小驼峰式命名法。常用于变量名,函数名。
要求第一个单词首字母小写,后面其他单词首字母大写。
1
2
3int myAge;
char myName[10];
float manHeight; -
帕斯卡命名法,又叫大驼峰式命名法。常用于类名,属性,命名空间等。
与小驼峰式命名法的最大区别在于,每个单词的第一个字母都要大写。
1
2
3int MyAge;
char MyName[10];
float ManHeight; -
下划线命名法。
下划线命名法并不如大小驼峰式命名法那么备受推崇,但是也是浓墨重彩的一笔。尤其在宏定义和常量中使用比较多,通过下划线来分割全部都是大写的单词。还有变量名太长的变量。
该命名规范,也是很简单,要求单词与单词之间通过下划线连接即可。
1
2
3int my_age;
char my_name[10];
float man_height; -
在C++编程中,变量名后加
_
的命名方式通常用于表示类的私有数据成员。这是一种命名约定,用于区分类的数据成员和局部变量,以提高代码的可读性。例如,fftwInput_
、fftwOutput_
和plan_
都是类的私有数据成员,它们的名字都以_
结尾。一般来说,类的数据成员应该被定义为私有(private),这是面向对象编程中的封装原则。通过将数据成员设为私有,可以防止外部代码直接访问或修改这些数据,从而保护类的内部状态的完整性。
然而,有时候,你可能会选择将某些数据成员设为公有(public)。这通常在数据成员是类的公开接口的一部分,或者类本身就是一个简单的数据结构时发生。
至于是否在公有数据成员的名称后加
_
,这完全取决于你的命名约定。在某些命名约定中,可能会在所有数据成员的名称后加_
,无论它们是公有的还是私有的。在其他命名约定中,可能只在私有数据成员的名称后加_
。总的来说,关键是选择一种命名约定,并在整个代码库中一致地遵循它,以提高代码的可读性和一致性。
变量名称前加下划线:这通常用于表示私有成员变量或者类的内部变量。然而,根据C++标准,名称以一个下划线开头的变量可能被保留给编译器的实现,所以一般不推荐这种做法。
-
在编程中,
i
、j
和k
通常用作循环变量,特别是在嵌套循环中。idx
和jdx
是对这些传统变量名的扩展,其中idx
可能表示"index",jdx
可能表示第二个索引。然而,更具描述性的变量名可能会使代码更易于理解。例如,如果
idx
遍历的是当前帧的目标检测框,那么你可能会选择名为curBoxIdx
的变量名。同样,如果jdx
遍历的是前一帧的目标检测框,那么你可能会选择名为prevBoxIdx
的变量名。 -
在我们的系统中,所有环境变量都使用大写字母命名。所以当我们声明局部变量时,应该使用小写字母来声明,以避免环境和局部变量名发生冲突。
-
等等。
命名空间
在C++中, using namespace std;
这条语句的作用是将 std
命名空间中的所有名称导入到当前作用域中,使得我们可以直接使用 std
命名空间中的类型和函数,而无需每次都完整地书写它们的命名空间。
然而,这种做法也有其缺点。首先,它会导致命名空间污染,即同一个作用域中可能存在多个同名的类型或函数,导致编译器无法区分它们。其次,这种做法可能会导致程序效率降低,因为导入的命名空间中的类型和函数可能会增加编译时间和运行时间。
因此,有些开发者会选择显式地书写类型和函数的完整名称,例如 std::cout
,而不是使用 using namespace std
; 。这种做法可以避免命名空间污染,并且可以确保编译器能够准确地解析类型和函数的名称。同时,这种做法也可以提高程序的可读性,使得代码更加清晰易懂。
总的来说,是否使用 using namespace std;
,以及是否显式地书写类型和函数的完整名称,取决于开发者的个人喜好和编程习惯。但是,在编写大型项目时,为了避免命名空间污染和保证程序的效率,建议尽量少使用 using namespace std;
,而是显式地书写类型和函数的完整名称。
代码注释
一般注释
C++
1 |
|
python
1 |
|
特殊注释
这些是注释中的标签(tag),有时也被称作“代码标签(codetag)”或“标记(token)”。
标识:
- TODO:标记代码中需要实现的功能或任务。
- FIXME:标记代码中需要修复的问题或缺陷。
- XXX:如果代码中有该标识,说明标识处代码虽然实现了功能,但是实现的方法有待商榷,代码有问题或具误导性,需引起警惕。希望将来能改进,要改进的地方会在说明中简略说明。
- HACK/BODGE/KLUDGE:标记临时性修复或不优雅的解决方案。英语翻译为砍。如果代码中有该标识,说明标识处代码我们需要根据自己的需求去调整程序代码。
- BUG/DEBUG:标记已知的Bug或错误。
- UNDONE:对之前代码改动的撤销。
- NOTE:提供额外的注释或提示信息,帮助理解代码意图或设计决策。
格式:
1 |
|
C++数据类型
C++形参和实参
形参和实参是函数中的两个重要概念。
形参(形式参数)是在函数定义中出现的参数,它是一个虚拟参数,只有在函数调用时才会接收到传递进来的实际参数。形参可以被看作是一个占位符,在函数定义时并没有实际的数值,只有在函数调用时才会得到实参的数值。形参的主要作用是表示函数需要从外部传递进来的数据。
实参(实际参数)是在函数中实际出现的参数,它的值可以是常量、变量、表达式、类等。实参的值是确定的,必须在函数调用时提供。实参的主要作用是向函数传递数据,将数据的值传递给形参,在函数体内被使用。
要注意的是,形参和实参之间的传递方式有两种:值传递和地址传递。值传递是指将实参的值复制给形参,形参在函数内部使用时不会改变实参的值。而地址传递是指将实参的地址传递给形参,形参在函数内部使用时可以通过地址修改实参的值。
总结起来,形参是函数定义中的参数,是一个虚拟的占位符,用于接收函数调用时传递进来的实参。实参是函数调用时提供的具体数值,用于向函数传递数据。形参和实参之间的传递方式可以是值传递或地址传递。
1 |
|
C++修饰符类型
C++ 允许在 char、int 和 double 数据类型前放置修饰符。
修饰符是用于改变变量类型的行为的关键字,它更能满足各种情境的需求。
下面列出了数据类型修饰符:
- signed:表示变量可以存储负数。对于整型变量来说,signed 可以省略,因为整型变量默认为有符号类型。
- unsigned:表示变量不能存储负数。对于整型变量来说,unsigned 可以将变量范围扩大一倍。
- short:表示变量的范围比 int 更小。short int 可以缩写为 short。
- long:表示变量的范围比 int 更大。long int 可以缩写为 long。
- long long:表示变量的范围比 long 更大。C++11 中新增的数据类型修饰符。
- float:表示单精度浮点数。
- double:表示双精度浮点数。
- bool:表示布尔类型,只有 true 和 false 两个值。
- char:表示字符类型。
- wchar_t:表示宽字符类型,可以存储 Unicode 字符。
修饰符 signed、unsigned、long 和 short 可应用于整型,signed 和 unsigned 可应用于字符型,long 可应用于双精度型。
这些修饰符也可以组合使用,修饰符 signed 和 unsigned 也可以作为 long 或 short 修饰符的前缀。例如:unsigned long int。
C++ 允许使用速记符号来声明无符号短整数或无符号长整数。您可以不写 int,只写单词 unsigned、short 或 long,int 是隐含的。
C++中的类型限定符
类型限定符提供了变量的额外信息,用于在定义变量或函数时改变它们的默认行为的关键字。
限定符 | 含义 |
---|---|
const | const 定义常量,表示该变量的值不能被修改。 |
volatile | 修饰符 volatile 告诉该变量的值可能会被程序以外的因素改变,如硬件或其他线程。。 |
restrict | 由 restrict 修饰的指针是唯一一种访问它所指向的对象的方式。只有 C99 增加了新的类型限定符 restrict。 |
mutable | 表示类中的成员变量可以在 const 成员函数中被修改。 |
static | 用于定义静态变量,表示该变量的作用域仅限于当前文件或当前函数内,不会被其他文件或函数访问。 |
register | 用于定义寄存器变量,表示该变量被频繁使用,可以存储在CPU的寄存器中,以提高程序的运行效率。 |
逻辑运算符
在C++中,"或"逻辑可以使用逻辑运算符||
来表示,|
是按位或运算符。
"与"逻辑可以使用逻辑运算符&&
来表示,&
是按位与运算符。
在C++中,if ( IMU_VERSION == 0)
和if ( IMU_VERSION = 0)
有着本质的区别。
if ( IMU_VERSION == 0)
是一个比较操作,它检查IMU_VERSION
是否等于0。如果IMU_VERSION
的值为0,那么条件为真,if
语句中的代码块将被执行。如果IMU_VERSION
的值不为0,那么条件为假,if
语句中的代码块将不会被执行。
而if ( IMU_VERSION = 0)
是一个赋值操作。它将IMU_VERSION
的值设置为0,然后检查赋值后的IMU_VERSION
是否为非零。在这种情况下,由于赋值为0,条件始终为假,因此if
语句中的代码块将不会被执行。同时,这也改变了IMU_VERSION
的原始值,这可能不是你想要的结果。因此,你应该谨慎使用赋值操作符=
在if
语句中,除非你确实想在检查条件的同时赋值。
C++函数
普通函数、类的普通成员函数和类的静态成员函数之间的区别
在C++中,普通函数、类的普通成员函数和类的静态成员函数之间有以下几点主要区别:
- 普通函数:属于全局函数,不受具体类和对象的限制,可以直接调用。
- 普通函数不属于任何类,它只能访问全局变量和其参数。它不能访问类的成员变量和成员函数(除非有一个类的对象或指针作为参数传入)。
- 类的普通成员函数:类的普通成员函数属于类的实例(对象),它可以访问类的所有成员(包括私有成员、保护成员和公有成员)。每个对象都有自己的成员函数副本。普通成员函数必须通过对象来调用。
- 本质上是一个包含指向具体对象
this
指针的普通函数,即C++类的普通成员函数都隐式包含一个指向当前对象的this
指针。
- 本质上是一个包含指向具体对象
- 类的静态成员函数:类的静态成员函数属于类本身,而不属于类的任何对象。它只能访问类的静态成员变量和静态成员函数,不能访问类的非静态成员变量和非静态成员函数。静态成员函数可以通过类名直接调用,也可以通过对象调用。
- 静态成员函数在某种程度上类似于全局函数,因为它们不依赖于类的任何特定对象,而是属于类本身。这意味着你可以在不创建类的对象的情况下调用静态成员函数。
- 然而,静态成员函数并不完全等同于全局函数。静态成员函数仍然是类的一部分,它可以访问类的静态成员(包括私有静态成员),而全局函数则不能。
- 静态成员函数没有
this
指针。this
指针是一个指向调用成员函数的特定对象的指针。因为静态成员函数不依赖于任何特定对象,所以它没有this
指针。这也意味着静态成员函数不能访问类的非静态成员变量或非静态成员函数。
如果成员函数想作为回调函数来使用,如创建线程等,一般只能将它定义为静态成员函数才行。
在C++中,回调函数通常需要是全局函数或静态成员函数,因为它们具有固定的函数签名,可以被用作函数指针。非静态成员函数不能直接用作回调函数,因为它们有一个隐含的
this
参数,这会改变它们的函数签名。然而,有一些方法可以让你使用非静态成员函数作为回调函数。例如,你可以使用
std::bind
或lambda
表达式来捕获this
指针,然后调用非静态成员函数。这种方法在C++11及以后的版本中可用。
以下是一个简单的例子来说明这三种函数的区别:
1 |
|
类的构造函数和析构函数的调用时机
在C++中,类的构造函数和析构函数的调用时机如下:
-
构造函数:当创建类的对象时,会自动调用类的构造函数。构造函数的主要任务是初始化对象的数据成员。构造函数的名称与类名相同,没有返回类型。构造函数可以有参数,可以被重载。
- 当创建一个类的对象时,例如
MyClass obj;
,会调用默认构造函数(无参数的构造函数)。 - 当以参数方式创建一个类的对象时,例如
MyClass obj(1, 2);
,会调用相应的参数构造函数。 - 当一个对象作为另一个对象的初始化参数时,例如
MyClass obj1 = obj2;
或MyClass obj1(obj2);
,会调用拷贝构造函数。
- 当创建一个类的对象时,例如
-
析构函数:在C++中,当类的对象需要在其生命周期结束时执行某些操作(如释放资源、关闭文件、断开网络连接等)时,就需要定义析构函数。当一个对象的生命周期结束时(例如,对象离开其作用域,或者使用
delete
删除动态分配的对象),会自动调用其析构函数。析构函数的主要任务是执行清理工作,例如释放对象可能拥有的资源。析构函数的名称是类名前加上波浪号~
,没有返回类型,也不能有任何参数,因此不能被重载。- 一个局部变量,那么当它的定义域(例如一个函数或一个代码块)结束时,它的析构函数会被调用。
- 一个全局变量或者静态变量,那么当程序结束时,它的析构函数会被调用。
而有的对象不会被析构函数自动释放,因此就不能使用空的析构函数,而是在析构函数内定义释放规则。
以下是一个例子,该例子中的类
FileWrapper
封装了一个文件指针,需要在析构函数中关闭文件:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22#include <cstdio>
class FileWrapper {
private:
FILE* file_;
public:
FileWrapper(const char* filename, const char* mode) {
file_ = std::fopen(filename, mode);
if (file_ == nullptr) {
throw std::runtime_error("Failed to open file.");
}
}
~FileWrapper() {
if (file_ != nullptr) {
std::fclose(file_); // 关闭文件
}
}
// 其他成员函数...
};在这个例子中,
FileWrapper
的构造函数打开一个文件,并将文件指针存储在file_
中。然后,FileWrapper
的析构函数在file_
不为nullptr
时关闭文件。这样,无论FileWrapper
对象何时被销毁(例如,离开作用域或被delete
删除),都会自动关闭文件,防止资源泄漏。如果
FileWrapper
使用空的析构函数,那么文件将不会被关闭,可能会导致资源泄漏和其他问题。
以下是一个简单的例子:
1 |
|
在这个例子中,当MyClass obj;
执行时,会调用MyClass
的构造函数。当obj
离开其作用域(即main
函数结束时),会调用MyClass
的析构函数。
输出函数的选择
-
C++ 中
printf
和cout
有什么区别?在 C++ 中,
printf
和cout
都用于输出,但它们来自不同的库,有不同的用法和特性。下面是一些主要的不同之处:- 来源:
printf
是 C 语言中的标准输入输出函数,来源于 C 语言的标准库(stdio.h)。在 C++ 中依然可用,但需要包含头文件<cstdio>
或者 C 风格的<stdio.h>
。cout
是 C++ 专有的,属于 C++ 标准库中的一个对象,用于输出。cout
是定义在<iostream>
头文件中的标准输出流对象。
- 使用语法:
printf
使用格式化字符串。例如:printf("%d %s", 100, "C++");
。cout
使用流插入运算符(<<
)。例如:cout << 100 << " C++";
。
- 类型安全:
printf
在处理类型时不够安全,因为它依赖于格式化字符串正确地匹配变量的类型;如果不匹配,可能会造成运行时错误或未定义行为。cout
是类型安全的,编译器会检查插入到cout
中的数据类型,确保与预期匹配,减少了错误的可能性。
- 性能:
printf
的性能通常情况下比cout
快,尤其是在格式化字符串操作时,因为cout
的类型安全和重载运算符使得它的运行效率稍低。- 但是,通过启用 C++ 的
std::ios_base::sync_with_stdio(false);
以及避开使用std::endl
(std::endl
会刷新输出缓冲区),可以提升cout
的性能,使其与printf
相近。
- 灵活性和功能:
- 使用
cout
更灵活,因为它支持对输出格式的更细致控制,例如设置宽度、精度和填充字符等。此外,cout
可以直接打印用户定义的类型,只要为这些类型重载了输出运算符(<<
)。 printf
在打印自定义类型时没有直接的方式,因为它需要预定义的格式化串。
- 使用
- 可移植性:
printf
基于 C 语言,因此在许多不同的平台和编译器中使用广泛,具有很高的可移植性。cout
虽然是 C++ 的一部分,理论上在所有支持 C++ 的平台上都是可用的,但在跨平台编程时,对输出格式的控制可能会遇到不一致性。
综上所述,
printf
和cout
都有各自的用途和优势,选择使用哪个取决于具体的编程需求和个人喜好。在实际编程中,了解它们的差异有助于更好地决策和优化代码。 - 来源:
-
在C++程序开发中,推荐使用
printf
还是cout
?在 C++ 程序开发中,通常推荐使用
cout
而不是printf
。这一推荐基于以下几个理由:- 类型安全:
cout
是类型安全的,这意味着编译器会对插入到cout
的数据类型进行检查,减少了类型不匹配的错误。而printf
需要开发者手动指定类型,在类型不匹配的情况下可能导致未定义行为或运行时错误。 - 易于使用:使用
cout
需要较少的手工干预来处理不同类型的数据,因为它可以自动调整输出格式,而printf
请求用户显式地指定格式化字符串。特别是在处理字符串和数字的混合输出时,cout
的语法更为直观和简洁。 - 可扩展性:对于自定义类型,
cout
允许开发者通过重载输出运算符 (<<
) 来定义自己的输出格式,这提供了良好的可扩展性。而printf
不能直接支持自定义类型的打印,除非通过某种方式将其转换为内建类型。 - 标准一致性:
cout
是 C++ 标准库的一部分,充分利用cout
能够使程序更加面向对象,更符合 C++ 的编程风格。printf
来自 C 语言标准库,虽然在 C++ 中仍然可用,但并不是 C++ 风格的首选。 - 灵活性:
cout
提供了更好的控制输出格式的功能,如设置宽度、填充、精度等,而且这些设置易于读写和维护。
然而,在某些特定情况下,
printf
可能更为有用: - 当您需要更详细地控制输出格式,尤其是在涉及复杂格式时,printf
的格式化字符串可能更易于使用和理解。 - 在性能极致敏感的场景中,经过适当的优化后,printf
在某些情况下可能比cout
有更好的性能。总之,虽然
cout
通常是在 C++ 中处理输出的首选,但确切选择取决于具体的应用场景、性能需求和个人偏好。在决定使用哪一个之前,考虑项目的需求和上下文非常重要。 - 类型安全:
-
在使用C++语言的ROS平台开发中,推荐使用
printf
、cout
还是ROS_INFO
?在使用 C++ 语言开发 ROS (Robot Operating System) 平台的应用时,通常推荐使用
ROS_INFO
以及其相关的宏(如ROS_WARN
,ROS_ERROR
,ROS_DEBUG
等)来进行消息的输出。这些宏专为 ROS 开发设计,提供了一些优势和特性,特别是在进行机器人软件开发时:- 统一的日志管理:
ROS_INFO
和其他相关宏能够将输出消息统一管理,使得日志记录、查看和分析变得更加容易。ROS 提供的 rqt_console 和 roslaunch 日志文件都可以捕捉这些输出,方便开发者调试和追踪问题。 - 级别管理:不同于
printf
和cout
只能单纯输出文本,ROS_INFO
等宏允许你按照不同的严重性级别输出消息,如信息、警告、错误、调试等。这有助于开发者在运行时筛选和控制想要查看的输出类型。 - 节点识别:使用 ROS 日志函数,每条日志消息都会自动附带节点名信息。这对于在复杂系统中跟踪消息来源特别有用,尤其是当多个节点同时运行时。
- 格式化和类型安全:
ROS_INFO
等宏支持类似printf
的格式化功能,使得在输出文本时可以包含变量。同时,由于ROS_INFO
是在 C++ 环境下设计的,因此也可以在一定程度上享受到 C++ 类型安全的好处(尽管其内部实现可能是利用printf
风格的格式化字符串)。 - 条件编译:
ROS_DEBUG
等调试相关的宏,在发布模式下可以通过编译选项完全禁用,从而不会对发布版本的性能造成影响。
综上所述,虽然在 ROS 平台下开发时
printf
和cout
依然可用,但ROS_INFO
和相关的宏提供了更多符合 ROS 开发环境和需求的优势。因此,在大多数情况下,推荐使用ROS_INFO
及其相关宏进行消息输出和日志记录。 - 统一的日志管理:
-
在使用C++语言的ROS平台开发中,推荐使用
printf
、cout
、ROS_INFO
还是ROS_INFO_STREAM
?在使用C++语言的ROS(Robot Operating System)平台开发时,选择
printf
、cout
、ROS_INFO
还是ROS_INFO_STREAM
取决于具体的应用场景和你的偏好。下面是它们各自的特点和适用情况:printf
- 来源于C语言,C++也支持。
- 用于格式化输出,语法稍显复杂,对类型安全的支持不如C++的IO流。
- 适用于快速的调试或者当开发环境更接近C语言时使用。
std::cout
- C++标准输入输出流的一部分,使用起来比
printf
更为方便和安全。 - 支持类型安全的输出,可以通过重载
<<
操作符来扩展对自定义类型的支持。 - 适用于C++程序的通用输出,特别是需要输出自定义类型数据时。
ROS_INFO
和ROS_INFO_STREAM
- 这是ROS中专门用于日志输出的宏和流。
ROS_INFO
类似于printf
,而ROS_INFO_STREAM
则类似于std::cout
,使用方法根据个人偏好和输出内容的复杂度来选择。- 它们的优势在于:
- 集成了ROS的命名空间和节点名,可以更清楚地知道是哪个节点产生的日志。
- 可以通过ROS的配置文件来调整日志的级别,方便调试和运行时的日志管理。
- 支持网络日志(rosout),使得可以在ROS的不同部分或不同机器上收集和查看日志。
推荐使用
- 对于简单的调试信息,如果你更习惯C++风格,推荐使用
ROS_INFO_STREAM
;如果你倾向于使用类似C语言的格式化输出,可以选择ROS_INFO
。 - 对于非ROS系统级的调试或者涉及大量复杂数据类型输出时,
std::cout
可能更为直接和方便。 - 一般建议尽量避免使用
printf
,除非你有特别的理由,因为它不提供类型安全且在C中使用std::cout
和ROS_INFO_STREAM
可以更好地利用C的特性。
综上所述,选择哪一种取决于你的具体需求和开发习惯。在ROS开发中,
ROS_INFO
和ROS_INFO_STREAM
因为其与ROS系统的集成,通常会是首选。
C++小技巧
模板参数必须在编译时是已知的常量
在C++中,模板参数必须在编译时是已知的常量。这意味着你不能使用运行时从配置文件中读取的值作为模板参数。
一种可能的解决方案是使用预处理器宏来定义N_dim
。你可以在编译时通过编译器的命令行参数来设置这个宏。例如:
1 |
|
预处理器宏(如
#define N 512
)在它被定义的文件以及#include
该文件的所有文件中都是可见的。如果你在一个文件中定义了N
,然后在另一个文件中想要使用它,你需要确保第二个文件#include
了定义N
的文件。
然后在编译时,你可以使用-D
选项来设置N_dim
的值:
1 |
|
另一种解决方案是使用一个固定大小的eFFT
对象,然后在运行时根据需要使用其中的一部分。这可能需要你修改eFFT
类的实现,使其能够处理不同大小的输入。
重复定义问题
你的问题是在多个源文件中都包含了parameters.h
,并且在这个头文件中定义了eFFT_SIZE
。这导致了eFFT_SIZE
的多重定义。
解决这个问题的一种方法是在parameters.h
中只声明eFFT_SIZE
,然后在一个源文件(例如parameters.cpp
)中定义它。这样,eFFT_SIZE
就只在一个地方定义了。
首先,你需要在parameters.h
中将eFFT_SIZE
的定义改为声明:
1 |
|
然后,在parameters.cpp
中定义eFFT_SIZE
:
1 |
|
这样,eFFT_SIZE
就只在parameters.cpp
中定义了一次,而在其他源文件中,它只是一个外部链接的声明。这应该可以解决你的问题。d
函数多个返回值
在C++中,有几种方法可以实现函数的多个返回值:
-
使用引用参数(Reference Parameters)/指针:你可以通过引用参数来修改函数外部的变量,从而实现多个返回值。
1
2
3
4
5
6
7
8
9
10void getValues(int& x, int& y) {
x = 5;
y = 10;
}
int main() {
int a, b;
getValues(a, b);
// Now a == 5 and b == 10
} -
使用std::pair或std::tuple:如果你的函数需要返回两个或更多的值,你可以使用
std::pair
或std::tuple
。1
2
3
4
5
6
7
8std::pair<int, int> getValues() {
return std::make_pair(5, 10);
}
int main() {
std::pair<int, int> values = getValues();
// Now values.first == 5 and values.second == 10
} -
使用结构体(Structs)或类(Classes):如果你的函数需要返回多个相关的值,你可以定义一个结构体或类来存储这些值。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16struct Values {
int x;
int y;
};
Values getValues() {
Values values;
values.x = 5;
values.y = 10;
return values;
}
int main() {
Values values = getValues();
// Now values.x == 5 and values.y == 10
} -
使用数组。
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#include <stdio.h>
// 将较大的元素存储在arr[0]中
void findGreaterSmaller(int a, int b, int arr[])
{
// Store the greater element at
// 0th index of the array
if (a > b) {
arr[0] = a;
arr[1] = b;
}
else {
arr[0] = b;
arr[1] = a;
}
}
// Driver code
int main()
{
int x, y;
int arr[2];
printf("输入两个数字: \n");
scanf("%d%d", &x, &y);
findGreaterSmaller(x, y, arr);
printf("\n最大值为:%d,最小值为:%d",
arr[0], arr[1]);
return 0;
} -
选择哪种方法取决于你的具体需求和编程风格。
多个源文件中共享一个变量/全局变量
在C++中,如果你想在多个源文件中共享一个变量,你可以使用extern
关键字。你可以在一个源文件中定义一个变量,并在其他源文件中使用extern
关键字声明同一个变量。
首先,在ESVIO/pose_graph/src/pose_graph_node.cpp
文件中,你需要将SYSTEM_MODE
变量的定义移动到一个头文件中,例如global.h
。然后在pose_graph_node.cpp
和keyframe.cpp
中都包含这个头文件。
global.h
文件内容如下:
1 |
|
然后在ESVIO/pose_graph/src/pose_graph_node.cpp
文件中,你需要包含global.h
并定义SYSTEM_MODE
:
1 |
|
最后,在ESVIO/pose_graph/src/keyframe.cpp
文件中,你也需要包含global.h
。这样你就可以在keyframe.cpp
中使用SYSTEM_MODE
变量了:
1 |
|
注意,extern
关键字告诉编译器变量的定义在别的地方,你不能在声明的时候初始化它。变量的定义(也就是初始化)应该在一个源文件中进行,而不是头文件。如果你在多个地方定义了同一个extern
变量,那么会导致链接错误。
类与对象
为什么要定义类与对象,什么时候定义类与对象?
在面向对象的编程语言中,类(Class)是一种定义新类型的方式,它封装了数据和操作数据的方法。对象(Object)则是类的实例,它包含了类定义的数据和方法。
定义类和对象的目的主要有以下几点:
- 封装:类可以将数据和操作数据的方法封装在一起,隐藏内部实现细节,提高代码的安全性和可维护性。
- 继承:类可以继承其他类的属性和方法,实现代码的复用。
- 多态:通过接口或者虚函数,不同的类对象可以执行相同的操作,但表现出不同的行为,提高代码的灵活性。
何时定义类和对象主要取决于你的需求。以下是一些可能需要定义类和对象的情况:
- 当你需要创建一个复杂的数据结构,这个数据结构包含多个相关的数据和操作这些数据的方法时,你可以定义一个类来封装这些数据和方法。
- 当你需要创建多个具有相同属性和行为的对象时,你可以定义一个类,然后创建这个类的多个对象。
- 当你需要实现面向对象的设计模式(如工厂模式、单例模式等)时,你需要定义类和对象。
怎么用类与对象
尽量实现,提供类所需要的输入数据,所有操作在类内部封装和实现,在外部直接输出结果。
这个想法是面向对象编程(OOP)的核心原则之一,称为封装(Encapsulation)。封装是将数据(变量)和操作数据的函数(方法)捆绑在一起形成一个对象(即类的实例),并规定了外部对这些数据和函数的访问方式。
封装有以下几个优点:
- 提高安全性:通过隐藏对象的内部状态,只允许通过对象的方法来访问和修改,可以防止外部代码随意修改对象的内部状态,提高了代码的安全性。
- 简化接口:对象的用户只需要知道对象提供了哪些方法,而不需要知道这些方法是如何实现的。这使得代码更易于理解和使用。
- 提高可维护性:由于对象的内部实现被封装起来,所以在不影响对象的用户的情况下,可以更容易地改变对象的内部实现。
面向对象编程(OOP)的核心原则主要有以下四个:
- 封装(Encapsulation):封装是将对象的状态(属性)和行为(方法)捆绑在一起,同时隐藏对象的内部实现细节,只提供有限的接口供外部访问。这样可以保护对象的内部状态,提高代码的安全性和可维护性。
- 继承(Inheritance):继承是子类可以继承父类的属性和方法,实现代码的复用。子类可以扩展和修改父类的行为,提供更具体的功能。
- 多态(Polymorphism):多态是指不同的对象可以对同一消息做出不同的响应。在运行时,根据对象的实际类型来调用相应的方法,提高了代码的灵活性。
- 抽象(Abstraction):抽象是将复杂的系统简化为更简单的模型。通过定义抽象的类和接口,隐藏具体的实现细节,让程序员只关注有用的信息。
这四个原则是面向对象编程的基础,它们使得代码更易于理解、维护和扩展。
模板类
模板类是C++中一种特殊的类,它可以用于创建处理不同数据类型的类的蓝图。模板类的定义以关键字template
开始,后面跟一个或多个模板参数。
例如,你可以定义一个模板类Array
,它可以用于创建处理不同类型元素的数组:
1 |
|
在这个例子中,T
是一个类型模板参数,N
是一个非类型模板参数。你可以用任何类型替换T
,用任何整数替换N
,来创建不同的Array
类:
1 |
|
模板类与一般的类的主要区别在于,模板类可以处理多种类型的数据,而一般的类只能处理特定类型的数据。模板类提供了一种机制,使得你可以在类定义时不指定具体的类型,而是在使用类时指定类型。这使得你的代码更加灵活,可以处理多种类型的数据。
成员初始化列表
成员初始化列表(Member Initializer List)是C++中一个重要的特性,它允许在构造函数中初始化类的成员变量。这个特性不仅可以提高代码的清晰度和执行效率,还是某些情况下初始化成员变量的唯一方法。
在C++中,构造函数可以包含一个初始化列表,用于在进入构造函数体之前初始化成员变量。初始化列表以冒号(:)开头,后面跟着用逗号(,)分隔的初始化表达式。例如:
1 |
|
在这个例子中,*x_和y_*是通过成员初始化列表进行初始化的,而不是在构造函数体内赋值。
有几种情况下必须使用成员初始化列表:
- const成员变量:const成员变量必须在声明时初始化,不能在构造函数体内赋值。
- 引用成员变量:引用成员同样需要在声明时初始化。
- 没有默认构造函数的类类型成员:如果类成员本身是一个对象,并且这个对象没有无参的构造函数,那么必须通过成员初始化列表来调用其有参构造函数。
- 继承中的基类成员:如果需要在子类中初始化基类的私有成员,也需要在成员初始化列表中显式调用基类的构造函数。
使用成员初始化列表初始化类成员比在构造函数体内赋值有几个优势:
- 性能:对于类类型的成员变量,使用成员初始化列表可以避免调用默认构造函数后再赋值,减少了一次不必要的构造和析构过程,提高了效率。
- 顺序:成员变量的初始化顺序与它们在类定义中的声明顺序一致,与初始化列表中的顺序无关。这有助于避免一些因初始化顺序不当导致的错误。
1 |
|
在这个示例中,类A的成员a是一个const成员,类B的成员b是一个引用成员。它们都通过成员初始化列表进行了初始化。
其它
-
在C++中,如果一个类没有明确指定访问修饰符(
public
、protected
或private
),那么默认的访问级别是private
。这意味着,如果你没有在类定义中看到任何访问修饰符,那么该类的所有成员都是私有的。 -
初始赋值:
1
2
3
4
5class FeatureTracker
{
private:
int trackIDCounter_ = 1;
}trackIDCounter_ = 1;
是对成员变量trackIDCounter_
的初始赋值。这意味着,当创建这个类的对象时,trackIDCounter_
的初始值将被设置为1。在类的成员函数中,你可以更改这个成员变量的值。例如,你可以在一个成员函数中增加
trackIDCounter_
的值:1
2
3void FeatureTracker::incrementTrackID() {
trackIDCounter_++;
}在这个例子中,
incrementTrackID
函数将trackIDCounter_
的值增加1。你可以在类的任何成员函数中更改trackIDCounter_
的值,只要这个函数有权限访问这个成员变量(即,这个函数不是const
的)。 -
等等。
结构体
概述
定义一个结构体类型就类似于定义了一个变量类型,结构体的用法类似于变量,只不过一个结构体里可以包含多个变量类型。
结构体(struct
)在C++中是一种复合数据类型,它可以包含多个不同类型的数据成员。你可以将结构体看作是一个“自定义的数据类型”,这个数据类型可以包含多个其他的数据类型。
例如,你可以定义一个BoundingBox
结构体来表示一个目标检测框,这个结构体包含了x
、y
、w
、h
四个整型数据成员。然后,你就可以像使用其他数据类型一样使用这个BoundingBox
结构体,例如创建BoundingBox
类型的变量,将BoundingBox
类型的变量作为函数的参数,等等。
将一组相关的变量定义为结构体(struct
)有以下几个好处:
-
组织性:结构体可以将一组相关的数据组织在一起,使代码更加清晰和易于理解。
-
代码复用:你可以多次使用同一个结构体,这有助于减少代码重复。
如果你需要在其他地方使用相同的一组变量,你可能需要再次声明和初始化这些变量,这就是代码重复。但是,如果你将这些变量定义为一个结构体,你就可以在需要的地方创建这个结构体的实例,而不需要重复声明和初始化这些变量。这就是代码复用。以下是一个例子:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20struct BoundingBox {
int x;
int y;
int w;
int h;
};
void processBoundingBox(BoundingBox box) {
// 在这个函数中处理目标检测框
}
int main() {
BoundingBox box1 = {1, 2, 3, 4};
processBoundingBox(box1);
BoundingBox box2 = {5, 6, 7, 8};
processBoundingBox(box2);
return 0;
}在这个例子中,我们定义了一个
BoundingBox
结构体,并在processBoundingBox
函数中使用它。在main
函数中,我们创建了两个BoundingBox
的实例box1
和box2
,并将它们传递给processBoundingBox
函数进行处理。这样,我们就复用了BoundingBox
结构体的定义,而不需要在每个需要使用目标检测框的地方都声明和初始化x
、y
、w
、h
这四个变量。 -
易于维护:如果你需要修改这组数据,只需要在一个地方(即结构体定义)进行修改,而不是在代码的多个地方。
当你需要修改或更新代码时,结构体可以使这个过程更加简单和直接。举个例子,假设你有一个关于目标检测框的结构体:
1
2
3
4
5
6struct BoundingBox {
int x;
int y;
int w;
int h;
};现在,你想要添加一个新的属性,比如目标检测框的颜色。如果你没有使用结构体,你可能需要在代码的多个地方添加新的变量,并且需要确保这些变量在所有的函数和方法中都被正确地更新和使用。
但是,如果你使用了结构体,你只需要在结构体的定义中添加新的属性:
1
2
3
4
5
6
7struct BoundingBox {
int x;
int y;
int w;
int h;
std::string color;
};这样,所有使用
BoundingBox
的地方都会自动获得新的color
属性,你只需要在适当的地方更新和使用这个新的属性即可。这就是结构体使代码"易于维护"的一个例子。 -
封装:结构体可以封装数据和操作,使得数据和操作紧密相关,提高代码的可读性和可维护性。
使用
定义
在C++中,struct
可以包含各种类型的成员,包括基本类型(如int
、double
等)、类对象、数组、vector
等。以下是一个例子:
1 |
|
在这个例子中,MyStruct
包含一个int
类型的成员id
和一个vector<int>
类型的成员values
。
你可以定义一个结构体来表示单个目标检测框,然后使用vector
来存储多个这样的目标检测框。这样做的好处是,你可以很容易地添加、删除和遍历目标检测框,而且代码的可读性和可维护性也会提高。
1 |
|
在这个例子中,BoundingBox
是一个结构体,它表示一个目标检测框。boxes_left
和boxes_right
是两个vector
,它们分别存储左目和右目的目标检测框。
当你需要添加一个新的目标检测框时,你可以创建一个BoundingBox
的实例,设置它的属性,然后将它添加到boxes_left
或boxes_right
中。当你需要遍历所有的目标检测框时,你可以遍历boxes_left
或boxes_right
,并对每个BoundingBox
实例进行操作。
赋值与引用
在C++中,你可以通过.
操作符来引用struct
中的成员。如果你的struct
中有一个vector
成员,你可以像下面这样引用它:
1 |
|
在这个例子中,MyStruct
是一个结构体,它有一个vector<int>
类型的成员values
。在main
函数中,我们创建了一个MyStruct
类型的变量s
,然后通过.
操作符来访问和操作它的values
成员。
1 |
|
在这个例子中,我们首先创建了一个BoundingBox
的实例box
,然后设置了box
的属性,最后将box
添加到boxes_left
中。这个过程在循环中重复,直到处理完所有的目标检测框。
我们使用了boxes_left[i]
来访问boxes_left
中的第i
个元素,然后使用.
操作符来访问这个元素的数据成员。这个过程在循环中重复,直到处理完boxes_left
中的所有元素。
在C++中,你可以使用vector
的clear
方法来清空vector
中的所有元素。以下是一个例子:
1 |
|
在这个例子中,boxes_left.clear()
将清空boxes_left
中的所有元素。这个操作将使boxes_left
的大小变为0,但不会改变它的容量。如果你希望同时清空vector
的元素和容量,你可以使用swap
方法:
1 |
|
在这个例子中,vector<BoundingBox>().swap(boxes_left)
将创建一个新的空vector
,然后与boxes_left
交换。这个操作将使boxes_left
的大小和容量都变为0。
清空/初始化
在C++中,你可以使用构造函数或者赋值运算符来初始化或清空一个结构体的值。以下是一个例子:
1 |
|
在这个例子中,BoundingBox
结构体有一个构造函数,它将所有的数据成员初始化为0。clear
方法将所有的数据成员设置为0。在main
函数中,我们创建了一个BoundingBox
类型的变量box
,然后使用构造函数和clear
方法来初始化和清空box
的值。
作为函数的参数
在C++中,你可以将结构体作为函数的输入参数或输出参数。以下是一个例子:
1 |
|
在这个例子中,printPoint
函数接收一个Point
类型的参数,getPoint
函数返回一个Point
类型的值。在main
函数中,我们创建了两个Point
类型的变量p1
和p2
,并使用printPoint
函数打印它们的值。
vector
添加元素
如果vector
变量的大小没有预先定义,你需要先调用resize
函数来设置它们的大小,然后再使用索引来赋值。或者,你可以使用push_back
或emplace_back
来添加元素。
以下是使用resize和索引赋值的示例:
1 |
|
以下是使用push_back
添加元素的示例:
1 |
|
如果你知道vector
的最终大小,那么使用resize
函数来预先设置大小,然后使用索引来赋值,通常会更快。这是因为resize
函数会一次性分配足够的内存,而不需要在每次添加元素时重新分配和复制内存。此外,使用索引来赋值可以直接访问和修改vector
中的元素,而不需要调用push_back
或emplace_back
函数。
如果你不知道vector
的最终大小,或者vector
的大小可能会动态变化,那么使用push_back
或emplace_back
来添加元素可能会更方便。这是因为push_back
和emplace_back
函数可以在不知道vector
最终大小的情况下动态添加元素。但是,你需要注意的是,如果vector
的大小增长很快,push_back
和emplace_back
函数可能需要多次重新分配和复制内存,这可能会影响性能。
总的来说,如果你知道vector
的最终大小,推荐使用resize
和索引赋值。如果你不知道vector
的最终大小,或者vector
的大小可能会动态变化,推荐使用push_back
或emplace_back
。
clear与容量
clear
在C++中,你可以使用vector
的clear
方法来清空vector
中的所有元素。以下是一个例子:
1 |
|
在这个例子中,curBoxesLeft_.clear()
将清空curBoxesLeft_
中的所有元素。这个操作将使curBoxesLeft_
的大小变为0,但不会改变它的容量。如果你希望同时清空vector
的元素和容量,你可以使用swap
方法:
1 |
|
在这个例子中,vector<BoundingBox>().swap(curBoxesLeft_)
将创建一个新的空vector
,然后与curBoxesLeft_
交换。这个操作将使curBoxesLeft_
的大小和容量都变为0。
然后,你可以使用push_back
或emplace_back
方法向curBoxesLeft_
添加新的目标检测框。例如:
1 |
|
在这个例子中,我们首先创建了一个新的BoundingBox
对象newBox
,然后将其添加到curBoxesLeft_
中。
在C++中,
vector
的clear
方法会移除vector
中的所有元素,但不会释放内存。如果vector
是空的(即,没有赋过值),那么clear
操作实际上什么也不会做,但这并不会导致问题。以下是一个例子:
1
2
std::vector<int> v;
v.clear(); // 这是安全的,即使v是空的在这个例子中,我们创建了一个空的
vector
,然后对其执行了clear
操作。这不会导致错误或异常。
容量
在C++中,vector
的容量(capacity)是指vector
在不重新分配内存的情况下可以存储的元素的最大数量。这个值通常大于或等于vector
的大小(size),vector
的大小是指它当前实际包含的元素的数量。
当你向vector
添加元素时,如果vector
的大小超过了它的容量,那么vector
会重新分配内存,以便能够存储更多的元素。这个过程可能会消耗一些时间,因此,如果你知道vector
将需要存储大量的元素,你可以使用reserve
方法来预先分配足够的内存,这样可以提高代码的性能。
1 |
|
在这个例子中,我们首先创建了一个空的vector
,然后添加了一个元素,然后预先分配了足够的内存来存储100个元素。你可以看到,vector
的大小和容量在这个过程中是如何变化的。
其它
std::vector
的赋值操作符(=
)执行的是内容的复制,而不是地址的复制。
mutex
互斥量(mutex),它是一种同步原语,用于保护共享数据免受多个线程同时访问。在多线程环境中,如果多个线程试图同时访问和修改同一块数据,可能会导致数据不一致和未定义的行为。为了防止这种情况,我们可以使用互斥量来确保在任何时候只有一个线程能够访问该数据。
比如在回调函数中被持续赋值,在其它函数中被修改。
即使变量在其他函数中只被读取,也需要保护它,因为在多线程环境中,一个线程可能在另一个线程正在写入变量的同时读取该变量,这可能会导致读取到的数据是不一致或者无效的。这种情况被称为“读-写冲突”。
一个或多个共享资源(例如,一个全局变量或一个在多个线程之间共享的数据结构),当一个线程想要访问这个共享资源时,它需要首先锁定(lock)互斥量。如果互斥量已经被另一个线程锁定,那么这个线程将会被阻塞,直到互斥量被解锁(unlock)。当线程完成对共享资源的访问后,它需要解锁互斥量,以便其他线程可以锁定互斥量并访问共享资源。
lock
1 |
|
你需要手动调用
lock()
和unlock()
来锁定和解锁互斥量。这种方式的问题是,如果在lock()
和unlock()
之间的代码抛出了异常,那么unlock()
可能永远不会被调用,从而导致死锁。
示例:
1 |
|
lock_guard
在C++中,std::lock_guard
对象的作用域是由其所在的代码块(即最近的大括号{}
内的区域)决定的。当std::lock_guard
对象在代码块内创建时,它会自动锁定传递给它的互斥量。当std::lock_guard
对象超出其作用域(即离开其所在的代码块)时,它的析构函数会被调用,从而自动解锁互斥量。
1 |
|
使用了
std::lock_guard
,这是一个RAII(Resource Acquisition Is Initialization)机制的互斥包装器,它在构造时提供一个已锁定的互斥,并在析构时解锁互斥。这意味着当std::lock_guard
对象超出其作用域并被销毁时,互斥量会自动被解锁,即使在lock_guard
的作用域内的代码抛出了异常。这样可以避免死锁,并使代码更安全、更易于理解。
示例:
1 |
|
Eigen
Matrix类与Array类
相对于Matrix类提供的线性代数(矩阵)运算,Array类提供了更为一般的数组功能。Array类为元素级的操作提供了有效途径,比如点加(每个元素加值)或两个数据相应元素的点乘。
1 |
|
OpenCV
在 OpenCV 中,
cv::Mat::at
函数的参数顺序是(row, column)
,对应于(y, x)
,而不是通常的(x, y)
。这是因为在图像处理中,我们通常将图像视为一个二维数组,其中第一个维度是行(对应于y坐标),第二个维度是列(对应于x坐标)。
OpenCV的数据类型
S = 有符号整型 U = 无符号整型 F = 浮点型
CV_8U - 8位无符号整数(0…255)
CV_8S - 8位有符号整数(-128…127)
CV_16U - 16位无符号整数(0…65535)
CV_16S - 16位有符号整数(-32768…32767)
CV_32S - 32位有符号整数(-2147483648…2147483647)
CV_32F - 32位浮点数(-FLT_MAX…FLT_MAX,INF,NAN)
CV_64F - 64位浮点数(-DBL_MAX…DBL_MAX,INF,NAN)
而后面的C1、C2、C3是什么意思呢?这里的1、2、3代表的是通道数,比如RGB就是3通道,颜色表示最大为255,所以可以用CV_8UC3这个数据类型来表示;灰度图就是C1,只有一个通道;而带alph通道的PNG图像就是C4,是4通道图片。
负值处理
cv::Mat
矩阵可以存储负值并进行运算,但这取决于你选择的数据类型。OpenCV中的cv::Mat
可以存储多种类型的数据,包括有符号整数和浮点数。
例如,如果你选择CV_32F
(32位浮点数)或CV_64F
(64位浮点数)作为你的数据类型,那么你的cv::Mat
矩阵就可以存储负值,并且可以进行各种运算,如加法、减法、乘法等。
以下是一个简单的例子:
1 |
|
在这个例子中,我们创建了一个3x3的矩阵,所有元素的初始值都是-1,然后我们将第一行第一列的元素值设置为-5。
当你使用OpenCV的imshow
函数来可视化一个cv::Mat
矩阵时,负值的处理方式取决于矩阵的数据类型。
如果你的cv::Mat
矩阵的数据类型是无符号整数(如CV_8U
),那么它不能存储负值,任何负值都会被视为零。
如果你的cv::Mat
矩阵的数据类型是有符号整数(如CV_8S
,CV_16S
)或浮点数(如CV_32F
,CV_64F
),那么它可以存储负值。在可视化这样的矩阵时,你需要先将矩阵的值规范化到0-255的范围内。你可以使用OpenCV的normalize
函数来实现这一点。
以下是一个例子:
1 |
|
在这个例子中,我们首先创建了一个3x3的矩阵,所有元素的初始值都是-1。然后我们使用cv::normalize
函数将矩阵的值规范化到0-255的范围内,并将数据类型转换为CV_8U
。最后,我们使用cv::imshow
函数显示规范化后的图像。
访问cv::Mat
对象中特定位置的像素值
在OpenCV中,有几种方法可以访问cv::Mat
对象中特定位置的像素值。以下是一些常见的方法:
-
使用
at
函数:这是最直接的方法,你可以使用模板参数来指定像素的数据类型。例如,如果你的图像是一个8位单通道图像,你可以这样访问像素值:1
unsigned char pixel_value = cur_event_mat_left_fft.at<unsigned char>(e_left.y, e_left.x);
在你的代码中,你使用了
cur_event_mat_left_fft.at<char>(i, j)
来访问像素值。这里的char
是一个有符号的8位整数,范围是-128到127。如果一个像素的真实值是255(在unsigned char
中表示为255),在char
中它会被表示为-1。为了解决这个问题,你应该使用
unsigned char
来访问cv::Mat
的数据。 -
使用
ptr
函数:这个函数返回一个指向图像某一行的指针,然后你可以像操作普通数组一样操作这个指针。这个方法通常比at
函数快,但是也更容易出错,因为你需要自己管理指针。例如:1
2unsigned char* row_ptr = cur_event_mat_left_fft.ptr<unsigned char>(e_left.y);
unsigned char pixel_value = row_ptr[e_left.x]; -
使用迭代器:你也可以使用C++的迭代器来访问
cv::Mat
中的像素。这个方法比较安全,但是通常比at
和ptr
函数慢。例如:1
2cv::Mat_<unsigned char>::iterator it = cur_event_mat_left_fft.begin<unsigned char>() + e_left.y * cur_event_mat_left_fft.cols + e_left.x;
unsigned char pixel_value = *it;
以上三种方法都可以用来访问和修改像素值。你可以根据你的需要选择最适合你的方法。
赋值
在OpenCV中,cv::Mat
的赋值操作符(=
)实际上是创建了一个新的头部,但是数据是共享的。这意味着,如果你有两个cv::Mat
对象A
和B
,并且你执行了A = B;
,那么A
和B
将共享相同的数据。如果你修改了A
中的数据,B
中的数据也会被修改。
这是因为cv::Mat
使用了引用计数机制来管理数据。当你创建一个新的cv::Mat
对象并赋值给另一个cv::Mat
对象时,它们都会指向同一个数据,而且这个数据的引用计数会增加。当一个cv::Mat
对象被销毁时,它会减少数据的引用计数。只有当引用计数变为0时,数据才会被释放。
如果你希望创建一个cv::Mat
的真正副本,你可以使用clone
或copyTo
方法。例如,cv::Mat B = A.clone();
将创建一个新的cv::Mat
对象B
,它包含了A
的一个副本。在这种情况下,A
和B
不会共享数据。
clone和copyTo
在OpenCV中,clone
和copyTo
函数都可以用来复制cv::Mat
对象,但它们的使用方式和行为有一些不同。
clone
函数创建一个新的cv::Mat
对象,并复制源对象的所有数据。它不需要一个已经存在的目标对象,因为它会创建一个新的对象。例如:
1 |
|
copyTo
函数将源对象的数据复制到目标对象。如果目标对象已经存在,它的大小和类型必须与源对象匹配,否则它会被重新分配。copyTo
还有一个可选的参数,允许你指定一个掩码,只有掩码中非零的元素才会被复制。例如:
1 |
|
总的来说,如果你只需要复制一个cv::Mat
对象,并且不需要使用掩码,那么clone
可能是更简单的选择。如果你需要使用掩码,或者你已经有一个目标对象并希望复制数据到这个对象,那么copyTo
可能是更好的选择。
组织管理一组变量或函数
在C++中,除了结构体(struct
),还有以下几种方式可以组织和管理一组变量或函数:
- 类(Class):类是C++中的一个核心概念,它可以包含变量(称为成员变量)和函数(称为成员函数)。类提供了封装、继承和多态等面向对象编程的特性。
- 命名空间(Namespace):命名空间可以用来组织一组相关的变量和函数,以避免命名冲突。
- 数组(Array)和向量(Vector):如果你有一组相同类型的变量,你可以使用数组或向量来存储它们。
- 函数(Function):如果你有一组相关的操作,你可以将它们封装在一个函数中。
- 枚举(Enum):如果你有一组相关的常量,你可以使用枚举来定义它们。
- 联合(Union):联合是一种特殊的数据类型,它可以存储不同类型的数据,但一次只能存储其中一种类型的数据。
选择哪种方式取决于你的具体需求和使用场景。
Python
if name == "main":
在Python中,if __name__ == "__main__":
是一个常见的模式。这行代码的作用是检查当前的模块是被直接运行还是被导入为一个模块。
当Python解释器读取一个源文件时,它会首先定义一些特殊的变量。其中一个就是 __name__
。如果该文件被直接运行,那么 __name__
的值会被设置为 "__main__"
。如果该文件被其他Python文件导入,那么 __name__
的值则会被设置为该文件的名字。
因此,if __name__ == "__main__":
这行代码的意思是,"如果这个文件被直接运行,那么执行以下的代码"。这个模式常常被用来在一个Python文件中编写一些测试代码,这些测试代码只有在文件被直接运行时才会执行,而在文件被导入时不会执行。
读取文件
1 |
|
在Python中,使用with
语句打开文件时,当with
语句的代码块执行完毕后,文件会自动关闭。所以,你不需要显式地调用file.close
。
这是因为with
语句创建了一个上下文,当离开这个上下文时,Python会自动清理相关的资源。在这个例子中,相关的资源就是打开的文件。
1 |
|
在data = yaml.safe_load(file)
这行代码执行完毕后,file
会自动关闭,无需手动关闭。
内存泄漏
什么是内存泄漏?
内存泄漏是指程序中已分配的内存未能(在离开其作用域、程序运行结束后)成功释放,导致可用内存逐渐减少的现象。在程序运行过程中,如果反复发生内存泄漏,最终可能会导致系统可用内存耗尽,从而影响程序的性能或导致程序崩溃。内存泄漏在长时间运行的程序中尤其危险,例如服务器或持续运行的后台任务。
内存泄漏的原因
内存泄漏通常发生在以下几种情况:
-
未释放动态分配的内存:当使用如
malloc
,calloc
,realloc
和new
等函数分配内存后,未使用对应的 free 或 delete 来释放内存。 -
资源占用:除了内存外,程序可能申请其他系统资源(如文件句柄、数据库连接等),未正确释放这些资源也会导致类似内存泄漏的问题。
-
数据结构错误:例如,链表、树等数据结构若未正确处理其元素的删除操作,可能导致部分节点成为不可达的,从而造成内存泄漏。
如何判断内存泄漏?
判断和诊断内存泄漏通常需要以下几个步骤或工具:
- 代码审查:通过审查代码来寻找可能未释放内存的地方。特别关注那些有动态内存分配的函数或模块。
- 运行时工具:
- Valgrind:这是一个编程工具,用于内存调试、内存泄漏检测等。在 Linux 环境下,使用 Valgrind 运行程序可以帮助检测内存泄漏。
- Visual Studio:在 Windows 环境下,Visual Studio IDE 提供了内置的内存泄漏检测工具。
- Sanitizers:如 AddressSanitizer,这是一种快速的内存错误检测工具,可以集成到 GCC 或 Clang 编译器中,用于检测内存泄漏和其他内存相关错误。
- 性能监控工具:使用系统或第三方性能监控工具来观察程序的内存使用情况,查看内存使用是否随时间持续增加。
- 日志和追踪:在代码中添加日志输出,特别是在分配和释放资源的地方,可以帮助追踪内存的使用和释放。
如何防止内存泄漏?
- 使用智能指针:在 C++中使用
std::unique_ptr
,std::shared_ptr
等智能指针可以自动管理内存,大大减少内存泄漏的风险。 - 资源获取即初始化(RAII, Resource Acquisition Is Initialization):这是一种编程范式。资源的获取即是初始化,资源的释放即是销毁。确保在对象的生命周期内资源被正确管理。通过在对象的构造函数中分配资源,并在析构函数中释放资源,可以保证资源总是被正确管理。
- 定期代码审查:定期进行代码审查可以帮助识别潜在的内存泄漏问题。
- 自动化测试:编写测试用例,尤其是针对资源管理的单元测试,可以在开发过程中早期发现和解决内存泄漏问题。
其它
-
code
单词做代码释义时是不可数名词。1
2不可数名词
Computer code is a system or language for expressing information and instructions in a form which can be understood by a computer. -
Python 并没有强制要求你用
Tab
缩进或者用空格缩进,但在PEP8中,建议使用4个空格来缩进。对于任何一个编辑器或者IDE,一般都有配置选项,可以设置把TAB
键展开为4个空格,以保证代码的兼容性。 -
命令行使用
\
实现换行:1
2
3sudo apt-get install \
ros-$1-sophus \
ros-$1-pcl-ros -
报错:
SyntaxError: Non-ASCII character '\xe5'
原因:Python默认是以ASCII作为编码方式的,如果在自己的Python源码中包含了中文(或者其他非英语系的语言),此时即使你把自己编写的Python源文件以UTF-8格式保存了,但实际上,这依然是不行的。
解决:在源代码的第一行加入:
1
# -*- coding: UTF-8 -*-
-
python引入本地字体:
1
2
3
4
5
6
7
8
9
10
11import matplotlib.font_manager as fm
fm.fontManager.addfont('/usr/share/fonts/truetype/Times-New-Roman/times.ttf')
fm.fontManager.addfont('/usr/share/fonts/truetype/simsun/simsun.ttc')
plt.title('层次聚类树形图', fontname="simsun", fontsize=30)
plt.xticks(fontname="Times New Roman", fontsize=26)
colorbar = plt.colorbar()
colorbar.ax.tick_params(labelsize=24) # 设置坐标轴标签的字体大小
for l in colorbar.ax.yaxis.get_ticklabels():
l.set_family('simsun') -
idea错误提示File was loaded in the wrong encoding: ‘UTF-8‘解决方法:
- 打开乱码文件,在软件右下角将当前页面的编码格式改为GB2312,弹出的提示消息中选择Reload;
- 在软件右下角将当前页面的编码格式改为utf-8,弹出的提示消息中选择Convert;
- 参考链接
-
如果你不想运行
predict.py
文件的main
函数第136行以下的代码,你可以使用Python的return
语句来提前结束函数的执行。你需要找到第136行的代码,并在其前面添加return
语句。这将导致函数在执行到这一行时立即返回,不再执行后续的代码。在循环语句里,可以使用
continue
。 -
syntax error near unexpected token '$'{\r''
字面意思上看是换行符出现问题,怀疑是Win下编辑过。
1
2
3# 用vim -b 查看,发现每一行多了~M
# 解决方法:
sed -i 's/\r//g' xxx.sh -
变量的定义和使用只在最近的
{}
内((主)函数、if
、for
等)适用。如果想要拓展变量的使用范围,可以在更外处的{}
内定义变量(然后在别处赋值);或者,声明为全局变量。 -
在C++中,函数的默认参数值通常在函数声明中给出,也就是在
.h
头文件中(而不用在函数定义的*.cpp
文件中再给出默认值)。这样,任何包含这个头文件的代码都可以看到这些默认值,并且可以选择是否提供自己的参数值。 -
一般来说,如果有 3 个或更多
if-else
分支,则应该考虑使用switch
。如果有10个或更多条件分支,您应该考虑使用config
变量或文件,并为该config
编写特定的函数来进行映射。如果映射逻辑复杂但使用频繁,可以考虑创建专用的规则引擎或DSL来处理。if
语句和switch
语句的选择:多个等值判断选择switch
,其它情况(区间范围等)选择if
。 -
在C++中,
i++
和++i
都会增加i
的值,但它们的主要区别在于它们的返回值和执行顺序。i++
是后置递增运算符。它首先返回i
的当前值,然后再将i
的值增加1。例如:1
2int i = 5;
int j = i++; // j现在是5,i现在是6++i
是前置递增运算符。它首先将i
的值增加1,然后返回新的i
值。例如:1
2int i = 5;
int j = ++i; // j现在是6,i现在也是6 -
在C++中,
#include <filename>
和#include "filename"
的主要区别在于编译器搜索头文件的方式。#include <filename>
:编译器在标准库路径中搜索头文件。这通常用于包含标准库的头文件,如<iostream>
、<vector>
、<mutex>
等。#include "filename"
:编译器首先在当前文件的目录中搜索头文件,如果在当前目录中找不到,编译器会在标准库路径中搜索。这通常用于包含用户自定义的头文件。
-
variable
的生命周期取决于它在哪里声明和定义。如果它在while
循环外部定义,那么它将在包含该循环的函数或作用域结束时被销毁。如果它在while
循环内部定义,那么它将在每次循环结束时被销毁,并在下一次循环开始时重新创建。 -
把函数的输入参数行想成是对输入参数的定义,把函数的调用行想成是对输入参数的赋值,函数内部就是变量赋值后的进一步操作。
-
变量
l
(包括函数的输入参数)被定义为const
,这意味着它的值在初始化后不能被修改。这是一种良好的编程实践,可以防止在后续的代码中意外修改l
的值。然而,如果你确定在后续的代码中不会修改
l
的值,那么将l
定义为const
并不是必须的。在这种情况下,将l
定义为const
主要是为了提高代码的可读性和可维护性,它可以让其他阅读你代码的人知道l
的值在初始化后不应该被修改。总的来说,是否将
l
定义为const
取决于你的编程风格和项目的编码规范。如果你的项目鼓励使用const
来增强代码的可读性和可维护性,那么将l
定义为const
是一个好的选择。 -
命令行参数:
1
2
3
4import sys
if __name__ == "__main__":
npy_file = sys.argv[1] if len(sys.argv) > 1 else __file__sys.argv
是一个包含命令行参数的列表,其中sys.argv[0]
是脚本的名称,sys.argv[1]
是第一个参数,sys.argv[2]
是第二个参数,以此类推。 -
初始化 ROS 工作空间:
1
alias initROS="mkdir -p catkin_ws/src && cd catkin_ws && catkin config --init --mkdirs --extend /opt/ros/melodic --merge-devel --cmake-args -DCMAKE_BUILD_TYPE=Release && cd src && catkin_init_workspace && cd .. && catkin build"
-
等等。
Windows
快捷键
快捷键 | 作用 |
---|---|
虚拟桌面 | |
Win+Ctrl+D | 创建新的虚拟桌面,并切换到它 |
Win+Ctrl+<-/-> | 切换桌面 |
Win+Tab | 打开任务视图 |
窗口 | |
Ctrl+N | 新建当前窗口 |
Ctrl+Shift+N | 新建文件夹 |
Win+ D | 将所有打开的窗口最小化,并转到桌面,再次按下即刻恢复所有内容 |
Win+<-/-> | 可以自动将应用窗口完全贴靠到屏幕两侧 |
Win+ ↑ | 可以将应用窗口最大化 |
Win+ ↓ | 可以将应用窗口最小化。若是当前窗口处于最大化状态,则按住 Win 键不放,连续按两下 ↓ 键即可将窗口最小化。 |
其它一些可参考Linux快捷键。
其它:
- 将窗口移动到另一个虚拟桌面(不幸的是,Windows不包括直接用于在虚拟桌面之间移动窗口的键盘快捷键):Win+Tab打开任务视图后,使用鼠标拖动应用窗口到另一个虚拟桌面。
Powershell
-
Powershell配置alias:
-
打开Powershell,执行
echo $PROFILE
命令,确定新建文件的名称和位置。 -
例如:在
C:\Users\username\Documents\WindowsPowerShell
文件夹下新建Microsoft.PowerShell_profile.ps1
文件。 -
按格式输入命令:
1
2function 别名 { 需要替代的命令 }
Set-alias 's' 'Select-Object' -
以管理员身份打开Powershell 执行命令:
1
2Set-ExecutionPolicy RemoteSigned
# `Set-ExecutionPolicy RemoteSigned` 是一个 Windows PowerShell 命令,用于设置 PowerShell 执行策略。执行策略用于控制是否允许在系统上运行脚本,以及允许哪些来源的脚本运行。 -
重启Powershell.
-
-
Powershell 执行多条命令:
1
2
3# 在powershell7上已经支持&&操作,如低于该版本请继续往下看
(mkdir test) -and (cd test)
mkdir test;cd test -
PowerShell命令自动补全:按Tab键。
-
在开始配置之前,请以管理身份运行
Install-Module -Name PSReadLine
将其更新到最新版本。安装路径:
C:\Program Files\WindowsPowerShell\Modules\PSReadLine
-
利用
Set-PSReadlineKeyHandler
命令可以设置 PowerShell 中所有按键及按键组合的功能。默认情况下,Tab 键的补全是行内补全,我们可以将其调整为带菜单的补全:1
Set-PSReadlineKeyHandler -Chord Tab -Function MenuComplete
Tab 键此时会弹出所有的可能选项,上下左右按键可切换候选,Esc 键取消补全。
-
根据历史输入提供建议。重启PowerShell(以管理员身份),使用命令
Set-PSReadLineOption -PredictionSource History
打开,默认为行内补全,按键盘右箭头接受补全建议。(可选,不推荐)如果需要用列表形式补全的话,添加-PredictionViewStyle ListView
参数即可。
-
-
将cmd/powershell中的打印信息输出保存为txt文本文件:
1
ping www.baidu.com >D:/log.txt
保存界面上已经执行过的输出内容:点击powershell左上角的下三角形,或直接
Ctrl+Shift+P
打开命令面板,找到“导出文本”选项,确认保存位置。 -
打开网址:
1
2
3
4# starts with default browser and adds to open browser
Start-Process -FilePath www.google.com
# starts with a specific browser
Start-Process -FilePath iexplore -ArgumentList www.google.com
小贴士
文件(夹)命名
命名规范
为了确保日常使用的方便与系统兼容性,个人电脑上的文件夹和文件命名应遵循一定的规范。以下是一些通用的指导原则和最佳实践:
- 简洁明了:命名应简单且直接表达文件内容或文件夹用途,避免过长或含糊不清的名称。
- 避免使用特殊字符:一般来说,文件名中不应包含如下字符:
\/:*?"<>|
。这些特殊字符在多数操作系统中有特定用途,可能导致错误或不被识别。 - 使用合适的文件扩展名:文件扩展名反映了文件的类型,如
.txt
表示文本文件,.jpg
表示JPEG图像文件。正确的扩展名有助于操作系统正确识别和打开文件。 - 利用日期和版本控制:在文件名中包含日期(如
2023-03-15
)可以帮助管理文件版本。对于频繁更新的文档,可能还需要添加版本号(如v1.0
、v2.0
)。 - 避免空格:虽然大多数现代操作系统可以很好地处理文件名中的空格,但在某些情况下(如命令行操作或网络传输)可能会引起问题。可以使用下划线(_)或破折号(-)来代替空格。
- 区分大小写:虽然Windows系统的文件名不区分大小写,但Linux和macOS区分大小写。为了兼容性,最好在命名时注意大小写的一致性。
- 组织性和易读性:对于包含多个文件的文件夹,可以采用有组织的命名体系,如按功能、日期或项目分类。
- 国际化和本地化:如果你的文件可能会被不同语言的用户访问,避免使用特定语言的字符,尽量使用英文和国际通用的符号。
遵循这些基本原则可以帮助提升文件管理的效率和降低因命名不当引起的问题。如果有特定的组织或项目命名规则,也应优先遵守这些专业指导标准。
命名方法
整词用驼峰或下划线
_
命名,整词间用-
分隔。
在个人电脑上命名文件夹和文件时,虽然没有像编程语言那样严格的命名规范,但仍可以借鉴一些编程中的命名方法来组织文件,使其更加有序和易于管理。以下是一些常见的命名方法,以及如何将它们应用于文件和文件夹的命名:
- 小驼峰式命名法 (lowerCamelCase):
- 应用: 适用于文件名,尤其是在需要保持名称简洁且连贯的情况下。
- 示例:
personalBudget.xlsx
,summerVacationPhotos.jpg
- 大驼峰式命名法 (UpperCamelCase):
- 应用: 适合用于较重要的文件和项目文件夹,特别是在编程项目中。
- 示例:
ProjectReport2023.docx
,TaxDocuments2023
- 下划线命名法 (snake_case):
- 应用: 适用于需要强调易读性的情况,也有助于避免在使用某些操作系统和环境时出现问题。
- 示例:
final_report_2023.pdf
,holiday_photos_2022
- 破折号命名法 (kebab-case):
- 应用: 更常见于网页文件和资源,但也可以用于普通文件和文件夹。
- 示例:
end-of-year-summary-2023.pdf
,project-budget-2023.xlsx
- 注意: 文件系统如Windows和记录系统通常对破折号支持良好,但在某些命令行环境中可能会遇到解释上的问题。
- 日期前缀命名:
- 应用: 对于需要按日期管理的文档或文件,如财务文件或定期报告。
- 示例:
2023-01-30_MeetingNotes.docx
,2023-03-15_Invoice.pdf
选择哪种命名方法取决于个人偏好、所处的工作环境以及是否需要跨平台交互。例如,如果在一个环境中主要用Python编程,那么使用snake_case可能更自然。而如果是在处理法律或业务文档,使用UpperCamelCase或日期前缀可能更为合适。
无论选择哪种命名方法,重要的是保持一致性,这样可以减少寻找和管理文件的时间,也能使文件系统更加有序。
中文文本中夹用英文时,是否要留有空格
推荐留空,方便整词选择。如果不留空,选择时可能会选择夹杂中英文的整行而不是单个英文单词。
中文文本中夹用英文时,应根据所选用的中英文字体、字符间距以及排版的视觉效果决定英文词句与中文文字之间是否留有空格间距。如留空格,应保证体例的统一。——中华人民共和国新闻出版行业标准CY/T 154—2017号《中文出版物夹用英文的编辑规范》第8.1节
- 但同时我更鼓励各类排版引擎自动在用户没有显式输入空格字符的情况下,也在中英文词汇间产生适当的间距。现在有一些已经做到了,比如 iOS 系统、微信、Microsoft 办公软件等。
浏览器搜索操作符
可以直接用此网址,不需要记命令,它自动组装为命令:
-
GitHub高级搜索:https://github.com/search/advanced
语法:
- 整词搜索:双引号。示例:"关键词"
- 逻辑与:空格。示例:关键词1 关键词2
- 逻辑或:空格OR空格。示例:关键词1 OR 关键词2
- 逻辑非:空格-(减号前面必须是空格,减号后面没有空格,要紧跟着需要排除的词)。示例:关键词 -排除词
- 模糊搜索:*(通配符,匹配任意词或短语)。示例:
国家* 匹夫*
- 指定网站:关键词 site:网站地址。注意:“site:”后面跟的站点域名,不要带“http://”。site:和站点名之间,不要带空格。示例: 编程教程 site:blog.csdn.net
- 排除指定网站:关键词 -site:网站地址。示例:编程教程 -site:blog.csdn.net
设置/重映射快捷键
-
添加快捷方式,右键快捷方式-属性-快捷键。参考链接
解决快捷键反应延迟问题:
下载
-
Windows系统下也可以运行Linux系统下的
*.sh
脚本/命令。网上有解决办法,例如;或者把shell脚本语言转为Powershell脚本语言。- 在Powershell下使用
Invoke-WebRequest
命令下载文件很慢。网上有解决方法,例如。 - Powershell下可以使用
wget
和curl
等命令。
- 在Powershell下使用
-
使用IDM、迅雷等下载器。
-
IDM
-
优点:(分块)下载速度快(比浏览器默认下载快);定时下载;(使用通配符)添加批量任务;运行站点抓取;资源嗅探
-
缺点:收费;(大文件)分块下载后合并下载块慢(解决:设置单线程下载;下载的临时文件夹和“保存至”文件夹不要在同一块硬盘上;高配电脑(CPU和SSD))
- 改成单线程的话,该文件下载速度会变慢。当然,如果你同时下载许多个文件,那么可以这么做,不影响。
- 同盘剪切一定快于跨盘剪切,但同盘复制一般慢于跨盘复制。
-
-
迅雷
- 优点:定时下载;下载速度快
- 缺点:国产软件……想要速度快需要开会员
-
其它
-
Anaconda Prompt与Anaconda PowerShell Prompt与系统cmd与powershell的区别:
1、anaconda prompt
优点:Python 行、
缺点:ls 不行、
2、anaconda powershell prompt【完美】
优点:Python 行、ls 行、
缺点:无
3、cmd
优点:无
缺点:Python不行;ls 不行、
4、powershell
优点:ls行
缺点:Python不行
-
剪切板历史:Win+V
-
导入环境变量:搜索“环境变量”——新建。
软件
CLion
快捷键
自定义快捷键:设置-按键映射
快捷键 | 作用 |
---|---|
搜索 | |
Shift + Shift | 全局搜索;搜索得更全,不光包括文件文本内容,还有文件夹名称,操作等 |
CTRL+SHIFT+F | 搜索整个工程或指定目录 注:连续按两次Esc,搜索框就会消失;专注搜索文件文本内容 |
CTRL+SHIFT+R | 全局搜索+替换 |
Ctrl+Shift+A | 查找(想要进行的)操作 |
ALT+F1 | 导航,查找文件所在目录位置 |
CTRL+F | 在当前窗口查找文本 |
CTRL+R | 在当前窗口替换文本 |
Ctrl+E | 最近的文件 |
Ctrl+Shift+E | 最近的位置,支持键盘直接输入模糊搜索。 |
Alt + 1 | 打开/关闭项目树 |
Alt + Shift + 1 | 在项目树定位打开的文件的位置 |
Ctrl + Tab | 跳转标签页 |
查看 | |
Ctrl+鼠标左键 | 前往声明或用法 |
Ctrl+鼠标右键 | 高亮当前区域(Rainbow Brackets插件) |
Ctrl+P | 将文本光标置于方法调用的圆括号之间,按Ctrl+P 可以显示有效形参的列表。 |
Alt+Q | 显示上下文信息(在函数内部快速查看函数定义) |
文本编辑 | |
Shift+Home/End | 选中行中鼠标前面(后面)的全部内容 |
Alt+M | String Manipulation(插件,字符串编辑) |
Ctrl+C/X | 复制/剪切整行 |
Ctrl+Y | 删除整行 |
Ctrl+Alt+Enter | 在上一行插入空行 |
Ctrl+D | 重复行或选区 |
Ctrl+Shift+上/下箭头 | 向上/下移动语句 |
Ctrl+Shift+U | 切换大小写 |
Shift+Alt+U | CamelCase(插件,一键转化变量为驼峰命名或下划线命名) |
Ctrl+Shift+J | 合并行 |
代码自动提示和补全 | 上下方向键选择,Enter插入 |
光标前/后移动一个整词 | Ctrl+<-/-> |
连续选中 | Shift+<-/-> |
整词选中 | Ctrl+Shift+<-/-> |
移动定位 | |
Ctrl+Shift+N | 转到文件 |
Ctrl+E | 最近的文件 |
CTRL+上/下箭头 | 代码向上/下滚动(类似鼠标滚动) |
ATL+上/下箭头 | 上一个/下一个变量/函数(方法) |
ALT+Shift+←/→; 鼠标侧面的两个前进和后退键 | 返回/前进上次光标所在的位置 |
CTRL+HOME/END | 光标跳转到第一行或最后一行下 |
Alt+←/→ | 上/下一个标签页 |
F4 | 跳转到源文件 |
Ctrl+G | 跳转到行/列 |
结构 | 工具栏-导航 |
Ctrl+F12 | 文件大纲 |
Alt+Shift+H/右边栏-层次结构 | 查看引入/包含文件的文件有哪些 |
Ctrl+Alt+Shift+2 | 在资源管理器里打开文件所在的特定上级文件夹 |
Ctrl+NumPad-/NumPad+ | 折叠/展开代码/文件夹 |
撤回 | |
CTRL+Z | 倒退 |
CTRL+SHIFT+Z | 向前 |
缩进与注释 | |
CTRL+ALT+I | 自动缩进 |
CTRL+/ | 注释// 添加或删除注释 |
CTRL+SHIFT+/ | 注释/*...*/ |
在函数名称上面一行输入/*! (或者/// 、/*** ) 然后回车,即可自动生成注释。 |
|
Translation翻译 | |
Ctrl+Shift+Y | 光标在单词上或选中单词 |
Ctrl+Shift+O | 打开翻译界面 |
其他 | |
Ctrl+F4/ESC | 关闭当前文件 |
按住 Alt 点击关闭标签页 | 关闭其他标签页 |
Ctrl+D | 比较文件差异。按F4跳转到源文件。直接点击<<是在同行直接接受,Ctrl+点击是换行插入。 |
F7 | 转到下一个差异 |
Shift+F6 | (批量)重命名 |
小技巧
查找用法
查找函数被调用的位置:
函数名-右键-查找用法(Alt+Shift+7
)。分为读取值(变量被赋值/数组被push/pop
的地方)和声明或预声明(变量被用到的地方)。
函数名-右键-转到-声明或用例(Ctrl+B
)/Ctrl
+鼠标左键点击。
“重命名”重构
您可以轻松重命名类、函数、变量和文件,同时在所有引用中实现自动纠正。为此,将文本光标放在所需符号上,然后按Shift+F6(重构|重命名)。开始输入新名称并在准备好后按 Enter。
用语言结构包围代码块
CLion提供了标准模板,用于将代码片段用各种基于源代码语言的结构包围起来。这包括了if...else
条件语句、do...while
和for
循环、折叠区域和其他结构。
- 选择所需的代码片段。
- 从主菜单中选择Code | Surround With,或按Ctrl+Alt+T(与打开终端的全局快捷键冲突,可以改为Ctrl+Alt+W)。
- 从列表中选择必要的包围语句。
创建带有补全功能的代码结构
您可以使用语句补全创建代码结构。开始输入方法声明、方法调用或语句,例如 if
、for do-while
、try-catch
、 switch-case
或 return
。按 Ctrl+Shift+Enter
可以将语句补全为语法正确的结构。
代码-生成
代码生成选项。使用此菜单,可以快速生成构造函数/析构函数、 getter/setter、各种运算符和实现/重写函数。
快捷键:Alt+Insert
格式化
您可以重新格式化部分代码、整个文件、文件组、目录和模块。您还可以从重新格式化中排除部分代码或某些文件。
重新格式化代码片段:
- 在编辑器中,选择要重新格式化的代码片段。如果您不选择代码片段,CLion 将重新格式化整个文件。
- (可选)预览:按 Alt+Enter 并单击调整代码样式设置。
- 格式化:在主菜单中,转到代码 |重新格式化代码 或 按 Ctrl+Alt+L 。
重新格式化行缩进:
在某些情况下,“设置”对话框中“缩进检测”部分中的“检测并使用现有文件缩进进行编辑”选项 ( Ctrl+Alt+S ) |编辑|代码样式可以覆盖您的设置。在这种情况下,CLion 将显示一条通知。
- 在编辑器中,选择必要的代码片段并按 Ctrl+Alt+I 。
- 如果需要调整缩进设置,请在“设置”对话框 ( Ctrl+Alt+S ) 中,转到编辑器 | 缩进设置-代码风格,选择您要更改缩进的语言。
- 在“制表符和缩进”选项卡上,指定适当的缩进选项,然后单击“确定”。
重新格式化文件:
- 在编辑器中打开文件并按 Ctrl+Alt+Shift+L 或在项目工具窗口中右键单击该文件并选择重新格式化代码。
- 在打开的“重新格式化文件”对话框中,如果需要,请选择以下重新格式化选项:
- 优化导入:如果您想要删除未使用的导入、添加缺失的导入或组织导入语句,请选择此选项。
- 代码清理:选择此选项可运行代码清理检查。
- 不保留换行符:根据代码样式设置重新格式化换行符。此选项会覆盖重新格式化时保留 |换行符设置。
- 单击运行。如果您想查看重新格式化期间对代码所做的确切更改,请使用本地历史记录功能。
在保存时自动格式化代码:设置-工具-保存时的操作:重新格式化代码
书签(快捷跳转)
- 添加无名行书签:在编辑器中,将光标定位在一行代码上,然后按下
F11
键。或者,右键单击要添加书签的代码行旁边的装订线,然后选择“添加书签”。 - 添加助记符行书签:在编辑器中,将光标定位在一行代码上,然后按下
Ctrl+F11
键。或者,右键单击要添加书签的代码行旁边的装订线,然后选择“添加助记符书签”。在打开的弹出窗口中,选择一个数字或字母作为此书签的标识符。或。直接按下Ctrl+Shift+Num
直接添加。 - 书签文件和文件夹:在项目工具窗口(
Alt+1
)中,右键单击要添加书签的项目,然后选择“书签” | “添加书签” (F11
) 或 “添加助记符书签” (Ctrl+F11
)。要为多个项目添加书签,请在工具窗口中选择它们,右键单击其中一个,然后选择“书签” | “添加书签” (F11
)。对于助记符书签,选择一个数字或字母作为此书签的标识符。按Enter
键或再次单击所选的字母或数字以保存书签。 - 删除书签。再次按下
F11
。 - 重命名书签:在装订线上单击书签图标,然后提供新的描述。
- 书签跳转。按住
Ctrl
键,然后按键盘上的数字助记符。或,按下Shift+F11
键,或者从主菜单中选择“编辑” | “书签” | “显示行书签”。或,转到下一个或上一个书签:“编辑” | “书签” | “上/下一行书签”。 - 所有的书签都分组在“书签”工具窗口中的列表中,您可以通过从主菜单选择“视图 | 工具窗口 | 书签”或按下
Alt+2
来打开它。您可以快速添加所有打开的文件的书签,并将这些书签添加到新列表中。
扩展代码选区(鼠标选择的区域)
要展开选区,请按Ctrl+W。每次按 Ctrl+W时,选区将扩展到代码的其他区域。例如,选区从一个方法名称扩展到调用此方法的表达式,然后扩展到整个语句,继而扩展到包含的块,等等。缩减选区:Ctrl+Shift+W。
导入缺失的头文件/函数声明
- 配置自动导入。您可以在“设置 | 编辑器 | 通用 | 自动导入”中为C/C++和其他语言配置自动导入行为。
- 对于提示找不到/未定义的变量/函数,选中,按下
Alt+Enter
。 - 在cpp文件中编写函数定义后,在函数名上按Alt+Enter组合键,然后选择“创建新函数/split function into declaration and definition”。这将在头文件中添加Class声明。
-
- 在CLion中打开源代码文件。
- 在代码中编写类的定义,并保存该文件。
- 右键单击源代码文件,选择“Refactor(重构)”,然后选择“Extract”。
- 在弹出的对话框中选择“Declaration”,然后选择“Extract”。
- 在新的头文件中,你可以看到类的声明已经被自动生成了。
从索引中排除
CLion为项目文件建立索引,以启用诸如搜索,导航,代码完成,代码生成和重构之类的功能。但是,您的项目可能包含不需要这些功能的文件,例如日志,二进制文件或导入的库。在这种情况下,为了减少索引时间,可以将文件标记为纯文本 ,将目录标记为排除或库。
临时文件
有时候您可能需要在项目上下文之外创建临时的笔记或者起草一些代码。而不必切换到不同的应用程序,您可以使用临时文件和临时缓冲区。
临时文件和缓冲区与特定项目无关。它们对于任何在特定IDE实例中打开的项目都是可用的。
创建临时文件:
- 从主菜单中选择 File | New | Scratch File,或者按下 CtrlAltShiftInsert。另外,可以在项目工具窗口中,右键点击任何区域,然后导航到 New | Scratch File。
- 选择临时文件的语言。相同类型的临时文件会自动编号,并添加到项目视图的 Scratches and Consoles 目录中。
- 另外,您可以根据当前编辑器中的选中内容创建一个新的临时文件。选择一些文本或代码,按下 Alt+Enter,然后选择 Create new scratch file from selection。CLion将尝试检测选中片段的语言,并使用适当的类型和扩展名。如果它无法检测出选中片段的语言,CLion将使用与原始文件相同的类型和扩展名创建文件。
临时文件和缓冲区的位置:默认情况下,CLion会将临时文件和缓冲区存储在 IDE配置目录 下的 scratches 目录中。它们可以在任何使用此配置目录的IDE和项目中使用。
1 |
|
将临时文件包含到您的项目中:如果一个临时文件变得足够大,以至于您希望在项目中使用它,可以将它移动到项目结构的所需目录中。
- 在编辑器中打开一个临时文件,或者在项目视图中的 Scratches and Consoles | Scratches 目录中选择它,按下 F6,然后选择项目中的目标目录。
- 在项目视图的 Scratches and Consoles | Scratches 目录中,将临时文件从源目录拖到项目中的目标目录。
- 在项目视图的 Scratches and Consoles | Scratches 目录中选择一个临时文件,按下 Ctrl+X,然后选择项目中的目标目录,按下 Ctrl+V。
其它
- 右键标签页-向下拆分/向右拆分:方便看大型内容。
Conda
在没有自行安装其他的conda环境的时候,默认会有一个base环境。
这里的*表示,这个是当前处于激活状态的环境。每次进入conda的时候,默认就是进入了base环境。
库都要安装在小环境中,不要安装在 base 里, 首先要用 conda 激活环境。
查询
1 |
|
mamba
除了激活和退出环境仍需要 conda 命令,安转和搜索包都可以使用 mamba 替换 conda,以提高速度。
1 |
|
环境管理
1 |
|
Windows:
1
2
3
4
5
6
# conda默认使用了hardlink,如果同时使用conda和pip的情况下,我个人建议加上 --copy 选项
conda create --copy -n envname python=3.9
# 打开新建环境下的lib文件夹的site.py文件(C:\Users\username\.conda\envs\conda_env\Lib\site.py), 找到USER_SITE、USER_BASE那两行, 默认是None, 修改后如下:
USER_SITE = r"C:\Users\username\.conda\envs\conda_env\Lib\site-packages"
USER_BASE = r"C:\Users\username\.conda\envs\conda_env\Scripts"
# 记得加上r抑制转义, 否则有可能因为非法转义报错, 导致进入不了虚拟环境。
可以通过配置auto_activate_base
关闭自动进入conda基础环境:
1 |
|
如要开启,将其设为true
就可以了:
1 |
|
库/包管理
清华源:https://pypi.tuna.tsinghua.edu.cn/simple
阿里源:https://mirrors.aliyun.com/pypi/simple/
-
安装包
-
在线安装
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
29conda search qt # 搜索可用版本
# 你可以指定所安装软件包的版本号,如果不指定的话,就默认安装最新版本。
conda install -y pkgs[==version] # 安装库,安装成功一般会出现三个 done 。-y 参数的作用是自动确认,忽略询问。
# 通过调用软件的帮助文档来经常是否下载成功。如果失败, 重新下载即可。
# 或
# 在anaconda下用pip装包的原因:尽管在anaconda下我们可以很方便的使用conda install来安装我们需要的依赖,但是anaconda本身只提供部分包,远没有pip提供的包多,有时conda无法安装我们需要的包,我们需要用pip将其装到conda环境里。
conda install pip # 进入环境后
# 首先用下面命令查看我们此时用的pip为哪个环境
# 如base环境的pip可能在/root/anaconda3/bin/pip,而其他conda环境的pip,可能在/root/anaconda3/envs/my_env/bin/pip
which -a pip
pip -V # pip --version
# 配置
pip -v config list
pip install --upgrade pip setuptools wheel # 升级pip工具包
pip install --upgrade pip # 更新 pip 至最新版
pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple # 永久换源(清华源)
# Writing to /home/xxx/.config/pip/pip.conf
# global.index-url='https://pypi.tuna.tsinghua.edu.cn/simple'
pip config unset global.index-url # 换回默认源
# 搜索可用版本
pip index versions [your python module name] # 或
pip install package_name==
pip install pkgs[==version] # pip安装的包,conda list结果中的build项目的Channel为pypi
pip install -r environment.txt # 根据 txt 文件创建使用 pip 安装的环境
pip3 --proxy 127.0.0.1:7890 install package_name
pip install xxxx -i https://pypi.tuna.tsinghua.edu.cn/simple # 临时换源(清华源)
pip install geometry_msgs --extra-index-url https://rospypi.github.io/simple # 临时添加额外的源
pip install -e path/ # 执行path/目录下的setup.py文件 -
本地安装
1
2
3# https://anaconda.org/这个是Anaconda包下载的地方,利用搜索框进行搜索,然后下载。
conda install --use-local package.tar.bz2 # 安装本地软件包。
pip install path/package_name.whl
-
-
查找包
1
2
3
4
5
6
7
8
9
10# 查找软件常用的链接(我们认知的软件名和conda给的名字不一样)
# https://bioconda.github.io/
# https://anaconda.org/search
conda search 软件名 # 查找库
# 查看环境中所装的包
conda list # 默认当前环境
conda list –n cpp_test_env # 查看指定环境
conda list fast* # 比如很早就安装某个软件,如果只想起四个字母,用通配符的去查找
pip show packagename -
更新与卸载包
1
2
3
4
5
6
7
8
9
10# 更新
conda update name # 更新库
pip install --upgrade 包名称==版本号
pip install package_name== # 会自动卸载旧版本
# 卸载
# --force 确保只卸载该库,而不同时卸载该库的其他库
conda uninstall pytorch --force # 卸载使用 conda 安装的库
conda remove name # 与上命令等同
pip uninstall torch # 卸载使用 pip 安装的库
环境移植
1 |
|
CTEX
-
创建通栏:
1
2
3
4
5
6
7
8
9
10\onecolumn
% 通栏图片/表格
\begin{multicols}{2}
% 继续双栏内容
\end{multicols}
\onecolumn
% 通栏图片/表格
\begin{multicols}{2}
% 继续双栏内容
\end{multicols}
Git
Windows下的Git Bash终端快捷键:
快捷键 | 作用 |
---|---|
Ctrl+insert | 复制 |
Shift+insert | 粘贴 |
GitHub
-
star
的原则:如果项目完善已停止更新或目前没有使用,就没有
star
的必要,来“污染”star库。star
主要用于及时获取更新消息和收藏常用项目。- 项目仍在持续更新。
- 感兴趣(但还没有深入研究)的项目(更推荐收藏在浏览器收藏夹中,还能做备注)。
-
默认
watch
:Participating and @mentions -
根据
Tag
找存储库的历史版本:1
2
3GIT_REPOSITORY https://github.com/jbeder/yaml-cpp
GIT_TAG 11607eb5bf1258641d80f7051e7cf09e317b4746
https://github.com/jbeder/yaml-cpp/tree/11607eb5bf1258641d80f7051e7cf09e317b4746 -
在GitHub上快速找到自己评论过或者发布过的问题:
1
2
3
4
5
6# 搜索框
commenter:username
is:issue commenter:username
is:issue involves:username
is:issue is:open # is:issue is:closed
# 要查看最近的活动,请从Sort下拉列表中选择Recently updated -
GitHub首页只会推荐有最新
release
的star
项目?如果想跟进项目的commit
,可以在Your stars
界面筛选Sort by: Recently active
。 -
每日/周/月排行:点击GitHub首页左上角
三道杠
-Explore
-Trending
。 -
搜索:
1
2java stars:>1000 # 目标语言是java且star数大于1k
dvs_msgs language:Python # 指定编程语言
Google Colab
-
Google上传和解压:
选中谷歌云端硬盘里的文件,右键-共享-共享-设置访问权限:知道链接的任何人。共享-复制链接:
1
2
3
4
5
6# https://drive.google.com/file/d/xxx/view?usp=drive_link
!gdown --id 'xxx' --output file.zip
!unzip file.zip -d /content/file
%cd /content/file
!pwd
!ls -
查看硬件信息:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17print("============查看GPU信息================")
# 查看GPU信息。注意提前把代码执行程序改为GPU。
!/opt/bin/nvidia-smi
print("==============查看pytorch版本==============")
# 查看pytorch版本
import torch
print(torch.__version__)
print("============查看虚拟机硬盘容量================")
# 查看虚拟机硬盘容量
!df -h
!df -lh
print("============查看cpu配置================")
# 查看cpu配置
!cat /proc/cpuinfo
print("=============查看内存容量===============")
# 查看内存容量
!cat /proc/meminfo
Mermaid
通用语法
流程图的方向定义:
- TB - 从上到下
- TD - 自上而下/与自上而下相同
- BT - 从下到上
- RL - 右到左
- LR - 左到右
流程图
简单示例:
flowchart TD
A[Enter Chart Definition] --> |Text| B(Preview)
B --- C{decide}
C -.-> D([Keep])
C ==> E[Edit Definition]
E <--> B
D --> F[Save Image and Code]
F --> B
markdown["`This **is** _Markdown_`"]
newLines["Line1
Line 2
Line 3"]
markdown --> newLines
Microsoft Edge
快捷键 | 作用 |
---|---|
窗口 | |
Ctrl+N | 新建窗口 |
Ctrl+Shift+N | 新建无痕窗口 |
标签页 | |
Ctrl+W | 关闭当前标签页 |
Ctrl+Shift+W | 关闭所有标签页 |
Ctrl+Shift+A | 查看最近关闭的标签页 |
Ctrl + Shift + K | 复制当前标签页 |
Ctrl + Tab | 切换到下一个标签页 |
Ctrl + Shift + Tab | 切换到上一个标签页 |
Ctrl + 1, 2, ... 8 | 切换到特定标签页 |
Ctrl + 9 | 切换到最后一个标签页 |
搜索 | |
Alt + D/Ctrl + L/F4 | 选择地址栏中的 URL 以进行编辑 |
Ctrl + Shift + L | 粘贴并搜索或粘贴并访问(不一定非得是URL) |
Ctrl + E/Ctrl + K | 在地址栏中打开搜索查询 |
Ctrl+Shift+E | 在边栏中搜索 |
其它 | |
F5 | 重新加载当前标签页 |
Ctrl+F/F3 | 查找当前标签页 |
Okular
快捷键 | 作用 |
---|---|
F6 | 注释 |
Ctrl+4 | 文本选择工具 |
鼠标双击 | 打开注释/持续选择工具 |
TeXstudio
快捷键 | 作用 |
---|---|
F5 | 编译并预览 |
F6 | 编译 |
Ctrl + F | 可对源码或者PDF查看器的内容进行搜索定位 |
Ctrl + 鼠标单击 | 定位Latex源码和PDF的对应位置 |
Ctrl + T | 注释选定代码 |
Ctrl + U | 取消选定代码注释 |
其他配置:
- 选项-设置-构建:若写中文论文,则需修改默认编译器为XelaTeX;若为英文,则用PdfLaTex。
Typora
Markdown语法
https://support.typoraio.cn/zh/Markdown-Reference/
语法高亮
- Shell: console, shell
- Bash: bash, sh, zsh
- Powershell: powershell, ps
- Dos: dos, bat, cmd
- language: js、javascript、java、python、c、c#、c++
- html、htmlbars、yaml、xml、json
- typescript、sql、nginx
1 |
|
脚注
1 |
|
快捷键
快捷键 | 作用 |
---|---|
Ctrl+T | 插入表格 |
Ctrl+Enter | 表格插入行 |
Ctrl+Shift+Backspace | 删除行 |
Ctrl+Shift+L | 显示/隐藏边栏 |
Ctrl+Shift+1 | 显示/隐藏大纲 |
Ctrl+/ | 源代码模式 |
Ctrl+K | 复制链接,在Typora中选中要建立超链接的文字,按下Ctrl+K ,Typora会自动读取剪切板来创建超链接。 |
Ctrl+Shift+` | 生成代码格式,也就是使用````进行包围 |
Ctrl+Y | 重做(与Ctrl+Z撤销相反) |
Ctrl+Shift+F | 当前目录下的文件全局搜索 |
Ctrl+L | 全选行 |
小贴士
- 有序列表回车后按再按回车:取消缩进,回到行首,取消继续编号;按 TAB,新建下级列表;按Backspace ,取消继续编号,可在保持缩进的情况下输入内容。
- 在列表中第一次回车,取消当前列表,第二次回车,创建上级列表(没有上级列表就是正文的回车)。
vim
vim键盘图
vi/vim的使用
1 |
|
基本上 vi/vim 共分为三种模式,命令模式(Command Mode)、输入模式(Insert Mode)和底线命令行模式(Command-Line Mode)。
命令模式
用户刚刚启动 vi/vim,便进入了命令模式。
此状态下敲击键盘动作会被 Vim 识别为命令,而非输入字符,比如我们此时按下 i
,并不会输入一个字符,i
被当作了一个命令。
以下是普通模式常用的几个命令:
i
:切换到输入模式,在光标当前位置开始输入文本。:
:切换到底线命令行模式,以在最底一行输入命令。x
:删除当前光标所在处的字符。a
:进入插入模式,在光标下一个位置开始输入文本。o
:在当前行的下方插入一个新行,并进入插入模式。O
:在当前行的上方插入一个新行,并进入插入模式。dd
:剪切当前行。yy
:复制当前行。p
(小写):粘贴剪贴板内容到光标下方。P
(大写):粘贴剪贴板内容到光标上方。u
:撤销上一次操作。Ctrl + r
:重做上一次撤销的操作。
若想要编辑文本,只需要启动 Vim,进入了命令模式,按下
i
切换到输入模式即可。
在其它模式下,可以随时按
ESC
键回到命令模式。
命令模式只有一些最基本的命令,因此仍要依靠底线命令行模式输入更多命令。
输入模式
在命令模式下按下 i
就进入了输入模式,使用 Esc
键可以返回到普通模式。
在输入模式中,可以使用以下按键:
- 字符按键以及
Shift
组合:输入字符 ENTER
:回车键,换行BACK SPACE
:退格键,删除光标前一个字符DEL
:删除键,删除光标后一个字符方向键 ↑↓←→
:在文本中移动光标HOME/END
:移动光标到行首/行尾Page Up/Page Down
:上/下翻页Insert
:切换光标为输入/替换模式,光标将变成竖线/下划线ESC
:退出输入模式,切换回命令模式
底线命令行模式
在命令模式下按下 :
(英文冒号)就进入了底线命令模式。
底线命令模式可以输入单个或多个字符的命令,可用的命令非常多。
在底线命令模式中,基本的命令有:
:w
:保存文件但不退出。:q
:退出 Vim 编辑器。:wq
:保存文件并退出 Vim 编辑器。:q!
:强制退出Vim编辑器,不保存修改。:w!
:若文件属性为【只读】,强制写入该档案:wq!
:强制保存后退出。:e!
:将档案还原到最原始状态!
vi/vim按键说明
VSCode
快捷键
(当快捷键冲突时,)您可以通过导航到文件 -> 首选项 -> 键盘快捷键(Ctrl+K, Ctrl+S)并在搜索栏中搜索“导航”来更改首选键绑定。
快捷键 | 作用 | 自定义 |
---|---|---|
一般 | ||
打开设置 | Ctrl+, | Ctrl+Alt+S |
控制台终端显示与隐藏 | ctrl + ~ | |
显示所有符号 | Ctrl + T | |
全局搜索 | Ctrl+Shift+F | |
查找 | Ctrl + F | |
替换 | Ctrl + H | |
最近打开的项目(文件夹) | Ctrl+P | |
通过文件名查找文件(快速打开最近的文件) | Ctrl+E | |
打开命令面板(通过搜索界面访问 VS Code 中存在的所有可用命令、快捷方式和功能) | Ctrl + Shift + P | |
跳转标签页 | Ctrl + Tab | |
关闭文件 | Ctrl+W | Ctrl+F4 |
关闭所有文件 | Ctrl + K, W | |
显示/隐藏侧边栏 | Ctrl+B | Alt+1 |
显示/隐藏终端 | Ctrl+` | |
打开文件所在文件夹 | Ctrl + Alt + R | Alt + F1,when editorFocus |
移动 | ||
向上/向下滚动行 | Ctrl+↑ / ↓ | |
快速滚动 | 滚动时按住 Alt 键可以以 5 倍速度滚动;此外,您还可以使用编辑器:快速滚动灵敏度 ( editor.fastScrollSensitivity ) 设置更改滚动倍数。 |
|
快速回到顶部 | ctrl + home | |
快速回到底部 | ctrl + end | |
前进(编辑位置) | Ctrl+Shift+- | Alt + Shift + -> |
后退(编辑位置) | Ctrl+Alt+- | Alt + Shift + <- |
转到行/列 | Ctrl+G | |
注释与缩进 | ||
单行注释 | [ctrl+k,ctrl+c] 或 ctrl+/ | |
取消单行注释 | [ctrl+k,ctrl+u] (按下ctrl不放,再按k + u) | |
多行注释 | [alt+shift+A] | |
行增加缩进 | ctrl + [ | |
行减少缩进 | ctrl + ] | |
多行注释 | /** | |
文本编辑 | ||
向上/下复制行 | Ctrl+Shift+Alt+↑ / ↓ | Ctrl+D |
向上/下移动行 | Alt + ↑ / ↓ | Ctrl+Shift+↑ / ↓ |
选择当前行 | Ctrl+L | |
删除整行 | Ctrl + Shift + K | Ctrl+Y |
缩小/扩大选区 | Shift+Alt+<-/-> | Ctrl+(Shift)+W |
代码格式化 | 当前选择的源代码: Ctrl+K Ctrl+F;整个文档格式:Ctrl+Shift+I | 当前选择的源代码: Ctrl+Alt+L |
重命名符号 | 选择一个符号,然后键入 F2 | |
光标前/后移动一个整词 | Ctrl+<-/-> | |
连续选中 | Shift+<-/-> | |
整词选中 | Ctrl+Shift+<-/-> | |
用语言结构包围代码块 | (插件:Surround)Ctrl+Shift+T | Ctrl+Alt+W |
重命名符号/文件(夹) | F2 | |
选择特定区域 | 第一个位置鼠标单击,第二个位置按住Shift再单击 | |
查看 | ||
查看和跳转符号 | Ctrl+Shift+O | |
跳转到函数开头 | Ctrl+Shift+O,后直接按Enter | |
跳转到函数结尾 | Ctrl+Shift+O,后按↓,后按Enter | |
查看用法 | Ctrl+Shift+F10 | |
转到定义 | 选择一个符号,然后键入 F12;Ctrl+单击;Shift+F12,小窗浏览定义 | |
查找所有引用 | Alt+Shift+F12 | Alt+Shift+7 |
差异跳转 | F7 和 Shift+F7 | |
将文件与剪贴板进行比较 | Ctrl+K C | |
转到括号(在所属最近的成对括号间来回跳转) | Ctrl+Shift+\ | |
全屏 | F11 | Shift+F11 |
编辑器 | ||
打开上一个编辑器 | Ctrl+PageUp | |
打开下一个编辑器 | Ctrl+PageDown | |
打开组中最后一个编辑器 | Ctrl+9 | |
书签 | ||
书签:插入/删除 | Ctrl+Alt+K | F11 |
书签:跳至上一个 | Ctrl+Alt+J | |
书签:跳至下一个 | Ctrl+Alt+L | Ctrl+Alt+K |
书签:将选择展开到上一个 | Shift+Alt+J | |
书签:将选择展开到下一个 | Shift+Alt+L | |
书签:收缩选择 | Shift+Alt+K | |
其它 | ||
CMake: 运行但不调试 | Ctrl+F5 | |
更改颜色主题 | Ctrl+K Ctrl+T | |
聊天,在侧边栏打开聊天 | Alt+3 |
小技巧
新建文件(夹)
-
鼠标双击左侧“文件夹”空白处,后输入:
1
2
3test.py # 新建文件
test/ # 新建文件夹
test/test.py # 新建文件夹内的文件 -
双击标签栏
-
Ctrl + N 键盘快捷键
-
命令面板(Ctrl+Shift+P) - Create: New File
-
单击文件资源管理器窗格中的
New File...
图标按钮 -
文件 > 新建文件
用户代码片段/实时模板
左下角设置-用户代码片段
VS Code的代码片段填充功能默认使用的是Tab键,而不是空格键或回车键。并且,这个行为是不能被修改的。当你在一个代码片段中,你可以使用Tab键在不同的位置(被
${1}
,${2}
等标记的位置)之间跳转。
1 |
|
生成函数注释
-
函数注释模板插件:
-
C++: Doxygen Documentation Generator
- 设置:C_Cpp › Doxygen: Generated Style
-
Python: autoDocstring - Python Docstring Generator
-
-
GitHub Copilot: 选中函数,
Ctrl+I
打开内联聊天,输入/doc in Chinese
创建(函数)声明/定义
鼠标选中,右键“创建声明/定义”
改变大小写
change-case:快速更改当前选择或当前单词的大小写(camelCase、CONSTANT_CASE、snake_case 等)
选中单词,按下Ctrl+Shift+P
搜索Change Case Commands
...
当然,可以按Ctrl+K S
搜索Change Case
为特定命令设置快捷键。
多代码库编码的工作空间
10 amazing VS Code tips and tricks for rapid coding
想象一下必须在 3 个打开的 VS Code 窗口中来回切换的痛苦;到处打开终端,在错误的代码库中搜索错误的文件,将 Alt + Tab 序列与其他打开的应用程序混合在一起,以及每次切换应用程序时都会造成的精神混乱和延迟。
每个文件夹都是 VS Code 的一个工作区,因此您可以使用文件 > 将文件夹添加到工作区...轻松添加更多文件夹。
一切完成后,您将拥有所需的所有文件夹,并且可以在“文件资源管理器”窗格中轻松访问其中的文件。
当您使用 Ctrl + P 或 Ctrl + Shift + F 搜索文件时,它将应用于所有文件夹中的每个文件。
您还可以使用任何文件夹作为工作目录快速创建新终端。
Zotero
快捷键 | 作用 |
---|---|
zotero-reference | |
单击蓝色区域 | 复制参考文献信息,连同标识符一起复制,如DOI。 |
双击顶部XX条参考文献文字 | 复制当前所有参考文献到剪贴板 |
长按蓝色区域 | 编辑参考文献信息。建议中文参考文献使用编辑功能以精简条目,提高导入成功率。 |
Ctrl+单击蓝色区域 | 用系统浏览器打开文献URL,偶尔会查询文献地址消耗一定的时间。 |
一般 | |
ctrl+鼠标滚轮 | 放大/缩小 |
ctrl+鼠标左键在原文/译文之间自由切换 | 翻译 |
主阅读界面若有跳转链接如Fig 4,点击后会在分割界面(横向/竖向)跳转,主阅读界面无跳转,避免点击前进后退。可满足看图,公式,表格的需求。但只针对有跳转链接的PDF。
其它
3-2-1备份规则
3-2-1 备份规则是一种简单、有效的策略,可确保数据安全。它建议您将数据的三份副本保存在两个不同的介质上,其中一份副本保存在异地。让我们来分解一下:
- 数据的三份副本:您的三份副本包括原始数据或生产数据以及另外两份副本。
- 在两种不同的介质上:您应该将数据存储在两种不同形式的介质上。这在今天意味着与 2000 年代末有所不同。我稍后会详细讨论这一点。
- 一份异地副本:您应该在异地远程位置保存一份数据副本,最好距离其他两份副本几英里远。
如果您想保护您的个人信息、照片、工作文件或其他重要数据,3-2-1 备份策略是您的最佳选择。它可以帮助您避免出现容易受到人为错误、硬盘驱动器崩溃、盗窃、自然灾害或勒索软件影响的单点故障。``