# 调试记录 记录顺表练习过程中遇到的问题和当时的想法。 --- ## 2026-09-17 当前进度 | 函数 | 状态 | |---|---| | `q_init` | 可用(但见问题 3) | | `q_add` | 可用 | | `printall` | 可用 | | `q_addpro` | 能跑,但有 bug(见问题 1、2) | | `q_reduce` | 空函数,未写 | | `q_reducepro` | 写了,未测试(见问题 4) | --- ## 问题 1:`q_addpro` 平移时多搬了一次,把垃圾值带进了表 **现象**:插入之后,数据末尾会多出一个来源不明的值。 **复现**:表长 3、元素 `1 2 3`,执行 `q_addpro(&q, 3, 666)`, 打印结果为 `1 2 3 666 3801155 ...` —— `3801155` 不是任何一次输入的数据。 **待查**:循环的**起点**取的应该是哪一个下标? > 补充:同样的问题在表长 6 时也存在,只是那次垃圾值落到了打印范围之外, > 所以没被发现。**同一个 bug,换个尺寸才现形。** --- ## 问题 2:`q_addpro` 不检查插入位置是否合法 **现象**:`q_addpro(&q, 11, 1919810)` 在表长只有 5 时被接受, 程序不报错、不崩溃,静默跑完。 **实际后果**:扩容后 `max = 8`(合法下标 0~7),但数据被写到了 **下标 11**。 也就是往不属于自己的内存里写了 4 格 —— **堆越界**。 **记住的教训**: - 越界 ≠ 崩溃。Windows 上这类写坏通常无声无息,只在特定边界才炸。 - 防御性编程:函数应该先检查参数,发现非法就**立刻返回**, 并用返回值告诉调用方"我失败了"。 - 这几个函数都声明成 `int`,但从来没 `return` 过 —— 那个 `int` 就是留给这件事的。 **待定**:合法插入位置的范围是什么?(`num` 能不能等于表长?能不能负数?) 检测到非法之后,函数该怎么返回?调用方该怎么处理? --- ## 问题 3:`q->length` 从未被初始化 `q_init` 里只设了 `max`、`front`、`end`,**没有给 `length` 赋值**。 但 `q_add` 的扩容判断用的是 `q->length == q->max` —— 等于拿一个栈上的随机数来决定要不要扩容。 目前测试没踩到,是因为运气。换个输入就会出问题。 **待定**:`length` 和 `end` 到底是不是同一个东西?如果循环队列要用 `length`, 那它得先被管起来。 --- ## 问题 4:`q_reducepro` 的平移方向 删除和插入的平移**方向是相反的**,但原理相通。 `q_reducepro` 目前写的是 `for (size_t i = num; i < q->end + 1; i++)`, 还没测过。另外注意 `size_t` 是无符号类型。 **待测**: - [ ] 删 0 号位 - [ ] 删最后一个 - [ ] 删中间 - [ ] `*x` 里拿到的是不是被删掉的那个值 - [ ] 删除后表长有没有正确减少 --- ## 待办的测试用例 - [ ] 插入到 0 号位 - [ ] 插入到末尾(`num == end`) - [ ] 表"差一格满"时(`end == max - 1`)插入 - [ ] 非法位置:负数、大于表长 - [ ] 往空表里插入 --- ## 环境和工具备注 - 源文件目前是 **GBK** 编码,PowerShell 里中文能正常显示。 (注意:不要再加 `-fexec-charset=GBK`,会转两次反而乱码) - 编译命令:`gcc -Wall 1.c -o 1.exe` - 本机 **没有装 ASan**(AddressSanitizer),想抓内存越界得用别的办法。 - `printall` 用的边界是 `q->end` —— 如果 `end` 本身不可信,打印出来的就是假象。 --- ## 已经解决的(记录一下思路,别忘了) **扩容时 `realloc` 的大小算错了** 写成了 `realloc(q->start, sizeof(q->max))`,而 `sizeof(q->max)` 是 "一个 `int` 占几字节"(4),不是"总共要几字节"。 正确写法是 `个数 × 每个的大小`。当时误申请到了 4 字节, 后面的数据全被堆的元数据覆盖,打印出来是一堆每次都不同的随机数。 > **识别技巧**:数据每次运行都不一样 → 读到的不是自己的数据,是别人的内存。 **忘了把 `x` 真正写进去** 平移做完了,但没执行插入动作,参数 `x` 从头到尾没被用过。 编译器的 warning 里其实点了名(`unused parameter 'x'`)。 > **识别技巧**:编译器说某个参数"没被用过",八成是漏写了什么。