[{"data":1,"prerenderedAt":2160},["ShallowReactive",2],{"site-search-sections-zh":3,"site-mobile-menu-taxonomies-zh":1149,"site-mobile-menu-page":1220,"tag-posts-zh-\u002Ftags\u002FDNS":1319},[4,17,24,30,37,43,49,55,66,71,77,83,89,95,101,107,111,116,121,126,137,142,148,154,160,165,170,175,181,187,193,199,205,210,216,222,228,234,240,246,252,258,264,270,276,282,288,294,300,306,312,318,324,332,338,344,350,356,362,368,382,389,395,402,409,416,423,430,440,446,460,466,473,480,487,494,502,509,516,523,530,537,544,551,558,565,572,579,588,595,602,609,616,623,630,637,644,651,657,665,672,679,686,693,700,707,714,721,728,735,741,749,756,763,770,777,784,791,798,805,811,820,827,834,841,848,855,862,869,876,883,890,896,908,916,922,934,940,953,959,966,973,980,987,996,1002,1009,1016,1023,1030,1037,1044,1051,1058,1065,1072,1079,1086,1093,1100,1107,1114,1121,1132,1138],{"path":5,"category":6,"tags":7,"created":11,"draft":12,"id":5,"title":13,"titles":14,"content":15,"level":16},"\u002Fabout","About",[8,9,10],"Computer Science","Blog","Life","2026-05-14",false,"关于 Leisuer",[],"Leisuer是谁？他从哪里来？他在做什么？他要做什么？ 这里是介绍我自己的页面，网站介绍请看关于本站。 我是Leisuer，在中国科学技术大学计算机科学与技术专业读大四，信仰共产主义。 我的父亲是湖南人，我的母亲是东北人（之后有机会的话我可以写一篇文章讲讲他们是怎么认识的～），我在吉林省吉林市出生长大，欢迎你们来吉林和湖南旅行！",1,{"path":5,"category":6,"tags":18,"created":11,"draft":12,"id":19,"title":20,"titles":21,"content":22,"level":23},[8,9,10],"\u002Fabout#兴趣爱好","兴趣爱好",[13],"骑行、手工、摄影、羽毛球、阅读、听歌、看动画 推理、悬疑爱好者 喜欢历史但缺乏系统性的知识和训练",2,{"path":5,"category":6,"tags":25,"created":11,"draft":12,"id":26,"title":27,"titles":28,"content":29,"level":23},[8,9,10],"\u002Fabout#计算机相关","计算机相关",[13],"几个比较感兴趣的方向： 大模型系统优化，训练和推理加速LLM agent、agentic RAG具身智能",{"path":5,"category":6,"tags":31,"created":11,"draft":12,"id":32,"title":33,"titles":34,"content":35,"level":36},[8,9,10],"\u002Fabout#关于前端","关于前端",[13,27],"一直很向往平面设计和UI\u002FUX设计，所以对前端也怀有一种好奇心。去年暑假的时候在b站上自学过三件套，除此之外再没有系统性学习过。好在有codex大人相助，我顺利vibe code出本网站，同时也学到了不少前端（特别是vue）知识。",3,{"path":5,"category":6,"tags":38,"created":11,"draft":12,"id":39,"title":40,"titles":41,"content":42,"level":23},[8,9,10],"\u002Fabout#高考之后曾设想的几个人生flag","高考之后曾设想的几个人生flag：",[13],"学做菜 学会游泳 学画画 掌握一门乐器 学开车",{"path":5,"category":6,"tags":44,"created":11,"draft":12,"id":45,"title":46,"titles":47,"content":48,"level":23},[8,9,10],"\u002Fabout#语言","语言",[13],"母语：中文 第一外语：英语（托福裸考95） 日语：跟着新标日和b站up学的，掌握的词汇和语法都非常有限，没时间学QAQ（但是片假名领域大神，音译能力max）韩语：四十音学了一点点，感谢饮冰老师教我如果以上全学会了（又在幻想了），下一步想学法语",{"path":5,"category":6,"tags":50,"created":11,"draft":12,"id":51,"title":52,"titles":53,"content":54,"level":23},[8,9,10],"\u002Fabout#by-the-way","By the way",[13],"我不太关注mbti以及类似的标签，但是如果你想知道的话，我之前测过是INFP，或许可以参考一下。",{"path":56,"category":6,"tags":57,"created":62,"draft":12,"id":56,"title":63,"titles":64,"content":65,"level":16},"\u002Fcolor-palette",[58,59,60,61],"Color","Theme","CSS","Design","2026-05-16","站点色彩方案",[],"Leisuer 博客当前使用的亮色、暗色与公用颜色整理。",{"path":56,"category":6,"tags":67,"created":62,"draft":12,"id":68,"title":63,"titles":69,"content":70,"level":16},[58,59,60,61],"\u002Fcolor-palette#站点色彩方案",[],"这篇文章整理了当前站点正在使用的颜色方案，按明亮模式、黑暗模式和公用颜色三类展示，便于后续统一主题、重构样式或补设计文档。",{"path":56,"category":6,"tags":72,"created":62,"draft":12,"id":73,"title":74,"titles":75,"content":76,"level":23},[58,59,60,61],"\u002Fcolor-palette#明亮模式","明亮模式",[63],"标识符色号--color-bg#eef7ff--color-text-main#111111--color-text-muted#666666--color-border#eaeaea--color-nav-bg#ffffff--color-surfacergb(255 255 255 \u002F 0.95)--color-surface-elevatedrgb(255 255 255 \u002F 0.98)--color-glass-bgrgb(255 255 255 \u002F 0.62)--color-glass-borderrgb(255 255 255 \u002F 0.7)--color-overlayrgb(15 23 42 \u002F 0.12)--color-shadowrgb(17 24 39 \u002F 0.12)--color-code-bg#f8fafc--color-code-header-bg#eef4fb--color-code-text#1e293b--color-code-border#dbe4ef--color-code-button-bgrgb(255 255 255 \u002F 0.96)--color-code-button-border#cbd5e1--color-code-button-text#475569--color-code-button-hover-bg#ffffff--color-code-button-hover-border#94a3b8--color-code-button-hover-text#0f172a--color-scrollbar-thumb-hoverrgb(59 130 246) --color-bg#eef7ff\n  \n  \n    \n    --color-text-main#111111\n  \n  \n    \n    --color-text-muted#666666\n  \n  \n    \n    --color-border#eaeaea\n  \n  \n    \n    --color-accent#53a3f1\n  \n  \n    \n    --color-nav-bg#ffffff",{"path":56,"category":6,"tags":78,"created":62,"draft":12,"id":79,"title":80,"titles":81,"content":82,"level":23},[58,59,60,61],"\u002Fcolor-palette#黑暗模式","黑暗模式",[63],"标识符色号--color-bg#0f141d--color-text-main#f5f7fb--color-text-muted#b4bfcc--color-borderrgb(255 255 255 \u002F 0.14)--color-nav-bgrgb(15 20 29 \u002F 0.92)--color-surfacergb(21 28 39 \u002F 0.92)--color-surface-elevatedrgb(24 32 45 \u002F 0.98)--color-glass-bgrgb(21 28 39 \u002F 0.6)--color-glass-borderrgb(255 255 255 \u002F 0.12)--color-overlayrgb(0 0 0 \u002F 0.3)--color-shadowrgb(0 0 0 \u002F 0.3)--color-code-bg#0f172a--color-code-header-bg#0b1220--color-code-text#e5e7eb--color-code-borderrgb(148 163 184 \u002F 0.22)--color-code-button-bgrgb(15 23 42 \u002F 0.92)--color-code-button-borderrgb(148 163 184 \u002F 0.45)--color-code-button-text#cbd5e1--color-code-button-hover-bgrgb(30 41 59 \u002F 0.96)--color-code-button-hover-borderrgb(203 213 225 \u002F 0.6)--color-code-button-hover-text#ffffff--color-scrollbar-thumb-hoverrgb(147 197 253) --color-bg#0f141d\n  \n  \n    \n    --color-text-main#f5f7fb\n  \n  \n    \n    --color-text-muted#b4bfcc\n  \n  \n    \n    --color-borderrgb(255 255 255 \u002F 0.14)\n  \n  \n    \n    --color-accent#53a3f1\n  \n  \n    \n    --color-nav-bgrgb(15 20 29 \u002F 0.92)",{"path":56,"category":6,"tags":84,"created":62,"draft":12,"id":85,"title":86,"titles":87,"content":88,"level":23},[58,59,60,61],"\u002Fcolor-palette#公用颜色","公用颜色",[63],"这些颜色在明亮模式和黑暗模式中保持一致。 标识符色号说明--color-accent#53a3f1站点主强调色，用于链接、标题装饰条、分类、标签与高亮状态--color-scrollbar-tracktransparent滚动条轨道--color-scrollbar-thumbrgb(96 165 250)滚动条滑块 --color-accent#53a3f1\n  \n  \n    \n    --color-scrollbar-tracktransparent\n  \n  \n    \n    --color-scrollbar-thumbrgb(96 165 250)",{"path":56,"category":6,"tags":90,"created":62,"draft":12,"id":91,"title":92,"titles":93,"content":94,"level":23},[58,59,60,61],"\u002Fcolor-palette#局部硬编码颜色","局部硬编码颜色",[63],"这些颜色不属于主题变量，但也确实出现在项目里。 色号用途#53a3f1搜索框聚焦边框、PWA 主题色、测试内容中的内联文字颜色#ffffff局部文字、局部背景、PWA 背景色#cbcbcb首页 Hero 大字中的浅灰文字#000遮罩渐变中的纯黑#ff5f57代码块顶部红色圆点#ffbd2e代码块顶部黄色圆点#28c840代码块顶部绿色圆点rgba(0, 0, 0, 0.1)Alert 组件阴影rgba(0, 0, 0, 0.15)Alert 组件悬停阴影 macOS Red#ff5f57\n  \n  \n    \n    macOS Yellow#ffbd2e\n  \n  \n    \n    macOS Green#28c840",{"path":56,"category":6,"tags":96,"created":62,"draft":12,"id":97,"title":98,"titles":99,"content":100,"level":23},[58,59,60,61],"\u002Fcolor-palette#备注","备注",[63],"整体上，这套颜色更像是以实际视觉效果为目标逐步微调出来的主题，而不是一开始就完全按严格 token 体系推导出的配色系统。因此会看到一些非常接近、但并不完全均匀的黑白灰与透明度组合。",{"path":102,"category":103,"tags":103,"created":103,"draft":12,"id":102,"title":104,"titles":105,"content":106,"level":16},"\u002F",null,"Welcome to Nuxt Content Starter",[],"This is the main page displaying Markdown located at content\u002Findex.md.",{"path":102,"category":103,"tags":103,"created":103,"draft":12,"id":108,"title":104,"titles":109,"content":110,"level":16},"\u002F#welcome-to-nuxt-content-starter",[],"This is the main page displaying Markdown located at content\u002Findex.md. Move to about page.",{"path":102,"category":103,"tags":103,"created":103,"draft":12,"id":112,"title":113,"titles":114,"content":115,"level":23},"\u002F#manage-your-contents","Manage your Contents",[104],"Create new pages or modify the existing ones in content\u002F directory.",{"path":102,"category":103,"tags":103,"created":103,"draft":12,"id":117,"title":118,"titles":119,"content":120,"level":23},"\u002F#query-render-pages","Query & Render Pages",[104],"You can find an example of querying contents and rendering them in a catch-all page",{"path":102,"category":103,"tags":103,"created":103,"draft":12,"id":122,"title":123,"titles":124,"content":125,"level":23},"\u002F#integrate-vue-component","Integrate Vue Component",[104],"The current alert and the counter below are Vue components integrated into the Markdown. Checkout out the documentation to learn more.",{"path":127,"category":128,"tags":129,"created":11,"draft":12,"id":127,"title":134,"titles":135,"content":136,"level":16},"\u002Fmarkdown-test","Test",[130,131,132,133],"Markdown","Nuxt Content","MDC","Syntax","Markdown Syntax Test",[],"A stress-test document for common Markdown, GFM, HTML, and Nuxt Content MDC syntax.",{"path":127,"category":128,"tags":138,"created":11,"draft":12,"id":139,"title":134,"titles":140,"content":141,"level":16},[130,131,132,133],"\u002Fmarkdown-test#markdown-syntax-test",[],"This document is a playground for testing Markdown rendering. It intentionally includes many edge cases and visual patterns. Jump to tables · Jump to code · Jump to MDC",{"path":127,"category":128,"tags":143,"created":11,"draft":12,"id":144,"title":145,"titles":146,"content":147,"level":23},[130,131,132,133],"\u002Fmarkdown-test#paragraphs-and-line-breaks","Paragraphs And Line Breaks",[134],"This is a normal paragraph. It contains enough text to wrap across multiple lines so spacing, line height, and width can be inspected in the renderer. This is another paragraph after a blank line. This line ends with two spaces.\nThis should render as a soft line break. This line uses an HTML break.\nThis should also appear on the next line.",{"path":127,"category":128,"tags":149,"created":11,"draft":12,"id":150,"title":151,"titles":152,"content":153,"level":23},[130,131,132,133],"\u002Fmarkdown-test#inline-formatting","Inline Formatting",[134],"This sentence has bold text, italic text, bold italic text, strikethrough text, inline code, and a normal link. Autolinks should work too: https:\u002F\u002Fexample.com and hello@example.com. Escaped characters: *not italic*, # not a heading, [not a link].",{"path":127,"category":128,"tags":155,"created":11,"draft":12,"id":156,"title":157,"titles":158,"content":159,"level":23},[130,131,132,133],"\u002Fmarkdown-test#headings","Headings",[134],"",{"path":127,"category":128,"tags":161,"created":11,"draft":12,"id":162,"title":163,"titles":164,"content":159,"level":16},[130,131,132,133],"\u002Fmarkdown-test#heading-1","Heading 1",[],{"path":127,"category":128,"tags":166,"created":11,"draft":12,"id":167,"title":168,"titles":169,"content":159,"level":23},[130,131,132,133],"\u002Fmarkdown-test#heading-2","Heading 2",[163],{"path":127,"category":128,"tags":171,"created":11,"draft":12,"id":172,"title":173,"titles":174,"content":159,"level":36},[130,131,132,133],"\u002Fmarkdown-test#heading-3","Heading 3",[163,168],{"path":127,"category":128,"tags":176,"created":11,"draft":12,"id":177,"title":178,"titles":179,"content":159,"level":180},[130,131,132,133],"\u002Fmarkdown-test#heading-4","Heading 4",[163,168,173],4,{"path":127,"category":128,"tags":182,"created":11,"draft":12,"id":183,"title":184,"titles":185,"content":159,"level":186},[130,131,132,133],"\u002Fmarkdown-test#heading-5","Heading 5",[163,168,173,178],5,{"path":127,"category":128,"tags":188,"created":11,"draft":12,"id":189,"title":190,"titles":191,"content":159,"level":192},[130,131,132,133],"\u002Fmarkdown-test#heading-6","Heading 6",[163,168,173,178,184],6,{"path":127,"category":128,"tags":194,"created":11,"draft":12,"id":195,"title":196,"titles":197,"content":198,"level":23},[130,131,132,133],"\u002Fmarkdown-test#blockquotes","Blockquotes",[163],"This is a simple blockquote. This is a multi-paragraph blockquote.It has a second paragraph. Nested blockquote:This is nested one level deeper.This is nested two levels deeper. Blockquote with formatting:Bold list iteminline codeA link",{"path":127,"category":128,"tags":200,"created":11,"draft":12,"id":201,"title":202,"titles":203,"content":204,"level":23},[130,131,132,133],"\u002Fmarkdown-test#custom-containers","Custom Containers",[163],"Here's some additional information. Here's a helpful suggestion. Be careful with this action as it might have unexpected results. This action cannot be undone. This content is hidden until the container is opened. This content is visible by default and can be collapsed.",{"path":127,"category":128,"tags":206,"created":11,"draft":12,"id":207,"title":208,"titles":209,"content":159,"level":23},[130,131,132,133],"\u002Fmarkdown-test#lists","Lists",[163],{"path":127,"category":128,"tags":211,"created":11,"draft":12,"id":212,"title":213,"titles":214,"content":215,"level":36},[130,131,132,133],"\u002Fmarkdown-test#unordered-lists","Unordered Lists",[163,208],"First itemSecond item\nNested item ANested item B\nDeep nested itemThird item Asterisk itemAnother asterisk item Plus itemAnother plus item",{"path":127,"category":128,"tags":217,"created":11,"draft":12,"id":218,"title":219,"titles":220,"content":221,"level":36},[130,131,132,133],"\u002Fmarkdown-test#ordered-lists","Ordered Lists",[163,208],"First ordered itemSecond ordered itemThird ordered itemNumbering can all be one.Markdown should still render it correctly.Useful for editing.",{"path":127,"category":128,"tags":223,"created":11,"draft":12,"id":224,"title":225,"titles":226,"content":227,"level":36},[130,131,132,133],"\u002Fmarkdown-test#mixed-lists","Mixed Lists",[163,208],"Ordered item\nNested unordered itemAnother nested unordered itemAnother ordered item\nNested ordered itemAnother nested ordered item",{"path":127,"category":128,"tags":229,"created":11,"draft":12,"id":230,"title":231,"titles":232,"content":233,"level":36},[130,131,132,133],"\u002Fmarkdown-test#task-lists","Task Lists",[163,208],"Completed task Incomplete task Task with bold text Task with inline code",{"path":127,"category":128,"tags":235,"created":11,"draft":12,"id":236,"title":237,"titles":238,"content":239,"level":23},[130,131,132,133],"\u002Fmarkdown-test#tables","Tables",[163],"Left alignedCenter alignedRight alignedAlphaBeta123Long content that wrapsBold and code456.78LinkExample999 FeatureStatusNotesParagraphsWorksBasic text renderingListsWorksNested and task listsCodeWorksInline and fenced",{"path":127,"category":128,"tags":241,"created":11,"draft":12,"id":242,"title":243,"titles":244,"content":245,"level":23},[130,131,132,133],"\u002Fmarkdown-test#code-blocks","Code Blocks",[163],"Inline code: const answer = 42. Indented code: const indented = true\nconsole.log(indented) Plain fenced code: No language here.\nJust plain text. TypeScript: console.log(\"hello\") JavaScript with filename: export default defineComponent({}) type Post = {\n  title: string\n  tags: string[]\n  draft?: boolean\n}\n\nconst post: Post = {\n  title: 'Markdown Syntax Test',\n  tags: ['markdown', 'nuxt-content'],\n} Code with Line Numbers: function calculateSum(a, b) {\n  const result = a + b;\n  console.log(\"Sum:\", result);\n  return result;\n} Line Numbers with Filename: const sum = (a: number, b: number) => a + b Line Numbers with Start: console.log(\"first visible line\")\nconsole.log(\"second visible line\") Vue: \u003Cscript setup lang=\"ts\">\nconst message = 'Hello Markdown'\n\u003C\u002Fscript>\n\n\u003Ctemplate>\n  \u003Cp>{{ message }}\u003C\u002Fp>\n\u003C\u002Ftemplate> Bash: npm run dev\nnpm run build Diff: - const oldTheme = 'light'\n+ const newTheme = 'dark' JSON: {\n  \"title\": \"Markdown Syntax Test\",\n  \"draft\": true,\n  \"tags\": [\"Markdown\", \"Nuxt Content\"]\n} Code Group (Nuxt Content MDC): npm install\nyarn install\npnpm install",{"path":127,"category":128,"tags":247,"created":11,"draft":12,"id":248,"title":249,"titles":250,"content":251,"level":23},[130,131,132,133],"\u002Fmarkdown-test#horizontal-rules","Horizontal Rules",[163],"Three hyphens: Three asterisks: Three underscores:",{"path":127,"category":128,"tags":253,"created":11,"draft":12,"id":254,"title":255,"titles":256,"content":257,"level":23},[130,131,132,133],"\u002Fmarkdown-test#images","Images",[163],"Local image: Image with title: Linked image:",{"path":127,"category":128,"tags":259,"created":11,"draft":12,"id":260,"title":261,"titles":262,"content":263,"level":36},[130,131,132,133],"\u002Fmarkdown-test#image-caption","Image Caption",[163,255],"使用 HTML 的 figure 和 figcaption，在图片下方显示说明文字： 本站背景插图，用于演示图片下方的说明文字。",{"path":127,"category":128,"tags":265,"created":11,"draft":12,"id":266,"title":267,"titles":268,"content":269,"level":36},[130,131,132,133],"\u002Fmarkdown-test#markdown-image-width-tests","Markdown Image Width Tests",[163,255],"Plain Markdown image: Markdown image with Nuxt MDC width and height attributes: Markdown image with class attributes: Markdown image with Tailwind arbitrary width class: Wide Markdown image: Wide Markdown image constrained by class: Markdown linked image: Missing image: Missing image with title: Missing image with explicit size:",{"path":127,"category":128,"tags":271,"created":11,"draft":12,"id":272,"title":273,"titles":274,"content":275,"level":23},[130,131,132,133],"\u002Fmarkdown-test#links","Links",[163],"Inline link: Nuxt Reference link: Nuxt Content Another reference link: Example",{"path":127,"category":128,"tags":277,"created":11,"draft":12,"id":278,"title":279,"titles":280,"content":281,"level":23},[130,131,132,133],"\u002Fmarkdown-test#footnotes","Footnotes",[163],"Here is a sentence with a footnote.1 Here is another footnote with more text.2",{"path":127,"category":128,"tags":283,"created":11,"draft":12,"id":284,"title":285,"titles":286,"content":287,"level":23},[130,131,132,133],"\u002Fmarkdown-test#html","HTML",[163],"Native details elementThis content is inside a native HTML details block. ⌘ + K Highlighted text using HTML mark. Inline HTML span with custom color.",{"path":127,"category":128,"tags":289,"created":11,"draft":12,"id":290,"title":291,"titles":292,"content":293,"level":23},[130,131,132,133],"\u002Fmarkdown-test#escaping-and-entities","Escaping And Entities",[163],"HTML entities: © — → \u003Cdiv> Backslash escaping: `not code` **not bold**",{"path":127,"category":128,"tags":295,"created":11,"draft":12,"id":296,"title":297,"titles":298,"content":299,"level":23},[130,131,132,133],"\u002Fmarkdown-test#definition-like-text","Definition-Like Text",[163],"Term\n: Definition syntax may or may not be supported depending on the Markdown parser. Another Term\n: Another definition body.",{"path":127,"category":128,"tags":301,"created":11,"draft":12,"id":302,"title":303,"titles":304,"content":305,"level":23},[130,131,132,133],"\u002Fmarkdown-test#math-like-text","Math-Like Text",[163],"Inline math-like text: $E = mc^2$ Block math-like text: $$\nE = mc^2\n$$ This section is here to verify whether math is supported or rendered as plain text.",{"path":127,"category":128,"tags":307,"created":11,"draft":12,"id":308,"title":309,"titles":310,"content":311,"level":23},[130,131,132,133],"\u002Fmarkdown-test#nuxt-content-mdc","Nuxt Content MDC",[163],"The following syntax tests Nuxt Content MDC. Rendering depends on available components. This is an MDC block using the Alert component if it is registered. This alert passes props. This is a raw MDC div container with classes. Inline Badge Card TitleCard content with Markdown inside.",{"path":127,"category":128,"tags":313,"created":11,"draft":12,"id":314,"title":315,"titles":316,"content":317,"level":23},[130,131,132,133],"\u002Fmarkdown-test#long-content","Long Content",[163],"This section intentionally adds repeated paragraphs so scroll behavior, anchors, and table of contents behavior can be tested. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer luctus, justo vitae aliquet porta, lectus augue blandit nisl, vitae tempor ipsum arcu a erat. Praesent commodo, magna vitae ullamcorper tristique, ipsum nibh efficitur justo, id volutpat sem ex nec massa. Curabitur posuere sapien ut ipsum luctus, sed feugiat mi porta. Donec feugiat, magna id dignissim dictum, velit mi rhoncus ipsum, vitae aliquet sapien augue sit amet odio.",{"path":127,"category":128,"tags":319,"created":11,"draft":12,"id":320,"title":321,"titles":322,"content":323,"level":23},[130,131,132,133],"\u002Fmarkdown-test#final-section","Final Section",[163],"If anchor links work correctly, clicking a heading anchor above should not unexpectedly jump here. End of test document. FootnotesThis is the first footnote. ↩This is a longer footnote.\nIt can contain multiple lines when indented. ↩ html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .sHdIc, html code.shiki .sHdIc{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#EEFFFF;--shiki-default-font-style:italic;--shiki-dark:#BABED8;--shiki-dark-font-style:italic}",{"path":325,"category":6,"tags":326,"created":328,"draft":12,"id":325,"title":329,"titles":330,"content":331,"level":16},"\u002Fposts\u002Fabout-site",[9,327,61],"Site","2026-05-27","关于本站",[],"网站的设立初衷、灵感来源、技术实现与未来规划。",{"path":325,"category":6,"tags":333,"created":328,"draft":12,"id":334,"title":335,"titles":336,"content":337,"level":23},[9,327,61],"\u002Fposts\u002Fabout-site#为什么要做个人网站","为什么要做个人网站",[329],"因为想做，所以就做了。 我想有一个能展示自己的平台，而大概没有什么比一个独属于自己的网站更酷了吧。另一方面，我也想把它当作一个练习场，用来尝试和展示我头脑中的一些 UI\u002FUX 设计灵感（不过目前还没放上来）。最后，当然也想通过写博客记录一些想法和生活——虽然不确定能否坚持，但这里总归是一个比社交媒体更自在的地方。",{"path":325,"category":6,"tags":339,"created":328,"draft":12,"id":340,"title":341,"titles":342,"content":343,"level":23},[9,327,61],"\u002Fposts\u002Fabout-site#为什么不用现成的博客框架和模板","为什么不用现成的博客框架和模板",[329],"如前面所说，我主要是想有一个能完全按自己想法设计和实现的空间来展示自己，博客并不是重点。我不想被禁锢在专为博客设计的模式里，也不希望被别人的页面框架拘束手脚。当然，本网站目前的 UI 参考了多个优秀网站的设计，但我可以自由选用和改造这些元素，而不用被某个特定模板束缚。",{"path":325,"category":6,"tags":345,"created":328,"draft":12,"id":346,"title":347,"titles":348,"content":349,"level":23},[9,327,61],"\u002Fposts\u002Fabout-site#参考和借鉴","参考和借鉴",[329],"fuwari：一个超级有影响力的开源博客模板，许多模板或是直接基于fuwari修改，或是参考了fuwari的风格。本站整体的设计和布局都受到了fuwari的启发。astro-koharu：一个astro博客主题，本站页面的信息排版和细节处理有参考该项目，同时本站默认封面图是直接从该项目照搬而来。apple.com：本项目的设计也受到苹果、Openai官网等同类简洁优雅网站的启发。多年来浏览并被潜移默化影响过的诸多站点（如bilibili、知乎）",{"path":325,"category":6,"tags":351,"created":328,"draft":12,"id":352,"title":353,"titles":354,"content":355,"level":23},[9,327,61],"\u002Fposts\u002Fabout-site#现在有什么","现在有什么",[329],"目前本站主要包括这些部分和功能： hero区：一个醒目到有些浮夸的门面，向访客问好：\"Hi, I'm Leisuer\"。文字背景来源于本人所拍摄的照片，有循环位移的动画效果。站点名片和索引markdown及其扩展功能支持文章目录和阅读进度追踪响应式布局和移动端适配：为不同尺寸和比例的设备分别设计布局样式移动端菜单栏：包含了导航栏、站点名片索引和文章目录的功能用以作为替代归档：按时间整理所有文章分类和标签：按主题整理文章收藏：放一些也许会有用的网站友链：保留和其他站点之间的连接搜索：快速找到内容随机推荐i18n：简中、繁中、英文、日语四种语言支持暗黑模式：可以在亮色和暗色主题之间切换，且有丝滑的过渡动画丝滑的过渡动画和细节交互背景的鼠标追踪视差动画背景樱花飘落css动画",{"path":325,"category":6,"tags":357,"created":328,"draft":12,"id":358,"title":359,"titles":360,"content":361,"level":23},[9,327,61],"\u002Fposts\u002Fabout-site#技术实现","技术实现",[329],"本站基于 Nuxt 4 开发，内容部分使用 Nuxt Content 管理。样式方面主要使用 Tailwind CSS v4 和少量按模块拆分的 CSS。 内容检索和整理主要依赖 Nuxt Content 的查询能力。归档按发布时间分组，分类和标签会从文章 frontmatter 中自动统计生成；搜索则基于 Nuxt Content 的搜索 section 数据，在本地对标题、正文、路径、分类和标签做匹配与排序。这样新增文章时，只要 frontmatter 写对，相关页面就会自动更新。",{"path":325,"category":6,"tags":363,"created":328,"draft":12,"id":364,"title":365,"titles":366,"content":367,"level":23},[9,327,61],"\u002Fposts\u002Fabout-site#未来计划","未来计划",[329],"添加页面切换加载动画 持续发文 优化细节 更完美的静态生成或对接后端 文章切换上一篇下一篇 评论区 RSS订阅",{"path":369,"category":370,"tags":372,"created":378,"draft":12,"id":369,"title":379,"titles":380,"content":381,"level":16},"\u002Fposts\u002Fcodex-network-fix",[371],"Tech",[373,374,375,376,377],"网络","DNS","Linux","Tailscale","阿里云","2026-09-30","阿里云服务器codex网络问题修复",[],"完整记录一次修复服务器网络问题的过程，同时也是提供一个tailscale和阿里云VPC内网DNS冲突的解决方案。",{"path":369,"category":383,"tags":384,"created":378,"draft":12,"id":385,"title":386,"titles":387,"content":388,"level":23},[371],[373,374,375,376,377],"\u002Fposts\u002Fcodex-network-fix#前言","前言",[379],"我在服务器配置好 tailscale 后，原本正常工作的 codex cli 突然无法连接到网络。这个时候我还没有意识到是 tailscale 的问题，尽管现在复盘一下看起来好明显（刚刚配置好一个网络相关的服务，网络就突然不通了，还能是什么原因）。但是当时晚上我头脑不是很清醒，已经忘了刚刚配置了 tailscale，甚至还怀疑是中转站或者阿里云的网络问题。于是我开始了漫长的排查过程。 Reconnecting... waiting for network\nConnection failed: error sending request",{"path":369,"category":390,"tags":391,"created":378,"draft":12,"id":392,"title":393,"titles":394,"content":159,"level":23},[371],[373,374,375,376,377],"\u002Fposts\u002Fcodex-network-fix#排查过程","排查过程",[379],{"path":369,"category":396,"tags":397,"created":378,"draft":12,"id":398,"title":399,"titles":400,"content":401,"level":36},[371],[373,374,375,376,377],"\u002Fposts\u002Fcodex-network-fix#找到问题到底出在谁身上","找到问题到底出在谁身上",[379,393],"首先，我先测试了一下使用其他设备访问 api 是否正常，发现仍然可以正常使用。这说明中转站提供的 api 本身应该是能够正常访问的。 然后，我尝试重启 codex，毕竟没有什么方法比重启更高效了。重启若干次后，问题依旧存在。 我开始怀疑可能是服务器网络出现问题了。由于 ssh 连接正常，阿里云控制台的检测数据也没出现什么问题，我决定先绕过 codex，自己 curl 一下 Base URL。 curl -v 中转站的BaseURL 结果： * Could not resolve host: 中转站的BaseURL\n* Closing connection\ncurl: (6) Could not resolve host: 中转站的BaseURL 说明应该确实不是 codex 的问题，请求卡在了 DNS 解析上。于是我手动指定了一个 DNS 服务器： $ nslookup 中转站的BaseURL 223.5.5.5\nServer:         223.5.5.5\nAddress:        223.5.5.5#53\n\nNon-authoritative answer:\nName:   中转站的BaseURL\nAddress: xx.xx.xx.xx 与之对比，若不指定： $ nslookup 中转站的BaseURL\n;; communications error to 127.0.0.53#53: timed out 看一下系统当前的 DNS 配置： $ cat \u002Fetc\u002Fresolv.conf\n# 此处省略若干行\nnameserver 127.0.0.53\noptions edns0 trust-ad\nsearch taile15594.ts.net Ubuntu 上 127.0.0.53 是 systemd-resolved 的本地 DNS Stub，我接着检查服务： systemctl status systemd-resolved --no-pager Active: active (running)\nStatus: \"Processing requests...\" 再看看 53 端口： $ sudo ss -luntp | grep ':53'\nudp   UNCONN 0      0                       127.0.0.54:53         0.0.0.0:*    users:((\"systemd-resolve\",pid=495,fd=16))                                   \nudp   UNCONN 0      0                    127.0.0.53%lo:53         0.0.0.0:*    users:((\"systemd-resolve\",pid=495,fd=14))                                   \ntcp   LISTEN 0      4096                    127.0.0.54:53         0.0.0.0:*    users:((\"systemd-resolve\",pid=495,fd=17))                                   \ntcp   LISTEN 0      4096                 127.0.0.53%lo:53         0.0.0.0:*    users:((\"systemd-resolve\",pid=495,fd=15)) 也就是说： systemd-resolved 没死；53 端口也正常监听；但是 DNS 查询就是没有结果。 我终于决定看看是不是 tailscale 的问题，于是我继续使用重启大法，结果没有任何效果。我只能先试着停掉 tailscale 服务： sudo systemctl stop tailscaled\nsudo systemctl restart systemd-resolved 此时，我再次尝试 curl Base URL，发现可以正常访问了。至此，我终于确认问题出在 tailscale 上。",{"path":369,"category":403,"tags":404,"created":378,"draft":12,"id":405,"title":406,"titles":407,"content":408,"level":36},[371],[373,374,375,376,377],"\u002Fposts\u002Fcodex-network-fix#找到问题的根源","找到问题的根源",[379,393],"resolvectl status eth0 的 DNS 是： 100.100.2.136\n100.100.2.138 这正是阿里云 ECS 默认的 VPC 内网 DNS。问题是 Tailscale 使用 100.64.0.0\u002F10 作为自己的 IPv4 CGNAT 地址池。真正致命的是：Linux 上 Tailscale 默认会加一条防火墙规则，大意是： 如果数据包源地址属于 100.64.0.0\u002F10\n但却不是从 tailscale0 接口进来的\n→ DROP 这是 tailscale 防止别人伪造 tailscale IP 的安全机制，官方文档也明确说明了这一点。 所以这一切就很好理解了，这是 tailscale 和 阿里云 VPC 内网 DNS 冲突导致的乌龙。",{"path":369,"category":410,"tags":411,"created":378,"draft":12,"id":412,"title":413,"titles":414,"content":415,"level":23},[371],[373,374,375,376,377],"\u002Fposts\u002Fcodex-network-fix#解决","解决",[379],"知道原因之后，这个问题并不难解决。我这台机器并不需要依赖阿里云的 PrivateZone 等 VPC 私有 DNS 功能，所以最直接的办法就是不再使用 100.100.2.136\u002F138 作为系统 DNS。 我的服务器使用 Netplan，因此单独增加一个配置： network:\n  version: 2\n  ethernets:\n    eth0:\n      dhcp4-overrides:\n        use-dns: false\n      nameservers:\n        addresses:\n          - 223.5.5.5\n          - 223.6.6.6 随后 sudo chmod 600 \u002Fetc\u002Fnetplan\u002F99-custom-dns.yaml\nsudo netplan generate\nsudo netplan try 值得注意的是这里并不是禁用 DHCP，只是不接受 DHCP 下发的 DNS。IP 地址和网关这些仍然照常通过 DHCP 获取。因为是在远程服务器上操作网络配置，我没有直接 netplan apply，这样即使配置有问题导致 SSH 断开，没有手动确认的话也会自动回滚。 最后重新测试，全部恢复正常。",{"path":369,"category":417,"tags":418,"created":378,"draft":12,"id":419,"title":420,"titles":421,"content":422,"level":23},[371],[373,374,375,376,377],"\u002Fposts\u002Fcodex-network-fix#后记","后记",[379],"感觉我经常被一个简单的问题缠上好久，然后发现成因其实很简单。还好有 ai 大人帮我一起排查，要是在前 llm 时代，我估计我可能要抓狂（这次还好，第二天仔细回忆一下不难通过问题产生的时间节点反推出是 tailscale 导致的）。希望这篇文章能帮到遇到类似问题的人。",{"path":369,"category":424,"tags":425,"created":378,"draft":12,"id":426,"title":427,"titles":428,"content":429,"level":36},[371],[373,374,375,376,377],"\u002Fposts\u002Fcodex-network-fix#本方案的局限性","本方案的局限性",[379,420],"这里采用的方案是直接将系统 DNS 从阿里云默认的 100.100.2.136\u002F138 换成公共 DNS，优点是简单直接，但也有一个明显的限制：如果机器需要使用 PrivateZone、ECS 私网域名等依赖阿里云内网 DNS 的功能，这些解析可能会失效。 更完整的做法应该是保留阿里云默认 DNS，同时针对 tailscale 的 100.64.0.0\u002F10 防火墙规则做例外处理。只是对于单纯跑公网服务、API 和 tailscale 组网的机器来说，直接更换 DNS 已经足够省事。 html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}",{"path":431,"category":61,"tags":432,"created":436,"draft":12,"id":431,"title":437,"titles":438,"content":439,"level":16},"\u002Fposts\u002Fcover-card-demo",[433,434,435],"Cover","Image","Visual","2026-05-10","带封面的文章卡片示例",[],"这篇用来测试文章封面、摘要和底部标签同时出现时的整体节奏。 封面可以让文章列表更有层次，但也会压缩文字区域的横向空间。因此这篇文章用来确认有封面时，元信息、标题和摘要是否仍然清晰。",{"path":431,"category":61,"tags":441,"created":436,"draft":12,"id":442,"title":443,"titles":444,"content":445,"level":23},[433,434,435],"\u002Fposts\u002Fcover-card-demo#封面","封面",[437],"之后可以为真实文章挑选更贴合内容的封面图。",{"path":447,"category":448,"tags":451,"created":456,"draft":12,"id":447,"title":457,"titles":458,"content":459,"level":16},"\u002Fposts\u002Fcpp-learning\u002Fcpp-1",[449,450],"Notes","C++复健笔记",[452,453,454,455],"C++","算法","机考","学习笔记","2026-05-28","C++复健笔记 Vol.1：重新开始手写代码",[],"AI很好用，但机考不会替我敲键盘。这个系列用来记录我在夏令营机考前重新找回C++手感的过程。",{"path":447,"category":461,"tags":462,"created":456,"draft":12,"id":463,"title":386,"titles":464,"content":465,"level":23},[449,450],[452,453,454,455],"\u002Fposts\u002Fcpp-learning\u002Fcpp-1#前言",[457],"由于现在 AI 发展迅速（AI 太好用了你们知道吗），我已经很少手写代码了。再加上大学程序设计课程学的是 C 语言，我现在可能实际上不怎么能手写 C++ 代码。 然而夏令营机考将至，本人不得不复习一下如何写 C++ 代码。程序员一定要会写程序。 所以，我打算写一系列文章，记录一下我复健的过程。",{"path":447,"category":467,"tags":468,"created":456,"draft":12,"id":469,"title":470,"titles":471,"content":472,"level":23},[449,450],[452,453,454,455],"\u002Fposts\u002Fcpp-learning\u002Fcpp-1#这个系列打算写什么","这个系列打算写什么",[457],"这个系列不是系统的 C++ 教程，也不是算法竞赛从入门到入土。它就只是一份复健日志：记录我从“知道应该怎么写”重新回到“手能自然写出来”的过程。 我希望每篇文章都解决一个很小的问题，比如： 常用输入输出和模板怎么写；vector、string、map、set 这些 STL 容器怎么快速上手；排序、二分、前缀和、双指针这些常见套路怎么写；BFS、DFS、最短路、并查集这些基础算法怎么恢复肌肉记忆；机考时怎样减少低级错误。 目标不是写出多么优雅的工程代码，而是在有限时间里写出正确、清楚、能通过样例和测试点的程序。",{"path":447,"category":474,"tags":475,"created":456,"draft":12,"id":476,"title":477,"titles":478,"content":479,"level":23},[449,450],[452,453,454,455],"\u002Fposts\u002Fcpp-learning\u002Fcpp-1#第一件事你好世界","第一件事：你好，世界",[457],"我知道你很急，但是你先别急。就像是学编程语言第一件事是写“Hello, World!”一样，复健当然也可以从这里开始。 #include \u003Ciostream>\nusing namespace std;\n\nint main() {\n    cout \u003C\u003C \"Hello, World!\" \u003C\u003C endl;\n    return 0;\n} 不过机考时，可以用下面这个版本： #include \u003Cbits\u002Fstdc++.h>\nusing namespace std;\n\nint main() {\n    ios::sync_with_stdio(false);\n    cin.tie(nullptr);\n    cout \u003C\u003C \"Hello, World!\" \u003C\u003C endl;\n    return 0;\n} #include \u003Cbits\u002Fstdc++.h> 是算法题里常见的“全家桶”头文件，能省掉很多按需引入标准库的时间；ios::sync_with_stdio(false); 和 cin.tie(nullptr); 用来加速输入输出。我在练习时尽量不用这个写法，以免忘记需要 #include 什么头文件。",{"path":447,"category":481,"tags":482,"created":456,"draft":12,"id":483,"title":484,"titles":485,"content":486,"level":23},[449,450],[452,453,454,455],"\u002Fposts\u002Fcpp-learning\u002Fcpp-1#顺手写一个小题","顺手写一个小题",[457],"为了让手动起来，先写一个最普通的问题：读入 n 个整数，输出它们的和、最大值和最小值。 #include \u003Ciostream>\n#include \u003Cvector>\nusing namespace std;\n\nint main(){\n    int n;\n    cin >> n;\n    vector\u003Cint> arr(n);\n    for(int i = 0; i \u003C n; i++){\n        cin >> arr[i];\n    }\n    long long sum = 0;\n    int min = arr[0];\n    int max = arr[0];\n    for(int i = 0; i \u003C n; i++){\n        sum += arr[i];\n        if(arr[i] \u003C min){\n            min = arr[i];\n        }\n        if(arr[i] > max){\n            max = arr[i];\n        }\n    }\n    cout \u003C\u003C \"Sum: \" \u003C\u003C sum \u003C\u003C '\\n';\n    cout \u003C\u003C \"Min: \" \u003C\u003C min \u003C\u003C '\\n';\n    cout \u003C\u003C \"Max: \" \u003C\u003C max \u003C\u003C '\\n';\n    return 0;\n} 这题本身当然不难，但它可以提醒我几件事： 求和最好用 long long，避免整数范围不够。初始化最大值和最小值时，可以直接用第一个元素。输出换行用 '\\n'，不要在大量输出里滥用 endl。能用范围 for 的时候，代码会清爽很多。",{"path":447,"category":488,"tags":489,"created":456,"draft":12,"id":490,"title":491,"titles":492,"content":493,"level":23},[449,450],[452,453,454,455],"\u002Fposts\u002Fcpp-learning\u002Fcpp-1#下一步","下一步",[457],"下一篇大概会从熟悉指针和引用开始。 希望这个系列写到后面，我能真正摆脱AI大人的喂饭，学会自己走路。加油！ html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}",{"path":495,"category":496,"tags":497,"created":498,"draft":12,"id":495,"title":499,"titles":500,"content":501,"level":16},"\u002Fposts\u002Fcpp-learning\u002Fcpp-2",[449,450],[452,455],"2026-05-29","C++复健笔记 Vol.2：指针和引用",[],"从 C 的指针语义过渡到 C++ 引用语义，顺手复习空指针、野指针、数组退化、const 引用和右值引用。 书接上回，在写了两个非常简单的宝宝巴士程序后，今天来点儿幼儿园巴士：指针和引用。",{"path":495,"category":503,"tags":504,"created":498,"draft":12,"id":505,"title":506,"titles":507,"content":508,"level":23},[449,450],[452,455],"\u002Fposts\u002Fcpp-learning\u002Fcpp-2#地址与指针","地址与指针",[499],"先从取地址开始。 #include \u003Ciostream>\nusing namespace std;\n\nint main() {\n    int x = 42;\n\n    cout \u003C\u003C x \u003C\u003C '\\n';\n    cout \u003C\u003C &x \u003C\u003C '\\n';\n\n    return 0;\n} &x 取到的是 x 的地址。指针变量可以保存这个地址，再通过解引用访问它指向的对象。 int x = 42;\nint* p = &x;\n\n*p = 100; \u002F\u002F x == 100 这里几个符号的含义是： &x：取 x 的地址。int* p：p 是指向 int 的指针。*p：访问 p 指向的对象。p + 1：移动到下一个 int 的位置，不是地址数值简单加 1。 函数参数如果按值传递，只会修改副本；如果传地址，就能修改外部对象。 void inc(int* p) {\n    (*p)++;\n}\n\nint x = 0;\ninc(&x); \u002F\u002F x == 1 这里必须写 (*p)++。",{"path":495,"category":510,"tags":511,"created":498,"draft":12,"id":512,"title":513,"titles":514,"content":515,"level":36},[449,450],[452,455],"\u002Fposts\u002Fcpp-learning\u002Fcpp-2#如果写成-p","如果写成 *p++",[499,506],"后置 ++ 的优先级比解引用 * 高。 它做的是： 先取当前 p 指向的值然后让指针 p 自己往后移动一个 int 的位置但取出来的那个值没有被修改 几个容易混的式子： (*p)++; \u002F\u002F 指向的值后置自增\n++*p;   \u002F\u002F 指向的值前置自增，等价于 ++(*p)\n*p++;   \u002F\u002F 取当前值，然后 p 后移，等价于 *(p++)\n*++p;   \u002F\u002F p 先后移，再取新位置的值",{"path":495,"category":517,"tags":518,"created":498,"draft":12,"id":519,"title":520,"titles":521,"content":522,"level":23},[449,450],[452,455],"\u002Fposts\u002Fcpp-learning\u002Fcpp-2#空指针和野指针","空指针和野指针",[499],"现代 C++ 用 nullptr 表示空指针。 int* p = nullptr;\n\nif (p != nullptr) {\n    cout \u003C\u003C *p \u003C\u003C '\\n';\n} 空指针可以比较，不能解引用。 野指针一般来自两类情况。第一种是未初始化： int* p;     \u002F\u002F 未初始化\n*p = 10;    \u002F\u002F UB 第二种是指向已经失效的位置，比如返回局部变量地址： int* bad() {\n    int x = 10;\n    return &x; \u002F\u002F 返回局部变量地址\n} 指针声明时初始化，不知道指向谁就设为 nullptr。解引用前确认指针有效。不返回局部变量地址。能用引用或容器时，不为了“像 C”而手写裸指针。",{"path":495,"category":524,"tags":525,"created":498,"draft":12,"id":526,"title":527,"titles":528,"content":529,"level":23},[449,450],[452,455],"\u002Fposts\u002Fcpp-learning\u002Fcpp-2#数组与指针","数组与指针",[499],"数组名在很多表达式里会退化为指向首元素的指针。 int a[3] = {10, 20, 30};\n\ncout \u003C\u003C *a \u003C\u003C '\\n';       \u002F\u002F 10\ncout \u003C\u003C *(a + 1) \u003C\u003C '\\n'; \u002F\u002F 20\ncout \u003C\u003C a[2] \u003C\u003C '\\n';     \u002F\u002F 30 a[i] 可以理解成 *(a + i)。但数组不是指针本身： int a[3] = {10, 20, 30};\nint* p = a;\n\ncout \u003C\u003C sizeof(a) \u003C\u003C '\\n'; \u002F\u002F 3 * sizeof(int)\ncout \u003C\u003C sizeof(p) \u003C\u003C '\\n'; \u002F\u002F sizeof(int*) 函数参数里的数组比较特殊： void f(int a[]) {\n    cout \u003C\u003C sizeof(a) \u003C\u003C '\\n'; \u002F\u002F 这里 a 已经是 int*\n} 也就是说，数组作为函数参数时会退化成指针，长度信息会丢掉。机考里如果没有特殊需求，我还是优先用 vector，省掉手动传长度和越界风险。",{"path":495,"category":531,"tags":532,"created":498,"draft":12,"id":533,"title":534,"titles":535,"content":536,"level":36},[449,450],[452,455],"\u002Fposts\u002Fcpp-learning\u002Fcpp-2#指针数组和数组指针","指针数组和数组指针",[499,527],"非常适合拿来折磨人。 指针数组：本质是数组，数组里的每个元素都是指针。 int a = 1;\nint b = 2;\nint c = 3;\n\nint* p[3] = {&a, &b, &c};\n\ncout \u003C\u003C *p[0] \u003C\u003C '\\n'; \u002F\u002F 1 p 先和 [3] 结合，所以 p 是一个数组；数组元素类型是 int*。 数组指针：本质是指针，指向一个数组。 int arr[3] = {1, 2, 3};\n\nint (*p)[3] = &arr;\n\ncout \u003C\u003C (*p)[0] \u003C\u003C '\\n'; \u002F\u002F 1\ncout \u003C\u003C (*p)[2] \u003C\u003C '\\n'; \u002F\u002F 3 这里括号不能省。如果写成： int* p[3]; 那就是指针数组；写成： int (*p)[3]; 才是数组指针。 数组指针如果作为函数返回值，声明会更难认： int (*getArray())[3] {\n    static int arr[3] = {1, 2, 3};\n    return &arr;\n} 这里 getArray 是一个函数，它返回 int (*)[3]，也就是“指向含 3 个 int 的数组的指针”。调用时可以这样写： int (*p)[3] = getArray();\n\ncout \u003C\u003C (*p)[0] \u003C\u003C '\\n'; 这种声明可读性比较差。实际写 C++ 时可以用 using 起别名： using IntArray3 = int[3];\n\nIntArray3* getArray() {\n    static int arr[3] = {1, 2, 3};\n    return &arr;\n} 这样就清楚很多：getArray 返回的是 IntArray3*。",{"path":495,"category":538,"tags":539,"created":498,"draft":12,"id":540,"title":541,"titles":542,"content":543,"level":23},[449,450],[452,455],"\u002Fposts\u002Fcpp-learning\u002Fcpp-2#引用","引用",[499],"引用可以理解成对象的别名。它声明时必须绑定，之后不能改绑。 int x = 42;\nint& r = x;\n\nr = 100; \u002F\u002F x == 100 引用传参可以替代很多“传指针进去改外部变量”的写法： void inc(int& x) {\n    x++;\n}\n\nint a = 0;\ninc(a); \u002F\u002F a == 1 和指针传参对比： void inc1(int* p) { (*p)++; }\nvoid inc2(int& x) { x++; }\n\ninc1(&a);\ninc2(a); 引用版本更像普通变量，调用点也更干净；指针版本则能显式表达“可能为空”“可能改指向”等语义。机考里的普通修改参数，引用通常更顺。 作为练习，我手写了两个版本的 swap。 引用版本： void swap(int& a, int& b) {\n    int t = a;\n    a = b;\n    b = t;\n} 指针版本： void swap(int* a, int* b) {\n    int t = *a;\n    *a = *b;\n    *b = t;\n} 调用点区别也很清楚： int x = 1;\nint y = 2;\n\nswap(x, y);   \u002F\u002F 引用版本\nswap(&x, &y); \u002F\u002F 指针版本 当然真正写题时直接用标准库： swap(a, b);",{"path":495,"category":545,"tags":546,"created":498,"draft":12,"id":547,"title":548,"titles":549,"content":550,"level":23},[449,450],[452,455],"\u002Fposts\u002Fcpp-learning\u002Fcpp-2#范围-for-里的引用","范围 for 里的引用",[499],"范围 for 默认按值拷贝。 vector\u003Cint> a = {1, 2, 3};\n\nfor (int x : a) {\n    x *= 2;\n}\n\n\u002F\u002F a 仍然是 {1, 2, 3} 要修改原容器元素： for (int& x : a) {\n    x *= 2;\n} 只读且避免拷贝： for (const string& s : names) {\n    cout \u003C\u003C s \u003C\u003C '\\n';\n} 这里的判断可以简单一点： 小类型只读：int x需要修改：T& x大对象只读：const T& x",{"path":495,"category":552,"tags":553,"created":498,"draft":12,"id":554,"title":555,"titles":556,"content":557,"level":23},[449,450],[452,455],"\u002Fposts\u002Fcpp-learning\u002Fcpp-2#const-引用","const 引用",[499],"const T& 是 C++ 函数参数里非常常见的写法：避免拷贝，同时承诺不修改。 long long sum(const vector\u003Cint>& a) {\n    long long ans = 0;\n    for (int x : a) {\n        ans += x;\n    }\n    return ans;\n} 如果写成： long long sum(vector\u003Cint> a) 会复制整个 vector。数据量一大，这种拷贝没有必要。 const 和指针放在一起时也需要恢复一下： const int* p1;       \u002F\u002F 指向 const int，不能通过 p1 改值，p1 可改指向\nint* const p2 = &x;  \u002F\u002F const 指针，p2 不可改指向，可通过 p2 改值\nconst int* const p3 = &x; \u002F\u002F 值和指向都不能改 读法技巧：从变量名开始往外读。p2 先遇到 const，说明指针本身 const；p1 指向的是 const int。",{"path":495,"category":559,"tags":560,"created":498,"draft":12,"id":561,"title":562,"titles":563,"content":564,"level":23},[449,450],[452,455],"\u002Fposts\u002Fcpp-learning\u002Fcpp-2#左值引用和右值引用","左值引用和右值引用",[499],"普通引用 T& 是左值引用，通常绑定到有名字的对象。 int x = 10;\nint& r = x; 非常量左值引用不能绑定临时值： int& r = 10; \u002F\u002F error 但 const T& 可以绑定临时值： const int& r = 10; 右值引用写作 T&&： int&& r = 10; 它主要服务于移动语义和完美转发。机考里通常不需要手写复杂右值引用，但至少要能区分： if (a && b) { } \u002F\u002F 逻辑与\n\nint&& x = 10;  \u002F\u002F 右值引用 同样是 &&，一个在表达式里，一个在类型里。",{"path":495,"category":566,"tags":567,"created":498,"draft":12,"id":568,"title":569,"titles":570,"content":571,"level":23},[449,450],[452,455],"\u002Fposts\u002Fcpp-learning\u002Fcpp-2#引用折叠","引用折叠",[499],"引用折叠主要在模板里出现。 template \u003Ctypename T>\nvoid f(T&& x) {\n} 这里的 T&& 在模板推导中不一定是右值引用。折叠规则： &  + &  -> &\n&  + && -> &\n&& + &  -> &\n&& + && -> && 记忆：只要有一个 &，结果就是 &；只有全是 &&，结果才是 &&。 这就是所谓 forwarding reference \u002F universal reference 的基础。现阶段先能看懂，不展开 std::forward。",{"path":495,"category":573,"tags":574,"created":498,"draft":12,"id":575,"title":576,"titles":577,"content":578,"level":23},[449,450],[452,455],"\u002Fposts\u002Fcpp-learning\u002Fcpp-2#小结","小结",[499],"指针：地址对象，核心操作是取地址和解引用。空指针：nullptr，能比较，不能解引用。野指针：未初始化、悬垂地址、生命周期结束后继续使用。数组：表达式中常退化成首元素指针，但数组本身不是指针。引用：对象别名，声明时绑定，不能改绑。引用传参：修改外部对象时比指针更顺手。const T&：避免拷贝且只读，传大对象时常用。T&&：右值引用，先认识语法和语境。引用折叠：模板转发相关，先记规则。 html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html pre.shiki code .sHdIc, html code.shiki .sHdIc{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#EEFFFF;--shiki-default-font-style:italic;--shiki-dark:#BABED8;--shiki-dark-font-style:italic}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}",{"path":580,"category":581,"tags":582,"created":584,"draft":12,"id":580,"title":585,"titles":586,"content":587,"level":16},"\u002Fposts\u002Fcpp-learning\u002Fcpp-3",[449,450],[452,583,455],"STL","2026-05-30","C++复健笔记 Vol.3：vector 和 string",[],"复习机考里最常用的两个 STL 类型：动态数组 vector 和字符串 string。 C 语言里写数组和字符串，总要操心长度、边界和结尾的 \\0。C++ 里有 vector 和 string，很多时候会舒服不少。当然，舒服不代表不用脑子，尤其是下标、迭代器失效、输入整行字符串这些地方，还是很容易写出一些自信但错误的代码。",{"path":580,"category":589,"tags":590,"created":584,"draft":12,"id":591,"title":592,"titles":593,"content":594,"level":23},[449,450],[452,583,455],"\u002Fposts\u002Fcpp-learning\u002Fcpp-3#vector动态数组","vector：动态数组",[585],"vector 可以理解成长度可变的数组。 #include \u003Ciostream>\n#include \u003Cvector>\nusing namespace std;\n\nint main() {\n    vector\u003Cint> a;\n\n    a.push_back(10);\n    a.push_back(20);\n    a.push_back(30);\n\n    cout \u003C\u003C a.size() \u003C\u003C '\\n';\n    cout \u003C\u003C a[0] \u003C\u003C ' ' \u003C\u003C a[1] \u003C\u003C ' ' \u003C\u003C a[2] \u003C\u003C '\\n';\n\n    return 0;\n} 常用操作先列一下： vector\u003Cint> a;：空 vector。vector\u003Cint> a(n);：长度为 n，元素默认初始化。vector\u003Cint> a(n, x);：长度为 n，每个元素初始化为 x。a.size()：元素个数。a.empty()：是否为空。a.push_back(x)：末尾加入元素。a.pop_back()：删除末尾元素。a.back()：最后一个元素。a.clear()：清空。 读入 n 个数时，最常见写法： int n;\ncin >> n;\n\nvector\u003Cint> a(n);\nfor (int i = 0; i \u003C n; i++) {\n    cin >> a[i];\n} 如果不知道数量，或者边读边加： vector\u003Cint> a;\nint x;\n\nwhile (cin >> x) {\n    a.push_back(x);\n}",{"path":580,"category":596,"tags":597,"created":584,"draft":12,"id":598,"title":599,"titles":600,"content":601,"level":23},[449,450],[452,583,455],"\u002Fposts\u002Fcpp-learning\u002Fcpp-3#遍历-vector","遍历 vector",[585],"需要下标时，用普通 for： for (int i = 0; i \u003C (int)a.size(); i++) {\n    cout \u003C\u003C i \u003C\u003C \": \" \u003C\u003C a[i] \u003C\u003C '\\n';\n} 不需要下标时，用范围 for： for (int x : a) {\n    cout \u003C\u003C x \u003C\u003C '\\n';\n} 需要修改元素时，用引用： for (int& x : a) {\n    x *= 2;\n} 大对象只读时，用 const 引用： for (const string& s : names) {\n    cout \u003C\u003C s \u003C\u003C '\\n';\n} 这里有一个小习惯：a.size() 的类型是 size_t，是无符号整数。机考里为了少和 signed \u002F unsigned warning 纠缠，我通常会写： for (int i = 0; i \u003C (int)a.size(); i++) {\n} 或者干脆提前存下来： int n = a.size();\nfor (int i = 0; i \u003C n; i++) {\n}",{"path":580,"category":603,"tags":604,"created":584,"draft":12,"id":605,"title":606,"titles":607,"content":608,"level":23},[449,450],[452,583,455],"\u002Fposts\u002Fcpp-learning\u002Fcpp-3#排序和查找","排序和查找",[585],"排序需要 \u003Calgorithm>。 #include \u003Calgorithm> 升序： sort(a.begin(), a.end()); 降序： sort(a.begin(), a.end(), greater\u003Cint>()); 自定义排序： vector\u003Cpair\u003Cint, int>> a = {{1, 3}, {1, 2}, {2, 1}};\n\nsort(a.begin(), a.end(), [](const auto& x, const auto& y) {\n    if (x.first != y.first) return x.first \u003C y.first;\n    return x.second > y.second;\n}); 这里 lambda 的意思是：先按第一维升序，第一维相同再按第二维降序。 查找某个值： auto it = find(a.begin(), a.end(), x);\n\nif (it != a.end()) {\n    cout \u003C\u003C \"found\" \u003C\u003C '\\n';\n} 如果 vector 已经有序，可以二分： bool ok = binary_search(a.begin(), a.end(), x); 找第一个大于等于 x 的位置： auto it = lower_bound(a.begin(), a.end(), x);\nint pos = it - a.begin(); 这部分后面二分专题再细写，现在先恢复 API。",{"path":580,"category":610,"tags":611,"created":584,"draft":12,"id":612,"title":613,"titles":614,"content":615,"level":23},[449,450],[452,583,455],"\u002Fposts\u002Fcpp-learning\u002Fcpp-3#二维-vector","二维 vector",[585],"二维数组可以用二维 vector： int n, m;\ncin >> n >> m;\n\nvector\u003Cvector\u003Cint>> grid(n, vector\u003Cint>(m));\n\nfor (int i = 0; i \u003C n; i++) {\n    for (int j = 0; j \u003C m; j++) {\n        cin >> grid[i][j];\n    }\n} 初始化为某个值： vector\u003Cvector\u003Cint>> dist(n, vector\u003Cint>(m, -1)); 方向数组也很常见： int dx[4] = {1, -1, 0, 0};\nint dy[4] = {0, 0, 1, -1};\n\nfor (int k = 0; k \u003C 4; k++) {\n    int nx = x + dx[k];\n    int ny = y + dy[k];\n} 二维 vector 写起来方便，但每一行本质上是单独的 vector，不是一整块连续的二维数组。一般机考不太需要关心这个，知道即可。",{"path":580,"category":617,"tags":618,"created":584,"draft":12,"id":619,"title":620,"titles":621,"content":622,"level":23},[449,450],[452,583,455],"\u002Fposts\u002Fcpp-learning\u002Fcpp-3#vector-的几个坑","vector 的几个坑",[585],"第一，[] 不检查越界。 vector\u003Cint> a(3);\ncout \u003C\u003C a[10] \u003C\u003C '\\n'; \u002F\u002F UB 如果想检查越界，可以用： a.at(10) 第二，空 vector 不能直接 back() 或 pop_back()。 if (!a.empty()) {\n    a.pop_back();\n} 第三，遍历时修改 vector 大小要小心。 for (int i = 0; i \u003C (int)a.size(); i++) {\n    if (a[i] == 0) {\n        a.push_back(1);\n    }\n} 这种代码可能导致循环范围不断变化。不是一定错，但要明确自己在干什么。",{"path":580,"category":624,"tags":625,"created":584,"draft":12,"id":626,"title":627,"titles":628,"content":629,"level":23},[449,450],[452,583,455],"\u002Fposts\u002Fcpp-learning\u002Fcpp-3#string更好用的字符数组","string：更好用的字符数组",[585],"string 是 C++ 里的字符串类型。 #include \u003Ciostream>\n#include \u003Cstring>\nusing namespace std;\n\nint main() {\n    string s = \"hello\";\n\n    cout \u003C\u003C s.size() \u003C\u003C '\\n';\n    cout \u003C\u003C s[0] \u003C\u003C '\\n';\n\n    return 0;\n} 常用操作： s.size()：长度。s.empty()：是否为空。s[i]：第 i 个字符。s.push_back(c)：末尾加字符。s += t：拼接字符串。s.substr(pos, len)：截取子串。s.find(t)：查找子串。 例子： string s = \"abcdef\";\n\ncout \u003C\u003C s.substr(2, 3) \u003C\u003C '\\n'; \u002F\u002F cde\ncout \u003C\u003C s.find(\"cd\") \u003C\u003C '\\n';   \u002F\u002F 2 find 找不到时会返回 string::npos： if (s.find(\"xy\") == string::npos) {\n    cout \u003C\u003C \"not found\" \u003C\u003C '\\n';\n}",{"path":580,"category":631,"tags":632,"created":584,"draft":12,"id":633,"title":634,"titles":635,"content":636,"level":23},[449,450],[452,583,455],"\u002Fposts\u002Fcpp-learning\u002Fcpp-3#cin-和-getline","cin 和 getline",[585],"cin >> s 会读到空白字符为止： string s;\ncin >> s; 输入： hello world 这里只会读到 hello。 如果要读整行，用 getline： string line;\ngetline(cin, line); 但如果前面刚用过 cin >> n，换行符还留在输入缓冲区里，直接 getline 可能读到空行。 int n;\ncin >> n;\n\nstring line;\ngetline(cin, line); \u002F\u002F 可能读到上一行剩下的换行 常用处理： int n;\ncin >> n;\ncin.ignore();\n\nstring line;\ngetline(cin, line); 如果要更稳一点，可以写： #include \u003Climits>\ncin.ignore(numeric_limits\u003Cstreamsize>::max(), '\\n');",{"path":580,"category":638,"tags":639,"created":584,"draft":12,"id":640,"title":641,"titles":642,"content":643,"level":23},[449,450],[452,583,455],"\u002Fposts\u002Fcpp-learning\u002Fcpp-3#字符串遍历和修改","字符串遍历和修改",[585],"遍历字符： for (char c : s) {\n    cout \u003C\u003C c \u003C\u003C '\\n';\n} 修改字符： for (char& c : s) {\n    if ('a' \u003C= c && c \u003C= 'z') {\n        c = c - 'a' + 'A';\n    }\n} 也可以用 \u003Ccctype>： #include \u003Ccctype>\n\nfor (char& c : s) {\n    c = toupper(c);\n} 注意 toupper 返回的是 int，赋回 char 一般没问题，但要知道它不是直接返回 char。",{"path":580,"category":645,"tags":646,"created":584,"draft":12,"id":647,"title":648,"titles":649,"content":650,"level":23},[449,450],[452,583,455],"\u002Fposts\u002Fcpp-learning\u002Fcpp-3#小练习统计单词长度","小练习：统计单词长度",[585],"写一个简单练习：读入一整行，按空格切分，输出每个单词的长度。 #include \u003Ciostream>\n#include \u003Csstream>\n#include \u003Cstring>\n#include \u003Cvector>\nusing namespace std;\n\nint main() {\n    string line;\n    getline(cin, line);\n\n    stringstream ss(line);\n    string word;\n    vector\u003Cint> lengths;\n\n    while (ss >> word) {\n        lengths.push_back(word.size());\n    }\n\n    for (int x : lengths) {\n        cout \u003C\u003C x \u003C\u003C ' ';\n    }\n    cout \u003C\u003C '\\n';\n\n    return 0;\n} 这里顺手用到了 stringstream，它可以把一个字符串当成输入流来读。以后处理“一行里有多个元素”的输入时会很方便。",{"path":580,"category":652,"tags":653,"created":584,"draft":12,"id":654,"title":576,"titles":655,"content":656,"level":23},[449,450],[452,583,455],"\u002Fposts\u002Fcpp-learning\u002Fcpp-3#小结",[585],"vector：动态数组，重点是初始化、遍历、排序、二维写法和边界。string：字符串，重点是 cin \u002F getline、子串、查找和逐字符处理。范围 for 默认拷贝，需要修改时写引用。大对象只读时用 const T&。sort、find、lower_bound 这些 API 要写熟。 html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .sHdIc, html code.shiki .sHdIc{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#EEFFFF;--shiki-default-font-style:italic;--shiki-dark:#BABED8;--shiki-dark-font-style:italic}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}",{"path":658,"category":659,"tags":660,"created":661,"draft":12,"id":658,"title":662,"titles":663,"content":664,"level":16},"\u002Fposts\u002Fcpp-learning\u002Fcpp-4",[449,450],[452,583,455],"2026-06-13","C++复健笔记 Vol.4：map 和 set",[],"复习常用的关联容器：用 set 去重和维护有序集合，用 map 统计、映射和查找。 上一篇复习了 vector 和 string，它们更像是“按位置访问”的容器。这一篇补两个很常见的关联容器：set 和 map。 简单说： set：存一堆不重复的元素，并且自动有序。map：存键值对，也就是从 key 映射到 value，并且按 key 自动有序。 机考里遇到“去重”“统计次数”“名字对应分数”“查某个东西是否出现过”之类的需求，经常就会用到它们。",{"path":658,"category":666,"tags":667,"created":661,"draft":12,"id":668,"title":669,"titles":670,"content":671,"level":23},[449,450],[452,583,455],"\u002Fposts\u002Fcpp-learning\u002Fcpp-4#set有序去重集合","set：有序去重集合",[662],"set 需要头文件： #include \u003Cset> 基本用法： #include \u003Ciostream>\n#include \u003Cset>\nusing namespace std;\n\nint main() {\n    set\u003Cint> s;\n\n    s.insert(3);\n    s.insert(1);\n    s.insert(2);\n    s.insert(3);\n\n    for (int x : s) {\n        cout \u003C\u003C x \u003C\u003C ' ';\n    }\n    cout \u003C\u003C '\\n';\n\n    return 0;\n} 输出是： 1 2 3 注意两个特点： 重复插入的 3 只会保留一个。遍历时是升序。 常用操作： s.insert(x)：插入元素。s.erase(x)：删除值为 x 的元素。s.count(x)：判断 x 是否存在，返回 0 或 1。s.find(x)：查找 x，返回迭代器。s.size()：元素个数。s.empty()：是否为空。s.clear()：清空。 判断一个数是否出现过： if (s.count(x)) {\n    cout \u003C\u003C \"yes\" \u003C\u003C '\\n';\n} else {\n    cout \u003C\u003C \"no\" \u003C\u003C '\\n';\n} 也可以用 find： auto it = s.find(x);\nif (it != s.end()) {\n    cout \u003C\u003C \"found\" \u003C\u003C '\\n';\n}",{"path":658,"category":673,"tags":674,"created":661,"draft":12,"id":675,"title":676,"titles":677,"content":678,"level":23},[449,450],[452,583,455],"\u002Fposts\u002Fcpp-learning\u002Fcpp-4#lower_bound-和-upper_bound","lower_bound 和 upper_bound",[662],"set 自己也有 lower_bound 和 upper_bound。 auto it = s.lower_bound(x); lower_bound(x) 找第一个大于等于 x 的元素。 auto it = s.upper_bound(x); upper_bound(x) 找第一个大于 x 的元素。 例子： set\u003Cint> s = {1, 3, 5, 7};\n\nauto it = s.lower_bound(4);\nif (it != s.end()) {\n    cout \u003C\u003C *it \u003C\u003C '\\n'; \u002F\u002F 5\n} 如果要找小于等于 x 的最大值，可以这样写： auto it = s.upper_bound(x);\nif (it != s.begin()) {\n    --it;\n    cout \u003C\u003C *it \u003C\u003C '\\n';\n} 这里要先判断 it != s.begin()，否则 --it 会越界。迭代器这东西，平时看着很优雅，一越界就很有自己的想法。",{"path":658,"category":680,"tags":681,"created":661,"draft":12,"id":682,"title":683,"titles":684,"content":685,"level":23},[449,450],[452,583,455],"\u002Fposts\u002Fcpp-learning\u002Fcpp-4#set-的排序方式","set 的排序方式",[662],"默认是升序。 如果想降序： set\u003Cint, greater\u003Cint>> s; 自定义结构体也可以放进 set，但要告诉它怎么比较。 struct Node {\n    int x;\n    int y;\n};\n\nstruct Cmp {\n    bool operator()(const Node& a, const Node& b) const {\n        if (a.x != b.x) return a.x \u003C b.x;\n        return a.y \u003C b.y;\n    }\n};\n\nset\u003CNode, Cmp> s; 对机考来说，更常见的写法其实是直接用 pair： set\u003Cpair\u003Cint, int>> s; pair 默认先比较 first，再比较 second，很多时候够用了。",{"path":658,"category":687,"tags":688,"created":661,"draft":12,"id":689,"title":690,"titles":691,"content":692,"level":23},[449,450],[452,583,455],"\u002Fposts\u002Fcpp-learning\u002Fcpp-4#multiset允许重复的-set","multiset：允许重复的 set",[662],"set 不存重复元素。如果需要有序，并且允许重复，可以用 multiset。 multiset\u003Cint> s;\n\ns.insert(1);\ns.insert(1);\ns.insert(2);\n\ncout \u003C\u003C s.count(1) \u003C\u003C '\\n'; \u002F\u002F 2 但是删除时要注意： s.erase(1); 这会把所有值为 1 的元素都删掉。 如果只想删一个，可以先 find： auto it = s.find(1);\nif (it != s.end()) {\n    s.erase(it);\n} 这个坑很常见，尤其是维护一个可以重复的有序集合时。",{"path":658,"category":694,"tags":695,"created":661,"draft":12,"id":696,"title":697,"titles":698,"content":699,"level":23},[449,450],[452,583,455],"\u002Fposts\u002Fcpp-learning\u002Fcpp-4#map键值映射","map：键值映射",[662],"map 需要头文件： #include \u003Cmap> 基本用法： #include \u003Ciostream>\n#include \u003Cmap>\n#include \u003Cstring>\nusing namespace std;\n\nint main() {\n    map\u003Cstring, int> score;\n\n    score[\"Alice\"] = 90;\n    score[\"Bob\"] = 85;\n    score[\"Alice\"] = 95;\n\n    cout \u003C\u003C score[\"Alice\"] \u003C\u003C '\\n';\n    cout \u003C\u003C score[\"Bob\"] \u003C\u003C '\\n';\n\n    return 0;\n} map\u003CK, V> 表示从类型 K 映射到类型 V。 比如： map\u003Cstring, int>：名字到分数。map\u003Cint, string>：编号到名字。map\u003Cchar, int>：字符到出现次数。map\u003Cstring, vector\u003Cint>>：名字到一组成绩。",{"path":658,"category":701,"tags":702,"created":661,"draft":12,"id":703,"title":704,"titles":705,"content":706,"level":23},[449,450],[452,583,455],"\u002Fposts\u002Fcpp-learning\u002Fcpp-4#用-map-统计次数","用 map 统计次数",[662],"统计每个数字出现几次： int n;\ncin >> n;\n\nmap\u003Cint, int> cnt;\n\nfor (int i = 0; i \u003C n; i++) {\n    int x;\n    cin >> x;\n    cnt[x]++;\n}\n\nfor (const auto& p : cnt) {\n    cout \u003C\u003C p.first \u003C\u003C ' ' \u003C\u003C p.second \u003C\u003C '\\n';\n} 这里 cnt[x]++ 很方便：如果 x 原来不存在，cnt[x] 会先被默认初始化成 0，然后再加一。 遍历 map 时，拿到的是 pair\u003Cconst K, V>： for (const auto& p : cnt) {\n    cout \u003C\u003C p.first \u003C\u003C ' ' \u003C\u003C p.second \u003C\u003C '\\n';\n} C++17 以后也可以写结构化绑定： for (const auto& [key, value] : cnt) {\n    cout \u003C\u003C key \u003C\u003C ' ' \u003C\u003C value \u003C\u003C '\\n';\n} 这个写法很舒服，但如果考场环境比较老，就还是用 p.first 和 p.second 稳一点。",{"path":658,"category":708,"tags":709,"created":661,"draft":12,"id":710,"title":711,"titles":712,"content":713,"level":23},[449,450],[452,583,455],"\u002Fposts\u002Fcpp-learning\u002Fcpp-4#map-的查找","map 的查找",[662],"判断某个 key 是否存在： if (mp.count(key)) {\n    cout \u003C\u003C \"exists\" \u003C\u003C '\\n';\n} 或者： auto it = mp.find(key);\nif (it != mp.end()) {\n    cout \u003C\u003C it->second \u003C\u003C '\\n';\n} 这里 it->first 是 key，it->second 是 value。 需要特别注意 operator[]： cout \u003C\u003C mp[key] \u003C\u003C '\\n'; 如果 key 不存在，这句会把它插入到 map 里，并把 value 默认初始化。 比如： map\u003Cstring, int> mp;\n\ncout \u003C\u003C mp[\"unknown\"] \u003C\u003C '\\n';\ncout \u003C\u003C mp.size() \u003C\u003C '\\n'; \u002F\u002F 1 所以如果只是想“查一下有没有”，不要随手写 mp[key]，用 count 或 find 更稳。",{"path":658,"category":715,"tags":716,"created":661,"draft":12,"id":717,"title":718,"titles":719,"content":720,"level":23},[449,450],[452,583,455],"\u002Fposts\u002Fcpp-learning\u002Fcpp-4#map-的遍历顺序","map 的遍历顺序",[662],"map 会按 key 从小到大遍历。 map\u003Cstring, int> mp;\nmp[\"banana\"] = 2;\nmp[\"apple\"] = 3;\nmp[\"cat\"] = 1;\n\nfor (const auto& p : mp) {\n    cout \u003C\u003C p.first \u003C\u003C ' ' \u003C\u003C p.second \u003C\u003C '\\n';\n} 输出顺序是： apple 3\nbanana 2\ncat 1 如果题目要求按字典序输出统计结果，map 就很顺手。 如果不需要有序，只关心查找和统计，可以考虑 unordered_map。",{"path":658,"category":722,"tags":723,"created":661,"draft":12,"id":724,"title":725,"titles":726,"content":727,"level":23},[449,450],[452,583,455],"\u002Fposts\u002Fcpp-learning\u002Fcpp-4#unordered_map-和-unordered_set","unordered_map 和 unordered_set",[662],"unordered_map 和 unordered_set 是哈希表，平均查找和插入更快，但不保证遍历顺序。 需要头文件： #include \u003Cunordered_map>\n#include \u003Cunordered_set> 常见用法： unordered_map\u003Cstring, int> cnt;\ncnt[\"apple\"]++; unordered_set\u003Cint> appeared;\nappeared.insert(x); 怎么选： 需要有序输出、找前驱后继、用 lower_bound：用 map \u002F set。只需要快速判断存在、统计次数：可以用 unordered_map \u002F unordered_set。 不过机考里如果数据量不大，map 和 set 已经够用，而且行为更稳定、遍历有序，调试时也更直观。",{"path":658,"category":729,"tags":730,"created":661,"draft":12,"id":731,"title":732,"titles":733,"content":734,"level":23},[449,450],[452,583,455],"\u002Fposts\u002Fcpp-learning\u002Fcpp-4#小练习统计单词出现次数","小练习：统计单词出现次数",[662],"读入一整行，统计每个单词出现几次，并按字典序输出。 #include \u003Ciostream>\n#include \u003Cmap>\n#include \u003Csstream>\n#include \u003Cstring>\nusing namespace std;\n\nint main() {\n    string line;\n    getline(cin, line);\n\n    stringstream ss(line);\n    string word;\n    map\u003Cstring, int> cnt;\n\n    while (ss >> word) {\n        cnt[word]++;\n    }\n\n    for (const auto& p : cnt) {\n        cout \u003C\u003C p.first \u003C\u003C \": \" \u003C\u003C p.second \u003C\u003C '\\n';\n    }\n\n    return 0;\n} 输入： cpp is good and cpp is useful 输出： and: 1\ncpp: 2\ngood: 1\nis: 2\nuseful: 1",{"path":658,"category":736,"tags":737,"created":661,"draft":12,"id":738,"title":576,"titles":739,"content":740,"level":23},[449,450],[452,583,455],"\u002Fposts\u002Fcpp-learning\u002Fcpp-4#小结",[662],"set：有序、不重复，适合去重、判断存在、维护有序集合。multiset：有序、允许重复，删除一个元素时要用迭代器。map：按 key 有序的键值映射，适合统计次数和建立对应关系。mp[key] 会在 key 不存在时插入默认值，只查找时优先用 count 或 find。需要哈希表时可以用 unordered_map \u002F unordered_set，但它们不保证遍历顺序。 下一篇可以继续补 stack、queue、deque 和 priority_queue，这些容器写 BFS、模拟和堆相关题目时特别常用。 html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html pre.shiki code .sHdIc, html code.shiki .sHdIc{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#EEFFFF;--shiki-default-font-style:italic;--shiki-dark:#BABED8;--shiki-dark-font-style:italic}",{"path":742,"category":743,"tags":744,"created":745,"draft":12,"id":742,"title":746,"titles":747,"content":748,"level":16},"\u002Fposts\u002Fcpp-learning\u002Fcpp-5",[449,450],[452,583,455],"2026-06-14","C++复健笔记 Vol.5：stack、queue、deque 和 priority_queue",[],"复习四个常见容器：栈、队列、双端队列和优先队列。 这一篇继续补 STL 里非常常见的几个容器：stack、queue、deque 和 priority_queue。 它们都很适合机考，因为语义直接，代码短，手写起来也不容易太绕。",{"path":742,"category":750,"tags":751,"created":745,"draft":12,"id":752,"title":753,"titles":754,"content":755,"level":23},[449,450],[452,583,455],"\u002Fposts\u002Fcpp-learning\u002Fcpp-5#stack后进先出","stack：后进先出",[746],"stack 可以理解成栈，最后放进去的元素最先出来。 #include \u003Ciostream>\n#include \u003Cstack>\nusing namespace std;\n\nint main() {\n    stack\u003Cint> st;\n\n    st.push(1);\n    st.push(2);\n    st.push(3);\n\n    cout \u003C\u003C st.top() \u003C\u003C '\\n'; \u002F\u002F 3\n    st.pop();\n    cout \u003C\u003C st.top() \u003C\u003C '\\n'; \u002F\u002F 2\n\n    return 0;\n} 常用操作： st.push(x)：入栈。st.pop()：出栈。st.top()：栈顶元素。st.empty()：是否为空。st.size()：元素个数。 栈特别适合做： 括号匹配；撤销\u002F回退类模拟；单调栈。 注意，pop() 只是删掉栈顶，不返回值。要先 top() 再 pop()。 int x = st.top();\nst.pop(); 空栈不能直接 top() 或 pop()。",{"path":742,"category":757,"tags":758,"created":745,"draft":12,"id":759,"title":760,"titles":761,"content":762,"level":23},[449,450],[452,583,455],"\u002Fposts\u002Fcpp-learning\u002Fcpp-5#queue先进先出","queue：先进先出",[746],"queue 是队列，先进去的先出来。 #include \u003Ciostream>\n#include \u003Cqueue>\nusing namespace std;\n\nint main() {\n    queue\u003Cint> q;\n\n    q.push(1);\n    q.push(2);\n    q.push(3);\n\n    cout \u003C\u003C q.front() \u003C\u003C '\\n'; \u002F\u002F 1\n    q.pop();\n    cout \u003C\u003C q.front() \u003C\u003C '\\n'; \u002F\u002F 2\n\n    return 0;\n} 常用操作： q.push(x)：入队。q.pop()：出队。q.front()：队头元素。q.back()：队尾元素。q.empty()：是否为空。q.size()：元素个数。 队列常见于： BFS；模拟排队；逐层处理问题。 和栈一样，pop() 不返回值，先取 front() 再 pop()。",{"path":742,"category":764,"tags":765,"created":745,"draft":12,"id":766,"title":767,"titles":768,"content":769,"level":23},[449,450],[452,583,455],"\u002Fposts\u002Fcpp-learning\u002Fcpp-5#deque双端队列","deque：双端队列",[746],"deque 是双端队列，前后都能进出。 #include \u003Ciostream>\n#include \u003Cdeque>\nusing namespace std;\n\nint main() {\n    deque\u003Cint> d;\n\n    d.push_back(2);\n    d.push_front(1);\n    d.push_back(3);\n\n    cout \u003C\u003C d.front() \u003C\u003C ' ' \u003C\u003C d.back() \u003C\u003C '\\n'; \u002F\u002F 1 3\n    d.pop_front();\n    d.pop_back();\n\n    return 0;\n} 常用操作： d.push_back(x)：尾部插入。d.push_front(x)：头部插入。d.pop_back()：尾部删除。d.pop_front()：头部删除。d.front()：头元素。d.back()：尾元素。d[i]：支持下标访问。 deque 的一个优点是既像队列，又能当数组用。 机考里它常出现在： 单调队列；滑动窗口；需要两端操作的模拟题。 这里可以顺手记住一句：deque 比 vector 更适合频繁头插头删，但随机访问通常还是 vector 更常见。",{"path":742,"category":771,"tags":772,"created":745,"draft":12,"id":773,"title":774,"titles":775,"content":776,"level":23},[449,450],[452,583,455],"\u002Fposts\u002Fcpp-learning\u002Fcpp-5#priority_queue优先队列","priority_queue：优先队列",[746],"priority_queue 默认是大根堆，也就是每次 top() 都拿到最大的元素。 #include \u003Ciostream>\n#include \u003Cqueue>\nusing namespace std;\n\nint main() {\n    priority_queue\u003Cint> pq;\n\n    pq.push(3);\n    pq.push(1);\n    pq.push(5);\n\n    cout \u003C\u003C pq.top() \u003C\u003C '\\n'; \u002F\u002F 5\n    pq.pop();\n    cout \u003C\u003C pq.top() \u003C\u003C '\\n'; \u002F\u002F 3\n\n    return 0;\n} 常用操作： pq.push(x)：插入元素。pq.pop()：删除堆顶。pq.top()：堆顶元素。pq.empty()：是否为空。pq.size()：元素个数。 和前面一样，pop() 不返回值。",{"path":742,"category":778,"tags":779,"created":745,"draft":12,"id":780,"title":781,"titles":782,"content":783,"level":23},[449,450],[452,583,455],"\u002Fposts\u002Fcpp-learning\u002Fcpp-5#小根堆","小根堆",[746],"默认是大根堆。如果要小根堆，可以加比较器： priority_queue\u003Cint, vector\u003Cint>, greater\u003Cint>> pq; 例子： priority_queue\u003Cint, vector\u003Cint>, greater\u003Cint>> pq;\npq.push(3);\npq.push(1);\npq.push(5);\n\ncout \u003C\u003C pq.top() \u003C\u003C '\\n'; \u002F\u002F 1 这个在写最短路、Top K、贪心题时很常见。",{"path":742,"category":785,"tags":786,"created":745,"draft":12,"id":787,"title":788,"titles":789,"content":790,"level":23},[449,450],[452,583,455],"\u002Fposts\u002Fcpp-learning\u002Fcpp-5#priority_queue-和-pair","priority_queue 和 pair",[746],"priority_queue 也可以放 pair。 priority_queue\u003Cpair\u003Cint, int>> pq;\npq.push({2, 10});\npq.push({1, 99});\npq.push({2, 5}); 默认会先比 first，first 相同再比 second。 所以上面堆顶会是 {2, 10}。 如果想按别的规则排序，就需要自定义比较器。 struct Cmp {\n    bool operator()(const pair\u003Cint, int>& a, const pair\u003Cint, int>& b) const {\n        if (a.first != b.first) return a.first > b.first;\n        return a.second \u003C b.second;\n    }\n};\n\npriority_queue\u003Cpair\u003Cint, int>, vector\u003Cpair\u003Cint, int>>, Cmp> pq; 这个比较器写法一开始容易绕，但核心只要记住：它决定“谁的优先级更高”。",{"path":742,"category":792,"tags":793,"created":745,"draft":12,"id":794,"title":795,"titles":796,"content":797,"level":23},[449,450],[452,583,455],"\u002Fposts\u002Fcpp-learning\u002Fcpp-5#这几个容器到底怎么选","这几个容器到底怎么选",[746],"可以粗暴记成： stack：只在一头进出，后进先出。queue：一头进，一头出，先进先出。deque：两头都能操作，灵活。priority_queue：每次拿最值，适合堆。 如果题目里出现“最近的”“上一个”“配对消除”，先想栈。 如果出现“按顺序处理”“一层一层扩散”，先想队列。 如果出现“头尾都要动”，看看 deque。 如果出现“每次取最大\u002F最小值”，优先想 priority_queue。",{"path":742,"category":799,"tags":800,"created":745,"draft":12,"id":801,"title":802,"titles":803,"content":804,"level":23},[449,450],[452,583,455],"\u002Fposts\u002Fcpp-learning\u002Fcpp-5#小练习模拟出栈顺序","小练习：模拟出栈顺序",[746],"读入一串操作，1 x 表示入栈，2 表示出栈并输出栈顶。 #include \u003Ciostream>\n#include \u003Cstack>\nusing namespace std;\n\nint main() {\n    int m;\n    cin >> m;\n\n    stack\u003Cint> st;\n    for (int i = 0; i \u003C m; i++) {\n        int op;\n        cin >> op;\n\n        if (op == 1) {\n            int x;\n            cin >> x;\n            st.push(x);\n        } else if (op == 2) {\n            if (!st.empty()) {\n                cout \u003C\u003C st.top() \u003C\u003C '\\n';\n                st.pop();\n            }\n        }\n    }\n\n    return 0;\n} 这个题型很适合拿来练手，因为它几乎就是在复述容器 API。",{"path":742,"category":806,"tags":807,"created":745,"draft":12,"id":808,"title":576,"titles":809,"content":810,"level":23},[449,450],[452,583,455],"\u002Fposts\u002Fcpp-learning\u002Fcpp-5#小结",[746],"stack：后进先出，适合括号、回退、单调栈。queue：先进先出，适合 BFS 和排队模拟。deque：双端都能操作，常用于滑动窗口和单调队列。priority_queue：默认大根堆，greater\u003Cint> 可变小根堆。pop() 都不返回值，先 top() \u002F front() 再删。 html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sHdIc, html code.shiki .sHdIc{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#EEFFFF;--shiki-default-font-style:italic;--shiki-dark:#BABED8;--shiki-dark-font-style:italic}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}",{"path":812,"category":813,"tags":814,"created":816,"draft":12,"id":812,"title":817,"titles":818,"content":819,"level":16},"\u002Fposts\u002Fcpp-learning\u002Fcpp-6",[449,450],[452,815,583,455],"IO","2026-07-01","C++复健笔记 Vol.6：保研夏令营机考 IO 备考指南",[],"面向保研夏令营机考的 C++ IO 复习：不用万能头，少做非必要优化，重点避开 getline、EOF、格式化输出和本地调试坑。 这一篇重新整理一下 C++ 的 IO。目标不是把标准库流讲完整，而是面向保研夏令营机考：能稳、能默写、少踩坑。 也就是说，我不再把 #include \u003Cbits\u002Fstdc++.h> 和快 IO 当成默认模板。机考环境可能是 Dev-C++ 或比较老的编译器，万能头不一定稳；大部分夏令营题的数据量也没有大到必须先优化 IO。",{"path":812,"category":821,"tags":822,"created":816,"draft":12,"id":823,"title":824,"titles":825,"content":826,"level":23},[449,450],[452,815,583,455],"\u002Fposts\u002Fcpp-learning\u002Fcpp-6#头文件不用万能头","头文件：不用万能头",[817],"考场上最稳妥的写法是只引入需要的标准头文件。 需要使用的功能头文件cin \u002F cout#include \u003Ciostream>string 字符串#include \u003Cstring>stringstream 字符串流解析#include \u003Csstream>vector 动态数组#include \u003Cvector>sort \u002F reverse 等算法#include \u003Calgorithm>setprecision 保留小数#include \u003Ciomanip>toupper \u002F isdigit 字符处理#include \u003Ccctype>freopen \u002F printf#include \u003Ccstdio> 考场常用模板可以先背这一版： #include \u003Ciostream>\n#include \u003Cstring>\n#include \u003Csstream>\n#include \u003Cvector>\n#include \u003Calgorithm>\n#include \u003Ciomanip>\nusing namespace std;\n\nint main() {\n    \u002F\u002F code here\n\n    return 0;\n} 如果题目需要字符判断，再加 #include \u003Ccctype>；如果本地调试要用 freopen，再加 #include \u003Ccstdio>。",{"path":812,"category":828,"tags":829,"created":816,"draft":12,"id":830,"title":831,"titles":832,"content":833,"level":23},[449,450],[452,815,583,455],"\u002Fposts\u002Fcpp-learning\u002Fcpp-6#oj-输入和-eof","OJ 输入和 EOF",[817],"OJ 判题时，系统会把测试数据重定向到程序的标准输入里。对代码来说，就是从 cin 读数据。 文件一定有结尾，所以这种写法会在读到 EOF 时自动停下： int x;\nwhile (cin >> x) {\n    cout \u003C\u003C x \u003C\u003C '\\n';\n} 如果是一行一行读，也一样： string line;\nwhile (getline(cin, line)) {\n    cout \u003C\u003C line \u003C\u003C '\\n';\n} 本地在 Dev-C++ 黑框里手动测试时，如果想模拟 EOF，Windows 下一般是输入完数据后按 Ctrl + Z，再按回车。 注意，不要写这种循环： while (!cin.eof()) {\n    cin >> x;\n    \u002F\u002F ...\n} 更稳的写法是把“读入动作”直接放进循环条件： while (cin >> x) {\n    \u002F\u002F ...\n}",{"path":812,"category":835,"tags":836,"created":816,"draft":12,"id":837,"title":838,"titles":839,"content":840,"level":23},[449,450],[452,815,583,455],"\u002Fposts\u002Fcpp-learning\u002Fcpp-6#不定长数据getline-stringstream","不定长数据：getline + stringstream",[817],"如果题目说每一行有不定数量的整数，最舒服的组合是 getline + stringstream。 比如：读入若干行，每行有若干整数，输出每行的和。 #include \u003Ciostream>\n#include \u003Cstring>\n#include \u003Csstream>\nusing namespace std;\n\nint main() {\n    string line;\n\n    while (getline(cin, line)) {\n        if (line.empty()) {\n            continue;\n        }\n\n        stringstream ss(line);\n\n        int sum = 0;\n        int x;\n        while (ss >> x) {\n            sum += x;\n        }\n\n        cout \u003C\u003C sum \u003C\u003C '\\n';\n    }\n\n    return 0;\n} 这个模板很适合处理： 每行数字个数不固定；输入行数没有提前给出；每一行都要单独统计或分析。",{"path":812,"category":842,"tags":843,"created":816,"draft":12,"id":844,"title":845,"titles":846,"content":847,"level":23},[449,450],[452,815,583,455],"\u002Fposts\u002Fcpp-learning\u002Fcpp-6#cin-和-getline-混用","cin 和 getline 混用",[817],"cin >> x 读走数据，但通常不会读走行末的换行符。紧接着写 getline(cin, line)，就可能直接读到一个空字符串。 典型场景：先读一个整数 T，后面跟着 T 行字符串，而且字符串里可能有空格。 int T;\ncin >> T;\ncin.ignore();\n\nstring line;\nfor (int i = 0; i \u003C T; i++) {\n    getline(cin, line);\n    \u002F\u002F 处理 line\n} 这里的 cin.ignore() 不能忘，它负责吃掉 T 后面的那个回车。 如果担心这一行里还有别的残留内容，可以写更完整的版本： #include \u003Climits>\n\ncin.ignore(numeric_limits\u003Cstreamsize>::max(), '\\n'); 不过考场上，多数题只是 cin >> T 后接整行字符串，cin.ignore() 通常够用。",{"path":812,"category":849,"tags":850,"created":816,"draft":12,"id":851,"title":852,"titles":853,"content":854,"level":23},[449,450],[452,815,583,455],"\u002Fposts\u002Fcpp-learning\u002Fcpp-6#字符串拆分","字符串拆分",[817],"如果一行数据按空格分隔，用 stringstream >> 就够了： string line = \"Tom 90 85 92\";\nstringstream ss(line);\n\nstring name;\nint a, b, c;\nss >> name >> a >> b >> c; 如果按逗号这种单字符分隔，可以用 getline 的第三个参数： string line = \"Tom,90,85,92\";\nstringstream ss(line);\n\nstring part;\nwhile (getline(ss, part, ',')) {\n    cout \u003C\u003C part \u003C\u003C '\\n';\n} 如果有多种分隔符，比如空格、逗号、句号混在一起，可以用 find_first_of。 #include \u003Ciostream>\n#include \u003Cstring>\n#include \u003Cvector>\nusing namespace std;\n\nvector\u003Cstring> splitMulti(const string& s, const string& delimiters) {\n    vector\u003Cstring> res;\n    size_t start = 0;\n\n    while (start \u003C s.size()) {\n        size_t end = s.find_first_of(delimiters, start);\n        string word = s.substr(start, end - start);\n\n        if (!word.empty()) {\n            res.push_back(word);\n        }\n\n        if (end == string::npos) {\n            break;\n        }\n        start = end + 1;\n    }\n\n    return res;\n} 用法： vector\u003Cstring> words = splitMulti(\"hello, world. C++\", \" ,.\"); 这种写法不算短，但很稳定。考场上如果分隔符复杂，不要硬用一堆 if 手搓。",{"path":812,"category":856,"tags":857,"created":816,"draft":12,"id":858,"title":859,"titles":860,"content":861,"level":23},[449,450],[452,815,583,455],"\u002Fposts\u002Fcpp-learning\u002Fcpp-6#格式化输出","格式化输出",[817],"保留小数用 \u003Ciomanip>： #include \u003Ciomanip>\n\ndouble x = 3.1415926;\ncout \u003C\u003C fixed \u003C\u003C setprecision(2) \u003C\u003C x \u003C\u003C '\\n'; \u002F\u002F 3.14 注意，fixed 和 setprecision(2) 设置后，会影响后续的浮点数输出。 cout \u003C\u003C fixed \u003C\u003C setprecision(2);\ncout \u003C\u003C 3.1415926 \u003C\u003C '\\n'; \u002F\u002F 3.14\ncout \u003C\u003C 2.0 \u003C\u003C '\\n';       \u002F\u002F 2.00 如果考场上实在忘了 \u003Ciomanip>，也可以用 C 风格输出： #include \u003Ccstdio>\n\nprintf(\"%.2f\\n\", x); 但不要一会儿用 cout 的格式控制，一会儿又切到 printf。能统一就统一。",{"path":812,"category":863,"tags":864,"created":816,"draft":12,"id":865,"title":866,"titles":867,"content":868,"level":23},[449,450],[452,815,583,455],"\u002Fposts\u002Fcpp-learning\u002Fcpp-6#vector-输出不要多空格","vector 输出不要多空格",[817],"很多 OJ 对行尾空格不严格，但不要把希望寄托在“它应该能过”上。输出数组时，最稳的是用下标控制空格。 vector\u003Cint> v = {1, 2, 3, 4};\n\nfor (int i = 0; i \u003C (int)v.size(); i++) {\n    if (i > 0) {\n        cout \u003C\u003C ' ';\n    }\n    cout \u003C\u003C v[i];\n}\ncout \u003C\u003C '\\n'; 核心就是：从第二个元素开始，先输出空格，再输出元素。",{"path":812,"category":870,"tags":871,"created":816,"draft":12,"id":872,"title":873,"titles":874,"content":875,"level":23},[449,450],[452,815,583,455],"\u002Fposts\u002Fcpp-learning\u002Fcpp-6#文件-io-和本地调试","文件 IO 和本地调试",[817],"OJ 提交时，正常情况下不需要写文件 IO，也不需要写 freopen。直接用 cin 和 cout，判题系统会自动处理输入输出。 本地调试时，可以在 .cpp 同目录下放一个 1.in 文件，然后临时加： #include \u003Ccstdio>\n\nfreopen(\"1.in\", \"r\", stdin); 完整位置一般放在 main 一开始： int main() {\n    freopen(\"1.in\", \"r\", stdin);\n\n    \u002F\u002F code here\n\n    return 0;\n} 提交前一定要删掉或注释掉这一行： \u002F\u002F freopen(\"1.in\", \"r\", stdin); 否则 OJ 上没有这个本地文件，轻则读不到数据，重则直接报错。",{"path":812,"category":877,"tags":878,"created":816,"draft":12,"id":879,"title":880,"titles":881,"content":882,"level":23},[449,450],[452,815,583,455],"\u002Fposts\u002Fcpp-learning\u002Fcpp-6#终极-clean-模板","终极 Clean 模板",[817],"如果题目是“先给 T，再给 T 行可能包含空格的内容”，可以套这个： #include \u003Ciostream>\n#include \u003Cstring>\n#include \u003Csstream>\n#include \u003Cvector>\n#include \u003Calgorithm>\n#include \u003Ciomanip>\nusing namespace std;\n\nint main() {\n    \u002F\u002F 本地调试时再打开，提交前删掉或注释掉\n    \u002F\u002F freopen(\"1.in\", \"r\", stdin);\n\n    int T;\n    cin >> T;\n    cin.ignore();\n\n    while (T--) {\n        string line;\n        getline(cin, line);\n\n        \u002F\u002F 处理 line\n    }\n\n    return 0;\n} 如果打开了 freopen 那行，记得补 #include \u003Ccstdio>。 如果题目没有给行数，直接读到 EOF： string line;\nwhile (getline(cin, line)) {\n    \u002F\u002F 处理 line\n}",{"path":812,"category":884,"tags":885,"created":816,"draft":12,"id":886,"title":887,"titles":888,"content":889,"level":23},[449,450],[452,815,583,455],"\u002Fposts\u002Fcpp-learning\u002Fcpp-6#考前一分钟自查","考前一分钟自查",[817],"有没有删掉 #include \u003Cbits\u002Fstdc++.h>？有没有删掉或注释掉本地调试用的 freopen？先读数字再读整行时，有没有写 cin.ignore()？需要读入含空格的字符串时，有没有用 getline？输出浮点数时，有没有加 #include \u003Ciomanip>？输出 vector 时，末尾有没有多余空格？读到 EOF 时，是不是写成了 while (cin >> x) 或 while (getline(cin, line))？题目要求按行处理时，有没有避免直接用 cin >> s 把空格后的内容丢掉？",{"path":812,"category":891,"tags":892,"created":816,"draft":12,"id":893,"title":576,"titles":894,"content":895,"level":23},[449,450],[452,815,583,455],"\u002Fposts\u002Fcpp-learning\u002Fcpp-6#小结",[817],"这篇的重点不是“C++ IO 有多少种玩法”，而是机考时怎么少丢分： 标准头文件比万能头更稳。简单 cin \u002F cout 已经够应付大多数夏令营机考题。getline 和 cin >> 混用时，先处理掉残留换行。不定长行数据优先想 getline + stringstream。输出格式、尾随空格、本地 freopen 都是小坑，但每一个都可能让人白丢分。 html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .sHdIc, html code.shiki .sHdIc{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#EEFFFF;--shiki-default-font-style:italic;--shiki-dark:#BABED8;--shiki-dark-font-style:italic}",{"path":897,"category":898,"tags":901,"created":904,"draft":12,"id":897,"title":905,"titles":906,"content":907,"level":16},"\u002Fposts\u002Fcrossroads-at-the-end-of-2025",[899,900],"随笔","生活",[10,902,903],"Diary","2025","2025-12-31","2025年的最后一天",[],"这篇博客整理自我在 2025-12-31 发布于朋友圈的一段文案。 本文实际上传于2026年5月，主要是为了测试归档页面多年份文章列表渲染效果 感觉自己恍惚立于大型交叉路口， 是左转？右转？前行？还是掉头。 二十岁的晨光只留有三分之二了， 一旦选定便渐行渐远，再无法重走。",{"path":909,"category":449,"tags":910,"created":912,"draft":12,"id":909,"title":913,"titles":914,"content":915,"level":16},"\u002Fposts\u002Ffirst-note",[9,911],"Nuxt","2026-05-01","第一篇占位文章",[],"用来观察普通标题、无封面、少量标签时文章卡片的基础表现。 这是一篇用于前端样式调试的占位文章。它的内容刻意保持简单，方便观察文章标题、摘要、标签、字数和阅读时间在列表卡片中的排版效果。",{"path":909,"category":449,"tags":917,"created":912,"draft":12,"id":918,"title":919,"titles":920,"content":921,"level":23},[9,911],"\u002Fposts\u002Ffirst-note#小标题","小标题",[913],"这里补一点正文，让文章统计不会太空。后续可以把这篇替换成真正的博客内容。",{"path":923,"category":924,"tags":925,"created":930,"draft":12,"id":923,"title":931,"titles":932,"content":933,"level":16},"\u002Fposts\u002Flong-title-layout-test","Frontend",[926,927,928,929],"Layout","Animation","Card","Responsive","2026-05-05","一个故意写得很长很长的文章标题用来测试换行和箭头动画",[],"这篇文章用于测试长标题在首页文章列表中的换行、hover 箭头、封面图和元信息行是否会互相挤压。 长标题经常会暴露卡片布局里的问题，比如标题换行后行高太松、箭头位置不自然、摘要和标签被挤到不舒服的位置。 这篇占位文章主要用于这些视觉边界的测试。",{"path":923,"category":924,"tags":935,"created":930,"draft":12,"id":936,"title":937,"titles":938,"content":939,"level":23},[926,927,928,929],"\u002Fposts\u002Flong-title-layout-test#测试点","测试点",[931],"标题 hover 是否自然封面图比例是否稳定元信息是否尽量保持一行标签换行是否好看",{"path":941,"category":942,"tags":943,"created":949,"draft":12,"id":941,"title":950,"titles":951,"content":952,"level":16},"\u002Fposts\u002Fmonitor-connection-fix",[371],[944,945,946,947,948],"Codex","AI","MacBook","Display","MacOS","2026-10-09","在修复 macbook 外接显示器连接中，ai 使我眩晕瘫坐，我仿佛看到原子弹爆炸",[],"ai你赢了",{"path":941,"category":954,"tags":955,"created":949,"draft":12,"id":956,"title":386,"titles":957,"content":958,"level":23},[371],[944,945,946,947,948],"\u002Fposts\u002Fmonitor-connection-fix#前言",[950],"话说我购买MacBook Air M4已经有半年多了，但是一直没有尝试过连接外接显示器（主要也是因为用不上，在宿舍都直接用 win 本连了）。十一前突然想起来测试一下新买的扩展坞的 hdmi 口，结果发现连接后没反应，经过反复对比排查后发现它应该坏了，好消息是最后成功退掉。但是在这一过程中，我发现我的 macbook 即使通过可用的 dp 转接头连接显示器也没法正常工作。这就好像是你耳机听不到音乐后，发现不仅是耳机坏了，连耳朵也聋了。我也是非常的崩溃，很想感慨自己怎么买的都是残次品。 更神奇的是以下现象： 我的显示器室友们的显示器我的 win 本正常正常我的 macbook异常正常室友A的 macbook正常正常室友A的 小平板异常正常 严格来说，连上之后并不是完全无信号的状态，显示器是有微弱的暗光的，信号源也没有被切走，说明至少显示器这一侧是能识别到我的 macbook 的。 图为我买的那款扩展坞，还挺漂亮的u1s1",{"path":941,"category":960,"tags":961,"created":949,"draft":12,"id":962,"title":963,"titles":964,"content":965,"level":23},[371],[944,945,946,947,948],"\u002Fposts\u002Fmonitor-connection-fix#我的尝试","我的尝试",[950],"作为一个肉体凡胎的废物大学生，我自然是没什么头绪的。我分别通过搜索引擎和大模型网页对话在网络上检索相关资料，但是均没有与我类似的案例。到这一步，我本来基本上是想放弃了，因为：1. 问题太罕见，找不到解决方案 2. 我平时基本不用 mac 连显示器，影响不大；连别的显示器又不会出现问题。解决这个问题收益远小于成本 3. 真要找人修，我可能需要把显示器和笔记本一起扛过去，还不一定能解决，感觉实在有点蠢。",{"path":941,"category":967,"tags":968,"created":949,"draft":12,"id":969,"title":970,"titles":971,"content":972,"level":23},[371],[944,945,946,947,948],"\u002Fposts\u002Fmonitor-connection-fix#ai-神力","AI 神力",[950],"今天想着试试 codex 能不能直接帮我搞定，于是把我的问题和实验结果都发给了它。经过一段时间的人机交互后，居然真的解决了。那一刻，我眩晕瘫坐在椅子上，仿佛看到了原子弹爆炸。",{"path":941,"category":974,"tags":975,"created":949,"draft":12,"id":976,"title":977,"titles":978,"content":979,"level":36},[371],[944,945,946,947,948],"\u002Fposts\u002Fmonitor-connection-fix#成因与解决","成因与解决",[950,970],"最后发现我的 mac 上存着一条有问题的显示器配置：IndependentOutput = true。把对应的配置项删掉再重启，显示器就能正常用了。 一开始，codex 也尝试了一些常规操作，包括让我给显示器断电、重新插拔、换电脑上的接口，以及重启控制中心。这些操作都没有解决问题。但是它也发现了 mac 底层能读到这块显示器的信息，只是系统没有把它启用为正常的桌面显示器。也就是说，虽然系统设置里找不到我的显示器，但电脑其实知道它连着，还能读到型号和它支持的显示模式。只是到了真正输出桌面的环节，这块屏幕被标记为未上线、未激活。 不过，仅凭这个现象本身，还不足以判断具体原因。 顺着这个线索，codex 去检查了 WindowServer 保存的显示配置。简单理解，WindowServer 是 macOS 负责管理窗口和屏幕显示的系统组件，它会保存显示器的相关状态。然后，codex 在用户级和系统级配置中，都找到了我这块显示器对应的 IndependentOutput = true。 这个配置项相当冷门。我后来让它上网找资料，才看到有开发者记录：调用相关的系统内部接口，会写入这个字段，导致屏幕黑屏、从显示器枚举中消失，而且状态会跨重启保留。恢复需要移除这个字段，再退出登录。",{"path":941,"category":981,"tags":982,"created":949,"draft":12,"id":983,"title":984,"titles":985,"content":986,"level":23},[371],[944,945,946,947,948],"\u002Fposts\u002Fmonitor-connection-fix#尾声","尾声",[950],"codex 不逃、不躲，稳稳地把问题接住了。但是，为什么会出现这个配置项？是显示器的问题，还是我 MacOS 的 bug ？这可能只能当作是未解之谜了。 我可以非常严肃地肯定，这个问题如果没有 AI ，我是绝对没有办法解决的，主要的三点原因之前已经提到了。还是很感慨，科技的力量真的改变了我的生活（不管是正向的还是负向的）。",{"path":988,"category":989,"tags":990,"created":992,"draft":12,"id":988,"title":993,"titles":994,"content":995,"level":16},"\u002Fposts\u002Fproject-markdown-guide",[449,131],[130,131,132,991],"写作指南","2026-05-15","在本站写文章：Markdown、GFM 与 MDC 语法指南",[],"从 frontmatter 到代码组、提示框和折叠面板，完整介绍 Leisuer Blog 当前支持的文章语法与项目约定。",{"path":988,"category":997,"tags":998,"created":992,"draft":12,"id":999,"title":993,"titles":1000,"content":1001,"level":16},[449,131],[130,131,132,991],"\u002Fposts\u002Fproject-markdown-guide#在本站写文章markdowngfm-与-mdc-语法指南",[],"本站的文章并不只是普通 Markdown。内容会先交给 Nuxt Content 解析，再由 Nuxt UI 的 prose 组件和项目中的自定义组件渲染。因此，除了标题、列表、链接这些基础语法，还可以使用代码高亮、文件名、行号、代码组、提示框和折叠面板。 这篇文章以项目当前的源码为准，整理一份可以直接复制使用的写作手册。 文章放在 content\u002F 中，文件路径就是页面路径。例如 content\u002Fposts\u002Fhello.md 对应 \u002Fposts\u002Fhello。",{"path":988,"category":1003,"tags":1004,"created":992,"draft":12,"id":1005,"title":1006,"titles":1007,"content":1008,"level":23},[449,131],[130,131,132,991],"\u002Fposts\u002Fproject-markdown-guide#一篇文章的最小结构","一篇文章的最小结构",[993],"每篇文章都由两部分组成：顶部的 frontmatter 和下面的 Markdown 正文。可以从这个模板开始： ---\ntitle: 文章标题\ndescription: 一句话摘要，也会作为页面的 SEO 描述。\ncreated: 2026-09-30\nupdated: 2026-09-30\ncategory: Notes\ntags:\n  - Markdown\n  - Nuxt Content\ncover: \u002Fimg\u002Fcover\u002Fexample.webp\npinned: false\ndraft: false\n---\n\n# 文章标题\n\n这里开始写正文。\n\n## 第一个章节\n\n章节内容。 项目当前认识的自定义字段如下： 字段类型用途title字符串页面标题、文章卡片标题和浏览器标题description字符串文章摘要和 meta descriptioncreated日期发布时间updated日期最后修改时间，可省略category字符串或字符串数组单级或多级分类tags字符串数组标签列表cover字符串文章卡片封面地址pinned布尔值是否在文章列表中置顶draft布尔值是否从文章列表、分类、标签和搜索中隐藏 多级分类可以写成数组： category: [Notes, Nuxt Content] wordCount 和 readingMinutes 虽然也存在于内容 schema 中，但构建时会根据正文自动计算，通常不需要手写。 draft: true 会让文章不出现在列表、分类、标签和搜索结果中，但当前文章路由仍可通过准确 URL 访问。它适合隐藏草稿，不适合保存私密内容。 如果新增 frontmatter 字段，还要同步修改 content.config.ts 的 schema，否则 Nuxt Content 可能会丢弃这个字段。修改 schema 后，开发服务器通常也需要重启。",{"path":988,"category":1010,"tags":1011,"created":992,"draft":12,"id":1012,"title":1013,"titles":1014,"content":1015,"level":23},[449,131],[130,131,132,991],"\u002Fposts\u002Fproject-markdown-guide#标题与目录","标题与目录",[993],"Markdown 使用 # 表示标题层级： # 文章标题\n\n## 一级章节\n\n### 二级章节\n\n#### 更深一层的标题 项目的文章壳会单独显示 frontmatter 中的 title，所以正文里的 # 一级标题 会被隐藏。保留它可以让 Markdown 文件脱离网站后仍有完整结构，但正文内容建议从 ## 开始。 页面会根据标题自动生成锚点和侧边目录。当前目录主要展示二级、三级标题，滚动时会同步高亮所在章节；标题层级应该依次递进，不要只为了字号从 ## 突然跳到 ####。",{"path":988,"category":1017,"tags":1018,"created":992,"draft":12,"id":1019,"title":1020,"titles":1021,"content":1022,"level":23},[449,131],[130,131,132,991],"\u002Fposts\u002Fproject-markdown-guide#段落与行内格式","段落与行内格式",[993],"空一行会开始一个新段落。普通换行通常仍属于同一段；如果确实需要强制换行，可以在行尾输入两个空格，或者使用 \u003Cbr>。 这是第一段，其中有 **粗体**、*斜体*、***粗斜体***、~~删除线~~ 和 `行内代码`。\n\n这是第二段。  \n这一行会强制换行。\n\n也可以使用 HTML 换行。\u003Cbr>\n这里会显示在下一行。 常用的行内写法： 效果写法粗体**重要内容**斜体*强调内容*粗斜体***特别强调***删除线~~旧内容~~行内代码`const answer = 42`高亮\u003Cmark>高亮内容\u003C\u002Fmark>键盘按键\u003Ckbd>⌘\u003C\u002Fkbd> + \u003Ckbd>K\u003C\u002Fkbd> 如果想原样显示 Markdown 符号，可以在它前面添加反斜杠： \\*这段文字不会变成斜体\\*\n\\# 这也不会变成标题",{"path":988,"category":1024,"tags":1025,"created":992,"draft":12,"id":1026,"title":1027,"titles":1028,"content":1029,"level":23},[449,131],[130,131,132,991],"\u002Fposts\u002Fproject-markdown-guide#链接与图片","链接与图片",[993],"行内链接、自动链接和引用式链接都可以使用： [Nuxt 官网](https:\u002F\u002Fnuxt.com)\n\n\u003Chttps:\u002F\u002Fcontent.nuxt.com>\n\n[Nuxt Content][content-docs]\n\n[content-docs]: https:\u002F\u002Fcontent.nuxt.com \"Nuxt Content 文档\" 站内链接可以直接写路径： [查看关于本站](\u002Fposts\u002Fabout-site) 图片建议放进 public\u002F，并从站点根路径引用： ![图片替代文本](\u002Fimg\u002Fexample.webp)\n\n![带标题的图片](\u002Fimg\u002Fexample.webp \"图片标题\") Nuxt Content 的 MDC 属性还可以控制图片尺寸和类名： ![96 像素头像](\u002Fimg\u002Favatar.jpg){width=\"96\" height=\"96\"}\n\n![圆角头像](\u002Fimg\u002Favatar.jpg){.w-24 .rounded-xl}\n\n![限制最大宽度](\u002Fimg\u002Fbackground.jpg){.max-w-md} 所有正文图片默认都会限制在文章宽度内、保持比例并居中显示。替代文本不能省略：除了无障碍访问，它也会在图片加载失败时提供必要信息。",{"path":988,"category":1031,"tags":1032,"created":992,"draft":12,"id":1033,"title":1034,"titles":1035,"content":1036,"level":23},[449,131],[130,131,132,991],"\u002Fposts\u002Fproject-markdown-guide#引用分隔线与列表","引用、分隔线与列表",[993],"引用使用 >，可以包含多个段落或继续嵌套： > 写作不是把所有内容堆在一起，而是帮助读者建立顺序。\n>\n> > 嵌套引用适合补充引用中的上下文。 三个连字符可以生成分隔线： --- 无序列表可以使用 -、* 或 +，有序列表使用数字： - 第一项\n- 第二项\n  - 嵌套项目\n  - 另一个嵌套项目\n\n1. 第一步\n2. 第二步\n3. 第三步 任务列表使用 GFM 的复选框语法： - [x] 已完成\n- [ ] 待处理\n- [ ] 支持 **粗体** 和 `行内代码` 缩进决定列表的嵌套关系。为了减少不同解析器之间的差异，建议每层统一缩进两个或四个空格。",{"path":988,"category":1038,"tags":1039,"created":992,"draft":12,"id":1040,"title":1041,"titles":1042,"content":1043,"level":23},[449,131],[130,131,132,991],"\u002Fposts\u002Fproject-markdown-guide#表格","表格",[993],"表格由竖线和表头分隔行组成，分隔行中的冒号控制对齐方式： | 功能 | 状态 | 备注 |\n| :--- | :---: | ---: |\n| 普通 Markdown | 支持 | 左对齐 |\n| MDC 组件 | 支持 | 居中 |\n| 代码高亮 | 支持 | 右对齐 | 功能状态备注普通 Markdown支持左对齐MDC 组件支持居中代码高亮支持右对齐 文章样式会给表头和隔行添加背景色；表格过宽时可以横向滚动，不会挤破页面。",{"path":988,"category":1045,"tags":1046,"created":992,"draft":12,"id":1047,"title":1048,"titles":1049,"content":1050,"level":23},[449,131],[130,131,132,991],"\u002Fposts\u002Fproject-markdown-guide#代码块","代码块",[993],"三个反引号可以创建围栏代码块，在开头写语言名即可启用语法高亮： ```ts\ntype Post = {\n  title: string\n  tags: string[]\n}\n\nconst post: Post = {\n  title: 'Hello Nuxt Content',\n  tags: ['Nuxt', 'Markdown'],\n}\n``` 渲染后的代码块会自动带有语言标题、macOS 风格顶栏和悬停出现的复制按钮。项目额外配置了 C++ 高亮，也可以使用 js、ts、vue、bash、json、diff、css、html、md 等常见语言标识。",{"path":988,"category":1052,"tags":1053,"created":992,"draft":12,"id":1054,"title":1055,"titles":1056,"content":1057,"level":36},[449,131],[130,131,132,991],"\u002Fposts\u002Fproject-markdown-guide#显示文件名","显示文件名",[993,1048],"把文件名放在语言标识后的方括号中： ```ts [content.config.ts]\nexport default defineContentConfig({\n  collections: {},\n})\n```",{"path":988,"category":1059,"tags":1060,"created":992,"draft":12,"id":1061,"title":1062,"titles":1063,"content":1064,"level":36},[449,131],[130,131,132,991],"\u002Fposts\u002Fproject-markdown-guide#显示行号","显示行号",[993,1048],"在代码块元信息中加入 {.line-numbers}： ```ts [example.ts] {.line-numbers}\nconst answer = 42\nconsole.log(answer)\n``` 如果示例来自原文件的第 8 行，可以在文件名后添加起始行号： ```ts [example.ts:8] {.line-numbers}\nconst answer = 42\nconsole.log(answer)\n```",{"path":988,"category":1066,"tags":1067,"created":992,"draft":12,"id":1068,"title":1069,"titles":1070,"content":1071,"level":36},[449,131],[130,131,132,991],"\u002Fposts\u002Fproject-markdown-guide#代码组","代码组",[993,1048],"同一操作有多种写法时，可以用 code-group 把它们组织成标签页： ::code-group\n  ```bash [npm]\n  npm install\n  ```\n  ```bash [pnpm]\n  pnpm install\n  ```\n  ```bash [yarn]\n  yarn install\n  ```\n:: 实际显示： npm install\npnpm install\nyarn install 缩进不是装饰：代码组中的每个代码块需要缩进到容器内部。",{"path":988,"category":1073,"tags":1074,"created":992,"draft":12,"id":1075,"title":1076,"titles":1077,"content":1078,"level":23},[449,131],[130,131,132,991],"\u002Fposts\u002Fproject-markdown-guide#提示框","提示框",[993],"项目提供了四种提示框：note、tip、warning 和 caution。 ::note\n用于补充背景信息。\n::\n\n::tip{title=\"小技巧\"}\n用于给出更高效的做法。\n::\n\n::warning{title=\"请留意\"}\n用于提醒潜在问题。\n::\n\n::caution{title=\"危险操作\"}\n用于强调严重后果。\n:: 实际显示： 这里放不影响主线、但值得读者了解的背景信息。 这里放更简洁、更高效的操作建议。 这里提醒读者操作可能带来的问题。 这里强调可能造成数据丢失或其他严重后果的操作。 在组件名后使用 {title=\"...\"} 即可传入标题。没有自定义标题时，提示框仍会保留对应的颜色和图标。\n:: 提示框内部仍然可以继续使用 Markdown： ::warning{title=\"发布前检查\"}\n- [ ] 日期是否正确\n- [ ] 图片是否有替代文本\n- [ ] 链接是否能访问\n::",{"path":988,"category":1080,"tags":1081,"created":992,"draft":12,"id":1082,"title":1083,"titles":1084,"content":1085,"level":23},[449,131],[130,131,132,991],"\u002Fposts\u002Fproject-markdown-guide#折叠内容","折叠内容",[993],"需要隐藏补充说明或答案时，可以使用 collapse；希望默认展开时使用 expand： ::collapse{title=\"点击查看答案\"}\n这里的内容默认折叠。\n::\n\n::expand{title=\"默认展开的说明\"}\n这里的内容打开页面时就能看到，也可以手动收起。\n:: 这是由项目中的 ProseCollapse 和 ProseDetails 组件渲染的内容。可以写多个段落也可以放列表与代码块 这个面板会默认展开，读者仍然可以点击标题将它收起。 details 是同一套底层组件的通用形式，还能传入 open、icon 和 type 等属性： ::details{title=\"通用详情面板\" icon=\"lucide:book-open\"}\n这里使用的是 `details` 组件。\n:: 实际显示： 这里使用的是 details 组件。日常写作仍建议优先选择语义更明确的 collapse 或 expand。",{"path":988,"category":1087,"tags":1088,"created":992,"draft":12,"id":1089,"title":1090,"titles":1091,"content":1092,"level":23},[449,131],[130,131,132,991],"\u002Fposts\u002Fproject-markdown-guide#脚注","脚注",[993],"脚注适合放来源、解释或不希望打断正文的补充信息： Nuxt Content 会把 Markdown 转换成可查询的结构化内容。[^content]\n\n[^content]: 详见 Nuxt Content 官方文档。 脚注定义可以包含多行，后续行需要缩进： 这是一条较长的说明。[^long-note]\n\n[^long-note]: 第一行内容。\n    这是同一条脚注的后续内容。",{"path":988,"category":1094,"tags":1095,"created":992,"draft":12,"id":1096,"title":1097,"titles":1098,"content":1099,"level":23},[449,131],[130,131,132,991],"\u002Fposts\u002Fproject-markdown-guide#原生-html-与-mdc-组件","原生 HTML 与 MDC 组件",[993],"Markdown 中可以嵌入少量原生 HTML。项目已经为 \u003Cdetails>、\u003Csummary>、\u003Ckbd>、\u003Cmark> 等元素准备了正文样式： \u003Cdetails>\n  \u003Csummary>原生折叠区域\u003C\u002Fsummary>\n\n  这里是折叠内容。\n\u003C\u002Fdetails>\n\n按 \u003Ckbd>⌘\u003C\u002Fkbd> + \u003Ckbd>K\u003C\u002Fkbd> 打开搜索。\n\n\u003Cmark>这是一段高亮文字。\u003C\u002Fmark> MDC 也可以直接调用已注册的 Vue 组件，属性写在花括号中： ::alert{color=\"#53a3f1\"}\n这是由 `app\u002Fcomponents\u002FAlert.vue` 渲染的内容。\n::\n\n::div{class=\"rounded-lg border p-4\"}\n这是带有 class 的原生 div 容器。\n::\n\n::counter\n::\n\n:badge[内联徽章]{color=\"info\"}\n\n::card\n### 卡片标题\n\n卡片中也可以继续使用 **Markdown**。\n:: 实际显示： 这是由 Alert.vue 渲染的提示区域，边框颜色来自传入的 color 属性。 这是一个通过 MDC 创建的 div，样式直接来自传入的 Tailwind class。 上面的计数器是一个真正可交互的 Vue 组件，点击按钮后数字会立即变化。 行内 Badge 的效果：:badge内联徽章 Card 的效果： 卡片标题卡片中也可以继续使用 Markdown、行内代码 和链接。 这种能力很强，但也会让文章依赖项目内部实现。通用内容优先使用标准 Markdown；只有确实需要交互或特殊布局时，再使用自定义组件。",{"path":988,"category":1101,"tags":1102,"created":992,"draft":12,"id":1103,"title":1104,"titles":1105,"content":1106,"level":23},[449,131],[130,131,132,991],"\u002Fposts\u002Fproject-markdown-guide#当前没有启用的语法","当前没有启用的语法",[993],"测试文档中还包含数学公式和定义列表，但项目目前没有配置数学公式插件或专门的定义列表渲染规则。下面的内容不应假定会得到数学排版： $E = mc^2$\n\n$$\nE = mc^2\n$$\n\n术语\n: 定义内容 它们可能只会作为普通文本显示。需要公式时，应先为 Nuxt Content 接入相应的 Markdown 插件并补充样式，再在正式文章中使用。",{"path":988,"category":1108,"tags":1109,"created":992,"draft":12,"id":1110,"title":1111,"titles":1112,"content":1113,"level":23},[449,131],[130,131,132,991],"\u002Fposts\u002Fproject-markdown-guide#发布前检查清单","发布前检查清单",[993],"文件位于 content\u002F，路径和文件名清晰且稳定 frontmatter 至少包含 title、description、created、category、tags 和 draft 正文结构从 ## 开始，标题层级连续 图片使用 public\u002F 下的根路径，并填写替代文本 代码块标注正确的语言，必要时补充文件名和行号 重要说明选择了合适的提示框，而不是只靠加粗或颜色 草稿已确认 draft 状态，且没有把私密内容当作草稿保存 新增 frontmatter 字段时同步更新了 content.config.ts 掌握这些规则后，写作时只需要关心内容本身：基础结构交给 Markdown，复杂展示交给 MDC，文章元信息则统一放在 frontmatter。这样写出的文件既保持可读，也能完整使用本站的目录、分类、搜索、代码高亮和主题样式。 html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}",{"path":1115,"category":10,"tags":1116,"created":992,"draft":12,"id":1115,"title":1118,"titles":1119,"content":1120,"level":16},"\u002Fposts\u002Fshort-post",[1117],"Note","短文",[],"测试极短标题和极短摘要。 很短的一篇占位文章。",{"path":1122,"category":128,"tags":1123,"created":1128,"draft":12,"id":1122,"title":1129,"titles":1130,"content":1131,"level":16},"\u002Fposts\u002Ftags-overflow-demo",[130,131,1124,1125,1126,1127,929],"Tailwind","UI","Interaction","Typography","2026-05-12","标签很多的时候会发生什么",[],"这篇文章用于测试大量标签在文章卡片底部的换行、分隔符和 hover 下划线动画。 标签数量多的时候，列表卡片很容易变得拥挤。这个占位内容用来测试标签换行之后是否仍然易读，以及 hover 动画是否不会影响布局。",{"path":1122,"category":128,"tags":1133,"created":1128,"draft":12,"id":1134,"title":1135,"titles":1136,"content":1137,"level":23},[130,131,1124,1125,1126,1127,929],"\u002Fposts\u002Ftags-overflow-demo#标签","标签",[1129],"这些标签没有实际含义，只是为了撑开界面。",{"path":1139,"category":128,"tags":1140,"created":62,"draft":12,"id":1139,"title":1146,"titles":1147,"content":1148,"level":16},"\u002Fposts\u002Fvery-long-tags-demo",[1141,1142,1143,1144,1125,1145],"SuperLongFrontendExperimentTag","一个非常非常长的中文标签名字","NuxtContentRenderingPipeline","ResponsiveLayoutBoundaryCase","Hover","超长标签截断测试",[],"这篇文章专门用于测试标签名称很长时，文章卡片底部的单行截断、加号计数和展开交互。 这是一篇用于测试超长标签的占位文章。 当标签本身很长时，组件应该尽量显示能放下的内容，并把剩余标签折叠成 +N。展开后可以观察卡片固定高度下的溢出表现。",{"categories":1150,"tags":1171},[1151,1156,1158,1160,1162,1165,1167,1169],{"name":449,"count":1152,"children":1153},8,[1154,1155],{"name":450,"count":192},{"name":131,"count":16},{"name":128,"count":36,"children":1157},[],{"name":6,"count":23,"children":1159},[],{"name":371,"count":23,"children":1161},[],{"name":899,"count":16,"children":1163},[1164],{"name":900,"count":16},{"name":61,"count":16,"children":1166},[],{"name":924,"count":16,"children":1168},[],{"name":10,"count":16,"children":1170},[],[1172,1173,1174,1175,1176,1177,1178,1179,1180,1181,1182,1183,1184,1185,1186,1187,1188,1189,1190,1191,1192,1193,1194,1195,1196,1197,1198,1199,1200,1201,1202,1203,1204,1205,1206,1207,1208,1209,1210,1211,1212,1213,1214,1215,1216,1217,1218,1219],{"name":455,"count":192},{"name":452,"count":192},{"name":583,"count":180},{"name":130,"count":36},{"name":131,"count":36},{"name":9,"count":23},{"name":61,"count":23},{"name":132,"count":23},{"name":929,"count":23},{"name":1125,"count":23},{"name":903,"count":16},{"name":377,"count":16},{"name":454,"count":16},{"name":453,"count":16},{"name":373,"count":16},{"name":991,"count":16},{"name":1142,"count":16},{"name":945,"count":16},{"name":927,"count":16},{"name":928,"count":16},{"name":944,"count":16},{"name":58,"count":16},{"name":433,"count":16},{"name":60,"count":16},{"name":902,"count":16},{"name":947,"count":16},{"name":374,"count":16},{"name":1145,"count":16},{"name":434,"count":16},{"name":1126,"count":16},{"name":815,"count":16},{"name":926,"count":16},{"name":10,"count":16},{"name":375,"count":16},{"name":946,"count":16},{"name":948,"count":16},{"name":1117,"count":16},{"name":911,"count":16},{"name":1143,"count":16},{"name":1144,"count":16},{"name":327,"count":16},{"name":1141,"count":16},{"name":133,"count":16},{"name":376,"count":16},{"name":1124,"count":16},{"name":59,"count":16},{"name":1127,"count":16},{"name":435,"count":16},{"id":1221,"title":104,"body":1222,"category":103,"cover":103,"created":103,"description":106,"draft":12,"extension":1311,"meta":1312,"navigation":1313,"path":102,"pinned":12,"readingMinutes":16,"seo":1314,"stem":1315,"tags":103,"updated":1316,"wordCount":1317,"__hash__":1318},"content\u002Findex.md",{"type":1223,"value":1224,"toc":1306},"minimark",[1225,1229,1240,1247,1251,1259,1262,1270,1273,1295,1297],[1226,1227,104],"h1",{"id":1228},"welcome-to-nuxt-content-starter",[1230,1231,1232,1233,1239],"p",{},"This is the main page displaying Markdown located at ",[1234,1235,1221],"a",{"href":1236,"rel":1237},"https:\u002F\u002Fgithub.com\u002Fnuxt\u002Fstarter\u002Fblob\u002Fcontent\u002Fcontent\u002Findex.md",[1238],"nofollow",".",[1230,1241,1242,1243,1246],{},"Move to ",[1234,1244,1245],{"href":5},"about"," page.",[1248,1249,113],"h2",{"id":1250},"manage-your-contents",[1230,1252,1253,1254,1258],{},"Create new pages or modify the existing ones in ",[1255,1256,1257],"code",{},"content\u002F"," directory.",[1248,1260,118],{"id":1261},"query-render-pages",[1230,1263,1264,1265],{},"You can find an example of querying contents and rendering them in a ",[1234,1266,1269],{"href":1267,"rel":1268},"https:\u002F\u002Fgithub.com\u002Fnuxt\u002Fstarter\u002Fblob\u002Fcontent\u002Fapp\u002Fpages\u002F%5B...slug%5D.vue",[1238],"catch-all page",[1248,1271,123],{"id":1272},"integrate-vue-component",[1274,1275,1277],"alert",{"color":1276},"green",[1230,1278,1279,1280,1284,1285,1290,1291,1294],{},"The current ",[1234,1281,1274],{"href":1282,"rel":1283},"https:\u002F\u002Fgithub.com\u002Fnuxt\u002Fstarter\u002Fblob\u002Fcontent\u002Fapp\u002Fcomponents\u002FAlert.vue",[1238]," and the ",[1234,1286,1289],{"href":1287,"rel":1288},"https:\u002F\u002Fgithub.com\u002Fnuxt\u002Fstarter\u002Fblob\u002Fcontent\u002Fapp\u002Fcomponents\u002FCounter.vue",[1238],"counter"," below are ",[1255,1292,1293],{},"Vue"," components integrated into the Markdown.",[1289,1296],{},[1230,1298,1299,1300,1305],{},"Checkout out the ",[1234,1301,1304],{"href":1302,"rel":1303},"https:\u002F\u002Fcontent.nuxt.com\u002Fdocs\u002Fgetting-started",[1238],"documentation"," to learn more.",{"title":159,"searchDepth":180,"depth":36,"links":1307},[1308,1309,1310],{"id":1250,"depth":23,"text":113},{"id":1261,"depth":23,"text":118},{"id":1272,"depth":23,"text":123},"md",{},true,{"title":104,"description":106},"index","2026-05-13",97,"hKXyPlqwGb9Nd8xzA84ifq6JscFG5ftTPDAZA8K1iaM",{"posts":1320,"taxonomies":2090},[1321],{"id":1322,"title":379,"body":1323,"category":2083,"cover":103,"created":378,"description":381,"draft":12,"extension":1311,"meta":2084,"navigation":1313,"path":369,"pinned":12,"readingMinutes":2003,"seo":2085,"stem":2086,"tags":2087,"updated":103,"wordCount":2088,"__hash__":2089},"content\u002Fposts\u002Fcodex-network-fix.md",{"type":1223,"value":1324,"toc":2072},[1325,1327,1330,1373,1375,1378,1381,1384,1387,1403,1406,1436,1439,1504,1507,1542,1545,1593,1600,1619,1649,1652,1828,1831,1844,1847,1878,1881,1883,1896,1903,1906,1914,1921,1927,1930,1933,1935,1942,1945,2007,2010,2047,2050,2053,2055,2057,2059,2065,2068],[1248,1326,386],{"id":386},[1230,1328,1329],{},"我在服务器配置好 tailscale 后，原本正常工作的 codex cli 突然无法连接到网络。这个时候我还没有意识到是 tailscale 的问题，尽管现在复盘一下看起来好明显（刚刚配置好一个网络相关的服务，网络就突然不通了，还能是什么原因）。但是当时晚上我头脑不是很清醒，已经忘了刚刚配置了 tailscale，甚至还怀疑是中转站或者阿里云的网络问题。于是我开始了漫长的排查过程。",[1331,1332,1336],"pre",{"className":1333,"code":1334,"language":1335,"meta":159,"style":159},"language-bash shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","Reconnecting... waiting for network\nConnection failed: error sending request\n","bash",[1255,1337,1338,1356],{"__ignoreMap":159},[1339,1340,1342,1346,1350,1353],"span",{"class":1341,"line":16},"line",[1339,1343,1345],{"class":1344},"sBMFI","Reconnecting...",[1339,1347,1349],{"class":1348},"sfazB"," waiting",[1339,1351,1352],{"class":1348}," for",[1339,1354,1355],{"class":1348}," network\n",[1339,1357,1358,1361,1364,1367,1370],{"class":1341,"line":23},[1339,1359,1360],{"class":1344},"Connection",[1339,1362,1363],{"class":1348}," failed:",[1339,1365,1366],{"class":1348}," error",[1339,1368,1369],{"class":1348}," sending",[1339,1371,1372],{"class":1348}," request\n",[1248,1374,393],{"id":393},[1376,1377,399],"h3",{"id":399},[1230,1379,1380],{},"首先，我先测试了一下使用其他设备访问 api 是否正常，发现仍然可以正常使用。这说明中转站提供的 api 本身应该是能够正常访问的。",[1230,1382,1383],{},"然后，我尝试重启 codex，毕竟没有什么方法比重启更高效了。重启若干次后，问题依旧存在。",[1230,1385,1386],{},"我开始怀疑可能是服务器网络出现问题了。由于 ssh 连接正常，阿里云控制台的检测数据也没出现什么问题，我决定先绕过 codex，自己 curl 一下 Base URL。",[1331,1388,1390],{"className":1333,"code":1389,"language":1335,"meta":159,"style":159},"curl -v 中转站的BaseURL\n",[1255,1391,1392],{"__ignoreMap":159},[1339,1393,1394,1397,1400],{"class":1341,"line":16},[1339,1395,1396],{"class":1344},"curl",[1339,1398,1399],{"class":1348}," -v",[1339,1401,1402],{"class":1348}," 中转站的BaseURL\n",[1230,1404,1405],{},"结果：",[1331,1407,1409],{"className":1333,"code":1408,"language":1335,"meta":159,"style":159},"* Could not resolve host: 中转站的BaseURL\n* Closing connection\ncurl: (6) Could not resolve host: 中转站的BaseURL\n",[1255,1410,1411,1421,1428],{"__ignoreMap":159},[1339,1412,1413,1417],{"class":1341,"line":16},[1339,1414,1416],{"class":1415},"sMK4o","*",[1339,1418,1420],{"class":1419},"sTEyZ"," Could not resolve host: 中转站的BaseURL\n",[1339,1422,1423,1425],{"class":1341,"line":23},[1339,1424,1416],{"class":1415},[1339,1426,1427],{"class":1419}," Closing connection\n",[1339,1429,1430,1433],{"class":1341,"line":36},[1339,1431,1432],{"class":1344},"curl:",[1339,1434,1435],{"class":1419}," (6) Could not resolve host: 中转站的BaseURL\n",[1230,1437,1438],{},"说明应该确实不是 codex 的问题，请求卡在了 DNS 解析上。于是我手动指定了一个 DNS 服务器：",[1331,1440,1442],{"className":1333,"code":1441,"language":1335,"meta":159,"style":159},"$ nslookup 中转站的BaseURL 223.5.5.5\nServer:         223.5.5.5\nAddress:        223.5.5.5#53\n\nNon-authoritative answer:\nName:   中转站的BaseURL\nAddress: xx.xx.xx.xx\n",[1255,1443,1444,1459,1467,1475,1480,1488,1496],{"__ignoreMap":159},[1339,1445,1446,1449,1452,1455],{"class":1341,"line":16},[1339,1447,1448],{"class":1344},"$",[1339,1450,1451],{"class":1348}," nslookup",[1339,1453,1454],{"class":1348}," 中转站的BaseURL",[1339,1456,1458],{"class":1457},"sbssI"," 223.5.5.5\n",[1339,1460,1461,1464],{"class":1341,"line":23},[1339,1462,1463],{"class":1344},"Server:",[1339,1465,1466],{"class":1457},"         223.5.5.5\n",[1339,1468,1469,1472],{"class":1341,"line":36},[1339,1470,1471],{"class":1344},"Address:",[1339,1473,1474],{"class":1348},"        223.5.5.5#53\n",[1339,1476,1477],{"class":1341,"line":180},[1339,1478,1479],{"emptyLinePlaceholder":1313},"\n",[1339,1481,1482,1485],{"class":1341,"line":186},[1339,1483,1484],{"class":1344},"Non-authoritative",[1339,1486,1487],{"class":1348}," answer:\n",[1339,1489,1490,1493],{"class":1341,"line":192},[1339,1491,1492],{"class":1344},"Name:",[1339,1494,1495],{"class":1348},"   中转站的BaseURL\n",[1339,1497,1499,1501],{"class":1341,"line":1498},7,[1339,1500,1471],{"class":1344},[1339,1502,1503],{"class":1348}," xx.xx.xx.xx\n",[1230,1505,1506],{},"与之对比，若不指定：",[1331,1508,1510],{"className":1333,"code":1509,"language":1335,"meta":159,"style":159},"$ nslookup 中转站的BaseURL\n;; communications error to 127.0.0.53#53: timed out\n",[1255,1511,1512,1520],{"__ignoreMap":159},[1339,1513,1514,1516,1518],{"class":1341,"line":16},[1339,1515,1448],{"class":1344},[1339,1517,1451],{"class":1348},[1339,1519,1402],{"class":1348},[1339,1521,1522,1525,1528,1530,1533,1536,1539],{"class":1341,"line":23},[1339,1523,1524],{"class":1415},";;",[1339,1526,1527],{"class":1344}," communications",[1339,1529,1366],{"class":1348},[1339,1531,1532],{"class":1348}," to",[1339,1534,1535],{"class":1348}," 127.0.0.53#53:",[1339,1537,1538],{"class":1348}," timed",[1339,1540,1541],{"class":1348}," out\n",[1230,1543,1544],{},"看一下系统当前的 DNS 配置：",[1331,1546,1548],{"className":1333,"code":1547,"language":1335,"meta":159,"style":159},"$ cat \u002Fetc\u002Fresolv.conf\n# 此处省略若干行\nnameserver 127.0.0.53\noptions edns0 trust-ad\nsearch taile15594.ts.net\n",[1255,1549,1550,1560,1566,1574,1585],{"__ignoreMap":159},[1339,1551,1552,1554,1557],{"class":1341,"line":16},[1339,1553,1448],{"class":1344},[1339,1555,1556],{"class":1348}," cat",[1339,1558,1559],{"class":1348}," \u002Fetc\u002Fresolv.conf\n",[1339,1561,1562],{"class":1341,"line":23},[1339,1563,1565],{"class":1564},"sHwdD","# 此处省略若干行\n",[1339,1567,1568,1571],{"class":1341,"line":36},[1339,1569,1570],{"class":1344},"nameserver",[1339,1572,1573],{"class":1457}," 127.0.0.53\n",[1339,1575,1576,1579,1582],{"class":1341,"line":180},[1339,1577,1578],{"class":1344},"options",[1339,1580,1581],{"class":1348}," edns0",[1339,1583,1584],{"class":1348}," trust-ad\n",[1339,1586,1587,1590],{"class":1341,"line":186},[1339,1588,1589],{"class":1344},"search",[1339,1591,1592],{"class":1348}," taile15594.ts.net\n",[1230,1594,1595,1596,1599],{},"Ubuntu 上 ",[1255,1597,1598],{},"127.0.0.53"," 是 systemd-resolved 的本地 DNS Stub，我接着检查服务：",[1331,1601,1603],{"className":1333,"code":1602,"language":1335,"meta":159,"style":159},"systemctl status systemd-resolved --no-pager\n",[1255,1604,1605],{"__ignoreMap":159},[1339,1606,1607,1610,1613,1616],{"class":1341,"line":16},[1339,1608,1609],{"class":1344},"systemctl",[1339,1611,1612],{"class":1348}," status",[1339,1614,1615],{"class":1348}," systemd-resolved",[1339,1617,1618],{"class":1348}," --no-pager\n",[1331,1620,1622],{"className":1333,"code":1621,"language":1335,"meta":159,"style":159},"Active: active (running)\nStatus: \"Processing requests...\"\n",[1255,1623,1624,1635],{"__ignoreMap":159},[1339,1625,1626,1629,1632],{"class":1341,"line":16},[1339,1627,1628],{"class":1344},"Active:",[1339,1630,1631],{"class":1348}," active",[1339,1633,1634],{"class":1419}," (running)\n",[1339,1636,1637,1640,1643,1646],{"class":1341,"line":23},[1339,1638,1639],{"class":1344},"Status:",[1339,1641,1642],{"class":1415}," \"",[1339,1644,1645],{"class":1348},"Processing requests...",[1339,1647,1648],{"class":1415},"\"\n",[1230,1650,1651],{},"再看看 53 端口：",[1331,1653,1655],{"className":1333,"code":1654,"language":1335,"meta":159,"style":159},"$ sudo ss -luntp | grep ':53'\nudp   UNCONN 0      0                       127.0.0.54:53         0.0.0.0:*    users:((\"systemd-resolve\",pid=495,fd=16))                                   \nudp   UNCONN 0      0                    127.0.0.53%lo:53         0.0.0.0:*    users:((\"systemd-resolve\",pid=495,fd=14))                                   \ntcp   LISTEN 0      4096                    127.0.0.54:53         0.0.0.0:*    users:((\"systemd-resolve\",pid=495,fd=17))                                   \ntcp   LISTEN 0      4096                 127.0.0.53%lo:53         0.0.0.0:*    users:((\"systemd-resolve\",pid=495,fd=15))\n",[1255,1656,1657,1685,1728,1761,1796],{"__ignoreMap":159},[1339,1658,1659,1661,1664,1667,1670,1673,1676,1679,1682],{"class":1341,"line":16},[1339,1660,1448],{"class":1344},[1339,1662,1663],{"class":1348}," sudo",[1339,1665,1666],{"class":1348}," ss",[1339,1668,1669],{"class":1348}," -luntp",[1339,1671,1672],{"class":1415}," |",[1339,1674,1675],{"class":1344}," grep",[1339,1677,1678],{"class":1415}," '",[1339,1680,1681],{"class":1348},":53",[1339,1683,1684],{"class":1415},"'\n",[1339,1686,1687,1690,1693,1696,1699,1702,1705,1707,1710,1713,1716,1719,1722,1725],{"class":1341,"line":23},[1339,1688,1689],{"class":1344},"udp",[1339,1691,1692],{"class":1348},"   UNCONN",[1339,1694,1695],{"class":1348}," 0 ",[1339,1697,1698],{"class":1457},"     0",[1339,1700,1701],{"class":1348},"                       127.0.0.54:53",[1339,1703,1704],{"class":1348},"         0.0.0.0:",[1339,1706,1416],{"class":1419},[1339,1708,1709],{"class":1348},"    users:",[1339,1711,1712],{"class":1415},"((",[1339,1714,1715],{"class":1344},"\"systemd-resolve\"",[1339,1717,1718],{"class":1344},",pid",[1339,1720,1721],{"class":1348},"=495,fd=16",[1339,1723,1724],{"class":1415},"))",[1339,1726,1727],{"class":1348},"                                   \n",[1339,1729,1730,1732,1734,1736,1739,1742,1744,1746,1748,1750,1752,1754,1757,1759],{"class":1341,"line":36},[1339,1731,1689],{"class":1344},[1339,1733,1692],{"class":1348},[1339,1735,1695],{"class":1348},[1339,1737,1738],{"class":1348},"     0 ",[1339,1740,1741],{"class":1348},"                   127.0.0.53%lo:53",[1339,1743,1704],{"class":1348},[1339,1745,1416],{"class":1419},[1339,1747,1709],{"class":1348},[1339,1749,1712],{"class":1415},[1339,1751,1715],{"class":1344},[1339,1753,1718],{"class":1344},[1339,1755,1756],{"class":1348},"=495,fd=14",[1339,1758,1724],{"class":1415},[1339,1760,1727],{"class":1348},[1339,1762,1763,1766,1769,1771,1774,1777,1779,1781,1783,1785,1787,1789,1792,1794],{"class":1341,"line":180},[1339,1764,1765],{"class":1344},"tcp",[1339,1767,1768],{"class":1348},"   LISTEN",[1339,1770,1695],{"class":1348},[1339,1772,1773],{"class":1348},"     4096 ",[1339,1775,1776],{"class":1348},"                   127.0.0.54:53",[1339,1778,1704],{"class":1348},[1339,1780,1416],{"class":1419},[1339,1782,1709],{"class":1348},[1339,1784,1712],{"class":1415},[1339,1786,1715],{"class":1344},[1339,1788,1718],{"class":1344},[1339,1790,1791],{"class":1348},"=495,fd=17",[1339,1793,1724],{"class":1415},[1339,1795,1727],{"class":1348},[1339,1797,1798,1800,1802,1804,1807,1810,1812,1814,1816,1818,1820,1822,1825],{"class":1341,"line":186},[1339,1799,1765],{"class":1344},[1339,1801,1768],{"class":1348},[1339,1803,1695],{"class":1348},[1339,1805,1806],{"class":1457},"     4096",[1339,1808,1809],{"class":1348},"                 127.0.0.53%lo:53",[1339,1811,1704],{"class":1348},[1339,1813,1416],{"class":1419},[1339,1815,1709],{"class":1348},[1339,1817,1712],{"class":1415},[1339,1819,1715],{"class":1344},[1339,1821,1718],{"class":1344},[1339,1823,1824],{"class":1348},"=495,fd=15",[1339,1826,1827],{"class":1415},"))\n",[1230,1829,1830],{},"也就是说：",[1832,1833,1834,1838,1841],"ul",{},[1835,1836,1837],"li",{},"systemd-resolved 没死；",[1835,1839,1840],{},"53 端口也正常监听；",[1835,1842,1843],{},"但是 DNS 查询就是没有结果。",[1230,1845,1846],{},"我终于决定看看是不是 tailscale 的问题，于是我继续使用重启大法，结果没有任何效果。我只能先试着停掉 tailscale 服务：",[1331,1848,1850],{"className":1333,"code":1849,"language":1335,"meta":159,"style":159},"sudo systemctl stop tailscaled\nsudo systemctl restart systemd-resolved\n",[1255,1851,1852,1866],{"__ignoreMap":159},[1339,1853,1854,1857,1860,1863],{"class":1341,"line":16},[1339,1855,1856],{"class":1344},"sudo",[1339,1858,1859],{"class":1348}," systemctl",[1339,1861,1862],{"class":1348}," stop",[1339,1864,1865],{"class":1348}," tailscaled\n",[1339,1867,1868,1870,1872,1875],{"class":1341,"line":23},[1339,1869,1856],{"class":1344},[1339,1871,1859],{"class":1348},[1339,1873,1874],{"class":1348}," restart",[1339,1876,1877],{"class":1348}," systemd-resolved\n",[1230,1879,1880],{},"此时，我再次尝试 curl Base URL，发现可以正常访问了。至此，我终于确认问题出在 tailscale 上。",[1376,1882,406],{"id":406},[1331,1884,1886],{"className":1333,"code":1885,"language":1335,"meta":159,"style":159},"resolvectl status\n",[1255,1887,1888],{"__ignoreMap":159},[1339,1889,1890,1893],{"class":1341,"line":16},[1339,1891,1892],{"class":1344},"resolvectl",[1339,1894,1895],{"class":1348}," status\n",[1230,1897,1898],{},[1899,1900],"img",{"alt":1901,"src":1902},"resolvectl status","\u002Fimg\u002Fposts\u002Fcodex-network-fix\u002F1.png",[1230,1904,1905],{},"eth0 的 DNS 是：",[1331,1907,1912],{"className":1908,"code":1910,"language":1911},[1909],"language-text","100.100.2.136\n100.100.2.138\n","text",[1255,1913,1910],{"__ignoreMap":159},[1230,1915,1916,1917,1920],{},"这正是阿里云 ECS 默认的 VPC 内网 DNS。问题是 Tailscale 使用 ",[1255,1918,1919],{},"100.64.0.0\u002F10"," 作为自己的 IPv4 CGNAT 地址池。真正致命的是：Linux 上 Tailscale 默认会加一条防火墙规则，大意是：",[1922,1923,1924],"blockquote",{},[1230,1925,1926],{},"如果数据包源地址属于 100.64.0.0\u002F10\n但却不是从 tailscale0 接口进来的\n→ DROP",[1230,1928,1929],{},"这是 tailscale 防止别人伪造 tailscale IP 的安全机制，官方文档也明确说明了这一点。",[1230,1931,1932],{},"所以这一切就很好理解了，这是 tailscale 和 阿里云 VPC 内网 DNS 冲突导致的乌龙。",[1248,1934,413],{"id":413},[1230,1936,1937,1938,1941],{},"知道原因之后，这个问题并不难解决。我这台机器并不需要依赖阿里云的 PrivateZone 等 VPC 私有 DNS 功能，所以最直接的办法就是不再使用 ",[1255,1939,1940],{},"100.100.2.136\u002F138"," 作为系统 DNS。",[1230,1943,1944],{},"我的服务器使用 Netplan，因此单独增加一个配置：",[1331,1946,1953],{"className":1947,"code":1948,"filename":1949,"highlights":1950,"language":1952,"meta":159,"style":159},"language-yaml shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","network:\n  version: 2\n  ethernets:\n    eth0:\n      dhcp4-overrides:\n        use-dns: false\n      nameservers:\n        addresses:\n          - 223.5.5.5\n          - 223.6.6.6\n","\u002Fetc\u002Fnetplan\u002F99-custom-dns.yaml",[1951],0,"yaml",[1255,1954,1955,1960,1965,1970,1975,1980,1985,1990,1995,2001],{"__ignoreMap":159},[1339,1956,1957],{"class":1341,"line":16},[1339,1958,1959],{},"network:\n",[1339,1961,1962],{"class":1341,"line":23},[1339,1963,1964],{},"  version: 2\n",[1339,1966,1967],{"class":1341,"line":36},[1339,1968,1969],{},"  ethernets:\n",[1339,1971,1972],{"class":1341,"line":180},[1339,1973,1974],{},"    eth0:\n",[1339,1976,1977],{"class":1341,"line":186},[1339,1978,1979],{},"      dhcp4-overrides:\n",[1339,1981,1982],{"class":1341,"line":192},[1339,1983,1984],{},"        use-dns: false\n",[1339,1986,1987],{"class":1341,"line":1498},[1339,1988,1989],{},"      nameservers:\n",[1339,1991,1992],{"class":1341,"line":1152},[1339,1993,1994],{},"        addresses:\n",[1339,1996,1998],{"class":1341,"line":1997},9,[1339,1999,2000],{},"          - 223.5.5.5\n",[1339,2002,2004],{"class":1341,"line":2003},10,[1339,2005,2006],{},"          - 223.6.6.6\n",[1230,2008,2009],{},"随后",[1331,2011,2013],{"className":1333,"code":2012,"language":1335,"meta":159,"style":159},"sudo chmod 600 \u002Fetc\u002Fnetplan\u002F99-custom-dns.yaml\nsudo netplan generate\nsudo netplan try\n",[1255,2014,2015,2028,2038],{"__ignoreMap":159},[1339,2016,2017,2019,2022,2025],{"class":1341,"line":16},[1339,2018,1856],{"class":1344},[1339,2020,2021],{"class":1348}," chmod",[1339,2023,2024],{"class":1457}," 600",[1339,2026,2027],{"class":1348}," \u002Fetc\u002Fnetplan\u002F99-custom-dns.yaml\n",[1339,2029,2030,2032,2035],{"class":1341,"line":23},[1339,2031,1856],{"class":1344},[1339,2033,2034],{"class":1348}," netplan",[1339,2036,2037],{"class":1348}," generate\n",[1339,2039,2040,2042,2044],{"class":1341,"line":36},[1339,2041,1856],{"class":1344},[1339,2043,2034],{"class":1348},[1339,2045,2046],{"class":1348}," try\n",[1230,2048,2049],{},"值得注意的是这里并不是禁用 DHCP，只是不接受 DHCP 下发的 DNS。IP 地址和网关这些仍然照常通过 DHCP 获取。因为是在远程服务器上操作网络配置，我没有直接 netplan apply，这样即使配置有问题导致 SSH 断开，没有手动确认的话也会自动回滚。",[1230,2051,2052],{},"最后重新测试，全部恢复正常。",[1248,2054,420],{"id":420},[1230,2056,422],{},[1376,2058,427],{"id":427},[1230,2060,2061,2062,2064],{},"这里采用的方案是直接将系统 DNS 从阿里云默认的 ",[1255,2063,1940],{}," 换成公共 DNS，优点是简单直接，但也有一个明显的限制：如果机器需要使用 PrivateZone、ECS 私网域名等依赖阿里云内网 DNS 的功能，这些解析可能会失效。",[1230,2066,2067],{},"更完整的做法应该是保留阿里云默认 DNS，同时针对 tailscale 的 100.64.0.0\u002F10 防火墙规则做例外处理。只是对于单纯跑公网服务、API 和 tailscale 组网的机器来说，直接更换 DNS 已经足够省事。",[2069,2070,2071],"style",{},"html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}",{"title":159,"searchDepth":180,"depth":36,"links":2073},[2074,2075,2079,2080],{"id":386,"depth":23,"text":386},{"id":393,"depth":23,"text":393,"children":2076},[2077,2078],{"id":399,"depth":36,"text":399},{"id":406,"depth":36,"text":406},{"id":413,"depth":23,"text":413},{"id":420,"depth":23,"text":420,"children":2081},[2082],{"id":427,"depth":36,"text":427},[371],{},{"title":379,"description":381},"posts\u002Fcodex-network-fix",[373,374,375,376,377],1840,"9QcVd3mni1Rm-rDV09yOBn56IUBUr8fCkSWtfDb44Rs",{"categories":2091,"tags":2111},[2092,2096,2098,2100,2102,2105,2107,2109],{"name":449,"count":1152,"children":2093},[2094,2095],{"name":450,"count":192},{"name":131,"count":16},{"name":128,"count":36,"children":2097},[],{"name":6,"count":23,"children":2099},[],{"name":371,"count":23,"children":2101},[],{"name":899,"count":16,"children":2103},[2104],{"name":900,"count":16},{"name":61,"count":16,"children":2106},[],{"name":924,"count":16,"children":2108},[],{"name":10,"count":16,"children":2110},[],[2112,2113,2114,2115,2116,2117,2118,2119,2120,2121,2122,2123,2124,2125,2126,2127,2128,2129,2130,2131,2132,2133,2134,2135,2136,2137,2138,2139,2140,2141,2142,2143,2144,2145,2146,2147,2148,2149,2150,2151,2152,2153,2154,2155,2156,2157,2158,2159],{"name":455,"count":192},{"name":452,"count":192},{"name":583,"count":180},{"name":130,"count":36},{"name":131,"count":36},{"name":9,"count":23},{"name":61,"count":23},{"name":132,"count":23},{"name":929,"count":23},{"name":1125,"count":23},{"name":903,"count":16},{"name":377,"count":16},{"name":454,"count":16},{"name":453,"count":16},{"name":373,"count":16},{"name":991,"count":16},{"name":1142,"count":16},{"name":945,"count":16},{"name":927,"count":16},{"name":928,"count":16},{"name":944,"count":16},{"name":58,"count":16},{"name":433,"count":16},{"name":60,"count":16},{"name":902,"count":16},{"name":947,"count":16},{"name":374,"count":16},{"name":1145,"count":16},{"name":434,"count":16},{"name":1126,"count":16},{"name":815,"count":16},{"name":926,"count":16},{"name":10,"count":16},{"name":375,"count":16},{"name":946,"count":16},{"name":948,"count":16},{"name":1117,"count":16},{"name":911,"count":16},{"name":1143,"count":16},{"name":1144,"count":16},{"name":327,"count":16},{"name":1141,"count":16},{"name":133,"count":16},{"name":376,"count":16},{"name":1124,"count":16},{"name":59,"count":16},{"name":1127,"count":16},{"name":435,"count":16},1791557902013]