1

I have read other questions that are somewhat related to this topic on StackOverflow (namely (1) and (2)), but they do not address my question regarding a member array specified within a struct in a manner that I could follow logically.

In C, I specify a double pointer as a member of my struct. This double pointer purposes as a 2-D dynamic array - strictly speaking, it is an array of pointers to an array of pointers to characters in memory. The rest of the members of the struct are meant to keep track of the current and allocated size of this 2-D dynamic array.

typedef struct CharArrayXY
{
    char **array; // A look-up table pointing to a 2-D arrangement of values stored in memory
    size_t used_x; // Number of arrays occupying 2-D array
    size_t *used_y; // Number of elements occupying each array
    size_t size_x; // Number of spaces allocated for arrays in 2-D array
    size_t *size_y; // Number of spaces allocated for elements in each array
} CharArrayXY;

I initialize all of the members of the struct with several malloc() calls in a separate function without any problems. This is the important bit from that function:

a->array = malloc(sizeof(*a->array) * x_dim);
// Check if it's NULL...
for (x_ind = 0; x_ind < x_dim; x_ind++)
{
    a->array[x_ind] = malloc(sizeof(char) * y_dim);
}

a->size_x = x_dim;

a->size_y = malloc(sizeof(size_t) * x_dim);
// Check if it's NULL...
for (x_ind = 0; x_ind < x_dim; x_ind++)
{
    a->size_y[x_ind] = y_dim;
}

a->used_x = 0;

a->used_y = malloc(sizeof(size_t) * x_dim);
// Check if it's NULL...
for (x_ind = 0; x_ind < x_dim; x_ind++)
{
    a->used_y[x_ind] = 0;
}

I resize the struct's double pointer member with a realloc() call.

void add_char_xy (CharArrayXY *a, char element, size_t x_ind)
{
    if (a->used_x == a->size_x)
    {
        printf("Reallocating for more space...\n");
        a->size_x += 2;
        temp_ptr = realloc(a->array, sizeof(*a->array) * a->size_x);
        if (temp_ptr == NULL)
        {
            fprintf(stderr, "realloc() call failed.\n");
            return;
        }
        a->array = temp_ptr;
    }

    if (a->used_y[x_ind] == a->size_y[x_ind])
    {
        a->size_y[x_ind] += 10;
        a->array[x_ind] = realloc(a->array[x_ind], sizeof(*a->array[x_ind]) * a->size_y[x_ind]);
    }

    printf("Storing '%c' in CharArrayXY[%zu][%zu].\n", element, x_ind, a->used_y[x_ind]);
    a->array[x_ind][a->used_y[x_ind]] = element;
    a->used_y[x_ind]++;
}

The realloc() call does not result in a NULL pointer, which suggests that there is no problem with reallocating the memory. I have traced the source of the problem to the used_y member of the struct, since it is updated to 56 instead of 0. My understanding is that the used_y member was reallocated to a new space in memory that I have not allocated for it, so it returns a trash value when I attempt to access it.

