1

I wanted to write a function that changes the size of dynamic array and allows user to fill it at once. I know that I should do it with using of "realloc" (so I did and so it works...) but my first attempt looked like this:

void ChangeDynamicArraySize(int* dArray, int oldSize, int newSize){

    int* tempArray = (int*) malloc(sizeof(int) * oldSize);
    CopyArray(tempArray, dArray, oldSize);
    free(dArray);
    dArray = (int*) malloc(sizeof(int) * newSize);
    CopyArray(dArray, tempArray, oldSize);

    for (int i = oldSize; i < newSize; i++){
        scanf("%i", &dArray[i]);
    }
    PrintArray(dArray, newSize);
    free(tempArray);
}

In function body "PrintArray(dArray, newSize);" worked correct. But when called from main() it gives a result like: - 17891602 - 17891602 - 17891602 - 17891602

So it looks like dArray was freed...? But as I know allocated memmory isn't automatically freed after exiting function.

Then what could be the reason?

WJuz
  • 37
  • 1
  • 6

2 Answers2

2

Inside the function you are assigning the new memory allocated by malloc to dArray, which has block scope. You need to pass this pointer back to the calling function. dArray is a copy of the pointer that you passed in, and when you return to the calling function, the original pointer is unchanged. You should have a function call like:

ptr = ChangeDynamicArraySize(ptr, oldSize, newSize);

Since dArray is a copy of the pointer passed in the function call, when you change the value of *dArray, THAT is visible outside of the function because you are changing the value stored at the memory location pointed to by both the original pointer and the copy. But when you reassign the dArray pointer inside the function, you are just saying that this pointer should now point to some other location. The original still points to the original location.

The solution in the original question suffers from a fundamental problem: when you pass a pointer to a section of memory to a function, and the pointer reallocates that memory using malloc() or calloc() or realloc(), the new memory has a new address. This is true even with realloc() because there might not be enough contiguous bytes at the old location to allocate the requested memory. The original solution reallocates memory inside of the ChangeDynamicArraySize() function, and then modifies the contents of this memory. But after returning, the calling function has no idea where the new memory is. So, you have to return a pointer to the newly allocated memory to the caller.

@Diti proposed an alternate solution to get around this by passing the address of the pointer to the first element of the array. This pointer can be dereferenced and given the value of the address of the newly allocated memory. In this way, the calling function is none the wiser, because whenever the calling function accesses the array, it does so through the pointer that provides the address of the first element of the array. Neat. But I still think I prefer to explicitly pass the pointers whenever possible-- it seems clearer to me.

ad absurdum
  • 19,498
  • 5
  • 37
  • 60
  • Hm, dArray is a pointer so i thought operation done in block would affect also the pointer outside it - like changing value of variable... – WJuz Oct 03 '16 at 13:24
  • 1
    Nope. Inside the function you are just working with a copy. If you change `*dArray` in the function, THAT will be visible outside of the function, because you are changing the value stored at the memory pointed to by both the original pointer, and the copy. But if you reassign the pointer inside the function, you are just saying that this pointer, which was a copy of some other pointer, should now point somewhere else. – ad absurdum Oct 03 '16 at 13:28
  • Ok, now I understand that if I'm calling realloc() in my ChangeSize function body then effect is visible also in main() because realloc() refers to the block of memory which is pointed both by my MainPointer and ChangeSizePointer... hope I am right... it's just changing its size, not location. – WJuz Oct 03 '16 at 14:55
  • Sure, but `realloc()` does return a pointer to void, so you will get compiler warnings (which won't keep the code from compiling, but you should try to fix these) if you don't do something with this value. I always return the pointer so that I can explicitly see that the pointer in the calling function has been reassigned. – ad absurdum Oct 03 '16 at 15:32
  • I should add that if you don't at assign the return value of `realloc()` to a pointer variable, and return it you will have a memory leak. – ad absurdum Oct 03 '16 at 16:14
  • 1
    Crap, I don't know what I was thinking-- when the memory for, say, `dArray` is reallocated, there is no guarantee that the new memory will be in the same location as the old memory! So, you really HAVE to pass the new location back to the calling function. I did a toy example of this with a small array (5 elements) without returning the new address, and it worked-- but there was a 4 byte memory leak. Bottom line: return the new address! – ad absurdum Oct 03 '16 at 16:30
2

In C, functions parameters are copied locally. Any change you make on the value dArray points to (*dArray) works, but any change you make on the (address of) dArray you passed as a parameter, is only local to this function, because it is a copy.

You may want to pass the address of your array (a &array in your main, a dArray** in your function prototype) and make changes to the pointer instead.

Something like this:

void ChangeDynamicArraySize(int** dArray, int oldSize, int newSize){

    int* tempArray = (int*) malloc(sizeof(int) * oldSize);
    CopyArray(tempArray, *dArray, oldSize);
    free(*dArray);
    *dArray = (int*) malloc(sizeof(int) * newSize);
    CopyArray(*dArray, tempArray, oldSize);

    for (int i = oldSize; i < newSize; i++){
        scanf("%i", dArray[i]);
    }
    PrintArray(*dArray, newSize);
    free(tempArray);
}

Alternatively, you may also just return the address of your new malloced array (making your function return this int* instead of no value).

Also, for good practices, you may want to not cast the return of malloc, and check whether it failed and changed ERRNO (if you use the POSIX API).

Community
  • 1
  • 1
Diti
  • 1,454
  • 3
  • 23
  • 38
  • A, ok... I got it, thanks :) – WJuz Oct 03 '16 at 13:30
  • I tried this but scanf("%i", dArray[i]) rises Access violation writing location 0xcccccccc. – WJuz Oct 03 '16 at 15:11
  • But that one: scanf("%i", &(*dArray)[i]) seems to be fine ;) Now it's working. – WJuz Oct 03 '16 at 15:17
  • @Diti-- please see the end of my answer. I think that there is a further confusion in your solution, but correct me if I am missing something: you have `*dArray = malloc(sizeof(int) * newsize))`, which allocates enough memory to store `newsize` `ints`, but the pointer to the first address of this memory thinks that it is pointing to a location holding the **address** of an `int`. – ad absurdum Oct 03 '16 at 19:43
  • @Diti-- apologies-- I see what you are doing now. I kept having a problem because the `free(*dArray)` is trying to free a segment of stack-allocated memory in my sample code, as I had started with an array in my `main()` instead `malloc`ing some space. I will update my answer below. – ad absurdum Oct 03 '16 at 20:20