为什么此代码通常可以正常工作,但有时会产生细分错误?

时间:2019-07-12 20:29:22

标签: c pointers segmentation-fault

这是我的代码

#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 个答案:

答案 0 :(得分:4)

看看这一行:

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

malloc最初由a指向一个元素的空间时,您实际上从未初始化过该内存。结果,**a未初始化,因此读取它被视为未定义行为。实际上,我怀疑有时分配给您的内存在某些情况下是空指针,而在其他情况下则不是,这说明了脆弱性。

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

if (a)

我会走得更远,甚至不为a分配初始数组,因为您实际上不会读取存储在其中的值。

其他要做的事情:函数append返回一个状态代码,该状态代码通知操作是成功还是失败。最好在每次调用append时检查该值,以防万一失败。您可能还希望将外部if更改为assert,以便如果有人用错误的参数调用它,则它会暂停并报告违反先决条件的问题,而不是失败并显示错误代码。毕竟,如果问题是“您给了我不可能正确的论点”,则意味着代码中存在逻辑错误。

希望这会有所帮助!