使用C打印指向字符串的指针数组会遗漏最后一个字符串

时间:2018-06-14 19:15:24

标签: c arrays pointers stdout

我希望创建一个指向从C中的文件读取的字符串的指针数组。但是当我尝试打印出复制到stdout的字符串时,文件的最后一行总是被省略。 该计划有时也会遇到segmentation fault我无法完全消除的问题。它发生在大约2次中有5次。

这是我的input.c代码:

#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "input.h"

#define MAXLINES 5000


void writelines(char *arr[], int l);

char *read_lines[MAXLINES];

void get_input(const char *fp) {
    FILE *contents;
    char *line;
    char *temp;
    size_t len;
    ssize_t read;
    int i;
    i = 0;
    contents = fopen(fp, "r");
    if (contents == NULL)
        exit(EXIT_FAILURE);
    while ((read = getline(&line, &len, contents)) != -1) {
        if ((temp = (char *) malloc(strlen(line) + 1)) == NULL) {
            printf("Could not allocate required memory.");
            exit(EXIT_FAILURE);
        }
        else {
            line[strlen(line) - 1] = '\0';
            strcpy(temp, line);
            read_lines[i++] = temp;
        }
    }
    fclose(contents);
    free(line);
    free(temp);
    writelines(read_lines, i);
    exit(EXIT_SUCCESS);
}

void writelines(char *arr[], int l) {
    int i;

    for (i = 0; i < l; i++) {
        printf("%s\n", arr[i]);
    }
}

我的main.c文件是:

#include <stdio.h>
#include "input.h"

int main(int argc, char *argv[]) {
    if (argc == 1)
        printf("Please provide a valid source code file.\n");
    else
        get_input(*(++argv));

    return 0;
}

我使用gcc main.c input.c -Wall进行编译,没有任何警告或错误。

使用gdb我可以确认该过程正常运行 当遇到分段错误时,后向跟踪会显示对strlen的调用,显然会失败。

1 个答案:

答案 0 :(得分:1)

来自documentation

  

如果* lineptr为NULL,则getline()将分配用于存储该行的缓冲区,该缓冲区应由用户程序释放。 (在这种情况下,忽略* n中的值。)

但是在您的情况下,您第一次将未初始化的值传递给getline,因此getline认为它可以写入该非法位置,这是未定义的行为(这解释了&#34;它发生在5次中的2次&#34;事情)

第一个修复应该是初始化line

char *line = NULL;

那么,为什么要创建line副本,并且您没有释放line(内存泄漏)并且您没有重置它到NULL。所以下次getline重用前一个缓冲区,这个缓冲区可能不够长,无法容纳下一行。

修复只是存储该行:

read_lines[i++] = line;

然后设置line = NULL,以便getline为下一行分配正确的len。并删除malloc代码,它没用。

固定部分(您不需要在len上传递指针,它会被忽略):

line = NULL;
while ((read = getline(&line, NULL, contents)) != -1) {
         read_lines[i++] = line;
         line[strcspn(line, "\n")] = 0;  // strip off linefeed if there's one
         line = NULL;
 }

(换行条改编自Removing trailing newline character from fgets() input