... Storing many characters in the array
Storing 'b' in CharArrayXY[4][0]. <-- Array is being populated fine (hasn't been reallocated yet, still filling initialized array)
Storing 'l' in CharArrayXY[4][1].
Storing 'd' in CharArrayXY[4][2].
Storing 'b' in CharArrayXY[4][3].
Storing 'e' in CharArrayXY[4][4].
Storing 'f' in CharArrayXY[4][5].
Storing 's' in CharArrayXY[4][6].
Storing 'a' in CharArrayXY[4][7].
Storing 'r' in CharArrayXY[4][8].
Storing 'c' in CharArrayXY[4][9].
Reallocating for more space...
Storing 'b' in CharArrayXY[5][65]. <-- Right after the realloc() call
Segmentation fault (core dumped)

Why does it appear that the value of a->used_y[x_ind] is filled with a trash value from memory during after I successfully reallocate the array member of the struct?

Community
  • 1
  • 1
Vladislav Martin
  • 1,504
  • 2
  • 15
  • 34
  • 1
    Side note: [is-2d-array-a-double-pointer?](http://stackoverflow.com/questions/7586702/is-2d-array-a-double-pointer) – LPs Jul 29 '16 at 13:06
  • 4
    The code you've shown so far doesn't seem to have an issue. The problem is probably elsewhere. Please update your question with a [Minimal, Complete, Verifiable Example](http://stackoverflow.com/help/mcve). – dbush Jul 29 '16 at 13:08
  • @LPs: Thank you for your note, I did recognize that. I don't know how else to refer to the double pointer. I'm using it to serve as an array of pointers, where each pointer points to the beginning of an array of characters. What should I refer to it as in my question / comments? The word "array" seems to fit its use in this situation. – Vladislav Martin Jul 29 '16 at 13:11
  • It was only to be sure you know the difference between matrix (contiguous allocation) and pointer of pointers. – LPs Jul 29 '16 at 13:13
  • 1
    Are you sure `realloc` doesn't fail? Always check function return values. – LPs Jul 29 '16 at 13:15
  • There is no requirement that `realloc()` resize the allocation in-place, even when the new size is less than the old size. That is why it returns a pointer to the new space. But I don't see why you suppose that would create a problem for the code you present. – John Bollinger Jul 29 '16 at 13:18
  • 2
    And though you claim to fully understand, comments such as "the member array is being passed into `realloc()` by it's value, not it's address" suggest that you don't quite. There is no member array, only a member pointer. If you pass that pointer to any function, then yes, it is passed by value, because C has no other argument passing convention, but that seems consistent with the expectations I infer from your code. – John Bollinger Jul 29 '16 at 13:23
  • The way to be sure whether your `realloc()` is failing is to ***check its return value***, as @LPs urged you to do. `realloc()` (and `malloc()` and `calloc()`) returns a null pointer when it fails. Your code can and should test for that, taking appropriate action -- perhaps just aborting the program -- if such a failure occurs. – John Bollinger Jul 29 '16 at 13:29
  • 3
    `a->used_y` isn't allocated/inited in posted code..... so `a->used_y[x_ind]` is UB. Is it your real code? – LPs Jul 29 '16 at 13:33
  • 1
    Without the code that manages `used_y` member is hard to say: addressing out of bounds?... – LPs Jul 29 '16 at 13:38
  • @VladislavMartin It's not about how many lines of code you want to include. It's about how many lines people need to answer your question. Not including it all means they can't. That's _worse_ than including too many. Either ask a complete question, or don't ask any. And why would CodeReview be any more willing to review incomplete code? Anyway, if you're doing anything that invokes UB, then that's why weird things are happening. But without all the code, how can we know? – underscore_d Jul 29 '16 at 13:38
  • 1
    `I am under the impression that the reason [...] is that the member array is being passed into realloc() by it's value, not it's address.` Names of arrays eagerly convert into pointers to the first element, so no. `Why does realloc() assign my array to a new block of memory when I call it to increase the size of my array?` _...really_? Clearly if the original block isn't large enough, one cannot allocate more elements within it (without running over and trashing subsequent memory that isn't yours). So _of course_ it needs moved to a new block. This is precisely the _definition_ of reallocation – underscore_d Jul 29 '16 at 13:55
  • @underscore_d: Huh. Well, thank you for letting me know that. In that case, then I presume the problem has nothing to do with the `realloc()` of my `array` member pointer, but instead with the trash value of `a->used_y[x_ind]`. – Vladislav Martin Jul 29 '16 at 13:57
  • 1
    You have to reallocate `a->used_y` too each time you change the size (increase) of array. – LPs Jul 29 '16 at 14:05
  • 1
    @LPs has got it. Of course when you change the number of elements, you need to change it _everywhere_. By not doing so, you end up wading into memory that isn't yours, which is UB, so anything can happen... and in this case, `65` happens. – underscore_d Jul 29 '16 at 14:07
  • @LPs: That's it!!! Thank you very much, can you submit that as an answer so I can accept it? – Vladislav Martin Jul 29 '16 at 14:08

1 Answers1

4

After some edits and comments :) the problem is:

You have to reallocate a->used_y and all variables correlated to the size of array each time you change the size (increase) of array.

Make also sure to init new allocated memory of a->used_y.

LPs
  • 16,045
  • 8
  • 30
  • 61
  • 1
    Thank you again, @LPs. I'll make sure to edit the formatting of my question so that it is easier to identify that this is the problem, just in case another SO user wants to do the same thing and has some similar error. – Vladislav Martin Jul 29 '16 at 14:20