How does this code allocate memory and why does it fail to write to the allocated memory?

59 Views Asked by At

Why does the code below compile successfully but fail to write memory other than address 0? (compiled in dev-cpp 6.3)

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

void alloc_arr(int **d_ptr);

int main() {
    int *ptr_i = NULL;
    alloc_arr(&ptr_i);
    
    if (ptr_i == NULL) {
        printf("ptr_i null");
        return 0;
    }
    
    printf("ptr_i %x value at 0 %d value at 1 %d",&ptr_i, ptr_i[0], ptr_i[1]);
    
    return 0;
}

void alloc_arr(int **d_ptr) {
    *d_ptr = (int *)malloc(2 * sizeof(int));
    if (d_ptr == NULL) {
        printf("cant allocate memory");
        exit(1);
    }
    *d_ptr[0] = 15;
    *d_ptr[1] = 5;
}

I am expecting the code to allocate an array of memory from a function.

3

There are 3 best solutions below

0
Gerhardh On

You need to revisit operator precedence table.

*d_ptr[1]=5;

This is the same as *(d_ptr[1])=5; where d_ptr[1] is illegal as d_ptr only points to a single int *.

You want

(*d_ptr)[1]=5;

There is also another error in your program:

    if(d_ptr==NULL){printf("cant allocate memory");exit(1);}

If you come here, you have already dereference d_ptr causing undefined behaviour. Also, according to the text you print, you want

    if(*d_ptr==NULL){printf("cant allocate memory");exit(1);}

Finally, you print the address of your pointer variable, not where it points to and you also use wrong format specifier:

 printf("ptr_i %x value at 0 %d value at 1 %d",&ptr_i, ptr_i[0], ptr_i[1]);

This should be:

 printf("ptr_i %p value at 0 %d value at 1 %d", (void*)ptr_i, ptr_i[0], ptr_i[1]);
0
Harith On

Wrong check:

*d_ptr=(int*) malloc(2*sizeof(int));
if(d_ptr==NULL) 

The check should be:

if (*d_ptr == NULL)

The code would trigger undefined behaviour if d_ptr was originally NULL and had been deferenced to store the return of malloc().


Unneeded cast:

// *d_ptr=(int*) malloc(2*sizeof(int));

   *d_ptr = malloc (sizeof *d_ptr * 2);

Dereferencing out of bounds memory:

*d_ptr[1]= 5

is parsed as:

*(d_ptr[1]) = 5;

But d_ptr only points to a single int *.

It should be:

(*d_ptr)[1] = 5;

Indicate failure on failure:

if (ptr_i == NULL) {
        printf("ptr_i null");
        return 0;
    }

Here, return 0 is equivalent to return EXIT_SUCCESS;.

I suggest changing it to:

return EXIT_FAILURE; // or return 1;

Wrong format specifier:

printf("ptr_i %x value at 0 %d value at 1 %d",&ptr_i, ptr_i[0], ptr_i[1]);

The %x format specifier is not for printing out pointer addresses; %p is. And it expects a void *, which requires a cast like so:

printf("ptr_i %p value at 0 %d value at 1 %d", (void *) &ptr_i, ptr_i[0], ptr_i[1]);

Note that &ptr_i produces a pointer to ptr_i, you're looking for ptr_i.

0
chqrlie On

There are multiple problems in your code:

  • if (d_ptr == NULL) does not check for memory allocation success, you only test that the function argument is not a null pointer. You should instead write

      if (*d_ptr == NULL)
    
  • *d_ptr[1] = 5; does not initialize the second int in the allocated array, but rather attempts to dereference an int * at the address that follows that of ptr_i in main, which has undefined behavior. Postfix operators bind stronger than prefix operators, so you should write

      (*d_ptr)[1] = 5;
    

    Note that *d_ptr[0] = 15; is confusing but not a problem because it is the same as (*d_ptr)[0] = 15; as well as **d_ptr = 15; or d_ptr[0][0] = 15; or even 0[*d_ptr] = 15; or *0[d_ptr] = 15; etc.

  • to print the value of a pointer in printf, you should use %p and cast the pointer to type (void *).

  • casting the return value of malloc() is not necessary in C.

Here is a modified version that is less error prone:

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

void alloc_arr(int **d_ptr);

int main(void) {
    int *ptr_i = NULL;
    alloc_arr(&ptr_i);
    
    if (ptr_i == NULL) {
        printf("ptr_i null\n");
        return 1;
    }
    
    printf("ptr_i %p value at 0 %d value at 1 %d\n",
           (void *)&ptr_i, ptr_i[0], ptr_i[1]);
    
    return 0;
}

void alloc_arr(int **d_ptr) {
    int *ptr = (int *)malloc(2 * sizeof(*ptr));
    if (ptr == NULL) {
        printf("cant allocate memory\n");
        exit(1);
    }
    ptr[0] = 15;
    ptr[1] = 5;
    *d_ptr = ptr;
}

Note also that passing the address of the pointer is not necessary as you could simply return the pointer to the allocated block:

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

int *alloc_arr(void);

int main(void) {
    int *ptr_i = alloc_arr();
    
    if (ptr_i == NULL) {
        printf("ptr_i is null\n");
        return 1;
    }
    
    printf("ptr_i %p value at 0 %d value at 1 %d\n",
           (void *)&ptr_i, ptr_i[0], ptr_i[1]);
    
    return 0;
}

int *alloc_arr(void) {
    int *ptr = malloc(2 * sizeof(*ptr));
    if (ptr == NULL) {
        fprintf(stderr, "cannot allocate memory\n");
        exit(1);
    }
    ptr[0] = 15;
    ptr[1] = 5;
    return ptr;
}