vim+cpp/c(0.05)

我的是OS:Fedora Linux 43 (KDE Plasma Desktop Edition) x86_64,Kernel: Linux 6.18.5-200.fc43.x86_64, 使用(vim+clang+lldb)作为集成开发环境(IDE)作为构建、运行、和调试cpp、c的工具。

本文章围绕Alex的LearnCpp和其中文笔记learncpp网站,根据0-6 安装集成开发环境(IDE),要使得(vim+clang+lldb)成为集成开发环境的要求。

典型的现代IDE通常包含以下功能:

  • 便捷的代码文件加载与保存机制(vim编辑器)
  • 具备编程友好特性的代码编辑器,例如行号显示、语法高亮、集成帮助、名称补全及自动代码格式化(vim,以及vim插件)
  • 基础构建系统:可将程序编译链接为可执行文件并运行(vim插件)。
  • 集成调试器:便于定位和修复软件缺陷(lldb)。
  • 插件安装机制:支持扩展IDE功能或添加版本控制等特性(Cmake暂未学, Git)。

---基本工具部分:start---

下载工具

sudo dnf update
sudo dnf install vim-enhanced #我的系统下vim是这个名字
sudo dnf clang clangd lldb ld.lld make  #有时候linux没有提供C++标准库libcxx,此时需要下载

命令行终端检查

echo "======= 开发工具版本检查 =======" && \
echo "=== Clang 编译器 ===" && clang++ --version && \
echo -e "\n=== LLDB 调试器 ===" && lldb --version && \
echo -e "\n=== LLD 链接器 ===" && ld.lld --version && \
echo -e "\n=== Make 构建工具 ===" && make --version

image

为了使得vim写起来更舒服点, 我们会在$HOME(即~) 目录下新建(如果没有的话)一个vim的配置文件.vimrc, 下面是我的基本配置
.vimrc

let mapleader = " "
syntax on
set tabstop=4
set shiftwidth=4
set softtabstop=4
set nopaste
set smartindent
set autoindent
set number
set relativenumber
set cursorline
set showcmd
set wildmenu
set hlsearch
set incsearch
set ignorecase
set smartcase
nnoremap <silent> <Space><CR> :nohlsearch<CR>
noremap n nzz
noremap N Nzz


nnoremap sk :set nosplitbelow<CR>:split<CR>
nnoremap sj :set splitbelow<CR>:split<CR>
nnoremap sh :set nosplitright<CR>:vsplit<CR>                                                                                                                                                                     
nnoremap sl :set splitright<CR>:vsplit<CR>

nnoremap tk :set nosplitbelow<CR>:terminal<CR>
nnoremap tj :set splitbelow<CR>:terminal<CR>
nnoremap th :set nosplitright<CR>:vertical terminal<CR>
nnoremap tl :set splitright<CR>:vertical terminal<CR>

map <Tab>k <C-w>k
map <Tab>j <C-w>j
map <Tab>h <C-w>h
map <Tab>l <C-w>l

