-1

My question is regarding freeing allocated memory in different functions. So my code is structured something like this:

int main()
{
    // Declare variables
    double *val1, *val2;

    // Call function 1
    function1(&val);

    // Call function 2
    function2(&val2);

    // Do some stuff .....

    // Free dynamically allocated memory
    free(val1);
    free(val2);

    // End program
    return 0;
}

void function1(double *val1)
{
    /* Allocate memory */
    val1 = (double*) malloc(n1*sizeof(double));
    if (val1 == NULL){
        printf("Error: Memory not allocated!");
        exit(0);
    }
}

void function2(double *val2)
{
    // Allocate memory
    val2  = (double*) malloc(n2*sizeof(double));
    if (val2== NULL){
        printf("Error: Memory not allocated!");
        // Here I want to free val1!
        exit(0);
    }
}

Meaning that some memory is allocated for val1 inside function1 and for val2 inside function2.

Now, the contents contained in val1 is not needed in function2, so I would at first sight not have to pass the pointer to val1.

However, if val2 is not allocated correctly I want to exit the program, but free any allocated memory first. Can I free the memory for val1 inside function2 without passing the pointer for val1?

Jabberwocky
  • 48,281
  • 17
  • 65
  • 115
Tim
  • 61
  • 4
  • "Can I free the memory for val1 inside function2 without passing the pointer for val1". How would you imagine that working? Certainly you can hack it by for example using global variables. But that's obviously not advisable. The normal way to do that is to let the calling code handle that kind of error checking and recovery. – kaylum Oct 02 '20 at 11:38
  • Also, your function parameters are wrong. Those parameters need to be `double **` and the allocations need to be `*val1 = malloc(..);` if you want to pass the allocated pointer back to the caller. – kaylum Oct 02 '20 at 11:39
  • pointer to pointer – hetepeperfan Oct 02 '20 at 11:46
  • Be aware that `function1` and `function2` are wrong in the first place. `val1` inside of `function1` is _local_ to `function1`. A change to `val1` wont modify the `val1` of `main`. Same form `function2`. – Jabberwocky Oct 02 '20 at 11:46
  • If malloc fails, your whole heap is toast anyway. Clean-up at that point is mildly important, it is unlikely that anything from the execution can be salvaged. – Lundin Oct 02 '20 at 13:50

4 Answers4

1

a way to structure functions with dynamic allocation would be to have a return value of an integer, like: int function1(double *val) that way if it fails you can return a value which would indicate it when the allocation fails, and act accordingly in main()

Gilad
  • 305
  • 1
  • 2
  • 8
1

Can I free the memory for val1 inside function2 without passing the pointer for val1?

No. C language doesn't have a concept of destructors, so commonly used in other languages. In C you have to "pick up the trash" yourself - so if you terminate your program it's nice to free all allocated memory. There are many styles of error handling, choose the one you like. I like kernel coding style. A function that terminates a program in case it fails would be very brutal. A return value that lets the user of the function handle the error case would be nicer and more predictible. It's typical (for me) for C functions to return an int with 0 for success and negative value for failure. Your program could look like this:

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

int function1(double **val1)
{
    size_t n1 = 10;
    *val1 = (double*) malloc(n1 * sizeof(**val1));
    if (*val1 == NULL){
       return -ENOMEM;
    }
    return 0;
}

int function2(double **val2)
{
    size_t n2 = 20;
    *val2  = malloc(n2 * sizeof(double));
    if (*val2== NULL){
        return -ENOMEM;
    }
    return 0;
}

int main()
{
    int err = 0;

    double *val1, *val2;

    err = function1(&val1);
    if (err) goto ERROR_function1;

    err = function2(&val2);
    if (err) goto ERROR_function2;

    err = do_some_calc(val1, val2);

    free(val2);
ERROR_function2:
    free(val1);
ERROR_function1:
    return err;
}

Note the error in your program - you were passing double** pointers yet your functions expected double* pointer. Parameters are passed to function by value - values of parameters are copied. To modify a value you have to pass a pointer - that includes pointers, so if you want to modify a pointer you have to pass a pointer to a pointer.

