FShiwani
FShiwani

Reputation: 181

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.

Upvotes: 8

Views: 1494

Answers (5)

Santiago Varela
Santiago Varela

Reputation: 2237

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" 

Upvotes: 4

0x2333
0x2333

Reputation: 42

A simple solution for understanding malloc:

malloc(sizeof(Each_Element) * Number_Of_Elements) returns an address(aka: a pointer) of a new buffer. And in this buffer, Each element has the same size: sizeof(Each_Element), and there are Number_Of_Elements elements.

For example:

malloc(sizeof(char)) returns a pointer of a char.

malloc(sizeof(char) * 3) returns a pointer of a char which is the first element of an char array contains 3 chars.

malloc(sizeof(char *) * 6) returns a pointer of a char pointer which is the first element of an char pointer array contains 6 char pointers.

Basically, malloc(sizeof(WHAT)) returns a pointer of WHAT. So simply understand: malloc(Sizeof(WHAT)) returns (WHAT *). malloc(Sizeof(WHAT *)) returns (WHAT **)... and so on.

Notice that actually malloc returns a (void *) pointer. An (Void *) pointer can be cast to any kind of pointer later. So there's a good manner for VisualStudio Users: int * pbuff = (int *) malloc(sizeof(int) * 6); The (int *) before function malloc convert the "Amoeba pointer" (void *) into (int *).

Upvotes: 0

jjm
jjm

Reputation: 461

malloc() returns void * which can be implicitly converted to any other pointer type. In your case, space allocated by malloc() can be used as storage for a char * value. Check below example

char str[10]= {'a'};
//char c = 'c';
char **output_str = malloc(sizeof(char*));

*output_str = str; /* Stored address of a char array of size 10*/

//*output_str = &c; // Stored address of a char, its also correct

printf("%c\n",**output_str); /* Will print the char element, if you are using string address it will print first element,
to print other elements in str use (*output_str)[index] */

Upvotes: 0

Abhineet
Abhineet

Reputation: 5389

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**.

Upvotes: 2

ScottK
ScottK

Reputation: 1556

Think of output_char as a pointer to an array of strings, and each string is a pointer to an array of characters. When you do malloc(sizeof(char*)), you are only providing a array of char pointers of length 1, so *output_char contains a single uninitialized pointer (NULL).

What you should be doing is allocating enough pointers so you can point to a bunch of strings. Let's say you want to store N strings, all referenced by output_char. Initialize it as:

// Allocate an array of N string pointers
char **output_str = malloc(sizeof(char*)*N); 

Then you need to allocate storage for each of those strings:

// Allocate storage for each of the N strings, assume 80 chars max each
// including the null character required at the end of the string
#define MAX_STRING_SIZE 80
int i;
for(i=0; i<N; i++)
   // Allocate storage for each of the strings, but they
   // still need to have some chars written to this storage
   // for these strings to be anything but null strings ""
   output_str[i] = malloc(MAX_STRING_SIZE*sizeof(char));

There is a great example of this at the start of every C program:

int main(int argc, char** argv) { /* your code */ }

Every C program starts with this main definition. It accepts two parameters, one is the number of strings being passed into your program via the command line (including the name of the program itself), and argv is a reference to an array of string parameters.

For example, invoking myprog with the following command line parameters:

   myprog -h "Fred" Jones

Results in main() being passed argc and argv such that:

   argc = 4
   argv[0] = "myprog"
   argv[1] = "-h"
   argv[2] = "Fred"
   argv[3] = "Jones"

Upvotes: 0

Related Questions