char ** and dereferencing 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 an开发者_如何学God 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
"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?? ...
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.
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
Dereferencing a pointer means accessing the value the pointer points to. For example,
char c = 'c'; // This is a primitive value. You cannot dereference it.
char* p1 = &c; // A pointer to the address of c
char** p2 = &p1; // A pointer to the address of p1
/* Now, the following is true:
*p1 == c, i.e. dereferencing p1 allows us to read from/write to c.
*p2 == p1
**p2 == *(*p2) == *p1 = c - dereferencing p2 twice is c, too */
The reason why you use a pointer to c instead of c directly is that a pointer allows you to access more than 1 value. Take this example:
char[4] str;
char c0 = 'a', c1 = 'b', c3 = 'c', c4 = '\0';
str[0] = c0; str[1] = c1; str[2] = c2; str[3] = c3;
str = "abc"; // Same as the above line
Now suppose we need the second character. We could access it with c1
. But as you can see, this notation is really cumbersome. Plus, if we read the string from a file instead of writing it, we'd have to do complicated things. Instead, we just write
str[1] /* or */ *(str+1)
Note that the first element has the index 0, the second 1 - that's why we're using 1 here. A char**
turns this up to eleven - we have an array of an array of chars. Suppose we have such an array, let's call it input
, and need to find out the length of all the strings in it. This is how we would do it:
int lensum(char **input) {
int res = 0;
while (*input) { // This loops as long as the value input points to is not 0.
char* p = *input; // Make a copy of the value input currently points to
while (*p != '\0') { // Loop while the copy does not point to a char '\0'
res += 1; // We found a character
p++; // Check next character in the next iteration
}
input++; // Check next string in the next iteration
}
return res;
}
精彩评论