簡體   English   中英

C 中的動態字符串數組無法正常工作

[英]Dynamic array of strings in C is not working properly

我在 C 中創建動態字符串數組時遇到了麻煩。 我沒有得到預期的結果,我想知道為什么?

readLine() function 將分別讀取每一行,並在必要時進行一些更改:

char *readLine(FILE *f, size_t *len)
{
    char *line = NULL;
    ssize_t nread;

    if (f == NULL)
    {
        return NULL;
    }

    if ((nread = getline(&line, len, f)) != -1)
    {
        if (line[nread - 1] == '\n')
        {
            line[strlen(line)-1] = '\0';
            *len = strlen(line);
        }
        return line;
    }
    else
    {
        return NULL;
    }
}

readFile() function 將在使用 readLine 讀取所有行之后返回一個字符串數組,然后將它們存儲到字符串數組中:

char **readFile(const char *filename, size_t *fileLen)
{
    char  *result;
    int    idx   = 0;
    char **array = calloc(1,  sizeof(char*) );

    if (filename == NULL || fileLen == NULL)
    {
        return NULL;
    }

    FILE *f = fopen(filename, "r");
    if (f == NULL)
    {
        return NULL;
    }

    while (1)
    {
        result = readLine(f, fileLen);
        if (result == NULL)
            break;
        else
        {
            *(array + idx) = malloc(LENGTH * sizeof(char *));
            strncpy(array[idx], result, strlen(result) + 1);
            idx++;
            array = realloc(array, (idx + 1) * sizeof(char *));
        }
    }

    return array;
}

在 main 中,我創建了一個臨時文件來測試我的功能,但它不能正常工作:

int main()
{    
    char   filename[] = "/tmp/prefXXXXXX";
    int    fd;
    size_t len = 0;
    FILE  *f;

    if (-1 == (fd = mkstemp(filename)))
        perror("internal error: mkstemp");

    if (NULL == (f = fdopen(fd, "w")))
        perror("internal error: fdopen");

    for (int i = 0; i < 10000; i++)
        fprintf(f, "%d\n", i);
    fclose(f);

    char **number = readFile(filename, &len);

    for (int i = 0; i < sizeof(number) / sizeof(number[0]); i++)
        printf("number[%i] = %s\n", i, number[i]);

    return 0;
}

當我執行程序時,我得到以下 output:

 number[0] = 0

我在這里做錯了什么?

該代碼中有很多問題,很難找到從哪里開始......

我們來看看每個 function。

char *readLine(FILE *f, size_t *len)
{
    char *line = NULL;
    ssize_t nread;

    if (f == NULL)
    {
        return NULL;
    }

    if ((nread = getline(&line, len, f)) != -1)
    {
        if (line[nread - 1] == '\n')
        {
            line[strlen(line)-1] = '\0';
            *len = strlen(line);
        }
        return line;
    }
    else
    {
        return NULL;
    }
}

這里沒有太大的錯誤。 但是geline的手冊頁告訴我們:

如果 *lineptr 在調用之前設置為NULL ,那么getline()將分配一個緩沖區來存儲該行。 即使getline()失敗,該緩沖區也應該由用戶程序free

如果nread==-1則不free緩沖區,而只return NULL; 可能導致 memory 泄漏。

您還應該檢查len==NUL是否已經使用f進行了檢查。

然后看下function:

char **readFile(const char *filename, size_t *fileLen)
{
    char  *result;
    int    idx   = 0;
    char **array = calloc(1,  sizeof(char*) );

    if (filename == NULL || fileLen == NULL)
    {
        return NULL;
    }

    FILE *f = fopen(filename, "r");
    if (f == NULL)
    {
        return NULL;
    }

    while (1)
    {
        result = readLine(f, fileLen);
        if (result == NULL)
            break;
        else
        {
            *(array + idx) = malloc(LENGTH * sizeof(char *));
            strncpy(array[idx], result, strlen(result) + 1);
            idx++;
            array = realloc(array, (idx + 1) * sizeof(char *));
        }
    }

    return array;
}

在此 function 中,如果您遇到return NULL;您將無法free(array) 出口。

readLinestrlen(result)放入filelen 為什么不用它來分配memory? 相反,您采用一些未知的固定長度LENGTH可能足以或可能不足以容納字符串。 相反,您應該使用fileLen+1strlen(result)+1 ,就像使用strncpy一樣。

您還使用了錯誤類型的大小。 您分配一個指向char的指針,而不是char* 由於char的大小定義為 1,您可以將大小部分放在這里。

然后, strncpy的長度參數應該保存目標的長度,而不是源的長度。 否則使用strncpy完全沒用。 正如您已經(應該)使用字符串長度來分配 memory 一樣,只需使用strncpy

然后,只是將fileLen傳遞給下一個 function 是沒有意義的。 readLine中,它意味着在readFile中沒有任何意義的行的長度。 相反,它應該意味着行數。 正如我們剛剛談到的主題......您應該將一些值傳遞給調用者。

最后,您不應該將realloc的返回值直接分配給您傳遞給它的變量。 如果出現錯誤,將返回NULL並且您不能再訪問或釋放舊指針。

該塊應如下所示:

        {
            array[idx] = malloc(fileLen+1);
            strcpy(array[idx], result);
            idx++;
            void *temp = realloc(array, (idx + 1) * sizeof(char *));
            if (temp != NULL)
              array = temp;
            // TODO: else <error handling>
        }
    }
    *fileLen = idx;
    return array;
}

這仍然存在一個缺陷,即您為另一個不使用的指針分配了 memory。 您可以將其更改為進一步優化。

最后是main的function:

int main()
{    
    char   filename[] = "/tmp/prefXXXXXX";
    int    fd;
    size_t len = 0;
    FILE  *f;

    if (-1 == (fd = mkstemp(filename)))
        perror("internal error: mkstemp");

    if (NULL == (f = fdopen(fd, "w")))
        perror("internal error: fdopen");

    for (int i = 0; i < 10000; i++)
        fprintf(f, "%d\n", i);
    fclose(f);

    char **number = readFile(filename, &len);

    for (int i = 0; i < sizeof(number) / sizeof(number[0]); i++)
        printf("number[%i] = %s\n", i, number[i]);

    return 0;
}

char **number = readFile(filename, &len); 你得到一個包含文件所有行的數組。 number是一個非常糟糕的名字。

如果出現錯誤,您可以從readFile return NULL 你應該在打電話后檢查一下。

然后你忘了 arrays 不是指針,指針也不是 arrays。 它們在許多地方的行為相似,但同時又非常不同。

i < sizeof(number) / sizeof(number[0])

這里的number是一個指針,它的大小是一個指針的大小。 number[0]也是一個指針。 不同的類型,但大小相同。 您想要的是從readFile獲得的行數。 使用該變量。

這部分應該如下所示:

    char **all_lines = readFile(filename, &len);

    if (all_lines != NULL)
    {
        for (int i = 0; i < len; i++)
            printf("all_lines[%i] = %s\n", i, all_lines[i]);

而且你不應該忘記你已經分配了很多 memory 你也應該釋放它們。 (當你終止你的程序時,這可能不是絕對必要的,但你應該記住在你身后清理)

    if (all_lines != NULL)
    {
        for (int i = 0; i < len; i++)
            printf("all_lines[%i] = %s\n", i, all_lines[i]);

        for (int i = 0; i < len; i++)
            free(all_lines[i];
        free(all_lines);
    }

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM