"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??
...