set laststatus=2
set statusline=%n:\ %<%F%m%r\ %{strftime(\"%H:%M\ %a\ %b\%d\ %Y\")}\ %=\ %l/%L:%c\ %p%%

filetype on
filetype plugin on
filetype indent on
filetype plugin indent on

行号显示相关set number, set relativenumber, set cursorline、语法高亮syntax on、查找搜索set hlsearch, set incsearch, set ignorecase, set smartcase, 命令相关set showcmd, set wildmenu

1-8 空白与基本格式::基本格式
采纳制表符tab设置为4个空格

  • set tabstop=4
  • set shiftwidth=4
  • set softtabstop=4
    关于自动格式化和风格指南有一个插件vim-clang-format,见后面安装。

现在我们可以使用vim写cpp(c)代码了,我们首先创建一个main.cpp的文件

$ vim main.cpp
#include <iostream>
int main()
{
    std::cout << "Hello, world!\n";
    return 0;
}

然后vim里面使用:wq来保存退出这段代码。

执行下列命令就可以用clang编译运行

$ clang main.cpp -o main
$ ./main

便会输出

Hello, world!

使用lldb调试也容易

$ clang -g -std=c++23 main.cpp -o main
$ lldb ./main
$ b main
$ run
....

上面我们是先在命令行界面进入vim里面,然后写完代码后返回到命令行界面,编译我们刚刚写好的程序并且运行。 但是如果遇到错误,我们得再次进入vim 然后回到命令行界面,如此往复。这仅仅是单个文件, 如果是多个文件呢?我们需要在多个文件中来回穿梭,而依然是那些重复的命令, 非常麻烦。

我们会想, 能不能不退出vim就可以完成这些操作呢?所幸以vim辅佐为编译器, 它支持扩展————vim插件, 而许多热心的开发者贡献了很多实用的vim插件, 这使得编写代码变得更容易。 林佬写的[异步插件asyncrun.vim]就解决了这个问题。但是在说下载异步插件前,我们先介绍一下vim-plug管理器。 以后我们会接触更多的vim插件,而插件多了, 那个需要更新,那个不需要了要卸载, 那个需要下载, 这就需要管理, 这就是vim-plug拿手的事情。

---基本工具部分:end---

---必要插件部分:start---

安装vim-plug插件管理器

安装

下载 plug.vim 会将其放入 “autoload” 目录。

curl -fLo ~/.vim/autoload/plug.vim --create-dirs \
    https://raw.githubusercontent.com/junegunn/vim-plug/master/plug.vim

此时我们会在~/.vim/autoload/看到plug.vim,这就表示下载成功了,如果没有可以自己新建这些目录, 再尝试下载。
image

用法

现在我们可以将其他插件(后续的插件)通过使用vim-plug来安装。 在您的 ~/.vimrc(或 Neovim 的 ~/.config/nvim/init.vim)中添加一个 vim-plug 部分

  • 以 call plug#begin() 开头
  • 使用 Plug 命令列出插件
  • 以 call plug#end() 结束该部分

例如:

call plug#begin('~/.vim/plugged')

" List your plugins here
Plug 'skywind3000/asyncrun.vim'

call plug#end()

注意
实例中我使用的异步插件asyncrun.vim,它的下载路径是https://github.com/skywind3000/asyncrun.vim.git, vim-plug插件可省略前缀‘https://github.com/’和后缀'.git', 简写为skywind3000/asyncrun.vim
.vimrc里面使用:source ~/.vimrc重新加载该配置文件或者重启vim后,即可执行:

:PlugInstall 安装插件
:PlugUpdate 安装或更新插件
:PlugDiff 查看上次更新后的变更
:PlugClean 移除列表中已不存在的插件


安装异步运行插件asyncrun.vim异步任务插件asynctasks.vim

我们想要在不离开vim,在里面就可以编辑,编译,测试,这需要这个asyncrun.vim异步运行插件。

基于异步运行插件,要更方便的调度building, testing 和 deploying 的任务,我们得使用 AsyncRun 的助手——asynctasks.vim异步任务

安装
使用 vim-plug 进行安装:

call plug#begin('~/.vim/plugged')

Plug 'skywind3000/asynctasks.vim'
Plug 'skywind3000/asyncrun.vim'

call plug#end()

重新加载文件或重启Vim后,即可执行:
:PlugInstall 安装插件

image

使用

提示
在安装asynctasks.vim和asyncrun.vim后,在.vimrc里面添加
let g:asyncrun_open = 6
告诉 asyncrun 运行时自动打开高度为 6 的 quickfix 窗口,不然你看不到任何输出,除非你自己手动用 :copen 打开它。
如果输出内容比较多可以使用:clist展开quickfix 窗口内容。

然后在我们给它设置快捷键:

  • 按下f6进行多文件调试
  • 按下f7进行多文件构建, 默认执行文件为统一为a.out
  • 按下f8进行多文件运行
  • 按下f9进行单文件构建, 默认执行文件为当前文件名字(不带扩展名)
  • 按下f10进行单文件运行

在.vimrc里面追加如下命令

.vimrc

noremap <silent><f6> :AsyncTask project-debug<cr>
noremap <silent><f7> :AsyncTask project-build<cr>
noremap <silent><f8> :AsyncTask project-run<cr>
noremap <silent><f9> :AsyncTask file-build<cr>
noremap <silent><f10> :AsyncTask file-run<cr>

在~/.vim/下创建.tasks.ini文件

.tasks.ini

[project-debug]
command:cpp=clang++ -std=c++23 -g -pedantic-errors -Wall -Weffc++ -Wextra -Wconversion -Wsign-conversion -Werror -Wshadow -fuse-ld=lld *.cpp -o a.out -lm -msse3 && lldb a.out
command:c=clang -std=c17 -g -pedantic-errors -Wall -Weffc++ -Wextra -Wconversion -Wsign-conversion -Werror -Wshadow -fuse-ld=lld *.c -o a.out -lm && lldb a.out                                                         
output=terminal
cwd=$(VIM_FILEDIR)
save=2
  
[project-build]
command:cpp=clang++ -std=c++23 -O2 -pedantic-errors -Wall -Weffc++ -Wextra -Wconversion -Wsign-conversion -Werror -Wshadow *.cpp -o a.out -lm -msse3
command:c=clang -std=c17 -O2 -pedantic-errors -Wall -Weffc++ -Wextra -Wconversion -Wsign-conversion -Werror -Wshadow *.c -o a.out -lm
command:make=make -f "$(VIM_FILEPATH)"
errorformat=%f:%l:%c: %m
output=quickfix
cwd=$(VIM_FILEDIR)
save=2
  
[project-run]
command=./a.out
errorformat=
output=quickfix
cwd=$(VIM_FILEDIR)
save=2
  
[file-build]
command:cpp=clang++ -std=c++23 -O2 -pedantic-errors -Wall -Weffc++ -Wextra -Wconversion -Wsign-conversion -Werror -Wshadow -fsanitize=address "$(VIM_FILEPATH)" -o "$(VIM_PATHNOEXT)" -lm -msse3
command:c=clang -std=c17 -O2 -pedantic-errors -Wall -Weffc++ -Wextra -Wconversion -Wsign-conversion -Werror -Wshadow "$(VIM_FILEPATH)" -o "$(VIM_PATHNOEXT)" -lm
command:make=make -f "$(VIM_FILEPATH)"
output=quickfix
errorformat=
cwd=$(VIM_FILEDIR)
save=2
  
[file-run]
command="$(VIM_FILEPATH)"
command:c,cpp="$(VIM_PATHNOEXT)"
output=quickfix
errorformat=
cwd=$(VIM_FILEDIR)
save=2 

注解:参考Alex中文翻译版0-9 配置编译器:构建配置0-10 配置编译器:编译器扩展,0-11 配置编译器:警告和错误级别0-12 配置编译器:选择语言标准

  • 控制优化设置:-O0 是调试构建的推荐优化级别;-O2 是发布构建推荐的优化级别;-O3 添加额外优化;优化选项
  • 禁用编译器扩展-pedantic-errors
  • 提高警告级别-Wall -Weffc++ -Wextra -Wconversion -Wsign-conversion
  • 将警告视为错误-Werror
  • 设置语言标准-std=c++23

对于单文件,现在我们可以在vim里面直接构建运行了, 比如使用vim打开在~/cpp/main.cpp写的代码
可以使用f9去构建main.cpp源码, f10运行由main.cpp生成的main执行文件。

image

对于多文件同但文件原理,只是在主文件(带main函数)按下f7构建(默认生成a.out执行文件),f8运行, 可是修改比较麻烦,我们后面配合目录树详细演示, 目录树部分。

好了总结一下:.tasks.ini是一个辅助异步运行插件和异步任务插件的文件,它使得clang编译器和vim编辑器结合起来, 在vim里面使用调用clang对cpp代码进行构建,运行。

回归这两个插件本身的安装和配置是都是在.vimrc里面:

  1. 将插件放在vim-plug插件配置列表里面{在call plug#begin和call plug#end()之间}。
  2. 将对应的配置使用" AsyncRun:注释一下说明是此插件的配置, 接着后面紧跟它的配置文件。

注意:当我们提及使用vim-plug插件下载往往会看到如下代码:

~/.vimrc

Plug 'skywind3000/asynctasks.vim'
Plug 'skywind3000/asyncrun.vim'

" AsyncRun:
    " automatically open quickfix 
    let g:asyncrun_open = 6

    noremap <silent><f6> :AsyncTask project-debug<cr>
    noremap <silent><f7> :AsyncTask project-build<cr>
    noremap <silent><f8> :AsyncTask project-run<cr>
    noremap <silent><f9> :AsyncTask file-build<cr>
    noremap <silent><f10> :AsyncTask file-run<cr>

当我们提及如下代码:

Plug 'skywind3000/asynctasks.vim'
Plug 'skywind3000/asyncrun.vim'

实际就是代表的是放在vim-plug插件列表中的, 即call plug#begin和call plug#end()之间.

call plug#begin('~/.vim/plugged')

Plug 'skywind3000/asynctasks.vim'
Plug 'skywind3000/asyncrun.vim'

call plug#end()
  1. 重新加载文件或重启Vim后,即可执行::PlugInstall 安装插件

但这些可能远远不够,如果是涉及到同一目录下多个文件呢?甚至是不同目录下的多个文件?如果需要修改其中的一个文件,我们就需要返回到命令行界面,然后查看所在目录下文件的名字,然后再次打开该文件修改并保存,再回到主文件(带main函数的)的vim界面再次构建运行,非常麻烦,但是如果我们像vscode那样有一个左侧栏有一个目录树呢,可以在各个文件之间游走切换,所幸vim真的有这样的功能nerdtree.vim插件。

image


目录树(NERDTree.vim)安装与配置

Plug 'preservim/nerdtree'

" NERDTree
    noremap ff :NERDTreeToggle<CR>

对于目录树的使用我比较简单,按下ff后在左侧打开/关闭目录树,按下默认的o打开光标所在文件或者展开目录。

操作类型 按键 说明
创建窗口
在左侧创建新窗口 sh 垂直分割,新窗口在左侧
在下方创建新窗口 sj 水平分割,新窗口在下方
在上方创建新窗口 sk 水平分割,新窗口在上方
在右侧创建新窗口 sl 垂直分割,新窗口在右侧
创建终端
在左侧打开终端 th 垂直终端,在左侧
在下方打开终端 tj 水平终端,在下方
在上方打开终端 tk 水平终端,在上方
在右侧打开终端 tl 垂直终端,在右侧
切换窗口 先按住 Tab,然后按方向键
切换到左侧窗口 Tab + h 移动到左边窗口
切换到下方窗口 Tab + j 移动到下方窗口
切换到上方窗口 Tab + k 移动到上方窗口
切换到右侧窗口 Tab + l 移动到右边窗口

使用目录树插件实践2-8 具有多个代码文件的程序就会容易很多, 左侧的目录为我们提供视图, 然后创建窗口搭配切换窗口就可以生成如下界面:

image

修改main.cpp, 添加add函数的向前声明之后,在main.cpp(带有main函数)的主文件按下f7开始构建, f8运行多文件程序.

创建终端搭配切换窗口可以通过lldb来调试程序,先使用f6生成可调式文件也是a.out, 然后通过lldb开始调试.(注意:因为这里是Tab+方向键是对vim设置的,所以回到bash界面将不起作用,可以采用Ctrl+w+w切换)

image

在 NERDTree 窗口中:

  • 按 r 刷新当前目录
  • 按 R 刷新根目录

基本上集成开发环境的大部分功能我们都实现了,但如果我们想要代码写得更快,代码补全单靠vim基本的设置还不够, 我们还需要一个功能强大的插件--智能代码补全引擎,它能自动补全,代码诊断,跳转到定义,查看文档,代码格式化, 代码片段等功能。

智能代码补全引擎(coc.nvim)的安装与配置

安装和配置

Plug 'neoclide/coc.nvim', {'branch': 'release'} 

" coc full code
     " https://raw.githubusercontent.com/neoclide/coc.nvim/master/doc/coc-example-config.vim
  
      " May need for Vim (not Neovim) since coc.nvim calculates byte offset by count
      " utf-8 byte sequence
      set encoding=utf-8
      " Some servers have issues with backup files, see #649
      set nobackup
      set nowritebackup
  
      " Having longer updatetime (default is 4000 ms = 4s) leads to noticeable
      " delays and poor user experience
      set updatetime=300
  
      " Always show the signcolumn, otherwise it would shift the text each time
      " diagnostics appear/become resolved
      set signcolumn=yes
  
      " Use tab for trigger completion with characters ahead and navigate
      " NOTE: There's always complete item selected by default, you may want to enable
      " no select by `"suggest.noselect": true` in your configuration file
      " NOTE: Use command ':verbose imap <tab>' to make sure tab is not mapped by
      " other plugin before putting this into your config
      inoremap <silent><expr> <TAB>
            \ coc#pum#visible() ? coc#pum#next(1) :
            \ CheckBackspace() ? "\<Tab>" :
            \ coc#refresh()
      inoremap <expr><S-TAB> coc#pum#visible() ? coc#pum#prev(1) : "\<C-h>"
  
      " Make <CR> to accept selected completion item or notify coc.nvim to format
      " <C-g>u breaks current undo, please make your own choice
      inoremap <silent><expr> <CR> coc#pum#visible() ? coc#pum#confirm()
                                    \: "\<C-g>u\<CR>\<c-r>=coc#on_enter()\<CR>"
  
      function! CheckBackspace() abort
        let col = col('.') - 1 
        return !col || getline('.')[col - 1]  =~# '\s'
      endfunction

      " Use `[g` and `]g` to navigate diagnostics
      " Use `:CocDiagnostics` to get all diagnostics of current buffer in location list
      nmap <silent><nowait> [g <Plug>(coc-diagnostic-prev)
      nmap <silent><nowait> ]g <Plug>(coc-diagnostic-next)
  
      " GoTo code navigation
      nmap <silent><nowait> gd <Plug>(coc-definition)
      nmap <silent><nowait> gy <Plug>(coc-type-definition)
      nmap <silent><nowait> gi <Plug>(coc-implementation)
      nmap <silent><nowait> gr <Plug>(coc-references)
  
      " Use K to show documentation in preview window
      nnoremap <silent> K :call ShowDocumentation()<CR>
  
      function! ShowDocumentation()
        if CocAction('hasProvider', 'hover')
          call CocActionAsync('doHover')
        else
          call feedkeys('K', 'in')
        endif
      endfunction
  
      " Highlight the symbol and its references when holding the cursor
      autocmd CursorHold * silent call CocActionAsync('highlight')
  
      " Symbol renaming
      nmap <leader>rn <Plug>(coc-rename)
  
      " Formatting selected code
      xmap <leader>f  <Plug>(coc-format-selected)
      nmap <leader>f  <Plug>(coc-format-selected)
  
      augroup mygroup
        autocmd!
        " Setup formatexpr specified filetype(s)
        autocmd FileType typescript,json setl formatexpr=CocAction('formatSelected')
      augroup end        
              
      " Applying code actions to the selected code block
      " Example: `<leader>aap` for current paragraph
      xmap <leader>a  <Plug>(coc-codeaction-selected)
      nmap <leader>a  <Plug>(coc-codeaction-selected)
  
      " Remap keys for applying code actions at the cursor position
      nmap <leader>ac  <Plug>(coc-codeaction-cursor)
      " Remap keys for apply code actions affect whole buffer
      nmap <leader>as  <Plug>(coc-codeaction-source)
      " Apply the most preferred quickfix action to fix diagnostic on the current line
      nmap <leader>qf  <Plug>(coc-fix-current)
  
      " Remap keys for applying refactor code actions
      nmap <silent> <leader>re <Plug>(coc-codeaction-refactor)
      xmap <silent> <leader>r  <Plug>(coc-codeaction-refactor-selected)
      nmap <silent> <leader>r  <Plug>(coc-codeaction-refactor-selected)
  
      " Run the Code Lens action on the current line
      nmap <leader>cl  <Plug>(coc-codelens-action)
  
      " Map function and class text objects
      " NOTE: Requires 'textDocument.documentSymbol' support from the language server
      xmap if <Plug>(coc-funcobj-i)
      omap if <Plug>(coc-funcobj-i)
      xmap af <Plug>(coc-funcobj-a)
      omap af <Plug>(coc-funcobj-a)
      xmap ic <Plug>(coc-classobj-i)
      omap ic <Plug>(coc-classobj-i)
      xmap ac <Plug>(coc-classobj-a)
      omap ac <Plug>(coc-classobj-a)
  
      " Use CTRL-S for selections ranges
      " Requires 'textDocument/selectionRange' support of language server
      nmap <silent> <C-s> <Plug>(coc-range-select)
      xmap <silent> <C-s> <Plug>(coc-range-select)
  
      " Add `:Format` command to format current buffer
      command! -nargs=0 Format :call CocActionAsync('format')
  
      " Add `:Fold` command to fold current buffer
      command! -nargs=? Fold :call     CocAction('fold', <f-args>)
  
      " Add `:OR` command for organize imports of the current buffer
      command! -nargs=0 OR   :call     CocActionAsync('runCommand', 'editor.action.organizeImport')

      " Add (Neo)Vim's native statusline support
      " NOTE: Please see `:h coc-status` for integrations with external plugins that
      " provide custom statusline: lightline.vim, vim-airline
      set statusline^=%{coc#status()}%{get(b:,'coc_current_function','')}
  
      " Mappings for CoCList
      " Show all diagnostics
      nnoremap <silent><nowait> <space>a  :<C-u>CocList diagnostics<cr>
      " Manage extensions
      nnoremap <silent><nowait> <space>e  :<C-u>CocList extensions<cr>
      " Show commands
      nnoremap <silent><nowait> <space>c  :<C-u>CocList commands<cr>
      " Find symbol of current document
      nnoremap <silent><nowait> <space>o  :<C-u>CocList outline<cr>
      " Search workspace symbols
      nnoremap <silent><nowait> <space>s  :<C-u>CocList -I symbols<cr>
      " Do default action for next item
      nnoremap <silent><nowait> <space>j  :<C-u>CocNext<CR>
      " Do default action for previous item
      nnoremap <silent><nowait> <space>k  :<C-u>CocPrev<CR>
      " Resume latest coc list
      nnoremap <silent><nowait> <space>p  :<C-u>CocListResume<CR>

使用

对于补全代码:coc.nvim 是一个基于 Language Server Protocol (LSP) 的智能代码补全和代码分析插件。需要下载coc-clangd,

使用 Coc 命令(推荐)
:CocInstall coc-clangd 下载扩展
:CocList extensions 查看已安装的扩展
:CocUninstall coc-clangd 卸载扩展

按下Tab进行补全,Shft+Tab进行选择,enter确认,space取消(Shft+Tab第二次)。
image


模糊查找(fzf.vim)安装与配置

安装和配置

Plug 'junegunn/fzf', { 'do': { -> fzf#install() } }
Plug 'junegunn/fzf.vim'

" fzf
    let g:fzf_vim = {}
    let g:fzf_vim.preview_window = ['hidden,right,50%,<70(up,40%)', 'ctrl-/']
    let g:fzf_vim.buffers_jump = 1
    let g:fzf_vim.grep_multi_line = 1
    let g:fzf_vim.commits_log_options = '--graph --color=always --format="%C(auto)%h%d %s %C(black)%C(bold)%cr"'

    nnoremap <C-p> :Files<CR>
    nnoremap <leader>f :GFiles<CR>
    nnoremap <leader>r :Rg<CR>
    nnoremap <leader>b :Buffers<CR>

    augroup fzf_settings
      autocmd!
      autocmd FileType fzf set laststatus=0 noshowmode noruler
      autocmd BufLeave <buffer> set laststatus=2 showmode ruler
    augroup END

使用

命令 列表
:Files [PATH] 文件($FZF_DEFAULT_COMMAND如果已定义则运行)
:GFiles [OPTS] Git 文件(git ls-files)
:GFiles? Git 文件(git status)
:Buffers 打开缓冲区
:Colors 配色方案
:Ag [PATTERN] Ag搜索结果(ALT-A全选,ALT-D全选)
:Rg [PATTERN] Rg搜索结果(ALT-A全选,ALT-D全选)
:RG [PATTERN] RG搜索结果;每次按键都重新启动 ripgrep
:Lines [QUERY] 已加载缓冲区中的行
:BLines [QUERY] 当前缓冲区中的行数
:Tags [PREFIX] 项目标签(ctags -R)
:BTags [QUERY] 当前缓冲区中的标签
:Changes 所有打开缓冲区中的变更列表
:Marks 分数
:BMarks 当前缓冲区中的标记
:Jumps 跳跃
:Windows 视窗
:Locate PATTERN locate命令输出
:History v:oldfiles并打开缓冲区
:History: 命令历史记录
:History/ 搜索历史
:Snippets 代码片段(UltiSnips)
:Commits [LOG_OPTS] Git 提交(需要fugitive.vim)
:BCommits [LOG_OPTS] 当前缓冲区的 Git 提交;可视化选择行以跟踪范围内的更改
:Commands 命令
:Maps 普通模式映射
:Helptags 帮助标签1
:Filetypes 文件类型

这个待定中, 所以没有使用快捷键设置, 这里演示一下查找文件,比如add.out,在vim命令行模式下输入Files,然后按下Enter键就可以搜索了, 它会显示文件内容,按下Esc键退出搜索.

image


vim-ai插件的安装与配置(还在测试阶段,不太好用)

vim-ai

安装配置和快捷键设置

Plug 'madox2/vim-ai'

let $OPENAI_API_KEY = 'YOUR_OPENAI_API_KEY'

let g:vim_ai_model = 'gpt-4'
let g:vim_ai_chat_config = {
    \ 'options': {
    \   'model': 'gpt-4',
    \   'temperature': 0.7,
    \   'max_tokens': 2000,
    \   'request_timeout': 30,
    \ },
    \ 'ui': {
    \   'open_chat_command': 'botright vnew | setlocal buftype=nofile bufhidden=wipe noswapfile wrap',
    \   'populate_options': 1,
    \   'code_syntax_enabled': 1,
    \ }
    \ }

let g:vim_ai_edit_config = {
    \ 'options': {
    \   'model': 'gpt-4',
    \   'temperature': 0.1,
    \   'max_tokens': 2000,
    \   'request_timeout': 30,
    \ },
    \ 'prompt': 'Please edit this code:',
    \ }

let g:vim_ai_generate_config = {
    \ 'options': {
    \   'model': 'gpt-4',
    \   'temperature': 0.7,
    \   'max_tokens': 2000,
    \   'request_timeout': 30,
    \ },
    \ }


nnoremap <Leader>ac :AIChat<CR>
nnoremap <Leader>ag :AIGenerate<CR>
nnoremap <Leader>aq :AIChatbot<CR>
vnoremap <Leader>ae :AIEdit<CR>
vnoremap <Leader>ax :AIExplain<CR>
vnoremap <Leader>at :AITest<CR>

安全问题(可选)

YOUR_OPENAI_API_KEY这个是ai的api令牌,需要去各ai官网(比如deepseek, openai,kimi)登录账户,创建令牌获得,大概样子例如sk-aOxxxxxxx
但是直接放在.vimrc配置里面容易暴露,我们可以建立一个存放ai令牌的文件,名字为openai.token,然后设置权限。

# save api key to `~/.config/openai.token` file
echo "YOUR_OPENAI_API_KEY" > ~/.config/openai.token

# 设置该文件的权限
chmod 600 ~/.config/openai.token

此时~/.vimrc里面需要添加刚才的ai令牌位置, `'~/.config/openai.token'这个路径也是vim-ai插件默认的路径, 不过我们最好显式设置上, 这样以后变动就容易修改。

let g:vim_ai_token_file_path = '~/.config/openai.token'

使用

由于这个是vim里面使用的,而前面c++的构建和运行显示在quickfix里,而quickfix只能显示c++运行构建输出的信息,不能用了交互,所以这个采用vim新建窗口来交互。

命令 快捷键 说明
:new 新打开一个窗口(推荐)
:split sk 水平分割,新窗口在上方
:split sj 水平分割,新窗口在下方
:vsplit sh 垂直分割,新窗口在左侧
:vsplit sl 垂直分割,新窗口在右侧
命令 快捷键 说明 模式
:AIChat 空格 + ac 打开 AI 聊天窗口 Normal
:AIEdit 空格 + ae 编辑选中内容 Visual
:AIGenerate 空格 + ag 根据提示生成代码 Normal
:AIExplain 空格 + ax 解释选中代码 Visual
:AITest 空格 + at 为选中代码生成测试 Visual
:AIChatbot 空格 + aq 快速底部聊天 Normal
---必要插件部分:end---

---可选插件部分:start---

其他插件(可选)

Plug 'junegunn/seoul256.vim'
Plug 'junegunn/vim-easy-align'                                                                    
Plug 'vim-scripts/cpp_doxygen'
Plug 'preservim/nerdcommenter'
Plug 'tpope/vim-surround'
Plug 'voldikss/vim-translator'

" nerdcommenter
    nmap gcc <leader>cc
    nmap gcu <leader>cu
    nmap gc<Space> <leader>c<Space>
    nmap gcm <leader>cm
    nmap gcs <leader>cs
    nmap gc$ <leader>c$

    vmap gcc <leader>cc
    vmap gcu <leader>cu
    vmap gc<Space> <leader>c<Space>

" vim-translator
    nnoremap <silent> <Leader>t viw:Translate<CR>

NERDCommenter — 快速注释
功能:快速添加、切换、取消代码注释,支持几乎所有编程语言。
核心用法

快捷键 功能
cc 注释当前行或选中行(// 或 # 等)
cu 取消注释
c 切换注释状态(注释↔取消)
cm 用最简符号注释(如 /* */ 变 //)
cs 使用块注释(/* */ 风格)
c$ 从光标位置注释到行尾

vim-surround — 括号/引号操作神器
功能:快速添加、修改、删除包围符号(括号、引号、HTML 标签等)。
核心用法

命令 功能 示例
ys{motion} 添加包围 ysiw" → |"word" → "word"
ds 删除包围 ds" → "word" → word
cs{old} 修改包围 cs"' → "word" → 'word'
yss 整行添加 yss) → print(x) → (print(x))
yS{motion} 添加+换行 yS{ → { 在新行,内容缩进

对于翻译:移动到不认识的词内,按下space+t就会对词翻译,对于没有收入其库的词不能翻译。
image

---可选插件部分:end---

---完整配置代码:start---

配置总汇

~/
├── .vimrc              # Vim 主配置文件
├── .vim/               # Vim 配置目录
│ ├── tasks.ini         # AsyncTask 任务定义
│ └── autoload/           # Vim管理器的路径
│ 		└── plug.vim       # vim-plug插件
│ └── plugged/           # Vim管理器下载插件的路径
│ 		├── asyncrun.vim   # 异步运行插件
│ 		└── asynctasks.vim # 异步任务插件
│ 		└── coc.nvim       # 代码补全
│ 		├── #下面fzf和fzf.vim是一起的, 它的作用是:超快的通用命令行模糊查找器   
│ 		├── fzf            # fzf 是引擎,是核心命令行工具     
│ 		└── fzf.vim        # 是给 Vim 这个“汽车”装上的专用方向盘、中控台和仪表盘,让你能在 Vim 里方便地使用这个引擎。
│ 		├── nerdtree        # 目录树
│ 		├── seoul256.vim      # vim颜色
│ 		├── ...             # 其他插件
│ 		└── vim-easy-align # 对齐
├── cpp/                # C++ 项目主目录
│ ├── xxx.h        # 分数类头文件
│ ├── xxx.cpp      # 分数类实现          
│ └── main.cpp          # 主程序文件
└── end

tasks.ini

[project-debug]
command:cpp=clang++ -std=c++23 -g -pedantic-errors -Wall -Weffc++ -Wextra -Wconversion -Wsign-conversion -Werror -Wshadow -fuse-ld=lld *.cpp -o a.out -lm -msse3 && lldb a.out
command:c=clang -std=c17 -g -pedantic-errors -Wall -Weffc++ -Wextra -Wconversion -Wsign-conversion -Werror -Wshadow -fuse-ld=lld *.c -o a.out -lm && lldb a.out
output=terminal
cwd=$(VIM_FILEDIR)
save=2

[project-build]
command:cpp=clang++ -std=c++23 -O2 -pedantic-errors -Wall -Weffc++ -Wextra -Wconversion -Wsign-conversion -Werror -Wshadow *.cpp -o a.out -lm -msse3
command:c=clang -std=c17 -O2 -pedantic-errors -Wall -Weffc++ -Wextra -Wconversion -Wsign-conversion -Werror -Wshadow *.c -o a.out -lm
command:make=make -f "$(VIM_FILEPATH)"
errorformat=%f:%l:%c: %m
output=quickfix
cwd=$(VIM_FILEDIR)
save=2

[project-run]
command=./a.out
output=quickfix
errorformat=
cwd=$(VIM_FILEDIR)
save=2

[file-build]
command:cpp=clang++ -std=c++23 -O2 -pedantic-errors -Wall -Weffc++ -Wextra -Wconversion -Wsign-conversion -Werror -Wshadow -fsanitize=address "$(VIM_FILEPATH)" -o "$(VIM_PATHNOEXT)" -lm -msse3
command:c=clang -std=c17 -O2 -pedantic-errors -Wall -Weffc++ -Wextra -Wconversion -Wsign-conversion -Werror -Wshadow "$(VIM_FILEPATH)" -o "$(VIM_PATHNOEXT)" -lm
command:make=make -f "$(VIM_FILEPATH)"
output=quickfix
errorformat=
cwd=$(VIM_FILEDIR)
save=2

[file-run]
command="$(VIM_FILEPATH)"
command:c,cpp="$(VIM_PATHNOEXT)"
output=quickfix
errorformat=
cwd=$(VIM_FILEDIR)
save=2                                                                                                                                                                                                           

.vimrc

let mapleader = " "                                                                                                                                                                                              
syntax on
set tabstop=4
set shiftwidth=4
set softtabstop=4
set nopaste
set smartindent
set autoindent
set number
set relativenumber
set cursorline
set showcmd
set wildmenu
set hlsearch
set incsearch
set ignorecase
set smartcase
nnoremap <silent> <Space><CR> :nohlsearch<CR>
noremap n nzz
noremap N Nzz


nnoremap sk :set nosplitbelow<CR>:split<CR>
nnoremap sj :set splitbelow<CR>:split<CR>
nnoremap sh :set nosplitright<CR>:vsplit<CR>
nnoremap sl :set splitright<CR>:vsplit<CR>

nnoremap tk :set nosplitbelow<CR>:terminal<CR>
nnoremap tj :set splitbelow<CR>:terminal<CR>
nnoremap th :set nosplitright<CR>:vertical terminal<CR>
nnoremap tl :set splitright<CR>:vertical terminal<CR>

map <Tab>k <C-w>k
map <Tab>j <C-w>j
map <Tab>h <C-w>h
map <Tab>l <C-w>l

set laststatus=2
set statusline=%n:\ %<%F%m%r\ %{strftime(\"%H:%M\ %a\ %b\%d\ %Y\")}\ %=\ %l/%L:%c\ %p%%

filetype on
filetype plugin on
filetype indent on
filetype plugin indent on

noremap <silent><f6> :AsyncTask project-debug<cr>
noremap <silent><f7> :AsyncTask project-build<cr>
noremap <silent><f8> :AsyncTask project-run<cr>
noremap <silent><f9> :AsyncTask file-build<cr>
noremap <silent><f10> :AsyncTask file-run<cr>

" vim-plug
call plug#begin('~/.vim/plugged')
    Plug 'skywind3000/asynctasks.vim'
    Plug 'skywind3000/asyncrun.vim'
    Plug 'neoclide/coc.nvim', {'branch': 'release'}
    Plug 'junegunn/fzf', { 'do': { -> fzf#install() } }
    Plug 'junegunn/fzf.vim'
    Plug 'preservim/nerdtree'
    Plug 'vim-scripts/cpp_doxygen'
    Plug 'preservim/nerdcommenter'
    Plug 'tpope/vim-surround'
    Plug 'voldikss/vim-translator'
call plug#end()

" AsyncRun: automatically open quickfix
    let g:asyncrun_open = 6

" NERDTree
    noremap ff :NERDTreeToggle<CR>

" coc full code
     " https://raw.githubusercontent.com/neoclide/coc.nvim/master/doc/coc-example-config.vim
  
      " May need for Vim (not Neovim) since coc.nvim calculates byte offset by count
      " utf-8 byte sequence
      set encoding=utf-8
      " Some servers have issues with backup files, see #649
      set nobackup
      set nowritebackup
  
      " Having longer updatetime (default is 4000 ms = 4s) leads to noticeable
      " delays and poor user experience
      set updatetime=300
  
      " Always show the signcolumn, otherwise it would shift the text each time
      " diagnostics appear/become resolved
      set signcolumn=yes
  
      " Use tab for trigger completion with characters ahead and navigate
      " NOTE: There's always complete item selected by default, you may want to enable
      " no select by `"suggest.noselect": true` in your configuration file
      " NOTE: Use command ':verbose imap <tab>' to make sure tab is not mapped by
      " other plugin before putting this into your config
      inoremap <silent><expr> <TAB>
            \ coc#pum#visible() ? coc#pum#next(1) :
            \ CheckBackspace() ? "\<Tab>" :
            \ coc#refresh()
      inoremap <expr><S-TAB> coc#pum#visible() ? coc#pum#prev(1) : "\<C-h>"
  
      " Make <CR> to accept selected completion item or notify coc.nvim to format
      " <C-g>u breaks current undo, please make your own choice
      inoremap <silent><expr> <CR> coc#pum#visible() ? coc#pum#confirm()
                                    \: "\<C-g>u\<CR>\<c-r>=coc#on_enter()\<CR>"
  
      function! CheckBackspace() abort
        let col = col('.') - 1 
        return !col || getline('.')[col - 1]  =~# '\s'
      endfunction

      " Use `[g` and `]g` to navigate diagnostics
      " Use `:CocDiagnostics` to get all diagnostics of current buffer in location list
      nmap <silent><nowait> [g <Plug>(coc-diagnostic-prev)
      nmap <silent><nowait> ]g <Plug>(coc-diagnostic-next)
  
      " GoTo code navigation
      nmap <silent><nowait> gd <Plug>(coc-definition)
      nmap <silent><nowait> gy <Plug>(coc-type-definition)
      nmap <silent><nowait> gi <Plug>(coc-implementation)
      nmap <silent><nowait> gr <Plug>(coc-references)
  
      " Use K to show documentation in preview window
      nnoremap <silent> K :call ShowDocumentation()<CR>
  
      function! ShowDocumentation()
        if CocAction('hasProvider', 'hover')
          call CocActionAsync('doHover')
        else
          call feedkeys('K', 'in')
        endif
      endfunction
  
      " Highlight the symbol and its references when holding the cursor
      autocmd CursorHold * silent call CocActionAsync('highlight')
  
      " Symbol renaming
      nmap <leader>rn <Plug>(coc-rename)
  
      " Formatting selected code
      xmap <leader>f  <Plug>(coc-format-selected)
      nmap <leader>f  <Plug>(coc-format-selected)
  
      augroup mygroup
        autocmd!
        " Setup formatexpr specified filetype(s)
        autocmd FileType typescript,json setl formatexpr=CocAction('formatSelected')
      augroup end        
              
      " Applying code actions to the selected code block
      " Example: `<leader>aap` for current paragraph
      xmap <leader>a  <Plug>(coc-codeaction-selected)
      nmap <leader>a  <Plug>(coc-codeaction-selected)
  
      " Remap keys for applying code actions at the cursor position
      nmap <leader>ac  <Plug>(coc-codeaction-cursor)
      " Remap keys for apply code actions affect whole buffer
      nmap <leader>as  <Plug>(coc-codeaction-source)
      " Apply the most preferred quickfix action to fix diagnostic on the current line
      nmap <leader>qf  <Plug>(coc-fix-current)
  
      " Remap keys for applying refactor code actions
      nmap <silent> <leader>re <Plug>(coc-codeaction-refactor)
      xmap <silent> <leader>r  <Plug>(coc-codeaction-refactor-selected)
      nmap <silent> <leader>r  <Plug>(coc-codeaction-refactor-selected)
  
      " Run the Code Lens action on the current line
      nmap <leader>cl  <Plug>(coc-codelens-action)
  
      " Map function and class text objects
      " NOTE: Requires 'textDocument.documentSymbol' support from the language server
      xmap if <Plug>(coc-funcobj-i)
      omap if <Plug>(coc-funcobj-i)
      xmap af <Plug>(coc-funcobj-a)
      omap af <Plug>(coc-funcobj-a)
      xmap ic <Plug>(coc-classobj-i)
      omap ic <Plug>(coc-classobj-i)
      xmap ac <Plug>(coc-classobj-a)
      omap ac <Plug>(coc-classobj-a)
  
      " Use CTRL-S for selections ranges
      " Requires 'textDocument/selectionRange' support of language server
      nmap <silent> <C-s> <Plug>(coc-range-select)
      xmap <silent> <C-s> <Plug>(coc-range-select)
  
      " Add `:Format` command to format current buffer
      command! -nargs=0 Format :call CocActionAsync('format')
  
      " Add `:Fold` command to fold current buffer
      command! -nargs=? Fold :call     CocAction('fold', <f-args>)
  
      " Add `:OR` command for organize imports of the current buffer
      command! -nargs=0 OR   :call     CocActionAsync('runCommand', 'editor.action.organizeImport')

      " Add (Neo)Vim's native statusline support
      " NOTE: Please see `:h coc-status` for integrations with external plugins that
      " provide custom statusline: lightline.vim, vim-airline
      set statusline^=%{coc#status()}%{get(b:,'coc_current_function','')}
  
      " Mappings for CoCList
      " Show all diagnostics
      nnoremap <silent><nowait> <space>a  :<C-u>CocList diagnostics<cr>
      " Manage extensions
      nnoremap <silent><nowait> <space>e  :<C-u>CocList extensions<cr>
      " Show commands
      nnoremap <silent><nowait> <space>c  :<C-u>CocList commands<cr>
      " Find symbol of current document
      nnoremap <silent><nowait> <space>o  :<C-u>CocList outline<cr>
      " Search workspace symbols
      nnoremap <silent><nowait> <space>s  :<C-u>CocList -I symbols<cr>
      " Do default action for next item
      nnoremap <silent><nowait> <space>j  :<C-u>CocNext<CR>
      " Do default action for previous item
      nnoremap <silent><nowait> <space>k  :<C-u>CocPrev<CR>
      " Resume latest coc list
      nnoremap <silent><nowait> <space>p  :<C-u>CocListResume<CR>

" fzf
    let g:fzf_vim = {}
    let g:fzf_vim.preview_window = ['hidden,right,50%,<70(up,40%)', 'ctrl-/']
    let g:fzf_vim.buffers_jump = 1
    let g:fzf_vim.grep_multi_line = 1
    let g:fzf_vim.commits_log_options = '--graph --color=always --format="%C(auto)%h%d %s %C(black)%C(bold)%cr"'

    nnoremap <C-p> :Files<CR>
    nnoremap <leader>f :GFiles<CR>
    nnoremap <leader>r :Rg<CR>
    nnoremap <leader>b :Buffers<CR>

    augroup fzf_settings
      autocmd!
      autocmd FileType fzf set laststatus=0 noshowmode noruler
      autocmd BufLeave <buffer> set laststatus=2 showmode ruler
    augroup END

" nerdcommenter
    nmap gcc <leader>cc
    nmap gcu <leader>cu
    nmap gc<Space> <leader>c<Space>
    nmap gcm <leader>cm
    nmap gcs <leader>cs
    nmap gc$ <leader>c$

    vmap gcc <leader>cc
    vmap gcu <leader>cu
    vmap gc<Space> <leader>c<Space>

" vim-translator
    nnoremap <silent> <Leader>t viw:Translate<CR>
---完整配置代码:end---

调试部分:

在Alex的Learn C++学习笔记3.6——使用集成调试器:单步执行步入引入了程序的调试环节,这一节基本上是单步执行。
提到单步执行,Alex解释说单步执行(stepping)是一组相关调试器功能的统称,它允许我们逐条执行(逐步遍历)代码语句。单步执行应该包括步入,步过,步出和传统调试器lldb和gdb未支持的步退。我总结如下表格,对于它们各自的演示详见笔记3.6——使用集成调试器:单步执行,这里我们专注于传统的调试器(gdb,和lldb)如何调试程序, 主要是步入,步过,步出三个, 步退暂不演示。

命令 (Command) 说明 (Description) LLDB 命令 GDB 命令
步入 (Step Into) 可理解为进入函数,执行下一条语句;若包含函数调用,则跳转到被调用函数的顶部并暂停 steps steps
步过 (Step Over) 可理解为跳过函数,又称执行下一条语句;若包含函数调用,则不间断地执行整个函数,在函数返回后暂停 nextn nextn
步出 (Step Out) 可理解为跳出函数,执行当前函数中所有剩余代码,直到函数返回 finishfin finishfin

main.cpp

#include <iostream>

void printValue(int value)
{
    std::cout << value << '\n';
}

int main()
{
    printValue(5);

    return 0;
}

在我的Konsole(KDE 的终端模拟器)上, 我们将上面的程序写在main.cpp里面。

rock@192:~/cpp$ ls
main.cpp
rock@192:~/cpp$ cat main.cpp
#include <iostream>

void printValue(int value)
{
    std::cout << value << '\n';
}

int main()
{
    printValue(5);

    return 0;
}

要调试main这个程序, 需要先使用clang++生产可调试程序, 在编译的时候需要带参数-g,才会生成可调试程序, 下面main.cpp是源程序, main是生成的可调试程序(注意:clang和lldb一对,gcc和gdb一对,他们配套使用)。

rock@192:~/cpp$ clang++ -g main.cpp -o main
rock@192:~/cpp$ ls
main  main.cpp

lldb和gdb调试大差不差,这里使用lldb来调试,只需要它们命令后面跟可调试程序名字就行。 前面出现(lldb)字样就说明已经进入lldb调试状态。

rock@192:~/cpp$ lldb main
(lldb) target create "main"
Current executable set to '/home/rock/cpp/main' (x86_64).
(lldb) 

在gdb中, 只需要使用start命令,该命令会在 main 函数的开头自动设置一个临时断点并运行程序, 而lldb需要在main函数处设置断点b main(全称breakpoint set --name main)然后使用run命令启动调试(gdb:start == lldb:b main && run)。

(lldb) b main
Breakpoint 1: where = main`main + 15 at main.cpp:10:5, address = 0x00000000004004bf
(lldb) run
Process 34356 launched: '/home/rock/cpp/main' (x86_64)
Process 34356 stopped
* thread #1, name = 'main', stop reason = breakpoint 1.1
    frame #0: 0x00000000004004bf main`main at main.cpp:10:5
   7   
   8    int main()
   9    {
-> 10       printValue(5);
   11  
   12       return 0;
   13   }
(lldb) 
main │ main.cpp:1

这样程序来到了main函数里面printValue函数的调用处,此时如果选择步入s,该程序就会进入printValue函数里面, 然后执行printValue里面的语句std::cout << value << '\n';

(lldb) s
Process 35199 stopped
* thread #1, name = 'main', stop reason = step in
    frame #0: 0x000000000040048b main`printValue(value=5) at main.cpp:5:18
   2   
   3    void printValue(int value)
   4    {
-> 5        std::cout << value << '\n';
   6    }
   7   
   8    int main()
(lldb) p value
(int) 5
(lldb)

我们可以使用p value(p全称print)打印变量value的值,发现value的值正是在main函数里面传入的实参5, 也可以看它执行该语句后的结果,然后回到调用printValue处。

(lldb) s
5
Process 35199 stopped
* thread #1, name = 'main', stop reason = step in
    frame #0: 0x00000000004004aa main`printValue(value=5) at main.cpp:6:1
   3    void printValue(int value)
   4    {
   5        std::cout << value << '\n';
-> 6    }
   7   
   8    int main()
   9    {
(lldb) 

我们发现该语句输出5, 继续使用步入命令,发现它会回到printValue的调用处, 然后指向下一步。

(lldb) s
Process 35199 stopped
* thread #1, name = 'main', stop reason = step in
    frame #0: 0x00000000004004c9 main`main at main.cpp:12:5
   9    {
   10       printValue(5);
   11  
-> 12       return 0;
   13   }
(lldb) 

然后我不小心又执行了步入s操作,会发现进入了不像是C++的代码。

(lldb) s
Process 35199 stopped
* thread #1, name = 'main', stop reason = step in
    frame #0: 0x00007ffff7a105b5 libc.so.6`__libc_start_call_main + 117
libc.so.6`__libc_start_call_main:
->  0x7ffff7a105b5 <+117>: movl   %eax, %edi
    0x7ffff7a105b7 <+119>: callq  0x7ffff7a29aa0 ; exit
    0x7ffff7a105bc <+124>: callq  0x7ffff7a7c840 ; __GI___nptl_deallocate_tsd
    0x7ffff7a105c1 <+129>: lock   
(lldb)

通过查询,其实libc.so.6__libc_start_call_main是glibc(C标准库)的内部函数, 而C标准库是用低级语言汇编编写的, 并不是我们要调试的目标, 而且我们也不知道后面还有多少代码执行, 碰到这种我们不想执行的程序我们怎么办呢? 就是使用步过fin(全称finish)跳出glibc的内部函数。

(lldb) fin
Process 35199 exited with status = 0 (0x00000000) 
(lldb) 

这样我们就调试完了,接下来如何结束调试,使用q(全称quit)或者exit, 有的系统会询问你是否结束, 输入y确认结束。

(lldb) q
rock@192:~/cpp$ 

然后我们再次使用lldb运行调试程序,回到我们之前的printValue函数调用处。

rock@192:~/cpp$ lldb main
(lldb) target create "main"
Current executable set to '/home/rock/cpp/main' (x86_64).
(lldb) b main
Breakpoint 1: where = main`main + 15 at main.cpp:10:5, address = 0x00000000004004bf
(lldb) run
Process 36044 launched: '/home/rock/cpp/main' (x86_64)
Process 36044 stopped
* thread #1, name = 'main', stop reason = breakpoint 1.1
    frame #0: 0x00000000004004bf main`main at main.cpp:10:5
   7   
   8    int main()
   9    {
-> 10       printValue(5);
   11  
   12       return 0;
   13   }
(lldb) 

有了前一次进入prinValue函数, 我们知道它输出5。 所以这次我们打算跳过prinValue函数, 如何做呢,就是使用步过n, 让我们实际看看效果;

(lldb) n
5
Process 36044 stopped
* thread #1, name = 'main', stop reason = step over
    frame #0: 0x00000000004004c9 main`main at main.cpp:12:5
   9    {
   10       printValue(5);
   11  
-> 12       return 0;
   13   }
(lldb) 

我们发现它却是直接输出了printValue函数的结果, 跳到下一个return 0;语句了, 并没有进入printValue函数里面。

(lldb) q
Quitting LLDB will kill one or more processes. Do you really want to proceed: [Y/n] y

我们来总结一下,单步执行是个包括步入s是一组相关调试器功能的统称, 它包括步入,步过,步出 和步退, 它们允许我们逐条执行(逐步遍历)代码语句。 遇到函数调用的时候,步入会进入函数,步过会跳过函数, 而如果已经在函数里了, 想要跳出当前函数就用步出。 其实这里说进入, 跳出函数有一个误解,会很容易理解为跳过了函数,弱化逐步执行的概念。 其实单步执行都是逐步执行的,步入最能体现,而步过是加快了进程,快进到了我们关注的那个地方, 而步出还会逐步执行当前函数的剩下的语句,并不是跳过了剩下的代码。步入,步过和步出都在逐步执行代码, 只不过我们观察的视角不同。


更新日志:

03/18/2026: 增加调试程序的单步执行部分, 3.6完毕。
02/26/2026: 8.6出现贯穿警告,可设置-Werror=implicit-fallthrough,本编译器未设置。
02/23/2026: 增添目录树刷新问题(按下当前r或者根目录R), 多文件构建不能使用:clist展开quickfix([project-build]添加errorformat=)
02/22/2026: 增添变量遮蔽 -Wshadow 警告
02/07/2026: 增添fzf,NERDCommenter,nerdcommenter配置,和使用用法
02/05/2026: 更新tasks.ini: 禁止扩展-pedantic-errors, 将警告视为错误-Werror

posted @ 2026-01-18 18:11  游翔  阅读(16)  评论(0)    收藏  举报