为什么这段代码通常能够运行但是有时会导致分段错误?

3

这是我的代码

#include <stdlib.h>
#include <string.h>
#include <stdio.h>

/**************************************************
a is a pointer to an array of strings
b is a string
This function appends b to *a
n is the number of strings currently "held" by *a
**************************************************/
int append(char ***a, char *b, unsigned long n) {
    if (a && *a && **a) {
        char** tmp = realloc(*a, (n + 1) * sizeof(**a));
        if (tmp) {
            tmp[n] = b;
            *a = tmp;
            return 0;
        }
    }
    return -1;
}

void test() {
    char *words[7] = { "food", "is", "good", "to", "eat,", "like", "pizza" };
    char** a = malloc(1 * sizeof(*a));

    for (int i = 0; i < 7; i++) {
        append(&a, words[i], i);
        int j = 0; 
        while (j <= i) 
            printf("%s ", a[j++]); 
        printf("\n");
    }
}

int main() {
    test();
    return 0;
}

代码总是能够编译并且没有任何警告。可执行文件运行的预期结果在95%的情况下是正确的,但约有5%的时间会出现“分段错误”。我知道错误发生在a[j++]处,但我不明白为什么。


1
如果事情“有时候”能够正常工作,那么很可能你正在调用“未定义行为”... - David C. Rankin
@DavidC.Rankin,而且分段错误使这个赌注成为一个明确的胜利 :) - Eugene Sh.
好的,谢谢大家。那么我在哪里调用了未定义行为? - waynemystir
4
请注意,成为三星程序员并不是一个称赞。你最好创建一个结构来保存指针数组,以及分配的大小和实际使用的大小。这样可以避免将数组大小增加一;这可能会导致非常差的性能(二次行为),因为在重新分配内存时数据被复制。但是,这不是崩溃的直接原因。 - Jonathan Leffler
另外,请注意这个链接:https://wandbox.org/permlink/zvsaK8OwfthbmY7U - Bob__
谢谢@Bob__。我只得到了“失败于0”? - waynemystir
1个回答

4

看看这一行:

if (a && *a && **a)

当你使用malloc为最初由a指向的一个元素分配空间时,你实际上没有初始化该内存。因此,**a未初始化,读取它被认为是未定义的行为。实际上,我怀疑有时分配给你的内存是空指针,而在其他情况下则不是,这就解释了其不稳定性。

我实际上认为你甚至不需要检查*a**a。只要指针a本身不是空指针,就可以修改它指向的指针(*a)。此外,在这里并不需要知道由*a指向的数组的第一个元素是否为空(**a)。因此,你可以将此检查替换为:

if (a)

我建议更进一步,甚至不需要为 a 分配初始数组,因为您永远不会实际读取存储在那里的值。
其他需要注意的事项:函数 append 返回一个状态码,通知操作是成功还是失败。每次调用 append 时检查该值是个好主意,以防它失败。您可能还想将外部的 if 语句更改为 assert,以便如果有人使用错误参数调用它,它会停止并报告违反前提条件而不是失败并返回错误代码。毕竟,如果问题是“你给了我不可能正确的参数”,这意味着代码中存在逻辑错误。
希望这有所帮助!

非常感谢@templatetypedef!我从未初始化过那块内存。而且我认为你说的关于不需要为a分配初始数组的想法也是正确的。在我确认之前,请给我一些时间来进行实验。再次感谢! - waynemystir
再次感谢@templatetypedef。您的答案和“其他要做的事情”确实很有帮助。我还开始查看您网站上的内容,发现它非常有用和有趣。谢谢! - waynemystir

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接