返回结构时出现分段错误

时间:2010-07-31 16:45:29

标签: c struct malloc segmentation-fault

我正在尝试做一件非常简单的事情 - 它正在读取一个文件,然后将其转换成一个将它分成行的字符。但是当我返回一个包含char **和size的结构时,我得到了Segmentation fault。我在这里读到:C segmentation fault before/during return statement它可能是“损坏的堆栈”。然而,我仍然不知道我做了什么来破坏它。这是我的代码:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include "comp_words.h"
#define BLOCK 4096

struct sized_str {
    char* str;
    long size;
};

struct sized_arr {
    char** content;
    int size;
};

struct sized_str readfile(char* name) {
    FILE *f;
    long filesize;
    char *buf;
    struct sized_str res;
    int r, p = 0;

    f = fopen(name, "r");
    fseek(f, 0, SEEK_END);
    filesize = ftell(f);
    rewind(f);
    buf = calloc(filesize + 1, sizeof(char));
    while ((r = fread(buf + p, sizeof(char), BLOCK, f))) {
        p += r;
    }
    res.str = buf;
    res.size = filesize + 1;

    return res;
}

struct sized_arr read_dict() {
    struct sized_str file_content;
    struct sized_arr result;
    char *buf, *buf_cpy, *buf_cpy_point, *line, **res;
    int i = 0, j, line_count = 0;

    file_content = readfile("/var/tmp/twl06.txt");
    buf = file_content.str;
    buf_cpy = (char*)malloc(file_content.size * sizeof(char));
    strcpy(buf_cpy, buf);
    buf_cpy_point = buf_cpy;

    while (strtok(buf_cpy_point, "\n\r")) {
        line_count++;
        buf_cpy_point = NULL;
    }

    res = (char**)malloc(sizeof(char*) * line_count);

    while ((line = strtok(buf, "\n\r"))) {
        res[i] = (char*)malloc(sizeof(char) * strlen(line));

        j = 0;
        while ((res[i][j] = tolower(line[j]))) {
            j++;
        }
        buf = NULL;
    }
    free(buf_cpy);
    result.size = line_count;
    result.content = res;

    return result;
}

// ...

int main (int argc, char** argv) {
    struct sized_str input;
    struct sized_arr dict;

    dict = read_dict();

    // ...
    return 0;

从read_dict函数返回时代码段错误。

2 个答案:

答案 0 :(得分:3)

至少乍一看,这似乎有几个问题。第一:

while ((line = strtok(buf, "\n\r"))) {

要使用strtok,您通常会先在首先传递所有缓冲区,然后对第一个参数进行后续调用“NULL”,直到strtok返回NULL(表示它已到达缓冲区的末尾)。 [编辑:进一步检查,显然这不是一个真正的错误 - 正如@Casablanca所指出的,他在循环中将buf设置为NULL,因此第二次和后续迭代实际上为第一个参数传递NULL - 所以当前代码有点难以理解,并且(至少可以说)有点脆弱,但实际上并不是错误的。]

其次,当您分配空间时,看起来您没有为终止NUL分配空间:

res[i] = (char*)malloc(sizeof(char) * strlen(line));

至少乍一看,它看起来应该是:

res[i] = malloc(strlen(line)+1);

[另外,sizeof(char)==1并从malloc转换回来可以掩盖未能#include <stdlib.h>在范围内获得正确原型的错误。]

你的其他一些代码并不完全错误,但让我感觉不那么理想。例如:

j = 0;
while ((res[i][j] = tolower(line[j]))) {
    j++;
}

这似乎是一种相当模糊的写作方式:

for (j=0; line[j] != '\0'; j++)
    res[i][j] = tolower((unsigned char)line[j]);

另请注意,当您调用tolower时,通常需要/想要将参数强制转换为unsigned char(传递负值会给出未定义的行为,以及带有重音符号,变音符号等的相当多的字符在char签名的典型情况下,通常会显示为负数。

你似乎也有内存泄漏 - read_dict调用readfile,它会分配一个缓冲区(calloc - 为什么不malloc?)并返回一个指向结构中该内存的指针。 read_dict接收结构,但除非我遗漏了某些东西,否则结构会超出范围而不会释放它所指向的内存。

我没有试图找到并解决你所看到的问题,而是立即重新开始。在我看来,你已经使问题变得比实际上复杂得多。如果我这样做,我可能会开始使用一个函数来分配空间并在空间中读取一行,这个顺序是这样的:

// Warning: Untested code.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

char *readline(FILE *file) { 
    char *buffer = NULL;
    size_t current_size = 1;
    char *temp;
    const int block_size = 256;

    do { 
        if (NULL == (temp = realloc(buffer, current_size+block_size)))
            break;
        buffer = temp;
        buffer[current_size-1] = '\0';
        if (fgets(buffer+current_size-1, block_size, file)==NULL) 
            return strlen(buffer) > 0 ? buffer : NULL;      
        current_size += block_size-1;
    } while (strchr(buffer, '\n') == NULL);

    strtok(buffer, "\n");
    if (NULL != (temp = realloc(buffer, strlen(buffer)+1)))
        buffer =temp;
    return buffer;
}

一旦它正常工作,读取文件中的所有行并将它们转换为大写如下:

// Warning: more untested code.
while (res[i] = readline(file)) {
    size_t j;
    for (j=0; res[i][j]; j++)
        res[i][j] = toupper((unsigned char)res[i][j]);
    ++i;
}

答案 1 :(得分:2)

在将每一行存储到结果数组中后,您似乎忘记增加i,因此最终将所有行存储到res[0]。但是你仍然在最后设置result.size = line_count,所以除了第一个之外的所有数组元素都是未定义的。在此循环结束时i++while ((line = strtok(buf, "\n\r")))应该修复它。