0

Say I want to dynamically allocate memory but with a function instead of in the main() function.

So I tried to do this:

dynamAlloc(int *fPtr)
{
   fPtr=malloc(cols * sizeof(*fPtr) );
   if(fPtr==NULL)
    { 
      printf("Can't allocate memory");
      exit(1);
    }
}

Then I realised: Even though memory allocated on the heap is available for the lifetime of program, that memory can only be referenced by formal argument fPtr and not the actual argument(let's call it aPtr). But once, function is exited, that memory is lost.

So how then do I dynamically allocate memory with a function?

trincot
  • 317,000
  • 35
  • 244
  • 286
Leon
  • 346
  • 3
  • 15
  • `formal arument fPtr and not the actual argumen` - what is a "formal argument"? what is an "actual argument"? How do they differ? Are you asking how to assign a value to a variable from outer scope from a function? – KamilCuk Jan 26 '20 at 07:28
  • Yup, I'm asking how to assign a memory block from heap(which is the "value" you spoke of) to the actual argument, `aPtr`(variable from outer scope). – Leon Jan 26 '20 at 07:38
  • 1
    So something like [How to change a variable in a calling function from a called function?](https://stackoverflow.com/questions/3377158/how-to-change-a-variable-in-a-calling-function-from-a-called-function)? `assign a memory block` - a pointer is not a memory block, it's just an address to the memory. – KamilCuk Jan 26 '20 at 07:40
  • 1
    So all I have to do is replace `int *fPtr` with `int **fPtr` to receive `&aPtr` as argument? – Leon Jan 26 '20 at 07:56
  • 4
    `dynamAlloc(int **fPtr)` and then `*fPtr=malloc(cols * sizeof(**fPtr) );` Otherwise you are assigning the allocated block to a ***copy*** of the pointer that is local to the function so the allocation is never seen back in `main()` (and is essentially a memory-leak). Call with `dynamAlloc (&pointer)` in `main()`. – David C. Rankin Jan 26 '20 at 07:56
  • 1
    @KamiCuk @DavidThe multiple dereferencing `*` operaters made it look harder than it actually is but I realised the concept is still the same. Much thanks for showing me the way! – Leon Jan 26 '20 at 08:04

4 Answers4

2

that memory can only be referenced by formal argument fPtr and not the actual argument(let's call it aPtr).

aPtr cannot denote to the heap memory object before the call to dynamAlloc() because the object has not been allocated yet and its address assigned to aPtr trough fPtr. Thereafter aPtr do reference the heap object.

We just need to pass the address of the pointer of aPtr to dynamAlloc(). So you need appropriate arguments(actual arguments) and parameters (formal arguments) to pass the address of the pointer aPtr between the functions, like you see below.

So how then do I dynamically allocate memory with a function?

You do it like you do it main(), doesn´t matter if the pointer was declared inside of main() or another function, you just need to pass the address of the pointer aPtr to the other functions, in which you want to use the heap memory object, like f.e.:

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

#define cols 5

void dynamAlloc(int** fPtr);

int main()
{
    int* aPtr;

    dynamAlloc(&aPtr);

    free(aPtr);

    return 0;
}


void dynamAlloc(int** fPtr)                 
{
   *fPtr = malloc(sizeof(*fPtr) * cols);
   if(*fPtr == NULL)
    { 
      printf("Can't allocate memory");
      exit(1);
    }
}

Do not forget to free() the heap memory!

  • 2
    ***it will remain untouched in memory after the program is terminated*** . Won't the OS claim back all the allocated memory to a program? – TruthSeeker Jan 26 '20 at 10:38
1

or just make it like this:

void dynamAlloc(int **fPtr)
{
   *fPtr=malloc(cols * sizeof(**fPtr) ); // malloc is returning void* so in that place it would be compiler error, so pointer returned from malloc should be casted to the pointer type of the value.
   if(*fPtr==NULL) // that would be a warning in gcc since NULL is a macro eq to 0, or (void*)0, it compiler version
    { 
      printf("Can't allocate memory");
      exit(1);
    }
}

and the fuction usage:

int* ptr = (int*)NULL;
dynamAlloc(&ptr);
*ptr = 1; // assign 1 to the first element, ptr is a valid pointer here

but double pointer syntax can turn out slow in some conditions, answer with return in the end od fucntion, copy of that local pointer is better practise.

  • as C is required it can be done like that ```int* ptr = (int*)NULL;``` – Johny Siemano Kolano Jan 26 '20 at 10:05
  • you function depends on global variables. Do not exit the program in such a functions. – 0___________ Jan 26 '20 at 12:21
  • that is not mine fuction, this is not a case to fix it, as embedded firmware engineer I would make many other changes to that code, starting with that the function in this case is waste of processor time, unless your doing kind of wrapper to that malloc that can check for the leaked bytes later, bus still, better choice is to make that wrapper online-with macro. – Johny Siemano Kolano Jan 26 '20 at 13:08
  • @JohnySiemanoKolano so you are saying that `int* dynamAlloc(int * fPtr)` is faster(i guess that's better?), ie, returning address in heap is better than double pointer syntax? Any reason why one would use double pointer syntax over thr other? – Leon Jan 26 '20 at 23:03
  • Also, I have read that casting return value of malloc is bad practice. See https://stackoverflow.com/q/605845/10701114. That's why I didn't cast mine as opposed to your comment following the `malloc()` statement. Although some are saying that casting is no longer bad based on the comments on the link. – Leon Jan 26 '20 at 23:09
0

It's more convenient to use a macro function, like this:

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

#define NEW_ARRAY(ptr, n) \
    { \
        (ptr) = malloc((size_t) (n) * sizeof (ptr)[0]); \
        if ((ptr) == NULL) { \
            fputs("Can't allocate memory\n", stderr); \
            exit(EXIT_FAILURE); \
        } \
    }

#define NEW(ptr) NEW_ARRAY((ptr), 1)

int main(void)
{
    int *myArray;
    const int myArrayLen = 100;
    int i;

    NEW_ARRAY(myArray, myArrayLen);
    for (i = 0; i < myArrayLen; i++) {
        /*...*/
    }
    return 0;
}

Update:

The purpose of the macro is to abstract away the details and make memory allocation less error prone. With a (non-macro) function we would have to pass the element size as a parameter as that information is lost when a pointer is passed to a formal parameter of type void pointer:

void NewArray(void *ptr, int n, int elemSize)
{
        *ptr = malloc((size_t) n * sizeof elemSize);
        if (*ptr == NULL) {
            fputs("Can't allocate memory\n", stderr);
            exit(EXIT_FAILURE);
        }
}

With the function NewArray the allocation call corresponding to the first example becomes

NewArray(&myArray, n, sizeof myArray[0]);

which doesn't buy us much.

August Karlstrom
  • 10,773
  • 7
  • 38
  • 60
  • 1
    why `MACRO` why not `inline` function instead? – TruthSeeker Jan 26 '20 at 10:48
  • @TruthSeeker `inline` is for a _function_. `NEW_ARRAY(ptr, n)` does not act like a _function_, more like `ptr = foo(n, typeof ptr)`. – chux - Reinstate Monica Jan 26 '20 at 12:00
  • @chux-ReinstateMonica: yes, that's true. The point I was making is when we can solve through regular function why to use MACRO's. After reading [this](https://stackoverflow.com/questions/14041453/why-are-preprocessor-macros-evil-and-what-are-the-alternatives). I try to avoid MACRO's wherever it is possible. – TruthSeeker Jan 26 '20 at 12:36
  • @TruthSeeker Then better to state your point (your want to express something) than ask a question (sounds like you want to learn something) – chux - Reinstate Monica Jan 26 '20 at 14:35
0

As you need to change the pointer itself - pointer to pointer is needed

void *allocate(void **tmp, size_t size)
{
    if(tmp)
    {
        *tmp = malloc(size);
    }
    return *tmp;
}


int main()
{
    int *ptr;

    if(!allocate((void**)&ptr, sizeof(*ptr) * 100))
    {
        perror("Error\n");
        exit(1);
    }
    /* do something*/
    free(ptr);
}
0___________
  • 60,014
  • 4
  • 34
  • 74