Skip to content

docs(c): 完善数组与 C 字符串练习参考答案 - #153

Merged
Charliechen114514 merged 3 commits into
Awesome-Embedded-Learning-Studio:mainfrom
xiaoshuaijie:exexcise10
Aug 20, 2026
Merged

docs(c): 完善数组与 C 字符串练习参考答案#153
Charliechen114514 merged 3 commits into
Awesome-Embedded-Learning-Studio:mainfrom
xiaoshuaijie:exexcise10

Conversation

@xiaoshuaijie

Copy link
Copy Markdown
Contributor

变更类型

  • 现有文章改进
  • Bug 修复
  • 新增文章
  • 翻译或双语同步
  • 其他

变更说明

完善 C 语言教程中《数组深入》和《C 字符串与缓冲区安全》的练习参考答案,帮助读者通过完整示例理解数组操作、动态内存管理以及字符串缓冲区安全。

本次主要修改包括:

  • 完善《数组深入》的练习参考答案
    • 补充矩阵转置和矩阵乘法的完整实现
    • 说明二维数组参数和 VLA 维度的含义
    • 补充 VLA 与 malloc 的对比示例
    • 说明两种分配方式的生命周期、失败处理及嵌入式场景取舍
  • 完善《C 字符串与缓冲区安全》的练习参考答案
    • 补充安全字符串复制与拼接示例
    • 补充基于 vsnprintf 的格式化字符串扩展答案
    • 补充字符串分割函数的完整实现
    • 说明字符串截断、空字符结尾、字段生命周期等注意事项
  • 为相关示例补充运行结果和复杂度、边界条件说明

影响范围

  • 仅涉及:
    • documents/vol1-fundamentals/c_tutorials/10-arrays-deep-dive.md
    • documents/vol1-fundamentals/c_tutorials/11-c-strings-and-buffer-safety.md
  • 不涉及网站导航或 Frontmatter 结构
  • 不涉及英文翻译
  • 不涉及项目运行时代码

验证情况

  • 所有新增示例均通过 GCC C11 编译
  • 使用 -Wall -Wextra -Wpedantic -Werror 编译通过
  • 已实际运行并核对示例输出
  • pnpm check:links 通过
  • Frontmatter 校验通过
  • git diff --check 通过

关联 Issue / Discussion

@Charliechen114514

Copy link
Copy Markdown
Member

感谢大佬贡献!请允许我稍作审查,稍后会给出评论

@xiaoshuaijie

Copy link
Copy Markdown
Contributor Author

辛苦了

@Charliechen114514

Copy link
Copy Markdown
Member

感谢贡献——因为出差,电脑上没有WSL和mingw-gcc,所有示例代码我用 clang -std=c11 -Wall -Wextra -Wpedantic -Werror 实际编译运行过,数值输出(矩阵乘积、UTF-8 字节数等)全部正确,details 折叠容器、frontmatter、markdownlint 均符合仓库惯例。但有以下问题需要您麻烦看看:

1. str_split 实现与文档约定矛盾

文档约定(11-c-strings-and-buffer-safety.md的第530行,您可以在Github上确认一下,或者是在本地的编辑器上):

约定:保留空字段。例如 "a,,b," 会得到 "a""""b""" 四个字段

但答案收尾条件if (token_count < max_tokens && start != end) 中的 start != end丢弃末尾空字段。实际编译运行结果:

"a,,b," => 3 个字段: [a] [] [b]     (约定说应为 4 个)
"a,,b"  => 3 个字段: [a] [] [b]     (正确)
""      => 0 个字段
",,"    => 2 个字段: [] []

您可以考虑进行修改,或者修改代码亦或者修改一下约定文本,这个看您的处理方式~

2. safe_str_cat 参考答案不安全

strncat(dst, src, dst_size - strlen(dst) - 1);

注意到,这里的表达式的求值很有可能发生下溢,也就是 dst_size - strlen(dst) - 1下溢成巨大的 size_tstrncat 变成无界写入——这正是本篇"缓冲区安全"要教读者防的场景。您考虑一下?
另:if (total_length >= dst_size) 分支在 strncat 正确限长时是死代码,建议一并清理。这样的话代码会更加干净!

您看看这些问题是否属实呢?

修正 str_split 对末尾空字段的处理,并为 safe_str_copy 和 safe_str_cat 补充重叠区域、截断检测及有界扫描处理。
@xiaoshuaijie

xiaoshuaijie commented Aug 20, 2026

Copy link
Copy Markdown
Contributor Author

谢谢大佬提供的宝贵建议!经核对,您指出的两个问题都属实,我已经逐项修正。

1. str_split 实现与文档约定矛盾

原实现中,if (token_count < max_tokens && start != end)start != end 会丢弃末尾空字段,我没有考虑到我的代码的if (token_count < max_tokens && start != end)start != end的处理没有考虑到最后一个字段为空的情况,只是要求最后一个字段必须非空,没有进行相关的处理,因此没有满足“保留空字段”的约定。例如 "a,,b," 原本只会得到三个字段。

2. safe_str_cat 参考答案不安全

  • 首先,如果 dst 所指数组本身只有 dst_size 个字节,且其中根本没有 '\0',那么 strlen(dst) 在执行减法之前就已经越界读取,程序行为已经未定义,甚至还没有机会讨论后续的下溢问题。
  • 其次我没有考虑到需要满足strlen(dst) < dst_size这个条件才可以,不然会导致负数的存在,转换成无符号运算会出现问题,一旦用于计算的目标串长度不小于 dst_size,dst_size - dst_length - 1 的无符号运算也可能下溢为巨大的 size_t 值,导致strncat变成无界写入。,同时我了解到在满足if (dst_length >= dst_size)total_length >= dst_size不可能成立和不可以直接用strlen(dst)计算长度。可能会出现根本没有 '\0'的问题。 是我没有考虑到
  • safe_str_cat 在 src dst 重叠时结果不正确,例如char buffer[16] = "ab"; safe_str_cat(buffer, buffer, sizeof(buffer));会重复进行复制现在已修正,
  • 我的 safe_str_cat返回值存在截断无法被调用者检测,当前代码返回的是实际写入后的长度,没有区分是否发生截断,我已经改为返回完整拼接所需的总长度方便进行判断.

