BIG MERGE!!!

BIG ADD:
- docker
- archlinux

FIX:
- vim
- c_cpp
  - string hash
  - linux /dev/random
  - thread
  - STL
- linux
  - command
    - last

OTHERS:
- add antenna.md
- mirrors
- makefile.md
This commit is contained in:
RongersLY 2025-04-04 17:35:35 +08:00
parent 7539960a7d
commit 93cecd7544
43 changed files with 3585 additions and 871 deletions

10
electronic/parameter.md Normal file
View File

@ -0,0 +1,10 @@
# 电子硬件参数
## 目录
- [电阻](#电阻)
- [电容](#电容)
- [电感](#电感)
- [微控制器](#微控制器)
- [逻辑器件](#逻辑器件)
- [晶体管](#晶体管)

0
miscellaneous/README.md Normal file
View File

19
os/linux/arch/command.md Normal file
View File

@ -0,0 +1,19 @@
# arch常用命令
- [pacman](#pacman)
## pacman
+ `-S` 同步
+ `-y` 更新软件包数据库
+ `-u` 升级已有安装包
+ `-R` 卸载
+ `-Q` 查看已安装
> 示例
+ `pacman -Sy` 同步数据库
+ `pacman -S <package_name>` 安装软件包
+ `pacman -Syu` 更新系统
+ `pacman -Rs <package_name>` 卸载同时删除无用的依赖
+ `pacman -Ss <keyword>` 搜索软件包
+ `sudo pacman -Sc` 清除缓存

125
os/linux/arch/install.md Normal file
View File

@ -0,0 +1,125 @@
# archlinux安装
## 目录
- [基础安装](#基础安装)
- [修改控制台字体](#修改控制台字体)
- [验证引导](#验证引导)
- [验证网络和更改系统时间](#验证网络和更改系统时间)
- [硬盘分区](#硬盘分区)
- [安装基础软件包](#安装基础软件包)
- [生成fstab](#生成fstab)
- [chroot配置](#chroot)
- [新系统](#新系统)
- [换源](#换源)
- [普通用户](#普通用户)
- [KDE](#kde)
## 基础安装
### 修改控制台字体
`/usr/share/kbd/consolefonts/` 存放字体的位置
`# setfont ter-132b`
### 验证引导
`# cat /sys/firmware/efi/fw_platform_size`
### 验证网络和更改系统时间
`ip addr`
`timedatectl`
### 硬盘分区
```bash
parted /dev/sdX # 进入parted交互界面
> mklabel gpt # 创建分区表格式
gdisk /dev/sdX # 分区
ef00 -> 1G
ef02 -> 2M
8300 -> /
**先用最小系统启动**
mkfs.fat -F 32 /dev/sdX1 # 分区格式化
mkfs.ext4 /dev/sdX3
mount /dev/sdX3 /mnt
mount --mkdir /dev/sdX1 /mnt/boot
```
### 安装基础软件包
`pacstrap -K /mnt xxx`
+ base linux linux-firmware
+ vim base-devel networkmanager archlinuxcn-keyring
---
以下是可以等系统启动后安装的
+ sof-firmware
+ yay
`pacman -S git base-devel && git clone https://aur.archlinux.org/yay.git && cd yay && makepkg -si`
+ man
+ fcitx5-im fcitx5-rime # 输入法
+ alsa-utils # 声卡驱动
+ 其他固件
### 生成fstab
`genfstab -U /mnt > /mnt/etc/fstab`
### chroot
```bash
ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime # 配置时间
hwclock --systohc
vim /etc/locale.gen
> 去掉 en_GB.UTF-8 以及 zh_CN.
locale-gen
vim /etc/locale.conf
> LANG=en_US.UTF-8
vim /etc/hostname
> [hostname]
passwd
**安装引导**
pacman -S grub
grub-install --target=i386-pc /dev/sdX
grub-mkconfig -o /boot/grub/grub.cfg
umount -R /mnt
reboot
```
## 新系统
### 换源
- `/etc/pacman.d/mirrorlist`
```
Server = https://mirrors.tuna.tsinghua.edu.cn/archlinux/$repo/os/$arch
Server = http://mirrors.aliyun.com/archlinux/$repo/os/$arch
Server = https://mirrors.ustc.edu.cn/archlinux/$repo/os/$arch
```
### 普通用户
`useradd -m -G wheel username`
`vim /etc/sudoers`
### kde
```
pacman -S noto-fonts noto-fonts-cjk noto-fonts-emoji # 安装字体
pacman -S plasma
systemctl enable sddm
cd ~/.local/share/fcitx5/rime # 配置输入法
git clone https://github.com/iDvel/rime-ice.git
cp -r ./rime-ice/* .
sudo usermod -a -G uucp $USER # 允许用户使用串口

File diff suppressed because it is too large Load Diff

View File

@ -1,117 +0,0 @@
# ~/.bashrc: executed by bash(1) for non-login shells.
# see /usr/share/doc/bash/examples/startup-files (in the package bash-doc)
# for examples
# If not running interactively, don't do anything
case $- in
*i*) ;;
*) return;;
esac
# don't put duplicate lines or lines starting with space in the history.
# See bash(1) for more options
HISTCONTROL=ignoreboth
# append to the history file, don't overwrite it
shopt -s histappend
# for setting history length see HISTSIZE and HISTFILESIZE in bash(1)
HISTSIZE=500
HISTFILESIZE=500
# check the window size after each command and, if necessary,
# update the values of LINES and COLUMNS.
shopt -s checkwinsize
# If set, the pattern "**" used in a pathname expansion context will
# match all files and zero or more directories and subdirectories.
#shopt -s globstar
# make less more friendly for non-text input files, see lesspipe(1)
[ -x /usr/bin/lesspipe ] && eval "$(SHELL=/bin/sh lesspipe)"
# set variable identifying the chroot you work in (used in the prompt below)
if [ -z "${debian_chroot:-}" ] && [ -r /etc/debian_chroot ]; then
debian_chroot=$(cat /etc/debian_chroot)
fi
# set a fancy prompt (non-color, unless we know we "want" color)
case "$TERM" in
xterm-color|*-256color) color_prompt=yes;;
esac
# uncomment for a colored prompt, if the terminal has the capability; turned
# off by default to not distract the user: the focus in a terminal window
# should be on the output of commands, not on the prompt
#force_color_prompt=yes
if [ -n "$force_color_prompt" ]; then
if [ -x /usr/bin/tput ] && tput setaf 1 >&/dev/null; then
# We have color support; assume it's compliant with Ecma-48
# (ISO/IEC-6429). (Lack of such support is extremely rare, and such
# a case would tend to support setf rather than setaf.)
color_prompt=yes
else
color_prompt=
fi
fi
if [ "$color_prompt" = yes ]; then
PS1='${debian_chroot:+($debian_chroot)}\[\033[01;32m\]\u@\h@\t\[\033[00m\]:\[\033[01;34m\]\w\[\033[00m\]\$ '
else
PS1='${debian_chroot:+($debian_chroot)}\u@\h@\t:\w\$ '
fi
unset color_prompt force_color_prompt
# If this is an xterm set the title to user@host:dir
case "$TERM" in
xterm*|rxvt*)
PS1="\[\e]0;${debian_chroot:+($debian_chroot)}\u@\h@\t: \w\a\]$PS1"
;;
*)
;;
esac
# enable color support of ls and also add handy aliases
if [ -x /usr/bin/dircolors ]; then
test -r ~/.dircolors && eval "$(dircolors -b ~/.dircolors)" || eval "$(dircolors -b)"
alias ls='ls --color=auto'
#alias dir='dir --color=auto'
#alias vdir='vdir --color=auto'
alias grep='grep --color=auto'
alias fgrep='fgrep --color=auto'
alias egrep='egrep --color=auto'
fi
# colored GCC warnings and errors
#export GCC_COLORS='error=01;31:warning=01;35:note=01;36:caret=01;32:locus=01:quote=01'
# some more ls aliases
alias ll='ls -ahlF'
alias l='ls -lF'
# Add an "alert" alias for long running commands. Use like so:
# sleep 10; alert
alias alert='notify-send --urgency=low -i "$([ $? = 0 ] && echo terminal || echo error)" "$(history|tail -n1|sed -e '\''s/^\s*[0-9]\+\s*//;s/[;&|]\s*alert$//'\'')"'
# Alias definitions.
# You may want to put all your additions into a separate file like
# ~/.bash_aliases, instead of adding them here directly.
# See /usr/share/doc/bash-doc/examples in the bash-doc package.
if [ -f ~/.bash_aliases ]; then
. ~/.bash_aliases
fi
# enable programmable completion features (you don't need to enable
# this, if it's already enabled in /etc/bash.bashrc and /etc/profile
# sources /etc/bash.bashrc).
if ! shopt -oq posix; then
if [ -f /usr/share/bash-completion/bash_completion ]; then
. /usr/share/bash-completion/bash_completion
elif [ -f /etc/bash_completion ]; then
. /etc/bash_completion
fi
fi
alias powerele="upower -i /org/freedesktop/UPower/devices/battery_BAT0"
export LANG=en_US.UTF-8

View File

@ -1,27 +0,0 @@
# ~/.profile: executed by the command interpreter for login shells.
# This file is not read by bash(1), if ~/.bash_profile or ~/.bash_login
# exists.
# see /usr/share/doc/bash/examples/startup-files for examples.
# the files are located in the bash-doc package.
# the default umask is set in /etc/profile; for setting the umask
# for ssh logins, install and configure the libpam-umask package.
#umask 022
# if running bash
if [ -n "$BASH_VERSION" ]; then
# include .bashrc if it exists
if [ -f "$HOME/.bashrc" ]; then
. "$HOME/.bashrc"
fi
fi
# set PATH so it includes user's private bin if it exists
if [ -d "$HOME/bin" ] ; then
PATH="$HOME/bin:$PATH"
fi
# set PATH so it includes user's private bin if it exists
if [ -d "$HOME/.local/bin" ] ; then
PATH="$HOME/.local/bin:$PATH"
fi

View File

@ -0,0 +1,252 @@
# STL算法
## 目录
- [非修改性算法](#)
- [find](#)
- [conut](#)
- [二分查找](#binary-search)
- [修改性算法](#)
- [copy](#)
- [replace](#):将指定范围内等于给定值的元素替换为新值
- [reverse](#):翻转序列
- [排序算法](#)
- [sort](#)
- [稳定排序](#stable-sort)
- [第n大数](#nth-element)
- [数值算法](#)
- [accumulate](#):计算指定范围内元素的累积和
- [计算两个序列的内积](#inner-product)
- [集合算法](#)
- [两个有序序列的差集](#set-difference)
- [将两个有序序列合并为一个有序序列](#merge)
- [其他算法](#)
- [去除相邻重复元素](#unique)
- [随机打乱序列](#shuffle)
## 非修改性算法
### find
```
template <class InputIterator, class T>
InputIterator find(InputIterator first, InputIterator last, const T& value);
```
+ InputIterator输入迭代器类型表示要搜索的范围的起始和结束位置。
+ first指向范围的起始位置的迭代器。
+ last指向范围的结束位置的迭代器不包含
+ value要查找的目标值。
+ 返回值:如果找到目标值,返回指向该元素的迭代器;如果没有找到,则返回 last
__注意事项__
1. 返回值的判断
如果未找到目标值,返回值是 last,通常需要通过比较返回值是否等于 last 来判断是否找到目标
2. 自定义对象的比较
如果要在自定义对象中使用 `std::find`,需要重载 `operator==`,以便算法能够比较对象是否相等
3. 性能
是线性查找算法,时间复杂度为 O(n),其中 n 是范围内的元素数量
4. 范围的正确性
确保 first 和 last 指向的范围是有效的,并且 first 不大于 last
### conut
```
template <class InputIterator, class T>
typename iterator_traits<InputIterator>::difference_type count(
InputIterator first,
InputIterator last,
const T& value
);
```
+ InputIterator输入迭代器类型表示要搜索的范围的起始和结束位置。
+ first指向范围的起始位置的迭代器。
+ last指向范围的结束位置的迭代器不包含
+ value要统计的目标值。
+ 返回值:返回等于目标值的元素个数,类型为 `iterator_traits<InputIterator>::difference_type`
__注意事项__
1. 返回值的判断
返回值类型是 `iterator_traits<InputIterator>::difference_type`,通常是与容器相关的整数类型 `int` `size_t`
2. 自定义对象的比较
需要重载 `operator==`,以便算法能够比较对象是否相等
3. 性能
是线性查找算法,时间复杂度为 O(n),其中 n 是范围内的元素数量
4. 范围的正确性
确保 first 和 last 指向的范围是有效的,并且 first 不大于 last
### binary-search
用于在*有序序列*中查找是否存在某个值,基于二分查找的思想,时间复杂度为 O(log n)
只返回布尔值,表示是否找到目标值,如果需要找到目标值的位置,可以结合 `std::lower_bound``std::upper_bound` 使用
```
template <class ForwardIterator, class T>
bool binary_search(ForwardIterator first, ForwardIterator last, const T& value);
```
+ ForwardIterator正向迭代器类型表示要搜索的范围的起始和结束位置。
+ first指向范围的起始位置的迭代器。
+ last指向范围的结束位置的迭代器不包含
+ value要查找的目标值。
+ 返回值:如果找到目标值,返回 true否则返回 false。
__还支持自定义比较函数__
```
template <class ForwardIterator, class T, class Compare>
bool binary_search(ForwardIterator first, ForwardIterator last, const T& value, Compare comp);
```
+ Compare比较函数用于定义元素之间的大小关系。如果未提供会使用默认的 < 比较运算符
## 修改性算法
### copy
用于将一个范围内的元素复制到另一个范围中,是一个线性算法,时间复杂度为 O(n)
```
template <class InputIterator, class OutputIterator>
OutputIterator copy(InputIterator first, InputIterator last, OutputIterator result);
```
+ InputIterator输入迭代器类型表示要复制的源范围的起始和结束位置。
+ first指向源范围的起始位置的迭代器。
+ last指向源范围的结束位置的迭代器不包含
+ OutputIterator输出迭代器类型表示目标范围的起始位置。
+ result指向目标范围的起始位置的迭代器。
+ 返回值:返回指向目标范围结束位置的迭代器(即 result + (last - first)
### replace
用于在指定范围内将所有等于某个特定值的元素替换为另一个值
```
template <class ForwardIterator, class T>
void replace(ForwardIterator first, ForwardIterator last, const T& old_value, const T& new_value);
```
+ ForwardIterator正向迭代器类型表示要操作的范围的起始和结束位置。
+ first指向范围的起始位置的迭代器。
+ last指向范围的结束位置的迭代器不包含
+ `old_value`:要被替换的旧值。
+ `new_value`:用于替换的新值。
如果需要更复杂的替换逻辑(例如基于条件的替换),可以结合 `std::replace_if` 使用,后者允许传入一个谓词函数来定义替换条件。
### reverse
反转指定范围内的元素顺序,要求输入范围的迭代器是双向迭代器,即支持双向遍历
```
template <class BidirectionalIterator>
void reverse(BidirectionalIterator first, BidirectionalIterator last);
```
+ BidirectionalIterator双向迭代器类型表示要反转的范围的起始和结束位置。
+ first指向范围的起始位置的迭代器。
+ last指向范围的结束位置的迭代器(不包含)
## 排序算法
### sort
指定范围内的元素进行排序,要求输入范围的迭代器是随机访问迭代器,是不稳定的排序算法,平均时间复杂度为 O(n log n)
```
template <class RandomAccessIterator>
void sort(RandomAccessIterator first, RandomAccessIterator last);
```
+ RandomAccessIterator随机访问迭代器类型表示要排序的范围的起始和结束位置
+ first指向范围的起始位置的迭代器
+ last指向范围的结束位置的迭代器不包含
此外std::sort 还支持自定义比较函数
```
template <class RandomAccessIterator, class Compare>
void sort(RandomAccessIterator first, RandomAccessIterator last, Compare comp);
```
### stable-sort
是一个稳定排序算法,对指定范围内的元素进行排序,同时保证相等元素的相对顺序不变
```
template <class RandomAccessIterator>
void stable_sort(RandomAccessIterator first, RandomAccessIterator last);
template <class RandomAccessIterator, class Compare>
void stable_sort(RandomAccessIterator first, RandomAccessIterator last, Compare comp);
```
+ first 和 last随机访问迭代器分别指向要排序范围的起始位置和结束位置不包含
+ Compare可选的比较函数用于定义排序规则。如果未提供默认使用 < 运算符
如果有足够的额外内存,`std::stable_sort` 的时间复杂度为 O(n log n),内存不足,时间复杂度可能退化为 O(n log² n)
### nth-element
用于部分排序序列,使得第 k 小的元素被放置到正确的位置,同时保证所有在它之前的元素都不大于它,所有在它之后的元素都不小于它,平均时间复杂度O(n),最坏时间复杂度O(n²)
```
template <class RandomIt>
void nth_element(RandomIt first, RandomIt nth, RandomIt last);
template <class RandomIt, class Compare>
void nth_element(RandomIt first, RandomIt nth, RandomIt last, Compare comp);
```
+ first指向序列起始位置的随机访问迭代器。
+ nth指向序列中第 k 个位置的迭代器0-based index
+ last指向序列结束位置的迭代器。
+ comp可选自定义比较函数默认使用 std::less
> 不会完全排序整个序列,它只保证第 k 小的元素在正确的位置上
> 该算法的性能优于完全排序
## 数值算法
### accumulate
用于计算指定范围内元素的累积和,时间复杂度为 O(n)
`std::accumulate` 从 first 开始,逐个处理范围内的元素,将每个元素与累积结果进行操作(默认为加法),并将最终结果返回
```
template <class InputIterator, class T>
T accumulate(InputIterator first, InputIterator last, T init);
template <class InputIterator, class T, class BinaryOperation>
T accumulate(InputIterator first, InputIterator last, T init, BinaryOperation op);
```
+ InputIterator输入迭代器类型表示要操作的范围的起始和结束位置。
+ first指向范围的起始位置的迭代器。
+ last指向范围的结束位置的迭代器不包含
+ T累积结果的类型。
+ init初始值用于开始累积操作。
+ BinaryOperation可选二元操作函数用于定义累积操作。如果未提供默认使用加法操作 `std::plus<T>` [预定义函数符](./containers.md#)
### inner-product
## 集合算法
### set-difference
### merge
## 其他算法
### unique
用于移除序列中相邻的重复元素,并返回一个指向“新序列”结束位置的迭代器。它不会改变原序列的长度,而是将重复元素移动到序列末尾,并返回一个指向“有效部分”结束位置的迭代器。因此,通常需要结合容器的 erase 方法来真正移除重复元素,O(n)
```
template <class ForwardIterator>
ForwardIterator unique(ForwardIterator first, ForwardIterator last);
template <class ForwardIterator, class BinaryPredicate>
ForwardIterator unique(ForwardIterator first, ForwardIterator last, BinaryPredicate pred);
```
+ ForwardIterator正向迭代器类型表示要操作的序列的起始和结束位置。
+ first指向序列起始位置的迭代器。
+ last指向序列结束位置的迭代器不包含
+ BinaryPredicate可选二元谓词函数用于定义两个元素是否相等。如果未提供默认使用 operator==
### shuffle
随机打乱指定范围内元素的顺序
```
template <class RandomAccessIterator, class URNG>
void shuffle(RandomAccessIterator first, RandomAccessIterator last, URNG&& g);
```
+ first 和 last随机访问迭代器分别指向要打乱范围的起始位置和结束位置不包含
+ URNG均匀随机数生成器,例如 `std::mt19937``std::default_random_enginei`
```
// 使用随机数生成器
unsigned seed = std::chrono::system_clock::now().time_since_epoch().count();
std::mt19937 rng(seed);
// 打乱顺序
std::shuffle(vec.begin(), vec.end(), rng);
```

View File

@ -0,0 +1,240 @@
# STL 容器
## 目录
- [概念](#)
- [通性](#)
- [序列容器](#)
- [vector](#)
- [deque](#)
- [list](#)
- [关联容器](#)
- [set](#)
- [multiset](#)
- [map](#)
- [multimap](#)
- [无序容器](#)
- [`unordered_set`](#unordered_set)
- [`unordered_multiset`](#unordered_multiset)
- [`unordered_map`](#unordered_map)
- [`unordered_multimap`](#unordered_multimap)
- [预定义函数符](#)
## 概念
**函数符**概念
其实就是重载了()的伪函数
- 生成器:无参数
- 一元函数:一个参数
- 二元函数:两个参数
- 谓词:返回bool的一元函数
- 二元谓词:返回bool的二元函数
## 通性
+ `begin()``end()` 用于获取容器的迭代器,指向容器的第一个元素和最后一个元素的“后继”
+ `rbegin()``rend()` 用于获取容器的反向迭代器,指向容器的最后一个元素和第一个元素的“前驱”位置
+ `size()` 返回容器中元素的数量
+ `empty()` 如果容器为空返回true否则返回false
+ `max_size()` 返回容器可能存储的最大元素数量
+ `clear()` 清空容器中的所有元素,但不释放内存
+ `swap()` 交换两个容器的内容
+ `data()` **仅适用于序列容器** 返回指向容器底层存储的指针
+ 提供对容器底层数据的直接访问
+ 适用于需要与C语言风格的API交互的场景
+ `front()``back()` **仅适用于序列容器** 返回容器的第一个元素和最后一个元素的引用
+ `operator[]``at()` **仅适用于序列容器** 访问容器中的特定元素
+ at() 进行边界检查,如果索引超出范围,会抛出`std::out_of_range`异常
## 序列容器
存储元素的顺序与插入顺序一致
### vector
1. 定义和初始化
`std::vector<int> vec1; // 定义一个空的 vector`
`std::vector<int> vec2 = {1, 2, 3, 4, 5}; // 使用初始化列表初始化`
`std::vector<int> vec3(10, 0); // 10个元素初始值为0`
`std::vector<int> vec4(vec2); // 拷贝构造`
`std::vector<int> vec5(std::move(vec2)); // 移动构造`
2. 元素访问
支持序列容器的大部分访问方法
3. 修改容器
`push_back()` 在容器末尾添加一个元素
`pop_back()` 删除容器末尾的元素
`insert()` 在指定位置插入一个或多个元素
`erase()` 删除指定位置的元素
4. 容量和大小
`capacity()` 返回容器当前分配的内存容量(以元素数量计)
`reserve()` 预分配内存,减少动态扩展的次数
`shrink_to_fit()` 收缩内存,释放多余空间
__使用技巧__
1. 预分配内存
如果知道容器将存储大量元素,可以使用 reserve() 预分配内存,以减少动态扩展的次数
2. 使用 `std::move()`
`vec2.push_back(std::move(vec1[0])); // 将vec1的第一个元素移动到vec2`
3. 使用vector的bool值特性
`std::vector<bool>` 是一个特化版本,它使用位存储来优化空间占用,但可能会牺牲某些操作的性能
4. 使用 `std::vector` 的指针特性
如果需要存储动态分配的对象,可以使用 `std::vector<std::unique_ptr<T>>``std::vector<std::shared_ptr<T>>`
__vector 的 capacity 和 size 属性区别__
size 是当前 vector 容器真实占用的大小,也就是容器当前拥有多少个容器。
capacity 是指在发生 realloc 前能允许的最大元素数,即预分配的内存空间。
使用 resize() 容器内的对象内存空间是真正存在的。
使用 reserve() 仅仅只是修改了 capacity 的值,容器内的对象并没有真实的内存空间(空间是"野"的)。
### deque
1. 插入和删除
+ `push_back()``pop_back()`:尾部操作
+ `push_front()``pop_front()`:头部操作
+ `insert()``erase()`:在任意位置插入或删除
2. 大小操作
+ `resize()`:调整容器大小
__使用技巧__
> 高效头部操作:`deque` 是处理头部和尾部频繁插入删除的理想选择
> 随机访问优化:虽然 `deque` 的内存不连续,但通过下标访问效率较高
> 排序:由于支持随机访问,可以使用 `std::sort``deque` 进行排序
> 迭代器失效:在添加或删除元素后,迭代器可能失效,需要重新获取
__注意事项__
> 内存不连续:`deque` 的元素可能分散在多个内存块中,但通过下标或迭代器访问不会受到影响
> 迭代器失效:在容器大小发生变化时(如插入或删除元素),迭代器可能会失效
> 性能权衡:虽然 `deque` 在头部和尾部操作高效,但在中间插入或删除元素的性能不如 `list`
__相比vector的优势__
> 头部操作高效deque 在头部插入和删除操作的时间复杂度为 O(1),而 vector 为 O(n)。
> 动态变化灵活deque 的*分块内存*分配使其更灵活,性能损耗更小,且内存不足时不用搬运所有元素
> 随机访问性能接近:虽然 deque 的随机访问效率略低于 vector但差距不大
### list
1. 特点
+ 双向链表结构:每个元素包含指向前后元素的指针,支持双向遍历
+ 高效插入和删除:在任意位置插入或删除元素的时间复杂度为 O(1),前提是已知位置
+ 不支持随机访问:无法通过索引直接访问元素,只能通过迭代器遍历
+ 稳定的迭代器:插入和删除操作不会使迭代器失效,除非删除了迭代器所指向的元素
1. 插入和删除
+ `push_back()``pop_back()`:尾部操作
+ `push_front()``pop_front()`:头部操作
+ `insert()``erase()`:在任意位置插入或删除
2. 其他
+ resize():调整容器大小
+ swap():交换两个 list 的内容
+ splice():将一个 list 的元素插入到另一个 list 的指定位置
+ sort()、merge()、reverse():排序、合并和反转
__注意事项__
> 不支持随机访问:无法通过索引访问元素,只能通过迭代器
> 内存开销:每个元素存储额外的指针,内存开销较大
> 缓存局部性差:元素不连续存储,遍历性能不如 std::vector
> 适用场景有限:只有在需要频繁中间插入和删除时才适合使用
## 关联容器
存储元素时会自动排序,通常基于键值对
### set
基于红黑树实现 适用于需要去重、排序和*高效查找*的场景
1. 特点
+ 元素唯一性:容器中不允许存储重复元素
+ 自动排序:元素会根据指定的比较规则自动排序,默认为升序
+ 高效查找:插入、删除和查找操作的时间复杂度均为 O(log n)
2. 构造与初始化
+ 默认构造
+ 初始化列表
+ 拷贝构造
+ 自定义比较规则
```
struct Compare {
bool operator()(int a, int b) const {
return a > b; // 降序排序
}
};
```
3. 常用操作
+ 插入元素 `s1.insert(5);`
+ 删除元素 `s1.erase(3); // 删除值为 3 的元素`
+ 查找元素 `s1.find(4); // 返回指向元素的迭代器,未找到时返回 s1.end()`
+ 统计元素 `int count = s1.count(4);`
+ 范围查询
```
auto lower = s1.lower_bound(3); // 返回第一个 >= 3 的元素的迭代器
auto upper = s1.upper_bound(5); // 返回第一个 > 5 的元素的迭代器
```
4. 高级用法
+ 自定义排序规则:使用仿函数或 `std::greater` 可以改变排序规则
+ 存储复杂数据类型:可以存储 `std::pair` 或自定义结构体,通过自定义比较函数排序
+ 范围删除 `s1.erase(s1.lower_bound(10), s1.upper_bound(50)); // 删除值在 [10, 50) 范围内的所有元素`
__注意事项__
> 修改元素:无法直接修改容器中的元素值,需要先删除再插入
> 性能开销:由于自动排序的特性,插入和删除操作比无序容器慢
> 迭代器失效:插入操作不会使迭代器失效,但删除操作会使指向被删除元素的迭代器失效
### map
用于存储键值对key-value pairs并根据键自动排序
1. 特点
+ 键的唯一性:每个键在 map 中是唯一的,不允许重复
+ 自动排序:容器会根据键的默认比较规则(通常是 < 运算符自动排序
+ 高效操作:插入、删除和查找操作的时间复杂度均为 O(log n),其中 n 是容器中元素的数量
2. 常用操作
+ 插入元素:`map1.insert({4, "four"});``map1[5] = "five"; // 如果键不存在,则插入新键值对;如果键存在,则更新值`
+ 查找元素:`map1.find(3);` 或者使用 `operator[]` 直接访问键对应的值
+ 删除元素:`map1.erase(3); // 删除键为 3 的元素。`
## 无序容器
存储元素时不排序,基于哈希表实现
## 预定义函数符
+ `+` plus
+ `-` minus
+ `*` multiplies
+ `/` divides
+ `%` modulus
+ `==` `equal_to`
+ `!=` `not_equal_to`
+ `>` `greater`
+ `<` `less`
+ `>=` `greater_equal`
+ `<=` `less_equal`
+ `&&` `logical_and`
+ `||` `logical_or`
+ `!` `logical_not`

View File

@ -0,0 +1,2 @@
#define offsetof(type, struct_name) (size_t)&(((struct type*)0)->struct_name)
// 计算结构体成员相对结构体首地址的偏移度

View File

@ -0,0 +1,10 @@
// 非加密型hash,速度极快,支持种子
uint32_t murmur3_32(const char *key, size_t len, uint32_t seed) {
for(uint32_t i=0; i<len; i++)
{
seed ^= (const uint8_t)key[i];
seed *= 0x5bd1e995;
seed ^= seed>> 15;
}
return seed;
}

View File

@ -0,0 +1,73 @@
#include <iostream>
#include <thread>
#include <vector>
#include <queue>
#include <functional>
#include <mutex>
#include <condition_variable>
class ThreadPool {
public:
ThreadPool(size_t num_threads);
~ThreadPool();
void enqueue(std::function<void()> task);
private:
std::vector<std::thread> workers;
std::queue<std::function<void()>> tasks;
std::mutex queue_mutex;
std::condition_variable condition;
bool stop;
};
ThreadPool::ThreadPool(size_t num_threads) : stop(false) {
for (size_t i = 0; i < num_threads; ++i) {
workers.emplace_back([this] {
while (true) {
std::function<void()> task;
{
std::unique_lock<std::mutex> lock(this->queue_mutex);
this->condition.wait(lock, [this] { return this->stop || !this->tasks.empty(); });
if (this->stop && this->tasks.empty()) {
return;
}
task = std::move(this->tasks.front());
this->tasks.pop();
}
task();
}
});
}
}
ThreadPool::~ThreadPool() {
{
std::unique_lock<std::mutex> lock(queue_mutex);
stop = true;
}
condition.notify_all();
for (std::thread& worker : workers) {
worker.join();
}
}
void ThreadPool::enqueue(std::function<void()> task) {
{
std::unique_lock<std::mutex> lock(queue_mutex);
if (stop) {
throw std::runtime_error("enqueue on stopped ThreadPool");
}
tasks.emplace(task);
}
condition.notify_one();
}
int main() {
ThreadPool pool(4);
for (int i = 0; i < 8; ++i) {
pool.enqueue([i] {
std::cout << "Task " << i << " is running on thread " << std::this_thread::get_id() << '\n';
});
}
return 0;
}

View File

@ -0,0 +1,24 @@
#include <stdio.h>
#include <ncurses.h>
char fastRead();
int main() {
char ch;
printf("Please down any key\n");
ch = fastRead();
printf("down : %c --- %d\n", ch, (int)ch);
return 0;
}
char fastRead() {
char ch;
filter(); // 阻止initscr 清空屏幕
initscr(); // 初始化
cbreak(); // 禁用缓冲区
noecho(); // 禁用回显
keypad(stdscr, TRUE);
ch = getchar();
refresh(); // 刷新
endwin(); // 关闭
return ch;
}

View File

@ -0,0 +1,25 @@
# linux随机数设备
从系统熵池进行获取
## index
- [random-urandom](# random-urandom)
- [note](# note)
## random-urandom
/dev/random 和 /dev/urandom 的区别
+ /dev/random:
- 阻塞型设备,当熵池耗尽时会阻塞,直到收集到足够的熵
- 适合生成高价值的密钥、证书等
- 可能会影响性能,因为可能需要等待
+ /dev/urandom:
- 非阻塞型设备,即使熵池耗尽也会继续提供数据
- 使用加密算法从初始熵生成伪随机数
- 对于大多数应用来说已经足够安全
- 推荐在大多数情况下使用
## note
- 对于大多数应用,使用 /dev/urandom 就足够了,它不会阻塞且性能更好
- 只在生成长期加密密钥等极高安全需求时使用 /dev/random
- 不要频繁打开和关闭设备文件,可以保持打开状态或重用文件描述符
- 检查所有系统调用的返回值,确保操作成功

View File

@ -1,19 +1,45 @@
# 对象和类 # 对象和类
## 目录
- [访问控制](#访问控制)
- [作用域运算符](#作用域运算符)
- [类的六大特殊成员函数](#类的六大特殊成员函数)
- [RAII](#RAII)
- [move语义](#move)
- [右值引用](#右值引用)
- [初始化列表](#初始化列表)
- [const 成员函数](#const成员函数)
- [this指针](#this指针)
- [作用域为类的常量](#作用域为类的常量)
- [作用域内枚举](#作用域内枚举)
- [友元](#友元)
- [类的自动转换和强制类型转换](#类的自动转换和强制类型转换)
- [转换函数](#转换函数)
- [继承](#继承)
- [继承方式](#继承方式)
- [虚函数](#virtual)
- [设计理念](#)
- [is a](#)
- [AbstactBaseClass](#ABC)
## 访问控制 ## 访问控制
```cpp ```cpp
class demo { class demo {
public : // 公有接口 public : // 公有接口
private: // 私有成员 private: // 私有成员
protected: // 保护 protected: // 保护,对外部是私有
}; };
``` ```
## 作用域运算符(::) ## 作用域运算符(::)
可用于在类体外指出函数所属的类(命名空间) 可用于在类体外指出函数所属的类(命名空间)
## 成员函数的参数名不可与类成员相同 **成员函数的参数名不可与类成员相同**
## 类的六大特殊成员函数(未定义时编译器提供默认版本) ## 类的六大特殊成员函数(未定义时编译器提供默认版本)
```cpp ```cpp
demo::demo(); // 默认构造函数 demo::demo(); // 默认构造函数
demo::~demo(); // 默认析构函数 demo::~demo(); // 默认析构函数
@ -23,7 +49,57 @@ demo::demo(demo&&); // 移动复制构造函数
demo& demo::operator = (demo&&); // 移动赋值构造函数 demo& demo::operator = (demo&&); // 移动赋值构造函数
``` ```
`demo::demo() = default;`显式声明为默认
`demo::demo() = delete;`显式声明为禁用
### RAII
在类的构造函数中申请堆内存,在析构函数中释放,这样可以保证类失效时内存被释放
### move
移动语义的核心思想是转移而非复制。当一个对象被移动时,其资源(如动态分配的内存、文件句柄等)被转移到目标对象,而源对象被置于一种有效但未定义的状态。这种状态通常是清空的,以确保源对象的资源在其生命周期内不会被重复释放
```cpp
MyString(MyString&& other) noexcept : data(other.data) { // 移动构造函数
other.data = nullptr;
}
MyString& operator=(MyString&& other) noexcept { // 移动赋值运算符
if (this != &other) {
delete[] data; // 释放当前对象的资源
data = other.data;
other.data = nullptr;
}
return *this;
}
```
`std::move` 是一个函数模板,用于将左值强制转换为右值。它本身并不执行移动操作,而是通过返回一个右值引用,触发移动构造函数或移动赋值运算符
`MyString str2 = std::move(str1); // 触发移动构造函数`
#### 右值引用
分为两类
> 普通右值引用直接声明的T&&,用于移动语义
> 转发引用模板函数中声明的T&&,用于完美转发
- [函数引用重载](./func#重载)
### 初始化列表
初始化列表是一种在构造函数中初始化类成员变量的机制
1. 初始化常量成员变量:常量成员变量必须在构造函数的初始化列表中初始化,不能在构造函数体内赋值。
2. 初始化引用成员变量:引用成员变量必须在构造函数的初始化列表中初始化,不能在构造函数体内赋值。
3. 调用基类的构造函数:如果类继承自基类,需要在初始化列表中显式调用基类的构造函数。
4. 优化性能:对于一些复杂的成员变量(如类对象),使用初始化列表可以避免默认构造后再赋值,从而提高效率。
> example
`MyClass(int a, int b, int c) : Base(a), myConst(b), myRef(c), myValue(10) {}`
## const成员函数 适合的成员函数要尽可能用,以帮助规避错误 ## const成员函数 适合的成员函数要尽可能用,以帮助规避错误
`void show() const;` 声明 `void show() const;` 声明
`void demo::show() const;` 定义 `void demo::show() const;` 定义
表明函数不会修改调用对象 表明函数不会修改调用对象
@ -70,3 +146,57 @@ operator typeName();
+ 必须是类成员 + 必须是类成员
+ 不能有参数 + 不能有参数
+ 不能指定返回类型 + 不能指定返回类型
## 继承
基类对象需要在进入派生类的构造函数之前被创建,通常使用初始化列表解决
`demo::demo() : base();`
### 继承方式
1. public
+ 基类的公有成员在派生类中仍然是公有的。
+ 基类的保护成员在派生类中仍然是保护的。
+ 基类的私有成员在派生类中不可访问。
+ 派生类*对象*可以访问基类的公有成员。
+ 派生类*对象*不能直接访问基类的*私有成员*
2. protected
+ 基类的公有成员和保护成员在派生类中都变成保护的。
+ 基类的私有成员在派生类中仍然不可访问。
+ 派生类*对象*不能直接访问基类的*所有成员*
3. private
+ 基类所有成员变成私有
+ 派生类*对象*不能直接访问基类的*所有成员*
__保护成员在派生类中的访问性__
无论继承方式,派生类都可以访问基类的 `protected` 这是因为保护成员的设计初衷就是允许派生类访问,但不允许*派生类的对象*访问
### virtual
虚函数是通过在基类中使用关键字 virtual 声明的成员函数
虚函数的主要作用是实现动态绑定或运行时多态.当通过基类指针或引用调用虚函数时,程序会根据对象的实际类型(运行时类型)来调用相应的函数版本
为了实现动态绑定C++ 编译器会在每个具有虚函数的类的对象中隐式添加一个指针指向一个虚函数表V-Table。V-Table 是一个函数指针数组,存储了类中所有虚函数的地址。当通过指针或引用调用虚函数时,程序会通过 V-Table 查找并调用正确的函数版本
**虚函数的特点**
+ 必须是成员函数
+ 派生类继承基类的虚函数,但可以重写它
+ 覆盖:派生类中的虚函数与基类中的虚函数具有相同的签名
+ 多态:通过基类指针或引用调用虚函数时,会根据对象的实际类型调用相应的函数版本
**纯虚函数与抽象类**
如果一个虚函数在基类中没有实现,而是通过在函数声明后加上 = 0 来表示,那么这个函数称为纯虚函数。包含纯虚函数的类称为抽象类
`virtual void display() = 0; // 纯虚函数`
## 设计理念
### is-a
### ABC
即使用[纯虚函数](#virtual)构造的抽象类
无法构建对象,只能用于构造其他派生类
用于提取一系列对象的共性以共用
即抽象类设计理念,把一系列对象的共性提取出来,创建一个*抽象*的类

View File

@ -0,0 +1,42 @@
# 预编译指令
## 目录
- [define](#define)
- [预定义宏](#预定义宏)
- [ifdef](#ifdef)
- [ifndef](#ifndef)
- [endif](#endif)
- [pragma](#pragma)
- [error](#error)
- [warning](#warning)
## define
### 预定义宏
1. 平台相关
+ `__STDC__` 如果编译器遵循ANSI C标准则定义为1
+ `__cplusplus` 如果编译器支持C++,则定义为一个版本号 如199711L表示C++98201103L表示C++11等
+ `_WIN32` 在Windows平台下定义表示目标系统是32位或64位
+ `_WIN64`
+ `__unix__`
+ `__linux__`
+ `__APPLE__`
2. 编译器相关
+ `__GNUC__` 如果编译器是GCC,则定义为GCC的主版本号
+ `__clang__` 如果编译器是Clang
+ `_MSC_VER` 如果编译器是Microsoft Visual C++,则定义为一个版本号
+ `__ICC` 如果编译器是Intel C/C++ Compiler则定义
3. 文件和行号相关的预定义宏
+ `__FILE__` 当前文件的名称
+ `__LINE__` 当前文件的行号
+ `__DATE__` 编译日期
+ `__TIME__` 编译时间
4. 其他
+ `__func__` `__FUNCTION__` 当前函数的名称
+ `__PRETTY_FUNCTION__` 在C++中,包含当前函数的完整声明
## pragma
`#pragma once` 确保头文件只被包含一次

View File

@ -0,0 +1,47 @@
# c-cpp function
## 目录
- [extern](#extern)
- [noexcept](#noexcept)
- [重载](#)
- [often](#often)
## extern
`exterm` 表明函数在外部定义,在链接期才去查找
`extern "C"` 表明以C的方式查找外部函数
## noexcept
```cpp
void myFunction() noexcept {
// 这个函数不会抛出异常
}
```
noexcept 的一个重要用途是优化性能,特别是在移动语义中。当一个函数被标记为 noexcept 时,编译器可以更积极地优化代码
## 重载
只要特征标不同就可以重载
**特征标**:原型除去返回值都是
+ 左值优先选择T&如果不可用则选择const T&
+ const左值选择const T&
+ 右值优先选择T&&, else const T&
+ 字面量或临时对象选择T&&
## often
```
abs(x); //求x的绝对值 e.g.abs(-5)=5
fabs() //实数绝对值
exp(x); //求x的自然指数e^x e.g.exp(1)=2.718282
floor(x); //向下取整
ceil(x); //向上取整
log(x); //求实数x的自然数对数 e.g.log(1)=0
pow(x,y); //计算x^y,输出为双精度实数 e.g.pow(2,3)=8
sqrt(x); //求x的平方根 e.g.sqrt(25)=5
strlen(str);获取字符数组长度(只能用在字符数组)。
max(a,b) //两数中最大
min(a,b) //两数中最小
swap(a,b) //交换两个类型相同的变量
```

View File

@ -6,6 +6,7 @@
- [c fio](# c fio) - [c fio](# c fio)
- [cpp stdio](# cpp stdio) - [cpp stdio](# cpp stdio)
- [cpp fio](# cpp fio) - [cpp fio](# cpp fio)
- [关闭缓冲区读取](# ../linux/ncurses.c)
## c stdio ## c stdio

View File

@ -0,0 +1,156 @@
# c-cpp的指针问题
## 目录
- [NULL and nullptr](#NULL-nullptr)
- [cpp智能指针](#cpp智能指针)
- [注意事项](#注意事项)
- [some example](#some-example)
## NULL-nullptr
在C和CPP中,NULL并不相同
```
#ifndef NULL
#ifdef __cplusplus
#define NULL 0
#else
#define NULL ((void *)0)
#endif
#endif
```
由源码可知, C中 `NULL``(void *)0`, 而cpp中视为整形常量
因此cpp中应使用nullptr,否则函数重载中会出现选择错误问题
## cpp智能指针
1. `std::unique_ptr`
表示独占所有权的指针,即同一时刻只能有一个`unique_ptr`指向某个对象。
* 不可复制(没有拷贝构造函数和拷贝赋值运算符)。
* 可以移动(支持移动语义)。
* 自动释放资源。
**由于不可复制不能将unique_ptr存储在标准容器中(如std::vector)**
```
std::unique_ptr<MyClass> ptr1 = std::make_unique<MyClass>(42); // 创建unique_ptr
std::unique_ptr<MyClass> ptr2 = std::move(ptr1); // 移动所有权
ptr1 = nullptr; // ptr1不再指向对象对象由ptr2管理
```
2. `std::shared_ptr`
表示共享所有权的指针,多个`shared_ptr`可以指向同一个对象
* 使用引用计数来管理对象的生命周期。
* 当最后一个`shared_ptr`被销毁时,对象才会被释放。
* 可以复制
* 引用计数可能会引入性能开销,尤其是在多线程环境中。
* 如果存在循环引用(如双向链表),可能导致内存泄漏。需要使用`std::weak_ptr`来解决。
```
std::shared_ptr<MyClass> ptr1 = std::make_shared<MyClass>(42);
std::shared_ptr<MyClass> ptr2 = ptr1; // 复制引用计数加1
std::cout << "ptr1 use count: " << ptr1.use_count() << std::endl; // 输出2
```
3. `std::weak_ptr`
解决`shared_ptr`的循环引用问题
* 不增加引用计数。
* 可以通过lock()方法获取一个`shared_ptr`,但需要检查对象是否仍然存在
* `weak_ptr`不能直接访问对象需要通过lock()方法转换为`shared_ptr`
* 如果对象已经被销毁lock()会返回一个空的`shared_ptr`
### 注意事项
***循环引用问题***
`std::shared_ptr`通过引用计数来管理对象的生命周期。当最后一个`shared_ptr`被销毁时,引用计数归零,对象才会被释放。然而,如果两个或多个`shared_ptr`相互引用,引用计数将永远不会归零,因为它们相互持有对方的引用。
> example:双向链表中的循环引用
```
struct Node {
std::shared_ptr<Node> next; // 指向下一个节点
std::shared_ptr<Node> prev; // 指向前一个节点
};
auto node1 = std::make_shared<Node>();
auto node2 = std::make_shared<Node>();
node1->next = node2; // node1 持有 node2 的共享所有权
node2->prev = node1; // node2 持有 node1 的共享所有权
```
为了解决循环引用问题C++标准库提供了`std::weak_ptr``std::weak_ptr`是一种弱引用,它不会增加引用计数,但可以指向一个由`std::shared_ptr`管理的对象
```
struct Node {
std::shared_ptr<Node> next; // 指向下一个节点
std::weak_ptr<Node> prev; // 使用 weak_ptr 指向前一个节点
};
auto node1 = std::make_shared<Node>();
auto node2 = std::make_shared<Node>();
node1->next = node2; // node1 持有 node2 的共享所有权
node2->prev = node1; // node2 使用 weak_ptr 指向 node1
```
如果对象仍然存在lock()返回一个指向对象的`std::shared_ptr` 如果对象已经被销毁lock()返回一个空的`std::shared_ptr`
__正确选择智能指针类型__
> 如果对象的所有权是唯一的,使用`std::unique_ptr`
> 如果对象的所有权需要共享,使用`std::shared_ptr`
> 如果需要解决循环引用问题,使用`std::weak_ptr`
### some-example
```
#include <iostream>
#include <memory>
#include <algorithm> // 用于 std::fill
int main() {
// 使用 std::unique_ptr 管理1000个 int 的内存空间
std::unique_ptr<int[]> data = std::make_unique<int[]>(1000);
// 初始化数据
std::fill(data.get(), data.get() + 1000, 42); // 将所有元素初始化为42
// data.get()返回底层指针可以用于标准库算法如std::fill
// 访问并打印部分数据
for (int i = 0; i < 10; ++i) {
std::cout << data[i] << " ";
}
std::cout << std::endl;
// 当 unique_ptr 超出作用域时,内存会自动释放
return 0;
}
```
```
#include <iostream>
#include <memory>
#include <algorithm> // 用于 std::fill
int main() {
// 使用 std::shared_ptr 管理1000个 int 的内存空间
std::shared_ptr<int[]> data(new int[1000]); // 使用 new[] 分配内存
// ***std::shared_ptr<int[]>需要显式使用new[]分配内存***
// 初始化数据
std::fill(data.get(), data.get() + 1000, 42); // 将所有元素初始化为42
// 访问并打印部分数据
for (int i = 0; i < 10; ++i) {
std::cout << data[i] << " ";
}
std::cout << std::endl;
// 当最后一个 shared_ptr 超出作用域时,内存会自动释放
return 0;
}
```

View File

@ -1,12 +1,13 @@
# 存储说明符 # 说明符
+ static
+ extern
+ mutable
+ thread_local
# cv 说明符 ## 目录
+ const - [static](#static)
+ volatile - [extern](#extern):引用声明
- [mutable](#mutable)
- [thread local](#`thread_local`)
- [const](#const)
- [volatile](#volatile):强制读内存
- [restrict](#restrict) : 指示编译器优化指针(C99)
## static ## static
@ -18,7 +19,7 @@
## extern ## extern
引用声明 引用声明
声明引用在其他地方定义的变量 声明引用在其他地方定义的变量或者函数
## mutable ## mutable
即使结构(类)被声明为const,也可以被修改的成员函数 即使结构(类)被声明为const,也可以被修改的成员函数
@ -36,4 +37,15 @@
就像使用了static 就像使用了static
如果希望连接性为外部则可以用extern覆盖 如果希望连接性为外部则可以用extern覆盖
## volatile 强制读内存 ## restrict
- 在没有 restrict 的情况下,编译器需要考虑指针之间可能存在的别名关系。编译器会频繁地从内存中读取数据,以确保每次使用指针时数据是最新的。这会导致不必要的内存访问
- 使用 restrict 后,编译器可以假设带有 restrict 的指针是唯一的,不会与其他指针指向同一块内存。因此,编译器可以减少不必要的内存访问
```c
void copy(int *restrict dest, int *restrict src, int n) {
for (int i = 0; i < n; i++) {
dest[i] = src[i];
}
}
```
__如果违反了 restrict 的约束(即多个 restrict 指针指向同一块内存程序的行为是未定义的__

View File

@ -1,35 +1,33 @@
# 模板 # 模板
## 目录
- [函数模板](#)
- [生成](#)
- [类模板](#)
- [完美转发](#)
- [可变参数](#可变参数)
## 函数模板 ## 函数模板
### 基本格式
```cpp ```cpp
template <typename T> template <typename T>
void swap(T &a, T &b) { void swap(T &a, T &b);
T temp;
temp = b;
b = a;
a = temp;
}
``` ```
__函数模板必须放在头文件里,因为它不是函数实体只能算编译指令__ __函数模板必须放在编译器可见的地方里因为不是函数实体只能算编译指令__
### 模板的几个术语 ### 生成
+ **隐式实例化** **隐式实例化**
```cpp ```cpp
int a=0, b=3; int a=0, b=3;
swap<int>(a, b); swap<int>(a, b);
``` ```
+ **显式实例化** **显式实例化**
```cpp ```cpp
template void swap<int>(int&, int&); // 使用模板生成int类型的函数定义 template void swap<int>(int&, int&); // 使用模板生成int类型的函数定义
``` ```
+ **显式具体化** **显式具体化** 这个可以放到其他文件里,因为是有实体的
```cpp ```cpp
template <> void swap<int>(int&, int&); template <> void swap<int>(int&, int&);
``` ```
__这个可以放到其他文件里因为是有实体的__
### 类型推导 ### 类型推导
+ decltype() + decltype()
@ -39,5 +37,29 @@ decltype(a) var;
``` ```
## 类模板 ## 类模板
同样没有实体,包括类成员方法等都要写成模板的格式
+ 同样没有实体,包括类成员方法等都要写成模板的格式 ## 完美转发
- [右值引用](./class.md#):有关完美转发的基础
完美转发允许模板函数将参数原封不动地转发给另一个函数,保留其左值或右值的性质。这在编写通用代码时非常有用,例如模板库或函数包装器
完美转发的核心是std::forward它根据模板参数T的类型左值引用或右值引用来决定如何转发参数
```
template <typename T>
void wrapper(T&& arg) {
// 转发 arg 到另一个函数,保留其左值或右值性质
targetFunction(std::forward<T>(arg));
}
```
## 可变参数
```
void print() {} // 终止递归
template <typename T, typename... Types)
void print(const T& firstArg, const Types&... args) {
std::cout << firstArg << std::endl;
print(args...);
}
```

View File

@ -1,35 +0,0 @@
#include <iostream>
#include <thread>
using namespace std;
void func(int a) {
cout << a << endl;
}
mutex mtx; // 互斥量
int main() {
// 同一个进程的多个线程共享该进程的全部系统资源
// 但各个线程有自己的调用栈,寄存器,本地存储
// thread类
// 1.
// thread() noexcept;默认构造函数,不执行任何任务
// 2.
// template<class Funtion, class ...Args>
// explicit thread(Function&& fx,Args&&... args);
thread t1(func, 1); //用2方法创建线程
// mtx.lock();
// mtx.unlock();
if(t1.joinable()) // 判断能否调用join
t1.join(); //回收线程t1
// t1.detach(); //分离线程
return 0;
}
// 注g++编译器要加 -pthread -std=c++11选项编译

View File

@ -0,0 +1,130 @@
# cpp多线程
C++11引入了标准的线程库 `<thread>`,使得多线程编程更加方便和标准化
## 目录
- [创建线程](#)
- [线程的分离和结合](#)
- [传递参数给线程函数](#)
- [线程的同步](#)
- [互斥锁](#mutex)
- [lock-guard](#)
- [条件变量](#)
- [原子操作](#atomic)
- [高级接口](#高级接口)
- [线程池](#)
## 创建线程
通过 `std::thread` 类来创建和管理线程
```
std::thread t(thread_function); // 创建线程并执行 thread_function
t.join(); // 等待线程结束
```
## 线程的分离和结合
+ `join()`:主线程等待子线程执行完毕。
+ `detach()`:将子线程与主线程分离,子线程在后台独立运行。
## 传递参数给线程函数
可以通过 `std::thread` 的构造函数传递参数给线程函数
`std::thread t(thread_function, 10, "Hello");`
## 线程的同步
多个线程访问共享资源时可能会出现竞争条件Race Condition。为了避免这种情况可以使用互斥锁Mutex来同步线程
### 互斥锁
`std::mutex` 用于保护共享资源,确保同一时间只有一个线程可以访问,`<mutex>`
```
std::mutex mtx; // 互斥量
void print_block() {
mtx.lock(); // 锁定互斥锁
// ...
mtx.unlock(); // 解锁互斥锁
}
// in main ->
std::thread t1(print_block, 50, '*');
std::thread t2(print_block, 50, '$');
t1.join();
t2.join();
```
### lock-guard
`std::lock_guard` 是一个 RAII 风格的互斥锁管理类它在构造时锁定互斥锁在析构时自动解锁0
```
std::mutex mtx;
std::lock_guard<std::mutex> lock(mtx);
```
## 条件变量
条件变量用于线程间的同步,允许线程在某些条件不满足时等待,直到其他线程通知条件满足
`<condition_variable>`
```cpp
std::mutex mtx;
std::condition_variable cv;
bool ready = false;
void print_id(int id) {
std::unique_lock<std::mutex> lock(mtx);
while (!ready) {
cv.wait(lock); // 线程等待条件变量
}
std::cout << "thread " << id << '\n';
}
void go() {
std::unique_lock<std::mutex> lock(mtx);
ready = true;
cv.notify_all(); // 通知所有等待的线程
}
// main
std::thread threads[10];
for (int i = 0; i < 10; ++i) {
threads[i] = std::thread(print_id, i);
}
go();
for (auto& th : threads) {
th.join();
}
```
## atomic
`std::atomic` 提供了原子操作,确保对共享变量的操作是不可分割的
## 高级接口
```cpp
void wait_s(time_t sec) { // 延时函数
std::cout << "BEGIN" << std::endl;
time_t begin = time(0);
while((time(0) - begin) < sec);
}
int main() {
std::future<void> r1(std::async(wait_s, 10));
// async let `a piece of functionality` run in alone thread
wait_s(10);
r1.get(); // 获得返回值,确保必将被调用
std::cout << "END" << std::endl;
return 0;
}
```
## 线程池
通过 `std::thread` 和任务队列来实现一个简单的[线程池](../library/thread-pool.cpp)
// thread类
// 1.
// thread() noexcept;默认构造函数,不执行任何任务
// 2.
// template<class Funtion, class ...Args>
// explicit thread(Function&& fx,Args&&... args);
if(t1.joinable()) // 判断能否调用join
t1.join(); //回收线程t1
// t1.detach(); //分离线程
return 0;
}
__注g++编译器要加 -pthread -std=c++11选项编译__

View File

@ -1,34 +0,0 @@
abs(x); //求x的绝对值 e.g.abs(-5)=5
fabs() //实数绝对值
a=clock();测量从程序开始到目前的时间并把值给a。单位毫秒级
exp(x); //求x的自然指数e^x e.g.exp(1)=2.718282
floor(x); //向下取整
ceil(x); //向上取整
log(x); //求实数x的自然数对数 e.g.log(1)=0
pow(x,y); //计算x^y,输出为双精度实数 e.g.pow(2,3)=8
sqrt(x); //求x的平方根 e.g.sqrt(25)=5
str.length();获取字符串长度(只能用在字符串string b="123456789"//字符串定义 初始化。
str.size();
strlen(str);获取字符数组长度(只能用在字符数组)。
abort() //终止程序运行 不能结束工作
exit() //终止程序运行 做结束工作
max(a,b) //两数中最大
min(a,b) //两数中最小
swap(a,b) //交换两个类型相同的变量
sort(数组名+起始单元,数组名+结束单元排序方法cmp倒)
sort(a,a+n);//sort默认为升序
int cmp(int a,int b)
{
return a>b;
}
Sleep(s时间);//程序运行到此处时暂停s毫秒注意大小写需调用头文件#include<windows.h>
void color(const unsigned short textColor) //颜色字函数
{
if(textColor>=0&&textColor<=15)
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),textColor);
else
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),7);
}

View File

@ -1,6 +1,7 @@
a1 = int(input("input:")) # input a1 = int(input("input:")) # input
a, b = map(int, input().split()) # 读取输入分割并转化
print('%8d'%a1, '\n') # print print('%8d'%a1, '\n') # print
# print(objects, sep=' ', wnd='\n') # print(objects, sep=' ', end='\n')
# 数据类型 # 数据类型

View File

@ -1,23 +0,0 @@
# 字符串
s = str("Hello world")
print(s[3])
print(s[-3])
print(s[0:3]) # 字符串切片
print(s[3:])
print(s[:5])
print(s[:-5])
print(s[::-1]) # 倒序
len(s) # 计算长度
# 字符串查询
s.find('w')
s.count('l')
s.replace('H','h') # H变成h
s.casefold() # 大写转小写
s.upper() # 小写转大写
s.swapcase() # 转换大小写
eval('12+3') # 计算值 返回15

View File

@ -0,0 +1,97 @@
# python基础合集
## 目录
- [程序之始](#run)
- [IO](#IO)
- [格式化输出](#)
- [字符串](#string)
- [DEBUG](#Debug)
- [变量](#)
- [数值类型](#)
## run
`if __name__ == '__main__'` 判断程序是否在主函数内(区别与模块等)
## IO
`print(values, sep, end)` 值, 分割符, 结尾符
> `print("hello", sep=',', end='_')`
> `print('%8d'%a1, '\n')`
a1 = int(input("input:")) # input
a, b = map(int, input().split()) # 读取输入分割并转化
### 格式化输出
1. 占位符
+ `%`
+ `format()`
+ `格式化f`
> example
`print("%4d" % a)`
## string
字符串可以用单引号或者双引号
三引号可以跨行
> example
```python
print('''Hello
world''')
```
```
s = str("Hello world")
print(s[3])
print(s[-3])
print(s[0:3]) # 字符串切片
print(s[3:])
print(s[:5])
print(s[:-5])
print(s[::-1]) # 倒序
len(s) # 计算长度
s.find('w')
s.count('l')
s.replace('H','h') # H变成h
s.casefold() # 大写转小写
s.upper() # 小写转大写
s.swapcase() # 转换大小写
eval('12+3') # 计算值 返回15
```
## Debug
1. pdb
`import pdb; pdb.set_trace()` 程序运行到这行代码时会进入调试模式
+ nnext执行下一行代码。
+ ccontinue继续运行直到遇到下一个断点。
+ qquit退出调试。
+ p <变量>:打印变量的值。
+ llist显示当前代码上下文。
+ b <行号>:设置断点。
+ clclear清除断点。
```
def add(a, b):
import pdb; pdb.set_trace() # 从这里进入断点调试
return a + b
print(add(3,5))
```
## 变量
name = value
### 数值类型
+ int
+ float
+ bool True = 1 False = 0
+ complex
+ [str](#string)
type()

View File

@ -1,8 +1,39 @@
安装docker # docker
sudo apt-get install docker-compose 是一个开源的应用容器引擎
配置/etc/docker/daemon.json ## 目录
- [核心组件](#)
- [install](#)
- [基本命令](#)
- [样例](#example-command)
- [数据管理](#)
- [高级用法](#)
- [daemon.json](#)
## 核心组件
+ 镜像Image只读模板包含运行应用所需的所有内容如代码、运行时、库等
+ 容器Container镜像的运行实例隔离运行应用的环境
+ 仓库Repository存储和分发镜像的地方Docker Hub 是最常用的公共仓库
## install
+ 更新系统并安装依赖包
+ 添加 Docker 官方 GPG 密钥并设置存储库
+ 安装 Docker 引擎并启动服务
+ 设置 Docker 开机自启
```bash
sudo apt-get update
curl -fsSL https://mirrors.aliyun.com/docker-ce/linux/ubuntu/gpg | sudo apt-key add -
echo "deb [arch=amd64] https://mirrors.aliyun.com/docker-ce/linux/ubuntu $(lsb_release -cs) stable" | sudo tee -a /etc/apt/sources.list.d/docker.list
sudo apt-get update
sudo apt-get install docker-ce docker-ce-cli containerd.io
# 验证安装
sudo docker --version
```
__配置/etc/docker/daemon.json__
```
{ {
"registry-mirrors": "registry-mirrors":
[ [
@ -11,23 +42,173 @@
"https://docker.nju.edu.cn" "https://docker.nju.edu.cn"
] ]
} }
```
docker ps #查看所有运行容器 ## 基本命令
docker ps -a #全部 1. 镜像操作
+ `docker pull <镜像名>` : 从仓库拉取镜像
+ `docker image ls` : 列出本地镜像
+ `docker rmi <镜像ID>` : 删除镜像
docker stop [ID / NAME] #停止某容器 2. 容器操作
+ `docker run -d <镜像名>` : 创建并后台运行容器
+ `docker ps` : 列出所有运行中容器
+ `docker stop [ID / NAME]` : 停止某容器
+ `docker start [ID / NAME]` : 启动已经停止的容器
+ `docker rm [ID / NAME]` : 删除容器
+ `docker commit <容器ID或名称> <新镜像名称>:<标签>` 将运行中的容器保存为镜像
+ `docker save -o my-new-image.tar my-new-image:latest` 导出为tar文件
+ `docker load -i my-new-image.tar` 导入镜像到目标环境
docker start [ID / NAME] #启动和删除容器 3. 其他
docker rm [ID / NAME] + `docker search <镜像名>` 搜索镜像
+ `docker logs <容器ID>` : 查看容器日志
+ `docker exec -it [ID / NAME] /bin/bash` : 进入容器
+ `c-P c-Q` : 退出容器
+ `sudo systemctl daemon-reload && sudo systemctl restart docker` 重启
+ `docker load < xxx.tar` #导入tar为镜像
+ `docker commit [ID] [镜像名]` #容器保存为镜像
docker pull 仓库名/镜像名 ### example-command
> `docker pull ubuntu` 拉取 Ubuntu 镜像
> `docker run -it ubuntu /bin/bash` 运行 Ubuntu 容器并进入交互式 shell
> `docker ps -a` 列出所有容器(包括停止的)
> `docker run -it --name mc-ser ubuntu -v /opt/mc:/opt/mc -p 25565:25565 /bin/bash`
docker save [ID] > xxx.tar #导出为tar文件 ## 数据管理
docker load < xxx.tar #导入tar为镜像 为了持久化数据Docker 提供了多种数据管理方式,主要包括 数据卷Volumes、绑定挂载Bind Mounts 和 临时文件系统tmpfs
docker commit [ID] [镜像名] #容器保存为镜像 1. Docker 数据卷Volumes
数据卷是 Docker 推荐的数据持久化方式。数据卷存储在 Docker 管理的文件系统中(通常是 /var/lib/docker/volumes/),与容器的生命周期分离,即使容器被删除,数据卷仍然存在
docker image ls #查看本地所有镜像 + 持久化:数据卷独立于容器,容器删除后数据仍然保留。
docker rmi [ID / NAME] #删除镜像 + 高性能:数据卷通常比绑定挂载性能更好。
+ 易于备份和迁移:数据卷可以通过 Docker 命令轻松备份和恢复。
docker exec -it [ID / NAME] bash #操作容器 > `docker volume create my_volume` 创建数据卷
> `docker volume ls` 查看数据卷
> `docker volume inspect my_volume` 查看数据卷详细信息
> `docker run -d --name my_container -v my_volume:/path/in/container <image_name>` 挂载数据卷到容器
> `docker volume rm my_volume` 删除数据卷
> `docker volume prune` 清理未使用的数据卷
2. 绑定挂载Bind Mounts
绑定挂载是将主机上的文件或目录直接挂载到容器中
+ 灵活性:可以直接挂载主机上的任意文件或目录。
+ 实时同步:主机和容器之间的文件修改会实时同步。
+ 依赖主机文件系统:绑定挂载的路径必须存在于主机上。
- `docker run -d --name my_container -v /host/path:/container/path <image_name>` 挂载主机目录到容器
3. 临时文件系统tmpfs
tmpfs 是一种基于内存的文件系统,适用于需要临时存储数据的场景。数据仅存储在内存中,容器停止后数据会丢失
+ 高性能:数据存储在内存中,读写速度快。
+ 临时性:数据不会持久化,容器停止后数据丢失。
`docker run -d --name my_container --tmpfs /container/path <image_name>` 挂载 tmpfs 到容器
4. 数据卷的高级用法
多个容器可以共享同一个数据卷,实现数据共享
```
docker run -d --name container1 -v my_volume:/data <image_name>
docker run -d --name container2 -v my_volume:/data <image_name>
```
可以将数据卷或绑定挂载设置为只读,防止容器修改数据
`docker run -d --name my_container -v my_volume:/data:ro <image_name>`
## 高级用法
1. Dockerfile
Dockerfile 是用于构建镜像的脚本文件,包含一系列指令,用于定义镜像的构建过程
```
# 使用基础镜像
FROM ubuntu:latest
# 维护者信息
MAINTAINER Your Name <your.email@example.com>
# 更新包索引并安装软件
RUN apt-get update && apt-get install -y \
software-properties-common \
python3
# 设置工作目录
WORKDIR /app
# 复制当前目录下的文件到容器中的 /app 目录
COPY . /app
# 暴露端口
EXPOSE 80
# 设置环境变量
ENV NAME World
# 运行命令
CMD ["python3", "app.py"]
```
`docker build -t <image_name> .` 构建镜像
2. Docker 网络
`docker network create <network_name>` 创建网络
`docker network ls` 查看网络
`docker network connect <network_name> <container_id>` 连接容器到网络
3. Docker 数据管理
`docker volume create <volume_name>` 创建数据卷
`docker volume ls` 查看数据卷
`docker run -v <volume_name>:/path/in/container <image_name>` 挂载数据卷到容器
4. Docker 资源限制
`docker run -m 512m <image_name>` 限制内存
`docker run --cpus="1.5" <image_name>` 限制 CPU
5. Docker 安全
> 使用非 root 用户运行容器
```dockerfile
FROM ubuntu
RUN useradd -m myuser
USER myuser
```
> 限制容器权限
`docker run --cap-drop=ALL --cap-add=NET_BIND_SERVICE <image_name>`
## daemon.json
```
{
"registry-mirrors": [
"https://docker.1ms.run",
"https://docker.xuanyuan.me",
"https://docker.zhai.cm",
"https://a.ussh.net",
"https://hub.littlediary.cn",
"https://hub.rat.dev",
"https://atomhub.openatom.cn",
"https://docker.m.daocloud.io",
"https://dytt.online",
"https://func.ink",
"https://lispy.org",
"https://docker.xiaogenban1993.com",
"https://docker.mybacc.com",
"https://docker.yomansunter.com",
"https://dockerhub.websoft9.com",
"https://docker-0.unsee.tech",
"https://docker-cf.registry.cyou",
"https://docker.1panel.live",
"https://docker.imgdb.de",
"https://docker.hlmirror.com",
"https://dockerpull.org",
"https://dockerhub.icu",
"https://proxy.1panel.live",
"https://docker.1panel.top",
"https://docker.ketches.cn"
]
}
```

View File

@ -1,29 +0,0 @@
g++ -S test.s test.cpp #编译成汇编文件
g++ -c test.o test.s #目标文件
g++ -o test test.o #链接
g++ -O1/-O2/-O3 // 优化选项
g++ -S -o - -fverbose-asm xxx.cpp > xxx.s // 输出带注释的汇编
#静态库制作:
g++ -c -o libtest.a {test.cpp 源代码文件清单}
// g++ -c test.cpp
// ar -crv libtest.a test.o
#动态库制作
g++ -fPIC -shared -o libtest.so {test.cpp 源代码文件清单}
// g++ -fPIC -c test.cpp
// g++ -shared -o libtest.so tset.o
两种方法
1 + .so 放到 /usr/lib 或 /lib
2 + 路径放到/etc/ld.so.conf 运行ldconfd 重建/etc/ld.so.cache
# 库使用
g++ -o main -l库名 -L库路径 -I头文件路径
// g++ -o main -ltest -L/home/e0x1a/test
如果是动态库,需要指定 LD_LIBRARY_PATH 环境变量 //待查
用 echo $LD_LIBRARY_PATH 查看

43
program/tool/gcc_g++.md Normal file
View File

@ -0,0 +1,43 @@
# gcc-g++ 编译器
## 目录
- [编译选项](#编译选项)
- [制作库文件](#制作库文件)
## 编译选项
`g++ -S test.s test.cpp` 编译成汇编文件
`g++ -c test.o test.s` 目标文件
`g++ -o test test.o` 链接
`g++ -O1/-O2/-O3` // 优化选项
`g++ -S -o - -fverbose-asm xxx.cpp > xxx.s` // 输出带注释的汇编
`gcc -s` 选项用于剥除可执行文件中的符号表和重定位信息,减小可执行文件的大小
- **正常功能保持**
- **性能无显著差异**
- **调试困难**:符号表中包含了函数名、变量名等调试信息。使用 `-s` 选项后,这些信息会被移除,使得调试变得非常困难
- **反汇编和分析不便**:没有符号表,反汇编工具(如 `objdump`)无法显示函数名和变量名等信息,只能显示汇编指令和地址。这会增加对程序进行反汇编分析的难度。
## 制作库文件
> 静态库制作:
g++ -c -o libtest.a {test.cpp 源代码文件清单}
// g++ -c test.cpp
// ar -crv libtest.a test.o
> 动态库制作
g++ -fPIC -shared -o libtest.so {test.cpp 源代码文件清单}
// g++ -fPIC -c test.cpp
// g++ -shared -o libtest.so tset.o
两种方法
1 + .so 放到 /usr/lib 或 /lib
2 + 路径放到/etc/ld.so.conf 运行ldconfd 重建/etc/ld.so.cache
> 库使用
g++ -o main -l库名 -L库路径 -I头文件路径
// g++ -o main -ltest -L/home/e0x1a/test
如果是动态库,需要指定 LD_LIBRARY_PATH 环境变量 //待查
用 echo $LD_LIBRARY_PATH 查看

63
program/tool/gdb.md Normal file
View File

@ -0,0 +1,63 @@
# gdb c/c++ 代码调试工具
使用 -g 生成调试文件,不可以加 -O 选项
## 目录
- [基础使用](#基础使用)
- [run](#run)
- [set](#set)
- [mem](#mem)
- [disassemble](#disassemble)
- [info](#info)
- [core](#core)
## 基础使用
`l n` 列出源码
`b n` 在第n行打断点
`break main if var_name > 10` 满足条件时打断点
`d n` 删除断点
`lay next` 切换窗口布局TUI
`Ctrl-x a` 退出TUI
`Ctrl-x 1` 恢复单窗口布局
`Ctrl-x o` 切换窗口焦点
`ref``Ctrl-L` 刷新布局
### run
`r` 开始运行到第一个断点
`c` 继续运行到下一个断点
`s` 步入
`si` 单步汇编
`n` 步过
`ni`
`q` 退出
### set
`set [argc]` 传入参数
`set var []` # 修改变量的值
`set $rsp = 0x7ccc` # 修改寄存器的值
### mem
`p` # 查看特定变量或表达式的值
`bt` # 查看函数调用栈
`bt full` # 详细信息
`x/10gx $rsp` # 查看内存中由$rsp开始的10个八字节数据 g:八字节 x:16进制
### disassemble
`disassemble` # 查看当前函数的汇编,可在后面加函数名看指定汇编
`x/i $pc` # 检查pc处的汇编
### info
`info locals` # 查看局部变量
`info registers` # 查看寄存器状态
`info program` # 查看程序运行信息
`info break` # 查看断点信息
`info threads` # 查看所有线程
## core
1. `ulimit -c unlimited` 使挂掉的程序产生core文件
2. 修改 `/proc/sys/kernel/core_pattern` 使其在当前目录下生成core文件
> `sudo echo "./core" > /proc/sys/kernel/core_pattern
3. gdb ./a.out core
gdb demo -p [pid] # 调试运行中的程序

View File

@ -1,35 +0,0 @@
gdb c/c++ 代码调试工具
使用 g++ -g 生成调试文件,不可以加 -O 选项
set [argc] # 传入参数
l n # 列出源码
r # 开始运行到第一个断点
b n # 在第n行打断点
break main if var_name > 10 # 满足条件时打断点
d n # 删除断点
c # 继续运行到下一个断点
s # 单步执行
si # 单步汇编
n # 单步执行,不进入函数
q # 退出
set var [] # 修改变量的值
set $rsp = 0x7ccccccc # 修改寄存器的值
p # 查看特定变量或表达式的值
bt # 查看函数调用栈
bt full # 详细信息
disassemble # 查看当前函数的汇编,可在后面加函数名看指定汇编
x/10gx $rsp # 查看内存中由$rsp开始的10个八字节数据 g:八字节 x:16进制
info locals # 查看局部变量
info registers # 查看寄存器状态
info program # 查看程序运行信息
info break # 查看断点信息
info threads # 查看所有线程
使用gdb调试core文件
使用ulimit -c unlimited 使挂掉的程序产生core文件
gdb ./a.out core
gdb demo -p [pid] # 调试运行中的程序

View File

@ -1,46 +1,486 @@
# makefile # make
makefile 是一个通用的项目代码构建器 ## 目录
由规则 依赖 命令构成 - [参数](#参数)
- [makefile](#makefile)
## 参数
`-n` 参数可以打印出make会执行的命令但不真正执行
`-f <filename>` 指定makefile
`-i` 忽略错误
`-k` 在某个目标失败时,尽量构建其他
`-s` 静默
`-j4` 4线程
`-C /dir` 进入指定目录再运行
`-d` debug
`-w` 打印工作目录
`-B` 强制重新构建
## makefile
Makefile 是一种用于自动化构建和管理项目的工具,尤其在 C/C++ 项目中广泛使用。除了基本的规则和依赖关系Makefile 还支持一些高级语法和功能,可以帮助你编写更灵活和高效的构建脚本
规则:依赖 规则:依赖
命令 命令
__注意: 命令前面必须要用Tab__
+ -n 参数可以打印出make会执行的命令但不真正执行 ## 普通变量定义
# 注意: 命令前面必须要用Tab
# 伪目标
.PHONY:clean
# 自动变量
$@ 目标文件
$< 第一个依赖文件
$^ 全部的依赖文件
# 普通变量定义
OPTION = -O3 -Wall OPTION = -O3 -Wall
# 变量使用 ## 变量使用
g++ $(OPTION) xxx g++ $(OPTION) xxx
# 使用通配符 ### 1. 变量和赋值
%.o: %.c Makefile 支持多种变量赋值方式:
g++ $(OPTION) -c $< -o $@
- **`=`**:递归展开变量,变量值在使用时展开。
- **`:=`**:简单展开变量,变量值在定义时展开。
- **`?=`**:条件赋值,只有当变量未定义时才赋值。
- **`+=`**:追加赋值,将值追加到变量末尾。
```makefile
CC = gcc
CFLAGS := -Wall -O2
CFLAGS += -g
```
### 2. 自动变量
Makefile 提供了一些自动变量,用于简化规则的编写:
- **`$@`**:目标文件名。
- **`$<`**:第一个依赖文件名。
- **`$^`**:所有依赖文件列表。
- **`$?`**:所有比目标新的依赖文件列表。
- **`$*`**:目标文件的主干名(不包含扩展名)。
```makefile
%.o: %.c
$(CC) $(CFLAGS) -c $< -o $@
```
### 3. 模式规则
模式规则允许你使用通配符来定义规则,适用于多个文件。
```makefile
%.o: %.c
$(CC) $(CFLAGS) -c $< -o $@
```
### 4. 函数
Makefile 提供了一些内置函数,用于处理字符串、文件名等。
- **`$(wildcard pattern)`**:匹配文件名模式。
- **`$(patsubst pattern,replacement,text)`**:模式替换。
- **`$(shell command)`**:执行 shell 命令并返回结果。
- **`$(foreach var,list,text)`**:循环遍历列表。
```makefile
SRCS = $(wildcard *.c)
OBJS = $(patsubst %.c,%.o,$(SRCS))
```
### 5. 条件判断
Makefile 支持条件判断,可以根据条件执行不同的操作。
```makefile
ifeq ($(DEBUG),1)
CFLAGS += -DDEBUG
else
CFLAGS += -DNDEBUG
endif
```
### 6. 包含其他 Makefile
你可以使用 `include` 指令将其他 Makefile 包含到当前 Makefile 中。
```makefile
include config.mk
```
### 7. 多目标规则
你可以为一个规则定义多个目标,这样它们可以共享相同的命令。
```makefile
all: target1 target2
target1 target2:
@echo Building $@
```
### 8. 伪目标
伪目标是指那些不对应实际文件的目标,通常用于执行一些操作,如清理、安装等。
```makefile ```makefile
.PHONY: clean .PHONY: clean
INCLUDE = -I/home/e0x2d/include
main: file1.o file2.o
g++ -o $@ file1.o file2.o
file1.o: file1.cpp
g++ -c file1.o file1.cpp
file2.o: file2.cpp
g++ -c file2.o file2.cpp ${INCLUDE}
clean: clean:
rm *.o demo rm -f *.o
```
### 9. 嵌套 Makefile
你可以在 Makefile 中调用另一个 Makefile通常用于构建子目录中的项目。
```makefile
SUBDIRS = dir1 dir2
$(SUBDIRS):
$(MAKE) -C $@
all: $(SUBDIRS)
```
### 10. 自定义函数
你可以通过 `define``endef` 定义自定义函数。
```makefile
define greet
@echo "Hello, $(1)!"
endef
all:
$(call greet,World)
```
### 11. 错误处理
你可以使用 `$(error text)``$(warning text)` 来生成错误或警告信息。
```makefile
ifeq ($(CC),)
$(error CC is not set)
endif
```
### 13. 隐式规则
Makefile 有一些内置的隐式规则,可以自动推导如何从源文件生成目标文件。你可以通过定义自己的隐式规则来覆盖默认行为。
```makefile
%.o: %.c
$(CC) $(CFLAGS) -c $< -o $@
```
### 14. 动态依赖
你可以使用 `-include` 来包含动态生成的依赖文件,通常用于自动生成头文件依赖。
```makefile
-include $(OBJS:.o=.d)
%.d: %.c
@$(CC) -MM $< > $@.$$$$; \
sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \
rm -f $@.$$$$
```
### 16. 环境变量
Makefile 可以访问环境变量,并且可以通过 `export` 将变量传递给子 Makefile。
```makefile
export PATH := $(PATH):/usr/local/bin
```
### 17. 多行命令
你可以使用反斜杠 `\` 将长命令分成多行。
```makefile
all:
@echo "This is a long command that spans \
multiple lines."
```
### 18. 命令前缀
命令前缀可以控制命令的执行方式:
- **`@`**:不显示命令本身,只显示输出。
- **`-`**:忽略命令的错误,继续执行。
```makefile
all:
@echo "This command will not be displayed"
-rm -f non_existent_file
```
### 19. 文件搜索路径
你可以使用 `VPATH``vpath` 来指定源文件的搜索路径。
```makefile
VPATH = src:include
vpath %.c src
vpath %.h include
```
### 20. 递归扩展变量
递归扩展变量在使用时才会展开,可以用于动态生成内容。
```makefile
FOO = $(BAR)
BAR = $(BAZ)
BAZ = Hello
all:
@echo $(FOO) # 输出 Hello
```
### 21. 静态模式规则
静态模式规则允许你为特定目标定义规则。
```makefile
objects = foo.o bar.o
$(objects): %.o: %.c
$(CC) $(CFLAGS) -c $< -o $@
```
### 22. 多架构支持
你可以通过条件判断来支持多架构编译。
```makefile
ifeq ($(ARCH),x86)
CFLAGS += -m32
else ifeq ($(ARCH),x64)
CFLAGS += -m64
endif
```
### 23. 自定义后缀规则
你可以定义自定义的后缀规则来处理特定类型的文件。
```makefile
.SUFFIXES: .cpp .o
.cpp.o:
$(CXX) $(CXXFLAGS) -c $< -o $@
```
### 24. 使用 `eval` 动态生成规则
`eval` 函数可以动态生成 Makefile 规则。
```makefile
define MAKE_RULE
$(1): $(2)
$(CC) $(CFLAGS) -c $$< -o $$@
endef
$(eval $(call MAKE_RULE,foo.o,foo.c))
```
### 25. 使用 `foreach` 循环
`foreach` 函数可以用于循环处理列表中的每个元素。
```makefile
DIRS = dir1 dir2 dir3
all:
$(foreach dir,$(DIRS),$(MAKE) -C $(dir);)
```
### 26. 使用 `call` 调用自定义函数
`call` 函数可以调用自定义函数,并传递参数。
```makefile
define greet
@echo "Hello, $(1)!"
endef
all:
$(call greet,World)
```
### 27. 使用 `shell` 函数执行外部命令
`shell` 函数可以执行外部命令并返回结果。
```makefile
DATE := $(shell date)
```
### 28. 使用 `info``warning` 输出信息
`info``warning` 函数可以用于输出信息。
```makefile
$(info Building target $(TARGET))
$(warning This is a warning message)
```
### 29. 使用 `if` 条件判断
`if` 函数可以用于条件判断。
```makefile
DEBUG = 1
CFLAGS += $(if $(DEBUG),-g,-O2)
```
### 30. 使用 `or``and` 逻辑操作
`or``and` 函数可以用于逻辑操作。
```makefile
ifeq ($(DEBUG),1)
CFLAGS += -g
else ifeq ($(OPTIMIZE),1)
CFLAGS += -O2
endif
```
### 31. 使用 `not` 取反
`not` 函数可以用于取反操作。
```makefile
ifneq ($(DEBUG),1)
CFLAGS += -O2
endif
```
### 32. 使用 `filter``filter-out` 过滤列表
`filter``filter-out` 函数可以用于过滤列表。
```makefile
SRCS = foo.c bar.c baz.c
C_SRCS = $(filter %.c,$(SRCS))
NON_C_SRCS = $(filter-out %.c,$(SRCS))
```
### 33. 使用 `sort` 排序列表
`sort` 函数可以用于排序列表。
```makefile
SRCS = foo.c bar.c baz.c
SORTED_SRCS = $(sort $(SRCS))
```
### 34. 使用 `strip` 去除空格
`strip` 函数可以用于去除字符串中的空格。
```makefile
STR = foo bar
STRIPED_STR = $(strip $(STR))
```
### 35. 使用 `findstring` 查找子字符串
`findstring` 函数可以用于查找子字符串。
```makefile
ifeq ($(findstring foo,$(STR)),foo)
@echo "Found foo"
endif
```
### 36. 使用 `subst` 替换字符串
`subst` 函数可以用于替换字符串。
```makefile
STR = foo bar baz
NEW_STR = $(subst foo,FOO,$(STR))
```
### 37. 使用 `patsubst` 模式替换
`patsubst` 函数可以用于模式替换。
```makefile
SRCS = foo.c bar.c baz.c
OBJS = $(patsubst %.c,%.o,$(SRCS))
```
### 38. 使用 `wildcard` 匹配文件名
`wildcard` 函数可以用于匹配文件名。
```makefile
SRCS = $(wildcard *.c)
```
### 39. 使用 `dir``notdir` 获取目录和文件名
`dir``notdir` 函数可以用于获取目录和文件名。
```makefile
FILE = /path/to/file.c
DIR = $(dir $(FILE))
NAME = $(notdir $(FILE))
```
### 40. 使用 `suffix``basename` 获取后缀和主干名
`suffix``basename` 函数可以用于获取文件的后缀和主干名。
```makefile
FILE = file.c
SUFFIX = $(suffix $(FILE))
BASENAME = $(basename $(FILE))
```
### 41. 使用 `addsuffix``addprefix` 添加后缀和前缀
`addsuffix``addprefix` 函数可以用于添加后缀和前缀。
```makefile
FILES = foo bar baz
FILES_WITH_SUFFIX = $(addsuffix .c,$(FILES))
FILES_WITH_PREFIX = $(addprefix src/,$(FILES))
```
### 42. 使用 `join` 连接列表
`join` 函数可以用于连接两个列表。
```makefile
LIST1 = foo bar
LIST2 = baz qux
JOINED_LIST = $(join $(LIST1),$(LIST2))
```
### 43. 使用 `word``wordlist` 获取列表中的元素
`word``wordlist` 函数可以用于获取列表中的元素。
```makefile
LIST = foo bar baz
FIRST = $(word 1,$(LIST))
SUBLIST = $(wordlist 2,3,$(LIST))
```
### 44. 使用 `words` 获取列表长度
`words` 函数可以用于获取列表的长度。
```makefile
LIST = foo bar baz
LENGTH = $(words $(LIST))
```
### 45. 使用 `firstword` 获取列表的第一个元素
`firstword` 函数可以用于获取列表的第一个元素。
```makefile
LIST = foo bar baz
FIRST = $(firstword $(LIST))
```
### 46. 使用 `lastword` 获取列表的最后一个元素
`lastword` 函数可以用于获取列表的最后一个元素。
```makefile
LIST = foo bar baz
LAST = $(lastword $(LIST))
```
### 47. 使用 `abspath` 获取绝对路径
`abspath` 函数可以用于获取绝对路径。
```makefile
FILE = file.c
ABS_PATH = $(abspath $(FILE))
```
### 48. 使用 `realpath` 获取规范化的绝对路径
`realpath` 函数可以用于获取规范化的绝对路径。
```makefile
FILE = ../file.c
REAL_PATH = $(realpath $(FILE))
``` ```

37
radio/antenna.md Normal file
View File

@ -0,0 +1,37 @@
# 天线相关
## index
- [极化](# 极化)
## 极化
在无线电通信中,垂直极化和水平极化是两种常见的天线极化方式,它们的区别主要体现在电磁波的振动方向、传播特性以及应用场景上。以下是具体对比:
### **1. 极化方向**
- **垂直极化**电磁波的电场分量E-field与地面垂直振动天线通常垂直于地面放置如直立杆状天线
- **水平极化**:电磁波的电场分量与地面平行振动,天线通常水平架设(如水平拉长的偶极天线)。
### **2. 传播特性**
- **地面反射影响**
- 垂直极化波在地面反射时相位变化较小更适合地面波传播如AM广播、车载通信
- 水平极化波的地面反射可能导致信号抵消(尤其在仰角较低时),但对天波传播(如短波通信)更有利。
- **抗干扰能力**
- 垂直极化更容易受到地面物体(如建筑物、树木)的反射干扰。
- 水平极化受地面干扰较小,但在城市环境中可能受多径效应影响。
### **3. 应用场景**
- **垂直极化**
- **移动通信**(如车载天线、对讲机):便于安装,适应移动设备姿态变化。
- **低频段通信**如30MHz以下地波传播效率高。
- **水平极化**
- **电视广播**(传统地面电视):减少地面反射干扰,覆盖更稳定。
- **卫星通信**:减少电离层对极化旋转的影响(常与圆极化结合使用)。
- **短波通信**:利用电离层反射时效率更高。
### **4. 天线安装与兼容性**
- **极化匹配**
- 收发天线极化方式需一致,否则会导致严重信号衰减(如垂直天线无法有效接收水平极化波)。
- 特殊场景(如卫星通信)可能采用圆极化以避免极化失配。
### **总结选择依据**
- **优先垂直极化**:移动通信、地波传播、简化安装。
- **优先水平极化**:固定点对点通信、减少地面干扰、高频段应用。

View File

@ -1,15 +0,0 @@
加速器
https://cloud.tsinghua.edu.cn/d/df482a15afb64dfeaff8/
推送到github
git remote add origin https://
git branch -M master
git push -u origin master
github访问令牌
创建:
用户头像 -> setting -> 最后一项 -> 创建令牌
tips:令牌要当场复制,离开页面就没有了
使用:
git remote set-url origin https://<your_token>@github.com/<USERNAME>/<REPO>.git

40
tool/git/github.md Normal file
View File

@ -0,0 +1,40 @@
# github
## 目录
- [search](#search)
## search
1. 限定搜索范围
- `in:name`:搜索仓库名称。
- `in:description`:搜索仓库描述。
- 使用`is:issue``is:pr`搜索Issue或Pull Request。
- **星标筛选**:使用`stars:>1000`筛选星标数超过1000的项目。
- **fork数量筛选**:使用`forks:>100`筛选fork数量超过100的项目。
- **语言筛选**:使用`language:java`筛选使用Java语言的项目。
- **仓库大小筛选**:使用`size:>=1000`筛选仓库大小超过1000KB的项目。
- **更新时间筛选**:使用`pushed:>2020-01-01`筛选自2020年1月1日之后有更新的项目。
- **搜索特定用户或组织的仓库**:使用`user:username``org:organization`
- **搜索特定文件**:使用`filename:README.md`搜索包含特定文件名的项目。
- **搜索代码**必须登录GitHub账户且仅对默认分支和小于384KB的文件进行索引。
2. 排序搜索结果
- **按交互排序**`sort:interactions`按反应和评论的最高组合数排序。
- **按反应排序**`sort:reactions`按最高反应数排序。
- **按作者日期排序**`sort:author-date`按作者日期降序排序。
加速器
https://cloud.tsinghua.edu.cn/d/df482a15afb64dfeaff8/
推送到github
git remote add origin https://
git branch -M master
git push -u origin master
github访问令牌
创建:
用户头像 -> setting -> 最后一项 -> 创建令牌
tips:令牌要当场复制,离开页面就没有了
使用:
git remote set-url origin https://<your_token>@github.com/<USERNAME>/<REPO>.git

31
tool/konsole.md Normal file
View File

@ -0,0 +1,31 @@
# konsole-use
## 目录
- [strat](#start)
- [标签页管理](#标签页管理)
- [窗口分割](#窗口分割)
- [shortkey](#shortkey)
## strat
`Ctrl-Alt-t`
## 标签页管理
`Ctrl-T` 打开新标签页
`Ctrl-W` 关闭标签页
`Ctrl-PgUp` and `Ctrl-PgDn` 切换标签页
`Alt-[num]` 切换到指定标签页
## 窗口分割
`Ctrl-(` 水平分割
`Ctrl-)` 垂直分割
`Ctrl-Shift-方向键` 聚焦指定方向的终端
`Ctrl-Shift-H` 分离
## shortkey
`Ctrl-F` 查找
`Shift-F3` 查找上一个
`F3` 查找下一个
`Shift-F10` 打开菜单

View File

@ -14,28 +14,73 @@ set cursorline "光标行高亮
set hlsearch "高亮显示搜索结果 set hlsearch "高亮显示搜索结果
set incsearch "搜索模式下,每输入一个字符,就跳到对应结果 set incsearch "搜索模式下,每输入一个字符,就跳到对应结果
set ignorecase "忽略搜索大小写 set ignorecase "忽略搜索大小写
set tags=./tags;,tags "表示从当前文件所在的目录开始查找 tags 文件,如果没有,则向上递归查找
"缓冲区操作
nnoremap <space>b :buffers<cr>:b<space> nnoremap <space>b :buffers<cr>:b<space>
nnoremap <space>e :b#<cr> nnoremap <space>e :b#<cr>
nnoremap <space>n :bnext<cr> nnoremap <space>n :bnext<cr>
"页面分割操作
nnoremap <space>s :split<space> nnoremap <space>s :split<space>
nnoremap <space>v :vsp<space> nnoremap <space>v :vsp<space>
nnoremap <space>d <c-w>w nnoremap <space>d <c-w>w
"保存
nnoremap <space>w :w<cr> nnoremap <space>w :w<cr>
nnoremap <space>q :q<cr> nnoremap <space>q :q<cr>
nnoremap <space>fq :q!<cr> nnoremap <space>fq :q!<cr>
nnoremap <space>pq :%s/\t/ /g<cr>:wq<cr>
"多标签页
nnoremap <space>tt :tabedit<space> nnoremap <space>tt :tabedit<space>
nnoremap <space>tm :vert term<cr> nnoremap <space>tm :vert term<cr>
"操作优化
nnoremap gm $
nnoremap <space>r :reg<cr>
inoremap jf <esc> inoremap jf <esc>
inoremap jl <c-x><c-l>
cnoremap jf <c-c> cnoremap jf <c-c>
"inoremap jq <esc>:wq<cr> vnoremap gm $
"inoremap jw <c-x><c-n> inoremap !
"nnoremap <space>to :tabonly<cr> inoremap (
inoremap )
inoremap 【 [
inoremap 】 ]
inoremap ;
inoremap :
inoremap '
inoremap “ "
inoremap ,
inoremap 。 .
inoremap ?
inoremap<
inoremap>
inoremap · `
"自动补全
inoremap jl <c-x><c-l>
inoremap jbf <c-x><c-f>
inoremap jk <c-x><c-k>
" 启用字典补全Ctrl-X Ctrl-K
set complete+=k
" 加载字典补全功能
" 根据文件类型设置不同的字典文件
autocmd FileType c setlocal dictionary+=~/.vim/dict/cpp.dict
autocmd FileType cpp setlocal dictionary+=~/.vim/dict/cpp.dict
"autocmd FileType python setlocal dictionary+=~/.vim/dict/python.dict
"autocmd FileType javascript setlocal dictionary+=~/.vim/dict/javascript.dict
"autocmd FileType html setlocal dictionary+=~/.vim/dict/html.dict
"autocmd FileType css setlocal dictionary+=~/.vim/dict/css.dict
augroup numbertoggle "智能切换绝对行号和相对行号 augroup numbertoggle "智能切换绝对行号和相对行号
autocmd! autocmd!
autocmd BufEnter,FocusGained,InsertLeave,WinEnter * if &nu && mode() != "i" | set rnu | endif autocmd BufEnter,FocusGained,InsertLeave,WinEnter * if &nu && mode() != "i" | set rnu | endif
autocmd BufLeave,FocusLost,InsertEnter,WinLeave * if &nu | set nornu | endif autocmd BufLeave,FocusLost,InsertEnter,WinLeave * if &nu | set nornu | endif
augroup END augroup END

View File

@ -1,52 +0,0 @@
# 小技巧
使用 q[Key] 开始录制宏,再次点击q结束录制
@[Key] 调用宏
@@ 重复调用
# vim中打开终端
`:term``:vert term`
从终端转到普通模式:`<c-\>+<c-n>`
# 快捷键
> 移动
## 0 gm
行首行尾移动
## ngg
移动到第n行
## fc tc
搜索字符 ; , 可重复
> 插入
## r R
替换光标下的字符
## n<< n>>
多行左右移动
> 删除
## x
删除光标下字符
## J
与下一行合并
> 复制
## "x
使用寄存器x进行下一次复制 粘贴 删除
:reg 显示所有寄存器
> 可视模式
## o
交换选择区域另一端和光标的位置
## aw as ap ab aB
选择一个单词 句子 段落 () {}
## vi{
选中一整个大括号内
## va{
选中一整个大括号全部
## ]f
在文件中若出现文件名,指针放在上面使用这个快捷键可直接打开

157
tool/vim/cpp.dict Normal file
View File

@ -0,0 +1,157 @@
include
auto
break
case
char
const
continue
default
do
double
else
enum
extern
float
for
goto
if
int
long
register
return
short
signed
sizeof
static
struct
switch
typedef
union
unsigned
void
volatile
while
class
delete
explicit
friend
inline
namespace
new
operator
private
protected
public
template
this
throw
try
catch
virtual
using
typename
mutable
const_cast
dynamic_cast
reinterpret_cast
static_cast
typeid
endl
printf
scanf
fopen
fclose
fgets
fputs
fread
fwrite
sprintf
sscanf
malloc
free
calloc
realloc
exit
atoi
atof
rand
srand
strcpy
strncpy
strcat
strncat
strcmp
strncmp
strlen
strstr
strtok
memset
memcpy
memmove
sin
cos
tan
sqrt
pow
log
exp
ceil
floor
fabs
time
clock
difftime
gmtime
localtime
strftime
vector
list
map
unordered_map
set
unordered_set
queue
stack
deque
pair
tuple
sort
find
copy
transform
accumulate
for_each
count
reverse
max_element
min_element
define
NULL
EOF
true
false
stdin
stdout
stderr
EXIT_SUCCESS
EXIT_FAILURE
open
close
read
write
fork
exec
pipe
dup2
select
poll

89
tool/vim/shortcutKeys.md Normal file
View File

@ -0,0 +1,89 @@
# vim小技巧
`.` 重复上一个操作
## 目录
- [录制宏](# 录制宏)
- [打开终端](# 打开终端)
- [快捷键](# 快捷键)
- [移动](# 移动)
- [插入](# 插入)
- [修改](# 修改)
- [删除](# 删除)
- [复制](# 复制)
- [基本补全](# 基本补全)
- [补全菜单](# 补全菜单)
- [vimrc](# ./.vimrc)
## 录制宏
使用 q[Key] 开始录制宏,再次点击q结束录制
@[Key] 调用宏
@@ 重复调用
## 打开终端
`:term``:vert term`
从终端转到普通模式:`<c-\>+<c-n>`
## 快捷键
### 移动
+ 0 gm 行首行尾
+ ngg 移动到第n行
+ fc tc 搜索字符 ; , 可重复
+ c-u 上半屏
+ c-d 下半屏
+ Shift-[ 跳转到上一个空行
+ * 移动到下一个与当前指针下相同单词的位置
+ # 移动到上一个与当前指针下相同单词的位置
+ Ctrl + ] 跳转到标签,依赖于tags文件
+ `ctags -R .` 创建目录下的跳转文件
+ Vim 会记录跳转历史,使用 Ctrl + T 或 Ctrl + O 返回到上一个位置
+ gf 跳转到文件路径
+ `c-o` 后退一步
+ `c-i` 前进一步
+ `[a-z] / `[A-Z] 跳转到标记位置 大写全局,小写本文件,用m[a-z]设置标记
+ ` 上次跳转的位置
+ . 最后一次修改的位置
### 插入
+ I A 行首 行末 插入
+ n<< n>> 多行左右移动
## 修改
+ r R 替换光标下的字符
+ c-i-( 替换括号内的内容可以使用任何括号匹配
### 删除
+ x 删除光标下字符
+ J 与下一行合并
+ Shift-d 从当前字符删除到行末
### 复制
+ "x 使用寄存器x进行下一次复制 粘贴 删除
:reg 显示所有寄存器
### 可视模式
+ o
交换选择区域另一端和光标的位置
+ aw as ap ab aB
选择一个单词 句子 段落 () {}
+ vi{
选中一整个大括号内
+ va{
选中一整个大括号全部
+ ]f
在文件中若出现文件名,指针放在上面使用这个快捷键可直接打开
## 基本补全
1. Ctrl+n - 普通关键字补全(向下浏览)
- 在当前文件和包含文件中查找匹配项
- 适用于变量名、函数名等
2. Ctrl+p - 普通关键字补全(向上浏览)
- 与 Ctrl+n 相同,只是方向相反
3. Ctrl+x Ctrl+f - 文件名补全
- 在输入路径时自动补全文件名
4. Ctrl+x Ctrl+l - 整行补全
- 补全整行代码(从当前文件中查找相似行)
### 补全菜单
1. `c-n``c-p` 上下移动
2. `c-y` 确认
3. `c-e` 退出