diff --git a/1.c b/1.c index 8831b67..66c52c1 100644 --- a/1.c +++ b/1.c @@ -3,51 +3,27 @@ #include typedef struct { -int max; //列表最大长度 -int length; //列表现在的长度 -int *start; //内存地址起始位置 -int front; //列表起始索引 -int end; //列表结束索引 +int max; //Áбí×î´󳤶È +int length; //ÁбíÏÖÔڵij¤¶È +int *start; //ÄڴæµØַÆðʼλÖà +int front; //ÁбíÆðʼË÷Òý +int end; //Áбí½áÊøË÷Òý }queue; -int main(){ -queue q; -printf("输入整个列表长度"); -int length; -scanf("%d",&length); -int b; -printf("接下来依次输入放进列表的东西"); -for (int i = 0; i < length; i++) -{ - /* code */ -scanf("%d",&b); - - -} - - - - - - - return 0; -} - -int q_init(queue *q,int length){ //就是普通的初始化。要给他什么参数呢?首先就是queue的地址,然后是一个初始长度。然后初始化全0就行了。 +int q_init(queue *q,int length){ //¾ÍÊÇÆÕͨµijõʼ»¯¡£Ҫ¸øËûʲô²ÎÊýÄأ¿Ê×ÏȾÍÊÇqueueµĵØַ£¬ȻºóÊÇһ¸ö³õʼ³¤¶ȡ£Ȼºó³õʼ»¯ȫ0¾ÍÐÐÁˡ£ q->start=malloc(length*sizeof(int)); q->max=length; q->front=0; q->end=0; -q->max=0; return 0; } -int q_add(queue *q,int num){ //最后边加上元素 -if(q->length==q->max){ //容量不够了的自动扩容 +int q_add(queue *q,int num){ //×îºó±߼ÓÉÏԪËØ +if(q->length==q->max){ //ÈÝÁ¿²»¹»Á˵Ä×Զ¯À©ÈÝ q->max+=5; q->start=realloc(q->start,q->max*sizeof(int)); } @@ -56,17 +32,80 @@ q->end++; q->length++; } -int q_addpro(){ //任意地方加上元素,要进去一个索引编号和数值 +int q_addpro(queue *q,int num,int x){ //numÊDzåÈëλÖã¬xÊDzåÈëµÄԪËء£ÈÎÒâµط½¼ÓÉÏԪËأ¬Ҫ½øȥһ¸öË÷Òý±àºźÍÊýֵ +//ÏÈÅжϹ»²»¹»λÖüÓÉÏȥÏÈ +if(q->end>=q->max){ + q->max=q->max+5; + q->start=realloc(q->start,q->max*sizeof(int));//ÕâÀï¾ÍÊÇÀ©ÈݴúÂ룬°ÑmaxҲһÆðÔö¼ÓÁË +} +//ȻºóдƽÒƵĴúÂë¶Σ¬°ѼÓÈëµÄλÖõĶ«Î÷µ½endµÄËùÓж«Î÷ÍùºóƽÒÆһ¸ö +for (size_t i = q->end; i > num-1; i--) +{ + /* code */ + q->start[i+1]=q->start[i]; + + +} +q->end++; +q->start[num]=x; + } -int q_reduce(){ //最前面拿走元素 +int q_reduce(){ //×îǰÃæÄÃ×ßԪËØ } -int q_reducepro(){ //任意地方拿走元素,也是索引号+数值 +int q_reducepro(queue *q,int num ,int *x){ //ÈÎÒâµط½ÄÃ×ßԪËأ¬ҲÊÇË÷ÒýºÅ+Êýֵ + *x=q->start[num]; +for (size_t i = num; i < q->end+1; i++) +{ + /* code */ + q->start[i]=q->start[i+1]; -} \ No newline at end of file +} + +} + +int printall(queue *q){ //´òӡȫ²¿¶«Î÷ + printf("ÏÂÃæÊÇÒÀ´δòӡ"); + for (size_t i = 0; i < q->end; i++) + { + /* code */ + + printf("\n%d\n",q->start[i]); + } + return 0; +} + + + + +int main(){ +queue q; +printf("ÊäÈëÕû¸öÁбí³¤¶È"); +int length; +scanf("%d",&length); +int b; +q_init(&q,length); +printf("½ÓÏÂÀ´ÒÀ´ÎÊäÈë·ŽøÁбíµĶ«Î÷"); +for (int i = 0; i < length; i++) +{ + /* code */ +scanf("%d",&b); +q_add(&q,b); +} +printall(&q); +printf("\n½ÓÏÂÀ´²âÊÔÖмä²åÈë: 3ºÅλÖòåÈë666£¬5ºÅλÖòåÈë-77,11ºÅλÖòåÈë1919810"); +q_addpro(&q,3,666); +q_addpro(&q,5,-77); +q_addpro(&q,11,1919810); +printall(&q); + + + return 0; +} + diff --git a/调试记录.md b/调试记录.md new file mode 100644 index 0000000..a5afb63 --- /dev/null +++ b/调试记录.md @@ -0,0 +1,121 @@ +# 调试记录 + +记录顺表练习过程中遇到的问题和当时的想法。 + +--- + +## 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'`)。 + +> **识别技巧**:编译器说某个参数"没被用过",八成是漏写了什么。