Search code examples
cpointersdynamicmallocallocation

Why is my array of pointers getting overwritten after dynamic allocation?


I'm working on a little C program for a class that reads the lines in from a file and then sorts them using qsort. Long story short, I am dynamically allocating memory for every line of a file, stored as a char*, in an array of char*. The reading in and storing ostensibly works fine based upon the output (see below), but when I print out the lines, they are all duplicates of the last line in the file. Can anyone point out my (most likely painfully obvious) error?

Here is the relevant code to the problem I'm currently running into:

char* trim_white_space(char* str);
char* get_line(FILE* infile, char temp[]);

int main(int argc, char* argv[]) {
    FILE* infile; 
    char* input_file = argv[1];
    int cnt = 0;
    char temp[MAX_LINE_LENGTH]; //to hold each line as it gets read in 
    char* tempPointer = temp;

    if (argc < 2) {
        printf("No input file provided");
        return EXIT_FAILURE;
    }

    //determine the number of lines in the file
    infile = fopen(input_file, "r");
    int num_lines_in_file = num_lines(infile);
    fclose(infile);

    //allocate pointers for each line
    char** lines = (char**) malloc(num_lines_in_file * sizeof(char*));

    //temporarily store each line, and then dynamically allocate exact memory for them
    infile = fopen(input_file, "r");
    for (cnt = 0; cnt != num_lines_in_file; cnt++) {
        tempPointer = get_line(infile, temp);  
        lines[cnt] = (char*) malloc(strlen(tempPointer) + 1);
        lines[cnt] = trim_white_space(tempPointer);
        printf("%d: %s\n", cnt, lines[cnt]);
    }

    fclose(infile);

    //print the unsorted lines (for debugging purposes)
    printf("Unsorted list:\n");
    for (cnt = 0; cnt != num_lines_in_file; cnt++) {
        printf("%s\n", lines[cnt]);
    }

char* get_line(FILE* infile, char temp[]) {
    fgets(temp, MAX_LINE_LENGTH-1, infile);
    char* pntr = temp;
    return pntr;
}

char *trimwhitespace(char *str)
{
  char *end;

  // Trim leading space
  while(isspace(*str)) str++;

  if(*str == 0)  // All spaces?
    return str;

  // Trim trailing space
  end = str + strlen(str) - 1;
  while(end > str && isspace(*end)) end--;

  // Write new null terminator
  *(end+1) = 0;

  return str;
}

I have this sample input file 5-1input.dat:

Hi guys
x2 My name is
Slim Shady
For real

And here's the output I'm getting:

user@user-VirtualBox ~/Desktop/Low-level/HW5 $ ./homework5-1 5-1input.dat 
0: Hi guys
1: x2 My name is
2: Slim Shady
3: For real
Unsorted list:
For real
For real
For real
For real

Solution

  • As in the comments, you should change your loop to:

    for (cnt = 0; cnt != num_lines_in_file; cnt++) {
        tempPointer = get_line(infile, temp);  
        lines[cnt] = (char*) malloc(strlen(tempPointer) + 1);
        strncpy(lines[cnt], trim_white_space(tempPointer), strlen(tempPointer)+1);
        printf("%d: %s\n", cnt, lines[cnt]);
    }
    

    The size in strncpy is based on the size of malloc you've used.

    Of course you can optimize this code, e.g. to count strlen only once, etc.