Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Double-asterisks and `malloc` in C

I've been studying pointers for a while and I can't seem to wrap my head around it fully. There seems to be a gap that isn't explained when you jump from tutorials explaining pointers to actual functions and code that assume you know about them. The piece of code that is annoyning me is as follows:

char **output_str = malloc(sizeof(char*));

Alright, so my understanding is as follows,

**output_str is a character
*output_str is a pointer to a character
output_str is a pointer to a pointer to a character

From what I know malloc() returns a pointer to the start of the memory that has just been allocated which has a size value equal to the size of a pointer to a character(char*). Now, shouldn't a pointer to the start of the memory have a single *? If so, how can we assign something with a * to a **? I'm quite confused, if anyone could provide some clarification and maybe with some insight into the memory part that would very nice.

like image 650
FShiwani Avatar asked Mar 22 '17 03:03

FShiwani


Video Answer


2 Answers

Your code block is correct. With this declaration:

char **output_str = malloc(sizeof(char*));

output_str is a char pointer to a char pointer, or it could be seen as a 2d array of chars, or a matrix of char.

Graphically represented:

Memory Address | Stored Memory Address Value
----------------------------------------------
0              |     .....
1              |     .....
2              |     .....
3              |     .....
4              |     .....
5              |     .....
6              |     .....
.              |     .....
.              |     .....
.              |     .....
n-1            |     .....

Imagine the memory as being a very big array in which you can access positions by its memory address (in this case we've simplified the addresses to natural numbers. In reality they're hexadecimal values). "n" is the total amount (or size) of the memory. Since Memory counts and starts in 0, size is equivalent to n-1.

1. When you invoke:

char **output_str = malloc(sizeof(char*));

The Operating System and the C compiler does it for us, but we can think that our memory has been changed. E.g. Memory address 3 now has a char pointer to a char pointer called output_str. .

    Memory Address | Name - Stored Memory Address Value (it points to ...)
    -----------------------------------------------------
    0              |     .....
    1              |     .....
    2              |     .....
    3              |     output_str = undefined
    4              |     .....
    5              |     .....
    6              |     .....
    .              |     .....
    .              |     .....
    .              |     .....
    n-1            |     .....

2. Now if we say:

*output_str = malloc(sizeof(char));

The memory has been changed again. E.g. Memory address 0 now has a char pointer called *output_str.

    Memory Address | Name - Stored Memory Address Value (it points to ...)
    -----------------------------------------------------
    0              |     *output_str = undefined
    1              |     .....
    2              |     .....
    3              |     output_str = 0
    4              |     .....
    5              |     .....
    6              |     .....
    .              |     .....
    .              |     .....
    .              |     .....
    n-1            |     .....

3. We declare a statically instanced char:

char a = 'a';

So again our memory has changed, it has placed at MemoryAddress[6] = 'a':

        Memory Address | Name -> Stored Memory Address Value (it points to ...)
        ------------------------------------------------------
        0              |     *output_str = undefined
        1              |     .....
        2              |     .....
        3              |     output_str = 0
        4              |     .....
        5              |     .....
        6              |     a = 'a' // 'a'is static memory
        .              |     .....
        .              |     .....
        .              |     .....
        n-1            |     .....

Lastly, we invoke *output_str = &a; we are now telling char pointer *output_str to point to/reference the previously instantiated char a.

So our final memory will look like this:

            Memory Address | Name - Stored Memory Address Value (it points to ...)
            -----------------------------------------------------
            0              |     *output_str = 6
            1              |     .....
            2              |     .....
            3              |     output_str = 0
            4              |     .....
            5              |     .....
            6              |     a = 'a' // 'a'is static memory
            .              |     .....
            n-1            |     .....

Further information

 Now printf("Value: " + a) will output "Value: a" 
 printf("Value: " + *output_str[0]) will also output "Value: a" 
 And printf("Value: " + **output_str) will output "Value: a" 
like image 67
Santiago Varela Avatar answered Oct 24 '22 19:10

Santiago Varela


That's a genuine doubt. I will try best to make it clear for you. From the details in your question, I am assuming (read, "pretty sure") that you understand mallocating a memory and the respective return type.

You have a doubt that if malloc returns a pointer (void*), how can it be converted to a pointer to pointer (char**).

  1. char **output_str is equivalent to char *(*output_str) which means *output_str is a pointer to some data of type char. So, this is what malloc is returning to it.
  2. Again, you see malloc returning a pointer. But essentially, you have a allocated memory for a pointer to char (char*), that "literally" means that, malloc is returning a pointer (void*) to a memory where you have char*, that makes it, a pointer to pointer to char, i.e., char**.
like image 22
Abhineet Avatar answered Oct 24 '22 20:10

Abhineet