<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/">
    <channel>
        <title>静墨博客</title>
        <link>https://mar14z.xyz/</link>
        <description>静墨的个人博客，记录技术思考、产品感悟与生活点滴</description>
        <lastBuildDate>Sat, 10 Oct 2026 18:15:57 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <language>zh-CN</language>
        <item>
            <title><![CDATA[C/C++ 编程的十个易忽略点]]></title>
            <link>https://mar14z.xyz/article?slug=c-cpp-common-pitfalls</link>
            <guid isPermaLink="false">https://mar14z.xyz/article?slug=c-cpp-common-pitfalls</guid>
            <pubDate>Wed, 23 Sep 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[这篇整理 C/C++ 里几个容易忽略、但实际影响很大的点。基本都是踩过坑才会注意到的。 1. 未定义行为：编译器会假设它不会发生 未定义行为（UB）不是"结果随机"，而是整个程序的行为失去保证。一旦触发，编译器有权假设它不会发生，从而做出反直觉的优化。 c int i = 0; int a[4]; a[i] = i++; // i 在同一表达式里既读又写，UB 还有符号整数溢出、解引用空指针、越界]]></description>
            <content:encoded><![CDATA[
这篇整理 C/C++ 里几个容易忽略、但实际影响很大的点。基本都是踩过坑才会注意到的。

## 1. 未定义行为：编译器会假设它不会发生

未定义行为（UB）不是"结果随机"，而是整个程序的行为失去保证。一旦触发，编译器有权假设它不会发生，从而做出反直觉的优化。

```c
int i = 0;
int a[4];
a[i] = i++;   // i 在同一表达式里既读又写，UB
```

还有符号整数溢出、解引用空指针、越界访问、使用未初始化的变量。它们不报错，只在特定优化等级、特定平台上出错。

## 2. 先看生命周期，再看类型

C++ 里最容易被类型掩盖的，是对象的生命周期。

```cpp
std::string_view sv = std::string("hello");
std::cout << sv;   // 临时对象已析构，sv 悬空
```

`string_view` 不是所有者，它只是个指针加长度。类似的还有 `const char*` 指向局部数组、返回局部变量的引用。看到引用或视图类型，先问一句：它指向的东西还活着吗？

## 3. 数组会退化成指针

数组作为参数传递时退化成指针，长度信息丢失。

```cpp
void f(int a[10]) { sizeof(a); }   // 结果是 sizeof(int*)，不是 40
```

所以要么同时传长度，要么用 `std::array` 或 `std::span` 把长度带上。

## 4. RAII：把资源的生命周期绑到对象上

RAII 的核心就一句话：资源在构造函数里获取，在析构函数里释放。栈对象的析构是确定的，即使抛异常也会执行。

```cpp
{
    std::lock_guard<std::mutex> lock(mtx);   // 出作用域自动解锁
    // ...
}
```

手动 `new`/`delete`、`lock`/`unlock` 都可能因为提前 return 或异常而漏掉。用智能指针、容器、锁守卫替掉它们。

## 5. const 是承诺，不是装饰

`const` 表达的是"我不改"，编译器据此做检查和优化。它能发现逻辑错误，也能让接口意图更清楚。

```cpp
void print(const std::string& s);   // 明确：不会改 s
```

成员函数加 `const` 表示不修改对象状态；`constexpr` 更进一步，要求能在编译期求值。

## 6. 值语义是默认，引用只是选择

C++ 默认是值语义，拷贝是真实发生的。

```cpp
void f(std::vector<int> v);   // 传值，整个 vector 被拷贝
```

大对象传参用 `const&`，需要移动的用右值引用和 `std::move`。但要记住 `std::move` 只是把对象转成右值引用，它自己不移动任何东西。

## 7. 局部对象析构顺序确定，全局对象初始化顺序不确定

同一作用域内的局部对象，按构造的逆序析构，这点可以依赖。但不同编译单元之间的全局对象，初始化顺序是未定义的。

```cpp
// a.cpp
extern Logger logger;   // 可能在 logger 构造完成前就被使用
```

需要跨单元依赖时，用函数内静态局部变量（C++11 起保证线程安全的懒初始化）来兜底。

## 8. 头文件是编译期的文本复制

`#include` 就是把文件内容原样贴进来，不是模块导入。

```cpp
// a.h
int value;          // 定义，多个 .cpp 包含会重复定义
extern int value;   // 声明，才是头文件该放的
```

头文件里放声明，定义放 .cpp；函数靠 `inline`、模板和类内定义来避免重复定义问题。

## 9. 隐式类型转换要留心

C/C++ 会在很多地方默默转换类型，尤其是无符号和有符号混用。

```c
unsigned u = 0;
if (u - 1 > 0) { }   // 无符号下溢成 UINT_MAX，条件为真
```

`-1 > 0u` 也为真，因为 `-1` 被转成了无符号。指针和整数、窄化和宽化转换同理。开 `-Wall -Wextra` 能拦下大部分。

## 10. 能在编译期做的，别留到运行期

`constexpr`、`static_assert`、模板把计算和检查前移。

```cpp
static_assert(sizeof(void*) == 8, "only 64-bit");
constexpr int square(int x) { return x * x; }
```

编译期算出来的常量不占运行时间，编译期能报的错也不会等到用户那里。

---

这十点的共同点是：C/C++ 不会自动兜底，但它提供了一整套工具——类型系统、RAII、const、constexpr、编译器警告。用不用、有没有用对，取决于你是否清楚它们各自在哪里生效。]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[《重来》读书笔记：更简单的工作方式]]></title>
            <link>https://mar14z.xyz/article?slug=reading-notes-rework</link>
            <guid isPermaLink="false">https://mar14z.xyz/article?slug=reading-notes-rework</guid>
            <pubDate>Fri, 14 Aug 2026 00:00:00 GMT</pubDate>
            <description><![CDATA["计划是胡扯。玩起来之后才知道是怎么回事。" —— 《重来》 这本书来自37signals公司（后更名为Basecamp），两位创始人用最直接的语言挑战商业世界根深蒂固的偏见。 关于计划 我们习惯了在做事前制定详细计划，仿佛未来可以被预测。但《重来》说，计划源于恐惧——对未知的恐惧，对失控的恐惧。 更务实的做法是：起步，开始，在行动中调整。 这与精益创业的MVP理念不谋而合。先做出最小可行产品，扔]]></description>
            <content:encoded><![CDATA[
"计划是胡扯。玩起来之后才知道是怎么回事。" —— 《重来》

这本书来自37signals公司（后更名为Basecamp），两位创始人用最直接的语言挑战商业世界根深蒂固的偏见。

## 关于计划

我们习惯了在做事前制定详细计划，仿佛未来可以被预测。但《重来》说，计划源于恐惧——对未知的恐惧，对失控的恐惧。

更务实的做法是：起步，开始，在行动中调整。

这与精益创业的MVP理念不谋而合。先做出最小可行产品，扔出去，收集反馈，快速迭代。

## 关于加班

加班文化是一种集体幻觉。我们用时长来证明努力，用忙碌来掩饰无效。

真正的问题是：你在做什么？为什么？能否更高效？

## 关于会议

会议是效率的天敌。作者建议：没有明确议程的会议不要开；会议控制在半小时以内；站立会议可以提高效率。

现实中我见过太多冗长的会议，参与者在走神，组织者在念PPT。会议结束后，没有人记得讨论了什么。

## 关于完美

完美主义是拖延的另一个名字。70分的作品扔出去，得到的反馈远比100分但从未问世的"完美"作品有价值。

"完成比完美更重要。"这句话已经被说烂了，但每一代人都需要自己领悟。

## 关于招聘

雇人要慢，炒人要快。能力可以培养，但态度难以改变。一个负能量的员工对团队的伤害，远超他的产出。

## 关于竞争对手

不要盯着竞争对手做产品。他们的昨天可能是你的明天，也可能不是。专注于你的用户，理解他们的需求，比研究竞品更有价值。

## 关于规模

不要过早优化。当公司只有5个人时，不要制定10页的流程手册；当用户只有100人时，不要引入复杂的CRM系统。

规模带来复杂度。保持小而灵活，直到不得不变大。

## 我的反思

这本书的核心观点可以概括为：相信直觉，快速行动，保持精简。这些道理说起来简单，做起来却需要对抗组织惯性和社会期待。

每个团队、每个阶段的最优解都不同。关键不是照搬书中的做法，而是理解背后的逻辑，然后找到适合自己的方式。

---

这本书适合放在床头，不时翻阅，提醒自己：或许有些事，我们想得太复杂了。
]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[产品思维：如何在约束中做出好决定]]></title>
            <link>https://mar14z.xyz/article?slug=product-decision-mindset</link>
            <guid isPermaLink="false">https://mar14z.xyz/article?slug=product-decision-mindset</guid>
            <pubDate>Tue, 26 May 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[产品经理每天都在做决定。这个功能要不要做？这个按钮放左边还是右边？这个版本先上哪些功能？ 这些看似微小的决策，塑造了用户对产品的感知。 用户不知道自己要什么 亨利·福特说："如果我问用户想要什么，他们会说一匹更快的马。"用户能描述症状，但很难给出药方。 产品经理的职责不是简单地把用户需求翻译成功能，而是理解需求背后的动机。 用户说"想要更大的输入框"，可能真正需要的是"更方便输入"——但也许解决方]]></description>
            <content:encoded><![CDATA[
产品经理每天都在做决定。这个功能要不要做？这个按钮放左边还是右边？这个版本先上哪些功能？

这些看似微小的决策，塑造了用户对产品的感知。

## 用户不知道自己要什么

亨利·福特说："如果我问用户想要什么，他们会说一匹更快的马。"用户能描述症状，但很难给出药方。

产品经理的职责不是简单地把用户需求翻译成功能，而是理解需求背后的动机。

用户说"想要更大的输入框"，可能真正需要的是"更方便输入"——但也许解决方案是语音输入，而不是更大的框。

## 少即是多

每个功能都有成本：开发时间、测试负担、后续维护、用户认知成本。新手产品经理喜欢做加法，堆功能；成熟的产品经理懂得做减法。

"功能是负债，不是资产。"

当你在添加功能时，问自己：移除什么可以更好地让这个功能发光？

## 权衡是常态

没有完美的方案，只有适合的权衡。

- 要速度还是要质量？
- 要覆盖广度还是要深度？
- 要用户增长还是要收入？

好的产品经理不是找到"正确答案"，而是清晰理解每个选项的代价，然后做出当下最合适的决定。

## 数据很重要，但不是全部

数据驱动是这些年最流行的词。但迷信数据和排斥数据一样危险。

数据告诉你"是什么"和"多少"，但不会告诉你"为什么"和"应该是什么"。用户访谈、用户观察、设计师的直觉，这些定性方法同样重要。

## 接受不完美

每个版本都是妥协。资源有限，时间有限，信息有限。你永远没有足够的数据，永远有没考虑到的情况。

完美的产品只存在于PPT里。上线的产品都是在不确定中摸着石头过河。

## 失败是最好的老师

错误的选择是产品经理成长的必经之路。重要的是从失败中学习：为什么会做出那个决定？下次如何避免？

不要为错误的选择过度自责，也不要掩盖错误。坦诚的复盘比完美的人设更有价值。

## 优先级是核心技能

产品经理最重要的能力是什么？很多人会说是需求分析、用户调研、数据分析。但我认为，最核心的技能是优先级排序。

无穷无尽的需求，有限的资源，永远不够的时间。决定先做什么、后做什么、不做什么——这才是产品经理创造价值的地方。

---

产品设计是一场与约束的对话。好的产品经理不是能实现一切的人，而是懂得什么值得做、什么不值得做的人。
]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[编写可维护代码的十大原则]]></title>
            <link>https://mar14z.xyz/article?slug=clean-code-principles</link>
            <guid isPermaLink="false">https://mar14z.xyz/article?slug=clean-code-principles</guid>
            <pubDate>Wed, 18 Feb 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[代码不仅是给机器执行的指令，更是与未来维护者的对话。那些深夜debug的程序员，往往在诅咒昨天写代码的自己。 1. 命名即文档 变量名应该像谜语一样让人一眼猜出答案，而不是像密码一样需要解码。 javascript // 糟糕 const d = new Date(); const u = users.filter(u = u.a 18); // 优雅 const currentDate = ne]]></description>
            <content:encoded><![CDATA[
代码不仅是给机器执行的指令，更是与未来维护者的对话。那些深夜debug的程序员，往往在诅咒昨天写代码的自己。

## 1. 命名即文档

变量名应该像谜语一样让人一眼猜出答案，而不是像密码一样需要解码。

```javascript
// 糟糕
const d = new Date();
const u = users.filter(u => u.a > 18);

// 优雅
const currentDate = new Date();
const adultUsers = users.filter(user => user.age > 18);
```

## 2. 函数要小，只做一件事

函数应该像鸡蛋一样，小而完美。一个函数如果需要用"并且"、"或者"来描述它的职责，那就是在告诉你它太大了。

## 3. 注释是失败的表现

当你在代码里写注释解释"为什么这样做"时，其实是在承认代码本身没有说清楚。好的代码应该是自解释的。

不过也有例外：解释业务规则的注释、解释复杂算法的注释。

## 4. 拒绝重复

DRY原则（Don't Repeat Yourself）不只是为了减少代码量，更是为了避免维护噩梦。当你需要修改一段逻辑时，应该只改一个地方。

## 5. 错误处理要优雅

不要忽视错误，不要用try-catch吞掉异常。错误应该被妥善处理，或者明确地向上传递。

## 6. 边界条件要显式处理

不要假设输入总是合法的。空数组、null、undefined、负数——这些边界条件往往藏着最多的bug。

## 7. 减少嵌套层级

嵌套超过三层就要警惕。可以用早返回、提取函数、卫语句来减少嵌套。

## 8. 对象优于原始类型

当数据项超过三个时，考虑用对象或类来封装。这比散落的一堆变量更容易理解和传递。

## 9. 保持一致性

在代码库的每个角落，相同的概念应该用相同的名字。团队需要一份命名规范，或者至少在code review时保持警觉。

## 10. 重构要趁早

代码写出来的那一刻就开始腐化。当代码变得难以理解、充满补丁时，就是重构的信号，而不是等到它彻底崩塌。

---

写代码是一场与时间的博弈。今天的偷懒会成为明天的债务。好的程序员不只是让代码工作，而是让代码易于工作。
]]></content:encoded>
        </item>
    </channel>
</rss>