3.safe_str_copy问题

  • 我注意到我原来的safe_str_copy不支持部分重叠的源、目标区域。例如safe_str_copy(buffer + 1, buffer, size);会有问题,现在已修正.
  • safe_str_copy原来 返回 的是复制的长度,这个不是关心的,我们应该关心原长度 与 dst_size 的关系,现在已修正

对佬的致谢:

我在这方面的基础知识不是很牢固,只是一个即将大三的学生,想学习使用C++来完成嵌入式的开发,看了佬的教程后对我的收获很大,谢谢大佬的帮助与包容。之前没有深刻理解示例教程中的 strncpy(dst, src, sizeof(dst) - 1); // 最多复制 7 个字符的含义,没有想到负数等情况还有昨天的(unsigned)type >= EVENT_TYPE_Num 可以巧妙避免type为负数的情况,谢谢佬的教导。我将继续努力跟随你的脚步。
再次感谢大佬的细致审查和建议!

@Charliechen114514

Copy link
Copy Markdown
Member

谢谢大佬提供的宝贵建议!经核对,您指出的两个问题都属实,我已经逐项修正。

1. str_split 实现与文档约定矛盾

原实现中,if (token_count < max_tokens && start != end)start != end 会丢弃末尾空字段,我没有考虑到我的代码的if (token_count < max_tokens && start != end)start != end的处理没有考虑到最后一个字段为空的情况,只是要求最后一个字段必须非空,没有进行相关的处理,因此没有满足“保留空字段”的约定。例如 "a,,b," 原本只会得到三个字段。

2. safe_str_cat 参考答案不安全

  • 首先,如果 dst 所指数组本身只有 dst_size 个字节,且其中根本没有 '\0',那么 strlen(dst) 在执行减法之前就已经越界读取,程序行为已经未定义,甚至还没有机会讨论后续的下溢问题。
  • 其次我没有考虑到需要满足strlen(dst) < dst_size这个条件才可以,不然会导致负数的存在,转换成无符号运算会出现问题,一旦用于计算的目标串长度不小于 dst_size,dst_size - dst_length - 1 的无符号运算也可能下溢为巨大的 size_t 值,导致strncat变成无界写入。,同时我了解到在满足if (dst_length >= dst_size)total_length >= dst_size不可能成立, 是我没有考虑到

3.safe_str_copy问题

  • 我注意到我原来的safe_str_copy不支持部分重叠的源、目标区域。例如safe_str_copy(buffer + 1, buffer, size);会有问题,现在已修正.
  • safe_str_copy原来 返回 的是复制的长度,这个不是关心的,我们应该关心原长度 与 dst_size 的关系,现在已修正
  • safe_str_cat 在 src dst 重叠时结果不正确,例如char buffer[16] = "ab"; safe_str_cat(buffer, buffer, sizeof(buffer));会重复进行复制现在已修正,同时我的 safe_str_cat返回值存在截断无法被调用者检测,当前代码返回的是实际写入后的长度,没有区分是否发生截断,我已经改为返回完整拼接所需的总长度方便进行判断.

对佬的致谢:

我在这方面的基础知识不是很牢固,只是一个即将大三的学生,想学习使用C++来完成嵌入式的开发,看了佬的教程后对我的收获很大,谢谢大佬的帮助与包容。之前没有深刻理解示例教程中的 strncpy(dst, src, sizeof(dst) - 1); // 最多复制 7 个字符的含义,没有想到负数等情况还有昨天的(unsigned)type >= EVENT_TYPE_Num 可以巧妙避免type为负数的情况,谢谢佬的教导。我将继续努力跟随你的脚步。 再次感谢大佬的细致审查和建议!

收到您的回复,内容我快速审查尚未发现问题!您的注释包括代码风格相当不错👍,这里真的点个赞。
(PS: 本人真的不是佬,不敢当这个字,我也在尝试探索C++嵌入式的学习, 这方面上我们是一致的!)

另外,我最近也在头大进一步的重写C++在嵌入式上应用的教程。如果有任何希望添加的内容和反馈的意见,不用担心,随便提出您任何想看的领域,我这边会进行排期,再出差结束后优先开始。

最后的最后,真的很感谢您热情的贡献!向您鞠个躬!这个PR可以合入了!

@Charliechen114514
Charliechen114514 merged commit 8da6d4f into Awesome-Embedded-Learning-Studio:main Aug 20, 2026
4 checks passed
@xiaoshuaijie

xiaoshuaijie commented Aug 20, 2026

Copy link
Copy Markdown
Contributor Author

好的好的谢谢老师,你可以看看(https://github.com/Jiu-xiao/libxr.git) 这个项目他的C++开发嵌入式 比较完善

@Charliechen114514

Copy link
Copy Markdown
Member

好的好的谢谢老师,你可以看看(https://github.com/Jiu-xiao/libxr.git) 这个项目他的C++开发嵌入式 比较完善

好嘞,直接点star了,哈哈

@xiaoshuaijie
xiaoshuaijie deleted the exexcise10 branch August 21, 2026 04:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

2 participants