Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

char ** and dereferencing pointers

Tags:

c

pointers

I would like to end my confusion with the char **

When once creates an array of character arrays(strings) how does char ** actually accomplish this?

i get that char * is a pointer to a char and that char *array[] is an array of char pointers, but what exactly does char ** do and how does it do it?

Also when I hear the word dereferences it makes me think the pointer is removed what exactly does dereference a pointer mean? Changing the value that the pointer points to?

Thanks

like image 946
some_id Avatar asked Feb 14 '11 14:02

some_id


3 Answers

"Dereferencing" a pointer means accessing the value the pointer points to. Assume the following declarations:

int a = 10;
int *p = &a;

Here's a hypothetical memory map of the two variables:

Item      Address      0x00  0x01  0x02  0x03
----      -------      ----  ----  ----  ----
   a      0x80001000   0x00  0x00  0x00  0x0A
   p      0x80001004   0x80  0x00  0x10  0x00

a contains the integer value 10. p contains the address of a (0x80001000). If we want to access the contents of a through p, we dereference p with the indirection operator *. Thus, the expression *p is equivalent to the expression a. If we wrote

 *p = 16;

that's the same as writing

  a = 16;

Here's a short snippet of code showing how to use an object of type char ** to create an array of strings:

#include <stdlib.h>
#define N      20  // For this example, we will allocate 20 strings
#define LENGTH 10  // of 10 characters each (not counting 0 terminator)
...
char **arr = malloc(sizeof *arr * N); 
if (arr)
{
  size_t i;
  for (i = 0; i < N; i++)
  {
    arr[i] = malloc(sizeof *arr[i] * (LENGTH + 1)); 
    strcpy(arr[i], "          "); 
  }
}

Going through it line by line,

char **arr = malloc(sizeof *arr * N); 

allocates a block of N elements, each large enough to store a pointer to char (sizeof *arr == sizeof (char *) since type of *arr == char *), and assigns the resulting pointer value to arr. IOW, arr points to the first pointer to char, hence the type char **. Note that if you separated the declaration and the function call, it would look like

char **arr;
...
arr = malloc(sizeof *arr * N);

We want to assign the result of malloc to arr, not to what arr points to.

if (arr)

It's possible for malloc to fail, so we want to check the result before using it. In the event malloc fails it will return a NULL pointer value.

{
  size_t i;
  for (i = 0; i < N; i++)
  {
    arr[i] = malloc(sizeof *arr[i] * (LENGTH + 1));

For each character pointer arr[i], we allocate a block of memory large enough for LENGTH+1 elements, each large enough to hold a char value (sizeof *arr[i] == sizeof (char), since type of *arr[i] == char; note that sizeof (char) is always 1) and assign the result to arr[i].

Since we allocate each string with a separate malloc call, it's unlikely that they are contiguous in memory. Here's another memory map showing a possible result of the code above:

Item         Address        0x00  0x01  0x02  0x03
----         -------        ----  ----  ----  ----
 arr         0x80001000     0xA0  0xCC  0x00  0x00  
             ...
 arr[0]      0xA0CC0000     0xA0  0xCC  0x20  0x00     
 arr[1]      0xA0CC0004     0xA0  0xCC  0x20  0x40
 arr[2]      0xA0CC0008     0xA0  0xCC  0x21  0x28
             ...
 arr[19]     0xA0CC0014     0xA0  0xCC  0x23  0x10
             ...
 arr[0][0]   0xA0CC2000     ' '   ' '   ' '   ' '
 arr[0][4]   0xA0CC2004     ' '   ' '   ' '   ' '
 arr[0][8]   0xA0CC2008     ' '   ' '   0x00  0x??
             ...
 arr[1][0]   0xA0CC2040     ' '   ' '   ' '   ' '
 arr[1][4]   0xA0CC2044     ' '   ' '   ' '   ' '
 arr[1][8]   0xA0CC2048     ' '   ' '   0x00  0x??
             ...
like image 65
John Bode Avatar answered Nov 04 '22 03:11

John Bode


A pointer is a type that holds an address to a value, instead of holding the actual value.

So, in the case of char *p, once allocated, p will contain an address A. Dereferencing that pointer means accessing the value that is stored at address A. The reason you can store strings in a char * is because memory that is allocated is contiguous. So, A is an address that stores the first character, A+1 is an address that stores the second character and so on.

In the case of char **pp, it stores an address of a char *. Call this address B. So, dereferencing pp means accessing value at address B, which happens to be a char *, which happens to hold a string. In the same way, B+1 (actually B + sizeof (char *)) stores the next value, which is another string.

Dereferencing pp twice (ie **pp) means you are first accessing value at address B, which for instance is A, and then dereferencing that once more to get the value at address A, which is some character.

like image 20
vmpstr Avatar answered Nov 04 '22 05:11

vmpstr


Diagrams are worth a 1000 words. Have a look here

char, char* and char** are simply types describing what a variable (area of memory) contains.

Using dereferencing like *variable actually says to treat the value in the variable as a memory address and actually return the value at that address. This is indirection.

**variable is simply two levels of indirection. i.e. the value in the variable is a memory address of yet another memory address of the data that will be returned.

Addresses typically come from the address of operator, & or from a memory allocation function/operator like new

like image 3
T33C Avatar answered Nov 04 '22 05:11

T33C