KamilCuk
  • 120,984
  • 8
  • 59
  • 111
0

You can write this a lot cleaner & fix some bugs by:

This is an artificial example overall, but you could for example do like this:

double function1 (void)
{
  return malloc(n1*sizeof(double));
}

double function2 (void)
{
  return malloc(n2*sizeof(double));
}

void exit_cleanup (double* v1, double* v2)
{
  free(v1);
  free(v2);
  exit(0);
}

int main (void)
{
  double* val1 = NULL;
  double* val2 = NULL;

  // ...

  val1 = function1();
  if(val1 == NULL) { exit_cleanup(&v1, &v2); }
    
  // ...
    
  val2 = function2();
  if(val2 == NULL) { exit_cleanup(&v1, &v2); }

  // Do some stuff .....

    
  exit_cleanup(&v1, &v2);
}

This works because both pointers are initialized to NULL and calling free(NULL) is a safe no-op. All error handling and clean-up has been centralized to a single function. No "on error goto" stuff is necessary.

Another rarely used beast is atexit, which is a standard C function which lets you register a number of functions that will get executed just before program termination. Would have worked just fine here - however, you'd need to make the variables file scope for that case, so it isn't ideal.

Lundin
  • 195,001
  • 40
  • 254
  • 396
-1

You will either have to pass the val1 pointer to function2 or to store val1 globally, both being pretty dirty approaches.

What would be a proper way to approach this issue is to return error codes from both of the functions and handle the errors from main (i.e. free any allocated memory that needs to be free'd).

As a side note, &val1 will have the type double **, not double *. Even though here the might work just fine, it is not correct, and might lead to unexpected behaviour. Your functions would need to be changed like this:

void function1(double **val1)
{
    /* Allocate memory */
    *val1 = malloc(n1*sizeof(double));
    if (*val1 == NULL){
        printf("Error: Memory not allocated!");
        exit(0);
    }
}

This will keep the data types pointer depth properly, and signal you any pointer problem earlier.

Vlad Rusu
  • 1,414
  • 12
  • 17
  • 1
    bad practice. it simply should be `*val1 =malloc(n1*sizeof(**val1));` – 0___________ Oct 02 '20 at 12:01
  • @P__J__ Agree on that. Just ignored that part and taken it from the question code snippet. Updated answer with this change – Vlad Rusu Oct 02 '20 at 12:28
  • @P__J__ This is a perfect example of why that style is overrated. Because it's very easy to slip and write `malloc(n1*sizeof(*val1));`. I tend to use the style you propose too, but it isn't universally good. Yet another alternative is `malloc( sizeof(double[n1]) );`, which is nice because it's self-documenting code. We want to allocate an array, so lets allocate the size of the array we want. – Lundin Oct 02 '20 at 14:18
  • @Lundin first of all void functions with double pointer parameters should ve avoided. `double *function1(size_t n1) { return malloc( double[n1]);}` – 0___________ Oct 02 '20 at 15:12
  • @P__J__ Yeah but many professional APIs reserve the return value for an error code, for all functions belonging to that API. Then you have no other option but pointer-to-pointer through one of the parameters. – Lundin Oct 05 '20 at 07:38
  • @Lundin NULL is the error value when returning pointer. Not very professional API of the it retuerns only integers. – 0___________ Oct 05 '20 at 08:00
  • @P__J__ And what if you more than one error in the "constructor", such as invalid parameter values passed? You can't distinguish between several different errors without using a custom error information type, most commonly an enum. "GetLastError" APIs using globals are notoriously buggy, thread-unsafe and bad design in general, as we know from experience from the crappily designed error handling in Windows, as well as equally crappy standard C `errno`. – Lundin Oct 05 '20 at 08:13
  • then single pointers to error codes. like mytype func(..., int *errcode, int *extended errcode, int *evenmoreextendederrcode, ....) – 0___________ Oct 05 '20 at 08:14