<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>BG3LNT</title>
  
  
  <link href="https://bg3lnt.xyz/atom.xml" rel="self"/>
  
  <link href="https://bg3lnt.xyz/"/>
  <updated>2025-10-19T17:36:57.509Z</updated>
  <id>https://bg3lnt.xyz/</id>
  
  <author>
    <name>eibbuw</name>
    
  </author>
  
  <generator uri="https://hexo.io/">Hexo</generator>
  
  <entry>
    <title>GDB Quick Reference</title>
    <link href="https://bg3lnt.xyz/posts/8ba9c62a/"/>
    <id>https://bg3lnt.xyz/posts/8ba9c62a/</id>
    <published>2024-07-16T06:06:51.000Z</published>
    <updated>2025-10-19T17:36:57.509Z</updated>
    
    <content type="html"><![CDATA[<blockquote><p>文章内容转载自开源项目<a href="https://github.com/jaywcjlove/reference/blob/main/docs/gdb.md">jaywcjlove/reference</a>（MIT © <a href="https://github.com/jaywcjlove">Kenny Wong</a>）</p></blockquote><p>最近常用到 GDB，一些命令总是需要查一下，找到了这个开源项目，常用的命令和工具总结的比较全面。</p><p>本清单提供了对 <a href="https://en.wikipedia.org/wiki/GNU_Debugger">GDB</a> 的入门简要概述，以及 <code>GDB</code> 常用示例，完整文档参阅 <a href="https://www.eecs.umich.edu/courses/eecs373/readings/Debugger.pdf">Debugging with gdb</a>，该文档最后有 <code>GDB index</code>，可以快速查找命令。</p><h2 id="常用命令"><a class="markdownIt-Anchor" href="#常用命令"></a> 常用命令</h2><p><code>[]</code> 内为命令缩写</p><table><thead><tr><th style="text-align:left">命令 <code>[缩写]</code></th><th style="text-align:left">说明</th></tr></thead><tbody><tr><td style="text-align:left"><code>help[h]</code></td><td style="text-align:left"><strong>查看命令帮助</strong>。如 <code>help run</code></td></tr><tr><td style="text-align:left"><code>run[r]</code></td><td style="text-align:left"><strong>运行程序</strong>。可搭配参数使用</td></tr><tr><td style="text-align:left"><code>start</code></td><td style="text-align:left"><strong>运行程序，停在第一条执行语句</strong>。可搭配参数使用</td></tr><tr><td style="text-align:left"><code>list[l]</code></td><td style="text-align:left"><strong>查看程序源码</strong></td></tr><tr><td style="text-align:left"><code>break[b]</code></td><td style="text-align:left"><strong>设置断点</strong>。可指定文件名、函数名和行号等参数来设置断点</td></tr><tr><td style="text-align:left"><code>watch</code></td><td style="text-align:left"><strong>设置监视点</strong>。当监视的变量发生更改时，程序会被中断</td></tr><tr><td style="text-align:left"><code>delete</code></td><td style="text-align:left"><strong>删除断点等</strong>。可用于删除断点、监视点、<code>display</code> 等</td></tr><tr><td style="text-align:left"><code>continue[c]</code></td><td style="text-align:left"><strong>继续执行程序</strong>。让程序继续执行，到下一个断点或程序结束</td></tr><tr><td style="text-align:left"><code>next[n]</code></td><td style="text-align:left"><strong>单步执行程序，跳过函数调用</strong></td></tr><tr><td style="text-align:left"><code>step[s]</code></td><td style="text-align:left"><strong>单步执行程序，进入函数调用</strong></td></tr><tr><td style="text-align:left"><code>finish</code></td><td style="text-align:left"><strong>结束当前函数</strong>。返回到函数调用点</td></tr><tr><td style="text-align:left"><code>kill</code></td><td style="text-align:left"><strong>杀死当前的调试进程</strong></td></tr><tr><td style="text-align:left"><code>backtrace[bt]</code></td><td style="text-align:left"><strong>查看函数调用栈</strong>。它会打印出当前的函数调用栈</td></tr><tr><td style="text-align:left"><code>frame[fr]</code></td><td style="text-align:left"><strong>切换栈帧</strong>。以查看该栈帧中的局部变量和参数等</td></tr><tr><td style="text-align:left"><code>info</code></td><td style="text-align:left"><strong>查看程序状态信息</strong>。例如断点、寄存器、线程、局部变量等</td></tr><tr><td style="text-align:left"><code>show</code></td><td style="text-align:left"><strong>查看 <code>gdb</code> 配置信息</strong>。与 <code>info</code> 不同， <code>show</code> 查看 <code>GDB</code> 本身的配置信息</td></tr><tr><td style="text-align:left"><code>set</code></td><td style="text-align:left"><strong>设置变量值</strong>。有时指定变量类型才能设置，如 <code>set *(int*)(&amp;a) = 3</code></td></tr><tr><td style="text-align:left"><code>whatis</code></td><td style="text-align:left"><strong>查看变量、函数类型</strong>。例如，<code>whatis a</code> 可以显示变量 <code>a</code> 的类型</td></tr><tr><td style="text-align:left"><code>ptype</code></td><td style="text-align:left"><strong>查看变量、函数类型</strong>。会显示完整的结构体类型</td></tr><tr><td style="text-align:left"><code>print[p]</code></td><td style="text-align:left"><strong>打印变量的值</strong>。例如，<code>print x</code> 可以显示变量 <code>x</code> 的当前值</td></tr><tr><td style="text-align:left"><code>display</code></td><td style="text-align:left"><strong>持续打印变量的值</strong>。与 <code>print</code> 类似，但它会在每次停下时自动输出值</td></tr><tr><td style="text-align:left"><code>thread</code></td><td style="text-align:left"><strong>切换线程</strong>。例如，<code>thread 2</code> 切换到编号为 <code>2</code> 的线程</td></tr><tr><td style="text-align:left"><code>signal</code></td><td style="text-align:left"><strong>向进程发送信号</strong>。例如，<code>signal 9</code> 发送编号为 <code>9</code> 的信号</td></tr></tbody></table><h2 id="启动调试"><a class="markdownIt-Anchor" href="#启动调试"></a> 启动调试</h2><p>启动进程，不带参数</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># gdb &lt;program&gt;</span></span><br><span class="line">(gdb) run</span><br></pre></td></tr></table></figure><p>启动进程，带参数 <code>&lt;args&gt;</code></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># gdb &lt;program&gt;</span></span><br><span class="line">(gdb) run &lt;args&gt;</span><br></pre></td></tr></table></figure><p>启动 <code>gdb</code> 时传入参数，<code>run</code> 就不用传入了</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># gdb --args &lt;program&gt; 1 2 3</span></span><br><span class="line">(gdb) run</span><br></pre></td></tr></table></figure><p>通过 <code>set</code> 设置参数</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># gdb &lt;program&gt;</span></span><br><span class="line">(gdb) <span class="built_in">set</span> args 1 2 3</span><br><span class="line">(gdb) run</span><br></pre></td></tr></table></figure><p>显示运行时将要或已经传递给程序的参数</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">(gdb) show args</span><br></pre></td></tr></table></figure><p>在启动进程前，添加环境变量</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">(gdb) <span class="built_in">set</span> <span class="built_in">env</span> DEBUG 1</span><br></pre></td></tr></table></figure><p>在启动进程前，清除环境变量</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">(gdb) <span class="built_in">unset</span> <span class="built_in">env</span> DEBUG</span><br></pre></td></tr></table></figure><p>通过进程号 <code>123</code> 连接到正在运行的进程</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">(gdb) attach 123</span><br></pre></td></tr></table></figure><h2 id="core-dump-文件"><a class="markdownIt-Anchor" href="#core-dump-文件"></a> core dump 文件</h2><p>默认情况下，<code>linux</code> 系统中程序崩溃时也不会生成 <code>core dump</code> 文件，需要先启用</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">ulimit</span> -c unlimited</span><br><span class="line"><span class="built_in">echo</span> <span class="string">&quot;/tmp/core-%e-%p-%t&quot;</span> &gt; /proc/sys/kernel/core_pattern</span><br></pre></td></tr></table></figure><p>调试 <code>core</code> 文件</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">gdb program /tmp/core-file</span><br></pre></td></tr></table></figure><h2 id="查看源码"><a class="markdownIt-Anchor" href="#查看源码"></a> 查看源码</h2><table><thead><tr><th style="text-align:left">命令</th><th style="text-align:left">说明</th></tr></thead><tbody><tr><td style="text-align:left"><code>(gdb) list 30</code></td><td style="text-align:left">查看第 <code>30</code> 行为中心的上下 <code>5</code> 行源码</td></tr><tr><td style="text-align:left"><code>(gdb) list main</code></td><td style="text-align:left">查看 <code>main</code> 函数为中心的上下 <code>5</code> 行源码</td></tr><tr><td style="text-align:left"><code>(gdb) list file.c:30</code></td><td style="text-align:left">查看 <code>file.c</code> 文件中 <code>30</code> 行的源码</td></tr><tr><td style="text-align:left"><code>(gdb) list file.c:main</code></td><td style="text-align:left">查看 <code>file.c</code> 文件中 <code>main</code> 函数</td></tr><tr><td style="text-align:left"><code>(gdb) disassemble</code></td><td style="text-align:left">查看当前可执行文件的汇编源码</td></tr><tr><td style="text-align:left"><code>(gdb) disassemble myfun</code></td><td style="text-align:left">查看指定函数的汇编源码</td></tr></tbody></table><h2 id="流程控制"><a class="markdownIt-Anchor" href="#流程控制"></a> 流程控制</h2><table><thead><tr><th style="text-align:left">命令</th><th style="text-align:left">说明</th></tr></thead><tbody><tr><td style="text-align:left"><code>(gdb) step[s]</code></td><td style="text-align:left">执行源码级别的单步进入操作</td></tr><tr><td style="text-align:left"><code>(gdb) stepi[si]</code></td><td style="text-align:left">执行指令级别的单步进入操作</td></tr><tr><td style="text-align:left"><code>(gdb) next[n]</code></td><td style="text-align:left">执行源码级别的单步跳过操作</td></tr><tr><td style="text-align:left"><code>(gdb) nexti[ni]</code></td><td style="text-align:left">执行指令级别的单步跳过操作</td></tr><tr><td style="text-align:left"><code>(gdb) continue[c]</code></td><td style="text-align:left">继续执行，到下一个断点或程序结束</td></tr><tr><td style="text-align:left"><code>(gdb) finish</code></td><td style="text-align:left">运行完当前函数，并返回到函数调用点</td></tr><tr><td style="text-align:left"><code>(gdb) return</code></td><td style="text-align:left">直接退出当前函数，不执行剩下代码块</td></tr><tr><td style="text-align:left"><code>(gdb) return expression</code></td><td style="text-align:left">可以指定返回值的内容</td></tr><tr><td style="text-align:left"><code>(gdb) until</code></td><td style="text-align:left">结束当前循环</td></tr></tbody></table><h2 id="断点命令"><a class="markdownIt-Anchor" href="#断点命令"></a> 断点命令</h2><table><thead><tr><th style="text-align:left">命令</th><th style="text-align:left">说明</th></tr></thead><tbody><tr><td style="text-align:left"><code>(gdb) break main</code></td><td style="text-align:left">在所有名为 <code>main</code> 的函数处设置一个断点</td></tr><tr><td style="text-align:left"><code>(gdb) break test.c:12</code></td><td style="text-align:left">在文件 <code>test.c</code> 的第 <code>12</code> 行设置断点</td></tr><tr><td style="text-align:left"><code>(gdb) break test.c:func</code></td><td style="text-align:left">在文件 <code>test.c</code> 的 <code>func</code> 函数处设置断点</td></tr><tr><td style="text-align:left"><code>(gdb) rbreak regular-expression</code></td><td style="text-align:left">在正则表达式匹配的函数名上设置断点</td></tr><tr><td style="text-align:left"><code>(gdb) break foo if a &lt; 100</code></td><td style="text-align:left">设置<strong>条件断点</strong>，条件满足才停止</td></tr><tr><td style="text-align:left"><code>(gdb) info break</code></td><td style="text-align:left">列出所有断点位置、编号</td></tr><tr><td style="text-align:left"><code>(gdb) delete 2</code></td><td style="text-align:left">删除指定编号的断点</td></tr><tr><td style="text-align:left"><code>(gdb) clear</code></td><td style="text-align:left">删除刚才停止处的断点</td></tr><tr><td style="text-align:left"><code>(gdb) disable 1</code></td><td style="text-align:left"><code>disable</code> 指定编号的断点</td></tr><tr><td style="text-align:left"><code>(gdb) enable 1</code></td><td style="text-align:left"><code>enable</code> 指定编号的断点</td></tr></tbody></table><h2 id="watch-命令"><a class="markdownIt-Anchor" href="#watch-命令"></a> watch 命令</h2><table><thead><tr><th style="text-align:left">命令</th><th style="text-align:left">说明</th></tr></thead><tbody><tr><td style="text-align:left"><code>(gdb) watch var</code></td><td style="text-align:left">监视变量，当值变化时会输出新、旧值</td></tr><tr><td style="text-align:left"><code>(gdb) info break</code></td><td style="text-align:left">列出断点，也包括 <code>watchpoint</code></td></tr><tr><td style="text-align:left"><code>(gdb) i watch</code></td><td style="text-align:left">只列出 <code>watchpoint</code></td></tr><tr><td style="text-align:left"><code>(gdb) delete 1</code></td><td style="text-align:left">删除指定的 <code>watchpoint</code></td></tr></tbody></table><h2 id="查看变量"><a class="markdownIt-Anchor" href="#查看变量"></a> 查看变量</h2><table><thead><tr><th style="text-align:left">命令</th><th style="text-align:left">说明</th></tr></thead><tbody><tr><td style="text-align:left"><code>(gdb) info args</code></td><td style="text-align:left">查看传入参数信息</td></tr><tr><td style="text-align:left"><code>(gdb) info local</code></td><td style="text-align:left">查看当前栈帧（函数）的本地变量</td></tr><tr><td style="text-align:left"><code>(gdb) print var</code></td><td style="text-align:left">查看指定变量的值</td></tr><tr><td style="text-align:left"><code>(gdb) print/x var</code></td><td style="text-align:left">以十六进制输出变量的值</td></tr><tr><td style="text-align:left"><code>(gdb) print ptr</code></td><td style="text-align:left">假设 <code>int *ptr=&amp;a</code>，输出变量 <code>a</code> 的地址</td></tr><tr><td style="text-align:left"><code>(gdb) print *ptr</code></td><td style="text-align:left">假设 <code>int *ptr=&amp;a</code>，输出变量 <code>a</code> 的值</td></tr><tr><td style="text-align:left"><code>(gdb) print *ptr@5</code></td><td style="text-align:left">假设 <code>int ptr[5]</code>，输出数组的值</td></tr><tr><td style="text-align:left"><code>(gdb) display var</code></td><td style="text-align:left">与 <code>print</code> 作用相同，但每次停下来都自动输出变量的值</td></tr><tr><td style="text-align:left"><code>(gdb) info display</code></td><td style="text-align:left">列出所有设置了 <code>display</code> 的变量</td></tr><tr><td style="text-align:left"><code>(gdb) undisplay 1</code></td><td style="text-align:left">与 <code>display</code> 相反，不能指定变量名，只能是编号</td></tr><tr><td style="text-align:left"><code>(gdb) delete display 1</code></td><td style="text-align:left">与 <code>undisplay</code> 类似，通过编号取消显示</td></tr><tr><td style="text-align:left"><code>(gdb) whatis var</code></td><td style="text-align:left">查看变量类型</td></tr><tr><td style="text-align:left"><code>(gdb) ptype var</code></td><td style="text-align:left">比 <code>type</code> 更详细，会给出结构体的定义</td></tr></tbody></table><h2 id="frame-栈帧"><a class="markdownIt-Anchor" href="#frame-栈帧"></a> frame 栈帧</h2><p>每当一个函数被调用时，一个新的栈帧 <code>frame</code> 就会被压入栈中，栈帧包含了该函数的局部变量、参数、返回地址和其他信息，当函数执行完毕后，这个栈帧会被弹出栈并销毁。</p><table><thead><tr><th style="text-align:left">命令</th><th style="text-align:left">说明</th></tr></thead><tbody><tr><td style="text-align:left"><code>(gdb) frame</code></td><td style="text-align:left">显示当前栈帧和源代码行</td></tr><tr><td style="text-align:left"><code>(gdb) backtrace</code></td><td style="text-align:left">打印出当前正在执行的所有栈帧</td></tr><tr><td style="text-align:left"><code>(gdb) backtrace 5</code></td><td style="text-align:left">只显示最近调用的 <code>5</code> 个栈帧</td></tr><tr><td style="text-align:left"><code>(gdb) frame 2</code></td><td style="text-align:left">切换到第 <code>2</code> 个栈帧，以查看信息</td></tr><tr><td style="text-align:left"><code>(gdb) up</code></td><td style="text-align:left">切换到上一级调用栈帧</td></tr><tr><td style="text-align:left"><code>(gdb) down</code></td><td style="text-align:left">切换到下一级调用栈帧</td></tr></tbody></table><h2 id="函数调用"><a class="markdownIt-Anchor" href="#函数调用"></a> 函数调用</h2><p><code>call</code> 和 <code>print</code> 调用的函数如果存在全局变量、静态变量的修改，在函数返回后会恢复到调用之前的值，这两个调用不会影响程序的状态</p><table><thead><tr><th style="text-align:left">命令</th><th style="text-align:left">说明</th></tr></thead><tbody><tr><td style="text-align:left"><code>(gdb) call func(a, b)</code></td><td style="text-align:left">调用指定的函数，不影响主线程变量</td></tr><tr><td style="text-align:left"><code>(gdb) print func(a, b)</code></td><td style="text-align:left">与 <code>call</code> 类似</td></tr><tr><td style="text-align:left"><code>(gdb) finish</code></td><td style="text-align:left">结束当前运行的函数</td></tr></tbody></table><h2 id="信号"><a class="markdownIt-Anchor" href="#信号"></a> 信号</h2><p><code>linux</code> 下使用 <code>kill -l</code> 查看信号编号与信号名，使用 <code>info signal</code> 查看信号的处理方式、描述等：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">(gdb) info signal</span><br><span class="line">Signal        Stop  Print   Pass to program Description</span><br><span class="line"></span><br><span class="line">SIGHUP        Yes   Yes     Yes             Hangup</span><br><span class="line">SIGINT        Yes   Yes     No              Interrupt</span><br><span class="line">SIGQUIT       Yes   Yes     Yes             Quit</span><br><span class="line">SIGILL        Yes   Yes     Yes             Illegal instruction</span><br></pre></td></tr></table></figure><table><thead><tr><th style="text-align:left">命令</th><th style="text-align:left">说明</th></tr></thead><tbody><tr><td style="text-align:left"><code>(gdb) signal SIGKILL</code></td><td style="text-align:left">向进程发送信号，用信号名或编号表示</td></tr><tr><td style="text-align:left"><code>(gdb) signal 9</code></td><td style="text-align:left">向进程发送信号，用信号名或编号表示</td></tr><tr><td style="text-align:left"><code>(gdb) handle &lt;signal&gt; actions</code></td><td style="text-align:left">指定信号的处理方式，选择如下，可以组合</td></tr><tr><td style="text-align:left"><code>stop/nostop</code></td><td style="text-align:left">收到信号是否停止进程，类似断点</td></tr><tr><td style="text-align:left"><code>print/noprint</code></td><td style="text-align:left">收到信号是否输出消息</td></tr><tr><td style="text-align:left"><code>pass/nopass</code></td><td style="text-align:left">是否将信号传递给程序</td></tr></tbody></table><h2 id="线程"><a class="markdownIt-Anchor" href="#线程"></a> 线程</h2><table><thead><tr><th style="text-align:left">命令</th><th style="text-align:left">说明</th></tr></thead><tbody><tr><td style="text-align:left"><code>(gdb) info threads</code></td><td style="text-align:left">列出所有线程，标识当前所在线程</td></tr><tr><td style="text-align:left"><code>(gdb) thread 2</code></td><td style="text-align:left">切换到编号为 <code>2</code> 的线程</td></tr><tr><td style="text-align:left"><code>(gdb) break file.c:23 thread all</code></td><td style="text-align:left">在所有线程中相应的行上设置断点</td></tr><tr><td style="text-align:left"><code>(gdb) thread apply all command</code></td><td style="text-align:left">让所有线程执行 <code>gdb</code> 命令</td></tr><tr><td style="text-align:left"><code>(gdb) thread apply ID1 ID2 command</code></td><td style="text-align:left">让指定线程执行 <code>gdb</code> 命令</td></tr><tr><td style="text-align:left"><code>(gdb) set scheduler-locking off</code></td><td style="text-align:left">所有线程都执行，这是默认值</td></tr><tr><td style="text-align:left"><code>(gdb) set scheduler-locking on</code></td><td style="text-align:left">只让当前线程执行</td></tr></tbody></table>]]></content>
    
    
      
      
    <summary type="html">&lt;blockquote&gt;
&lt;p&gt;文章内容转载自开源项目&lt;a href=&quot;https://github.com/jaywcjlove/reference/blob/main/docs/gdb.md&quot;&gt;jaywcjlove/reference&lt;/a&gt;（MIT © &lt;a href=&quot;h</summary>
      
    
    
    
    <category term="C/C++" scheme="https://bg3lnt.xyz/categories/C-C/"/>
    
    
    <category term="GDB" scheme="https://bg3lnt.xyz/tags/GDB/"/>
    
  </entry>
  
  <entry>
    <title>初始化 Python 二维列表</title>
    <link href="https://bg3lnt.xyz/posts/1b565acd/"/>
    <id>https://bg3lnt.xyz/posts/1b565acd/</id>
    <published>2024-05-05T00:26:42.000Z</published>
    <updated>2025-10-19T17:36:57.519Z</updated>
    
    <content type="html"><![CDATA[<h2 id="先说结论"><a class="markdownIt-Anchor" href="#先说结论"></a> 先说结论</h2><p>使用 Python 的列表做二维数组时，初始化应当使用列表推导式或者使用嵌套循环逐行逐个元素添加，不应当使用<code>*</code>运算创建多行列表，使用<code>list * n</code>形式创建的多个行列表的<code>id</code>是相同的，也就是说这些行引用的是内存中的相同地址，修改任何一行的值，其他行都会被修改。</p><h2 id="验证代码"><a class="markdownIt-Anchor" href="#验证代码"></a> 验证代码</h2><p>使用一个简短的程序对这个问题进行验证，分别使用了列表推导式、<code>*</code>与列表推导式结合、完全使用<code>*</code>三种方式对二维数组初始化。</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">def</span> <span class="title function_">print_array</span>(<span class="params">array</span>):</span><br><span class="line">    [<span class="built_in">print</span>(*row) <span class="keyword">for</span> row <span class="keyword">in</span> array]</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">init1</span>(<span class="params">rows, cols</span>):</span><br><span class="line">    array = [[<span class="number">0</span> <span class="keyword">for</span> _ <span class="keyword">in</span> <span class="built_in">range</span>(cols)] <span class="keyword">for</span> _ <span class="keyword">in</span> <span class="built_in">range</span>(rows)]</span><br><span class="line">    <span class="keyword">return</span> array</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">init2</span>(<span class="params">rows, cols</span>):</span><br><span class="line">    array = [[<span class="number">0</span>] * cols <span class="keyword">for</span> _ <span class="keyword">in</span> <span class="built_in">range</span>(rows)]</span><br><span class="line">    <span class="keyword">return</span> array</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">init3</span>(<span class="params">rows, cols</span>):</span><br><span class="line">    array = [[<span class="number">0</span>] * cols] * rows</span><br><span class="line">    <span class="keyword">return</span> array</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> __name__ == <span class="string">&quot;__main__&quot;</span>:</span><br><span class="line">    rows = <span class="number">3</span>  <span class="comment"># 行数</span></span><br><span class="line">    cols = <span class="number">5</span>  <span class="comment"># 列数</span></span><br><span class="line"></span><br><span class="line">    array1 = init1(rows, cols)</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">&quot;\narray1:&quot;</span>)</span><br><span class="line">    print_array(array1)</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">f&quot;row0 id:<span class="subst">&#123;<span class="built_in">id</span>(array1[<span class="number">0</span>])&#125;</span>, row1 id:<span class="subst">&#123;<span class="built_in">id</span>(array1[<span class="number">1</span>])&#125;</span>&quot;</span>)</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">f&quot;row0 id == row1 id ?  <span class="subst">&#123;<span class="built_in">id</span>(array1[<span class="number">0</span>])==<span class="built_in">id</span>(array1[<span class="number">1</span>])&#125;</span>&quot;</span>)</span><br><span class="line"></span><br><span class="line">    array2 = init2(rows, cols)</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">&quot;\narray2:&quot;</span>)</span><br><span class="line">    print_array(array2)</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">f&quot;row0 id:<span class="subst">&#123;<span class="built_in">id</span>(array2[<span class="number">0</span>])&#125;</span>, row1 id:<span class="subst">&#123;<span class="built_in">id</span>(array2[<span class="number">1</span>])&#125;</span>&quot;</span>)</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">f&quot;row0 id == row1 id ?  <span class="subst">&#123;<span class="built_in">id</span>(array2[<span class="number">0</span>])==<span class="built_in">id</span>(array2[<span class="number">1</span>])&#125;</span>&quot;</span>)</span><br><span class="line"></span><br><span class="line">    array3 = init3(rows, cols)</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">&quot;\narray3:&quot;</span>)</span><br><span class="line">    print_array(array3)</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">f&quot;row0 id:<span class="subst">&#123;<span class="built_in">id</span>(array3[<span class="number">0</span>])&#125;</span>, row1 id:<span class="subst">&#123;<span class="built_in">id</span>(array3[<span class="number">1</span>])&#125;</span>&quot;</span>)</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">f&quot;row0 id == row1 id ?  <span class="subst">&#123;<span class="built_in">id</span>(array3[<span class="number">0</span>])==<span class="built_in">id</span>(array3[<span class="number">1</span>])&#125;</span>&quot;</span>)</span><br></pre></td></tr></table></figure><p>程序运行的结果如下</p><div class="tag-plugin image"><div class="image-bg" style="width:100%;"><img class="lazy" src="https://imgoss.bg3lnt.xyz/imgs/240505090031_f2cf529346e45216.jpg" data-src="https://imgoss.bg3lnt.xyz/imgs/240505090031_f2cf529346e45216.jpg" alt="程序运行结果" data-fancybox="true" style="width:600px;"onerror="this.src=&quot;data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='2rem' height='2rem' viewBox='0 0 24 24'%3E%3C!-- Icon from Solar by 480 Design - https://creativecommons.org/licenses/by/4.0/ --%3E%3Cpath fill='%23F44336' d='M22 12.698c-.002 1.47-.013 2.718-.096 3.743c-.097 1.19-.296 2.184-.74 3.009a4.2 4.2 0 0 1-.73.983c-.833.833-1.893 1.21-3.237 1.39C15.884 22 14.2 22 12.053 22h-.106c-2.148 0-3.83 0-5.144-.177c-1.343-.18-2.404-.557-3.236-1.39c-.738-.738-1.12-1.656-1.322-2.795c-.2-1.12-.236-2.512-.243-4.241Q1.999 12.737 2 12v-.054c0-2.148 0-3.83.177-5.144c.18-1.343.557-2.404 1.39-3.236s1.893-1.21 3.236-1.39c1.168-.157 2.67-.175 4.499-.177a.697.697 0 1 1 0 1.396c-1.855.002-3.234.018-4.313.163c-1.189.16-1.906.464-2.436.994S3.72 5.8 3.56 6.99C3.397 8.2 3.395 9.788 3.395 12v.784l.932-.814a2.14 2.14 0 0 1 2.922.097l3.99 3.99a1.86 1.86 0 0 0 2.385.207l.278-.195a2.79 2.79 0 0 1 3.471.209l2.633 2.37c.265-.557.423-1.288.507-2.32c.079-.972.09-2.152.091-3.63a.698.698 0 0 1 1.396 0' opacity='.5'/%3E%3Cpath fill='%23F44336' fill-rule='evenodd' d='M17.5 11c-2.121 0-3.182 0-3.841-.659S13 8.621 13 6.5s0-3.182.659-3.841S15.379 2 17.5 2s3.182 0 3.841.659S22 4.379 22 6.5s0 3.182-.659 3.841S19.621 11 17.5 11m-1.47-7.03a.75.75 0 1 0-1.06 1.06l1.47 1.47l-1.47 1.47a.75.75 0 0 0 1.06 1.06l1.47-1.47l1.47 1.47a.75.75 0 1 0 1.06-1.06L18.56 6.5l1.47-1.47a.75.75 0 0 0-1.06-1.06L17.5 5.44z' clip-rule='evenodd'/%3E%3C/svg%3E&quot;"/><div class="lazy-icon" style="background-image:url(https://api.iconify.design/eos-icons:three-dots-loading.svg?color=%231cd0fd);"></div></div><div class="image-meta"><span class="image-caption center">程序运行结果</span></div></div><p>可以看到在创建行时不能简单地使用<code>*</code>创建多行，这样创建的行的内存地址相同。</p><h2 id="这个问题的来源"><a class="markdownIt-Anchor" href="#这个问题的来源"></a> 这个问题的来源</h2><p>最近用 Python 做到了 LeetCode 中的一道动规题<a href="https://leetcode.cn/problems/unique-paths/description/">62.不同路径</a></p><h3 id="思路"><a class="markdownIt-Anchor" href="#思路"></a> 思路</h3><p>按描述，走到某一位置的路径可能有两个来源，一个是当前位置左侧的位置，另一个是当前位置的上侧位置，符合基本的动规思路</p><ul><li>动规数组：<code>dp[i][j]</code>表示从起点到坐标 i,j 处的路径数</li><li>递推关系：<code>dp[i][j] = dp[i-1][j] + dp[i][j-1]</code>，含义是当前位置的路径数是所有可能的上一步的路径数的加和，可能的上一步即当前位置<code>[i][j]</code>的左侧<code>[i-1][j]</code>或者上侧<code>[i][j-1]</code></li><li>初始化：根据递推关系含义，应当对起始的左侧和上侧进行初始化，也就是对地图的左边界，上边界初始化，每个格子的路径都是 1，因为不可能再有其他方案走到这些位置</li><li>遍历顺序：<code>i,j</code>需要两层循环，两层循环都需要顺序遍历，因为递推关系中使用的是当前位置之前的状态值。两层循环的先后顺序可以任意安排，可以从左到右从上到下 (先<code>j</code>后<code>i</code>，<code>i</code>在<code>j</code>之外)，也可以从上到下从左到右 (先<code>i</code>后<code>j</code>，<code>j</code>在<code>i</code>之外)，因为这不影响<code>dp[i][j]</code>总是在<code>dp[i-1][j]</code>和<code>dp[i][j-1]</code>之后计算这一基本要求</li></ul><h3 id="代码"><a class="markdownIt-Anchor" href="#代码"></a> 代码</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">def</span> <span class="title function_">uniquePaths</span>(<span class="params">self, m: <span class="built_in">int</span>, n: <span class="built_in">int</span></span>) -&gt; <span class="built_in">int</span>:</span><br><span class="line">    <span class="comment"># m 行 n 列</span></span><br><span class="line">    dp = [[<span class="number">0</span>] * n] * m</span><br><span class="line">    <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(m):</span><br><span class="line">        dp[i][<span class="number">0</span>] = <span class="number">1</span></span><br><span class="line">    <span class="keyword">for</span> j <span class="keyword">in</span> <span class="built_in">range</span>(n):</span><br><span class="line">        dp[<span class="number">0</span>][j] = <span class="number">1</span></span><br><span class="line">    <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">1</span>, m):</span><br><span class="line">        <span class="keyword">for</span> j <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">1</span>, n):</span><br><span class="line">            dp[i][j] = dp[i - <span class="number">1</span>][j] + dp[i][j - <span class="number">1</span>]</span><br><span class="line">    <span class="comment"># for j in range(1, n):</span></span><br><span class="line">    <span class="comment">#     for i in range(1, m):</span></span><br><span class="line">    <span class="comment">#         dp[i][j] = dp[i - 1][j] + dp[i][j - 1]</span></span><br><span class="line">    <span class="keyword">return</span> dp[i][j]</span><br></pre></td></tr></table></figure><p>当我尝试用注释掉的两层循环来代替程序中先<code>j</code>后<code>i</code>的循环来说明两层循环的次序并不影响结果时，意外的出现了错误。经过调试发现，在经过对边界值进行初始化的两个循环 (<code>for i in range(m)</code>和<code>for j in range(n)</code>) 后，二维数组中出现了错误，此时数组中的所有值全都变成了 1，只不过程序中先<code>j</code>后<code>i</code>的循环，会一次性使用完一整行的元素之后去遍历下一行，恰好避开了这个错误。但是交换循环次序后，逐列遍历会反复在不同行之间修改元素的值，这会导致其他行也被修改，取到错误的数值，得到错误结果。</p><p>将创建<code>dp</code>数组的代码修改后两种循环次序均能够得到正确的结果。</p>]]></content>
    
    
      
      
    <summary type="html">&lt;h2 id=&quot;先说结论&quot;&gt;&lt;a class=&quot;markdownIt-Anchor&quot; href=&quot;#先说结论&quot;&gt;&lt;/a&gt; 先说结论&lt;/h2&gt;
&lt;p&gt;使用 Python 的列表做二维数组时，初始化应当使用列表推导式或者使用嵌套循环逐行逐个元素添加，不应当使用&lt;code&gt;*&lt;/cod</summary>
      
    
    
    
    <category term="Python" scheme="https://bg3lnt.xyz/categories/Python/"/>
    
    
    <category term="Python" scheme="https://bg3lnt.xyz/tags/Python/"/>
    
    <category term="动态规划" scheme="https://bg3lnt.xyz/tags/%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92/"/>
    
  </entry>
  
  <entry>
    <title>为 Windows 系统中的 Bash / Zsh 增加 watch 命令</title>
    <link href="https://bg3lnt.xyz/posts/f9eb2aa3/"/>
    <id>https://bg3lnt.xyz/posts/f9eb2aa3/</id>
    <published>2023-11-07T07:40:30.000Z</published>
    <updated>2025-10-19T17:36:57.524Z</updated>
    
    <content type="html"><![CDATA[<p>使用 shell 工具的<code>alias</code>别名创建一个名为<code>watch</code>的函数，具体做法是在<code>.bashrc</code>或者<code>.zshrc</code>文件中添加如下代码，添加位置可以放在<code>Example aliases</code>部分。</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Example aliases</span></span><br><span class="line"><span class="comment"># alias zshconfig=&quot;mate ~/.zshrc&quot;</span></span><br><span class="line"><span class="comment"># alias ohmyzsh=&quot;mate ~/.oh-my-zsh&quot;</span></span><br><span class="line"><span class="function"><span class="title">watch</span></span> () &#123;</span><br><span class="line">  ARGS=<span class="string">&quot;<span class="variable">$&#123;@&#125;</span>&quot;</span></span><br><span class="line">  clear;</span><br><span class="line">  <span class="keyword">while</span> (<span class="literal">true</span>); <span class="keyword">do</span></span><br><span class="line">    OUTPUT=`<span class="variable">$ARGS</span>`</span><br><span class="line">    clear</span><br><span class="line">    <span class="built_in">echo</span> -e <span class="string">&quot;Every 1.0s: <span class="variable">$ARGS</span>&quot;</span></span><br><span class="line">    <span class="built_in">echo</span> <span class="string">&quot;&quot;</span></span><br><span class="line">    <span class="built_in">echo</span> -e <span class="string">&quot;<span class="variable">$&#123;OUTPUT[@]&#125;</span>&quot;</span></span><br><span class="line">    <span class="built_in">sleep</span> 1</span><br><span class="line">  <span class="keyword">done</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;使用 shell 工具的&lt;code&gt;alias&lt;/code&gt;别名创建一个名为&lt;code&gt;watch&lt;/code&gt;的函数，具体做法是在&lt;code&gt;.bashrc&lt;/code&gt;或者&lt;code&gt;.zshrc&lt;/code&gt;文件中添加如下代码，添加位置可以放在&lt;code&gt;Exampl</summary>
      
    
    
    
    <category term="工具" scheme="https://bg3lnt.xyz/categories/%E5%B7%A5%E5%85%B7/"/>
    
    
    <category term="Zsh" scheme="https://bg3lnt.xyz/tags/Zsh/"/>
    
    <category term="Bash" scheme="https://bg3lnt.xyz/tags/Bash/"/>
    
    <category term="Windows" scheme="https://bg3lnt.xyz/tags/Windows/"/>
    
    <category term="环境配置" scheme="https://bg3lnt.xyz/tags/%E7%8E%AF%E5%A2%83%E9%85%8D%E7%BD%AE/"/>
    
  </entry>
  
</feed>
