4

I'm studying C and have some question about dynamic memory allocation syntax.

The code below is an example of dynamic memory allocation. If I understand correctly

 (char *) malloc (50 * sizeof (char));

will return a pointer to a data type of char and

  pr = (char *) malloc (50 * sizeof (char));

will assign the pointer 'pr' to the pointer declared when allocating dynamic memory. So we will have a pointer point to another pointer

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

int main()
{
   char *pr;

   pr = (char*)malloc( 50 * sizeof(char) ); 

   return 0;
}

I still have a hard time understanding the syntax used to allocate dynamic memory. Can someone explain in detail? thank you

Claudio
  • 10,614
  • 4
  • 31
  • 71
Quốc Cường
  • 417
  • 4
  • 12
  • 1
    [Do I cast the result of malloc?](https://stackoverflow.com/q/605845/2173917) – Sourav Ghosh May 16 '19 at 11:50
  • Note also that `sizeof( char )` is always one, by definition. Per [**6.5.3.4 The sizeof and _Alignof operators**, paragraph 4](https://port70.net/~nsz/c/c11/n1570.html#6.5.3.4p4): "When `sizeof` is applied to an operand that has type `char`, `unsigned char`, or `signed char`, (or a qualified version thereof) the result is 1." – Andrew Henle May 16 '19 at 12:39

3 Answers3

4

will assign the pointer 'pr' to the pointer declared when allocating dynamic memory. So we will have a pointer point to another pointer

No, you'll have the value of the pointer variable stored.

This is just the same as

int x = 5;

or, in a more complex case

int test(void) { return 5;}
int x = test();

in this case, test() returns a value 5, which is assigned to x. Same way

char * pr = malloc (50 * sizeof (*pr));  // no casting, and sizeof (*pr) is a
                                         // more robust way to have the size defined 

assigns the returned pointer by malloc() to pr. There's no pointer to pointer here.

In other words, pr holds the pointer which points to the memory location allocated by the call to malloc(), or a NULL (null-pointer constant) in case the call failed.

Sourav Ghosh
  • 133,132
  • 16
  • 183
  • 261
3

When you call malloc(X) then the system attempts to allocate X bytes, and return a pointer to the first byte (if the allocation was successful, otherwise it returns NULL).

When you call calloc(X, Y) the system attempts to allocate X * Y bytes, and then set all bits of that memory to zero. This is essentially equivalent to calling malloc(X * Y) followed by memset.

I also recommend that you read Do I cast the result of malloc? Which can really be summarized as: No.

Some programmer dude
  • 400,186
  • 35
  • 402
  • 621
  • There are arguments [both](https://stackoverflow.com/a/605858/169346) [ways](https://stackoverflow.com/a/14879184/169346) on whether you should put the cast in or not. – JeremyP May 16 '19 at 12:45
2

First let us clear up a little bit of terminology:

So we will have a pointer point to another pointer

This is incorrect. When you assign a pointer to another pointer, the second pointer does not point to the first pointer, it points to the same thing as the first pointer. Pointing to a pointer implies two levels of dereference needed - in this case - to get to the char i.e. char a = **pr;

So, let's look at the code.

pr = (char*)malloc( 50 * sizeof(char) ); 

The prototype of malloc is

void *malloc(size_t size);

malloc allocates size bytes of memory and returns a pointer to that block of memory (or NULL if it can't allocated a block of memory). malloc has no idea of what kind of thing you want it to point to, so it chooses to return a void * which means a pointer to something unknown. The (char*) on the front of your call to malloc is a type cast. It changes the type of the expression to its right to the type enclosed in parentheses which is char * in this instance (you can put in or leave out the space between the char and the * without any effect).

The pointer, which now has type char * is then assigned to pr which also has type char *.

The only thing is that C will do the cast automatically, as long as it is from void * and to another pointer type. So what you wrote is exactly equivalent to

pr = malloc( 50 * sizeof(char) );

It's generally considered better style to do it this way than put the explicit cast in. It's easier to read and less cluttered.

There also used to be the danger that, if you forgot to #include <stdlib.h> the compiler would assume malloc returns an int If you left the cast out, the compiler would flag the attempted cast from int to char * as an error, but if you put it in, the error would be suppressed. This issue no longer exists because C11 forbids using a function that hasn't been declared.

There is another problem which is sort of the reverse of the one described above. If you have

char* pr;
// Lots of code
pr = malloc( 50 * sizeof(char) );
pr[49] = 0; 

and you decide that pr really needs to point to int you might end up with

int* pr;
// Lots of code
pr = malloc( 50 * sizeof(char) );
for (int i = 0 ; i < 50 ; ++i)
{
    pr[i] = 0; // buffer overflow!
} 

Which would compile but is undefined behaviour because the allocated block is not big enough to hold 50 ints. You could solve that by reverting to the explicit cast (int* pr = (char*)malloc(...) is an error), but the better way is to do a sizeof on the dereferenced pointer

int* pr;
// Lots of code
pr = malloc( 50 * sizeof *pr );
for (int i = 0 ; i < 50 ; ++i)
{
    pr[i] = 0; // OK if malloc returns non NULL, otherwise undefined behaviour, probably a SIGSEV
} 

calloc is like malloc except instead of supplying the absolute count of bytes, you supply a count of the things pointed to and the size of the things pointed to separately. calloc also zeros the bytes in the allocated block.

int *pr = calloc(50, sizeof *pr);

is effectively the same as the previous bits of code, except it is not undefined behaviour if the memory can't be allocated.


NB: A lot of people are saying you absolutely shouldn't put the explicit cast in, but there really are arguments both ways. Also this.

JeremyP
  • 84,577
  • 15
  • 123
  • 161