Martin Martin - 3 months ago 9
C Question

struct pointers to same memory address producing different data?

I have this simple code to read the lines of a file and store them in a 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;
}


This is the output when I run it:

lines: 2
max string len: 6

line 1: hello
line 2: world

line 1: hello
line 2: H��


For some reason when it reaches the second index in ptr->items, it prints gibberish output. But yet, if I throw some printf()'s in there to show the pointer addresses, they're exactly the same.

Valgrind also prints this when iterating over the char array the second time:

==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


But that really doesn't give me any clues in this case.

I'm using gcc 4.9.4 with glibc-2.24 if that matters.

Answer

list is an non-static local variable and using it after exiting its scope (returning from read_file in this case) will invoke undefined behavior because it will vanish on exiting its scope. Allocate it dynamically (typically on the heap) like

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

Adding code to check if malloc()s are successful will make your code better.