简体   繁体   中英

Trying to understand C sizing better

First things first, the code works, but it didn't for a while, and I'm trying to understand why what I did fixes it.

So I have a function:

int array_size(const char **array) {
    int i = 0;

    while (array[i] != NULL) ++i;

    return i;
}

I also have this pointer which I started with one element and a call to a function which modifies local_mig:

int main(void) {
    char **local_mig = malloc(sizeof(char *) * 1);

    populate_local_mig(&local_mig);

    int size = array_size(local_mig); // 9
}

This function looks like this (note the comment on second to last line):

void populate_local_mig(char ***local_mig) {
    // ...above here reads a directory with 5 .sql files
    while ((directory = readdir(dir)) != NULL) {
        int d_name_len = strlen(directory->d_name);

        char *file_name = malloc(sizeof(char) * (d_name_len + 1));

        strcpy(file_name, (const char *)directory->d_name);

        size_t len = strlen(file_name);

        if (len > 4 && strcmp(file_name + len - 4, ".sql") == 0) {
            (*local_mig)[i] = malloc(sizeof(char) * (len + 1));
            strcpy((*local_mig)[i], file_name);
            ++i;

            *local_mig = realloc(*local_mig, sizeof(char *) * (i + 1));
        }
    }

    //(*local_mig)[i] = NULL;
}

Still with me? Good.

Later on, I call array_size(local_mig); and it returns 9. What the? I was expecting 5. So naturally when I iterate over local_mig later, I eventually segfault when it tries to read the 6th element.

So, I added (*local_mig)[i] = NULL; and suddenly everything was ok and it returned 5, like it should have.

All along I figured since I allocated exactly enough space to fit each character array, that the size would obviously be the number of times I resized local_mig .

Turns out I was wrong... very very wrong. But why, I ask...

If you don't set the last pointer in your list to NULL, you will encounter undefined behavior in your array_size function, as it rolls right past the end of the array (with no marker to stop it) and into memory that you probably do not own and is not initialized.

The unpredicted size of 9 is the result of the aforementioned undefined behavior. It's probably the result of whatever was in memory at the time. Really, though, with UB, anything can happen.

The loop in array_size eventually gets up to testing array[i] != NULL , where i is the last index in the space you allocated with realloc .

If you actually did set this entry to NULL then all is well. But if you didn't: uninitialized values are different to null pointers. Reading an uninitialized value may cause a crash, or the compiler may optimize the program based on the assumption that you never read uninitialized values because the language specification says you aren't meant to do that!

A likely result is that this last entry will appear to contain a junk value which probably does not match NULL . And then your loop continues to read past the end of the allocated space , with unpredictable results.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM