Search code examples
arrayscpointersmemorydynamic-memory-allocation

In C, almost all indexes from an array have `0x0`, but some try to automatically access memory?


I'm trying to create an array of strings in C. My plan is for the program to read line from a file and them gradually build this array.

But I noticed that after I allocate memory to this array, some of its indices are 0x0, and some are trying to access memory in a weird manner. For example:

char** arr_docs = (char**)malloc(sizeof(char*));

In gdb, I'll try to see the memory addresses of many of this array's indexes:

arr_docs[0]
> 0x0
arr_docs[1]
> 0x0
arr_docs[2]
> 0x0
arr_docs[3]
> 0x1fae1 <error: Cannot access memory at address 0x1fae1>
arr_docs[4]
> 0x0

Wait, what?? Why does arr_docs[3] is trying to access that address?

I have also noticed that when I'm building the array of strings, the program correctly puts the intended string in arr_docs[0], but at some point in the loop (In the debugger, it shows that is when i == 4), arr_docs[0] get allocated again! Here's the for-loop code and the behavior arr_docs[0] shows in the debugger:

void getlinha(char* buf, FILE* arq){
    fgets(buf, 50, arq);
    int size = strlen(buf);
    // final \n replaced by \0
    buf[size-1] = '\0';
}
char* temp = (char*)malloc(sizeof(char));
for(i = 0; i < 6; i++){
        //char* temp = (char*)malloc(50);
        arr_docs[i] = (char*)malloc(sizeof(char));
        getlinha(temp, input);
        strcpy(arr_docs[i], temp);
    }

In the debugger, when i < 4:

> arr_docs[0]: 0x55555555a530 "sigaa 2"

When i == 4 (More specifically, when arr_docs[4] = (char*)malloc(sizeof(char));):

> arr_docs[0] : 0x55555555a530 "\260\245UUUU"

I'm completely lost.


Update

Following recommendations, I edited the code. Dumped dynamic memory allocation, since I know how many strings to store. Still, some problems arise. The new code:

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

void getlinha(char* buf, FILE* arq){
    fgets(buf, 50, arq);
    int size = strlen(buf);
    // final \n replaced by \0
    buf[size-1] = '\0';
}

int main(void){
    char* arr_docs[6];
    int i;
    FILE* input = fopen("file.input", "r");
    for(i = 0; i < 6; i++){
        getlinha(arr_docs[i], input);
    }
}

In this program, fgets rises Segmentation fault error; In the debugger, arr_docs[0] is correctly assigned. But arr_docs[1] throws the error:

arr_docs[1]
> 0x5555555552bd <__libc_csu_init+77> "H\203\303\001H9\335u\352H\203\304\b[]A\\A]A^A_\303ff.\017\037\204"

fgets(arr_docs[1], 50, input)
> Program received signal SIGSEGV, Segmentation fault.
> __memmove_avx_unaligned_erms () at ../sysdeps/x86_64/multiarch/memmove-vec-unaligned-erms.S:314


Update 2

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

void getlinha(char* buf, FILE* arq){
    fgets(buf, 50, arq);
    int size = strlen(buf);
    // final \n replaced by \0
    buf[size-1] = '\0';
}

int main(void){
    char* arr_docs[6];
    int i;
    FILE* input = fopen("file.input", "r");
    for(i = 0; i < 6; i++){
        // assuming the upper bound size of one doc is 50 chars
        arr_docs[i] = malloc(50 * sizeof(char));
        getlinha(arr_docs[i], input);
    }
    for(i = 0; i < 6; i++){
        free(arr_docs[i]);
        arr_docs[i] = NULL;
    }
    fclose(input);
    return 0;
}

Solution

  • There are still some problems in your latest update:

    • you do not check for fopen failure to open the file
    • you do not check for memory allocation failure
    • you do not check for fgets() failure
    • if a line read by fgets() is longer than 49 bytes including the newline, it will be spill to the next array.
    • you have undefined behavior if strlen(buf) returns 0.
    • you overwrite the last byte of buf without checking that it is a newline.

    Here is a modified version without dynamic memory allocation that truncates long lines:

    #include <errno.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    // read a line of input
    // return -1 and set buf to the empty string at end of file
    // otherwise return the line length
    // line read is truncated to fit in buf and null terminated if size>0
    // truncation can be detected by testing if the return value >= buffer length.
    int getlinha(char *buf, int size, FILE *fp) {
        int c, i, j;
        for (i = j = 0; (c = getchar()) != EOF && c != '\n'; i++) {
            if (i + 1 < size)
                buf[j++] = (char)c;
        }
        if (size > 0)
            buf[j] = '\0';
        if (i == 0 && c == EOF)
            return -1;
        return i;
    }
    
    int main(void) {
        char arr_docs[6][50];
        int i, n;
        FILE *input = fopen("file.input", "r");
        if (input == NULL) {
            fprintf(stderr, "cannot open %s: %s\n", "file.input", strerror(errno));
            return 1;
        }
        for (n = 0; n < 6; n++) {
            // assuming the upper bound size of one doc is 50 chars
            if (getlinha(arr_docs[n], sizeof arr_docs[n], input) < 0)
                break;
        }
        for (i = 0; i < n; i++) {
            printf("%d: %s\n", i + 1, arr_docs[i]);
        }
        fclose(input);
        return 0;
    }