2016-08-15 4 views
0

У меня есть этот простой код, чтобы прочитать строки файла и сохранять их в структуры:struct указатели на тот же адрес памяти, производящий разные данные?

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

struct filedata { 
    char **items; 
    int lines; 
};  

struct filedata *read_file(char *filename) { 
    FILE* file = fopen(filename, "r");  

    if (file == NULL) { 
     printf("Can't read %s \n", filename); 
     exit(1); 
    }  

    char rbuff;  

    int nlines = 0; // amount of lines 
    int chr = 0; // character count 
    int maxlen = 0; // max line length (to create optimal buffer) 
    int minlen = 2; // min line length (ignores empty lines with just \n, etc)  

    while ((rbuff = fgetc(file) - 0) != EOF) { 
     if (rbuff == '\n') { 
      if (chr > maxlen) { 
       maxlen = chr + 1; 
      }  

      if (chr > minlen) { 
       nlines++; 
      }  

      chr = 0; 
     }  

     else { 
      chr++; 
     } 
    }  

    struct filedata *rdata = malloc(sizeof(struct filedata)); 
    rdata->lines = nlines;  

    printf("lines: %d\nmax string len: %d\n\n", nlines, maxlen); 
    rewind(file);  

    char *list[nlines];  

    int buffsize = maxlen * sizeof(char); 
    char buff[buffsize];  

    int i = 0; 
    while (fgets(buff, buffsize, file)) { 
     if (strlen(buff) > minlen) { 
      list[i] = malloc(strlen(buff) * sizeof(char) + 1); 
      strcpy(list[i], buff); 
      i++; 
     } 
    }  

    rdata->items = (char **)list; 
    fclose(file);  

    int c = 0; 
    for (c; c < rdata->lines; c++) { 
     printf("line %d: %s\n", c + 1, rdata->items[c]); 
    }  

    printf("\n"); 
    return rdata; 
}  

int main(void) { 
    char fname[] = "test.txt"; 
    struct filedata *ptr = read_file(fname);  

    int c = 0; 
    for (c; c < ptr->lines; c++) { 
     printf("line %d: %s\n", c + 1, ptr->items[c]); 
    }  

    return 0; 
} 

Это выход, когда я запускаю его:

lines: 2 
max string len: 6 

line 1: hello 
line 2: world 

line 1: hello 
line 2: H�� 

По какой-то причине, когда он достигает второй индекс в пунктах ptr->, он печатает вывод таблеток. Но все же, если я нахожу там какие-то printf(), чтобы показать адреса указателей, они точно такие же.

Valgrind также печатает это когда итерация массив символов во второй раз:

==3777== Invalid read of size 8 
==3777== at 0x400AB3: main (test.c:81) 
==3777== Address 0xfff000540 is on thread 1's stack 
==3777== 240 bytes below stack pointer 

Но что на самом деле не дает мне никаких подсказок в этом случае.

Я использую gcc 4.9.4 с glibc-2.24, если это имеет значение.

+0

'(rbuff = fgetc (file) - 0)! = EOF' сравнение' char' с 'EOF' не кажется хорошей идеей, потому что это делает невозможным отличать действительный символ (байт) с' EOF'. Почему бы не использовать 'int' для' rbuff'? – MikeCAT

+1

'list' в' rdata-> items = (char **) list; '- локальный массив, который не может использоваться после возвращения функции. Кроме того, удалите бросок, это вам не поможет. – Kninnug

+0

@MikeCAT Я не знал, что 'int' может содержать персонажа тоже, но, похоже, он работает. Знаете ли вы ссылку на хороший ресурс, который объясняет это? – Martin

ответ

0

list является нестатическим локальным переменным и использовать ее после выхода из его объема (возвращающийся из read_file в данном случае) будет вызывать неопределенное поведение, поскольку она будет исчезать при выходе его объема. Выделяют динамически (как правило, в куче), как

char **list = malloc(sizeof(char*) * nlines); 

Добавление кода, чтобы проверить, если malloc() s успешно сделает ваш код лучше.

+0

Спасибо. Это имеет смысл. Я, должно быть, пропустил код 20 раз, но пропустил эту подлая часть. Это было действительно запутанно, поскольку во второй части было напечатано «привет», поэтому я подумал, возможно, это был компилятор или libc, lol :) – Martin

0

Переменная list является местной для read_file, но вы пишете указатель на list в rdata->items. Когда read_file возвращает, rdata->items является обвисшим указателем, и доступ к нему - неопределенное поведение.