开发者

char ** and dereferencing pointers

开发者 https://www.devze.com 2023-02-11 10:57 出处:网络
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 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;
}
0

精彩评论

暂无评论...
验证码 换一张
取 消