C语言 为什么要使用双重间接?或者为什么使用指向指针的指针?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/5580761/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-02 08:18:25  来源:igfitidea点击:

Why use double indirection? or Why use pointers to pointers?

cpointers

提问by manju

When should a double indirection be used in C? Can anyone explain with a example?

什么时候应该在 C 中使用双重间接?谁能用例子解释一下?

What I know is that a double indirection is a pointer to a pointer. Why would I need a pointer to a pointer?

我所知道的是,双重间接是指向指针的指针。为什么我需要一个指向指针的指针?

回答by pmg

If you want to have a list of characters (a word), you can use char *word

如果你想要一个字符列表(一个单词),你可以使用 char *word

If you want a list of words (a sentence), you can use char **sentence

如果你想要一个单词列表(一个句子),你可以使用 char **sentence

If you want a list of sentences (a monologue), you can use char ***monologue

如果你想要一个句子列表(一个独白),你可以使用 char ***monologue

If you want a list of monologues (a biography), you can use char ****biography

如果你想要一个独白的列表(传记),你可以使用 char ****biography

If you want a list of biographies (a bio-library), you can use char *****biolibrary

如果你想要一个传记列表(一个生物图书馆),你可以使用 char *****biolibrary

If you want a list of bio-libraries (a ??lol), you can use char ******lol

如果你想要一个生物库列表(a ??lol),你可以使用 char ******lol

... ...

……

yes, I know these might not be the best data structures

是的,我知道这些可能不是最好的数据结构



Usage example with a very very very boring lol

一个非常非常无聊的例子,哈哈

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int wordsinsentence(char **x) {
    int w = 0;
    while (*x) {
        w += 1;
        x++;
    }
    return w;
}

int wordsinmono(char ***x) {
    int w = 0;
    while (*x) {
        w += wordsinsentence(*x);
        x++;
    }
    return w;
}

int wordsinbio(char ****x) {
    int w = 0;
    while (*x) {
        w += wordsinmono(*x);
        x++;
    }
    return w;
}

int wordsinlib(char *****x) {
    int w = 0;
    while (*x) {
        w += wordsinbio(*x);
        x++;
    }
    return w;
}

int wordsinlol(char ******x) {
    int w = 0;
    while (*x) {
        w += wordsinlib(*x);
        x++;
    }
    return w;
}

int main(void) {
    char *word;
    char **sentence;
    char ***monologue;
    char ****biography;
    char *****biolibrary;
    char ******lol;

    //fill data structure
    word = malloc(4 * sizeof *word); // assume it worked
    strcpy(word, "foo");

    sentence = malloc(4 * sizeof *sentence); // assume it worked
    sentence[0] = word;
    sentence[1] = word;
    sentence[2] = word;
    sentence[3] = NULL;

    monologue = malloc(4 * sizeof *monologue); // assume it worked
    monologue[0] = sentence;
    monologue[1] = sentence;
    monologue[2] = sentence;
    monologue[3] = NULL;

    biography = malloc(4 * sizeof *biography); // assume it worked
    biography[0] = monologue;
    biography[1] = monologue;
    biography[2] = monologue;
    biography[3] = NULL;

    biolibrary = malloc(4 * sizeof *biolibrary); // assume it worked
    biolibrary[0] = biography;
    biolibrary[1] = biography;
    biolibrary[2] = biography;
    biolibrary[3] = NULL;

    lol = malloc(4 * sizeof *lol); // assume it worked
    lol[0] = biolibrary;
    lol[1] = biolibrary;
    lol[2] = biolibrary;
    lol[3] = NULL;

    printf("total words in my lol: %d\n", wordsinlol(lol));

    free(lol);
    free(biolibrary);
    free(biography);
    free(monologue);
    free(sentence);
    free(word);
}

Output:

输出:

total words in my lol: 243

回答by Asha

One reason is you want to change the value of the pointer passed to a function as the function argument, to do this you require pointer to a pointer.

一个原因是您想要更改作为函数参数传递给函数的指针的值,为此您需要指向指针的指针。

In simple words, Use **when you want to preserve (OR retain change in) the Memory-Allocation or Assignment even outside of a function call.(So, Pass such function with double pointer arg.)

简单地说,使用**时要保留(或保留在变化)的内存分配或分配甚至外面的函数调用。(因此,使用双指针 arg 传递此类函数。)

This may not be a very good example, but will show you the basic use:

这可能不是一个很好的例子,但会向您展示基本用法:

void allocate(int** p)
{
  *p = (int*)malloc(sizeof(int));
}

int main()
{
  int* p = NULL;
  allocate(&p);
  *p = 42;
  free(p);
}

回答by Brian Joseph Spinos

  • Let's say you have a pointer. Its value is an address.
  • but now you want to change that address.
  • you could. by doing pointer1 = pointer2, you give pointer1 the address of pointer2.
  • but! if you do that within a function, and you want the result to persist after the function is done, you need do some extra work. you need a new pointer3 just to point to pointer1. pass pointer3 to the function.

  • here is an example. look at the output below first, to understand.

  • 假设您有一个指针。它的值是一个地址。
  • 但现在您想更改该地址。
  • 你可以。通过这样做pointer1 = pointer2,您将指针 1 的地址指定为指针 2 的地址。
  • 但!如果您在函数中执行此操作,并且希望在函数完成后结果保持不变,则需要做一些额外的工作。你需要一个新的指针 3 来指向指针 1。将指针 3 传递给函数。

  • 这是一个例子。先看下面的输出,就明白了。

#include <stdio.h>

int main()
{

    int c = 1;
    int d = 2;
    int e = 3;
    int * a = &c;
    int * b = &d;
    int * f = &e;
    int ** pp = &a;  // pointer to pointer 'a'

    printf("\n a's value: %x \n", a);
    printf("\n b's value: %x \n", b);
    printf("\n f's value: %x \n", f);
    printf("\n can we change a?, lets see \n");
    printf("\n a = b \n");
    a = b;
    printf("\n a's value is now: %x, same as 'b'... it seems we can, but can we do it in a function? lets see... \n", a);
    printf("\n cant_change(a, f); \n");
    cant_change(a, f);
    printf("\n a's value is now: %x, Doh! same as 'b'...  that function tricked us. \n", a);

    printf("\n NOW! lets see if a pointer to a pointer solution can help us... remember that 'pp' point to 'a' \n");
     printf("\n change(pp, f); \n");
    change(pp, f);
    printf("\n a's value is now: %x, YEAH! same as 'f'...  that function ROCKS!!!. \n", a);
    return 0;
}

void cant_change(int * x, int * z){
    x = z;
    printf("\n ----> value of 'a' is: %x inside function, same as 'f', BUT will it be the same outside of this function? lets see\n", x);
}

void change(int ** x, int * z){
    *x = z;
    printf("\n ----> value of 'a' is: %x inside function, same as 'f', BUT will it be the same outside of this function? lets see\n", *x);
}

Here is the output: (read this first)

这是输出:(首先阅读此内容

 a's value: bf94c204

 b's value: bf94c208 

 f's value: bf94c20c 

 can we change a?, lets see 

 a = b 

 a's value is now: bf94c208, same as 'b'... it seems we can, but can we do it in a function? lets see... 

 cant_change(a, f); 

 ----> value of 'a' is: bf94c20c inside function, same as 'f', BUT will it be the same outside of this function? lets see

 a's value is now: bf94c208, Doh! same as 'b'...  that function tricked us. 

 NOW! lets see if a pointer to a pointer solution can help us... remember that 'pp' point to 'a' 

 change(pp, f); 

 ----> value of 'a' is: bf94c20c inside function, same as 'f', BUT will it be the same outside of this function? lets see

 a's value is now: bf94c20c, YEAH! same as 'f'...  that function ROCKS!!!. 

回答by Silviu

Adding to Asha'sresponse, if you use single pointer to the example bellow (e.g. alloc1() ) you will lose the reference to the memory allocated inside the function.

添加到Asha 的响应中,如果您使用指向下面示例的单个指针(例如 alloc1() ),您将丢失对函数内部分配的内存的引用。

void alloc2(int** p) {
   *p = (int*)malloc(sizeof(int));
   **p = 10;
}

void alloc1(int* p) {
   p = (int*)malloc(sizeof(int));
   *p = 10;
}

int main(){
   int *p = NULL;
   alloc1(p);
   //printf("%d ",*p);//undefined
   alloc2(&p);
   printf("%d ",*p);//will print 10
   free(p);
   return 0;
}

The reason it occurs like this is that in alloc1the pointer is passed in by value. So, when it is reassigned to the result of the malloccall inside of alloc1, the change does not pertain to code in a different scope.

出现这种情况的原因是在alloc1指针中是按值传入的。因此,当它被重新分配给malloc内部调用的结果时alloc1,更改与不同范围内的代码无关。

回答by ziyuang

I saw a very good example today, from this blog post, as I summarize below.

我今天从这篇博客文章中看到了一个很好的例子,我总结如下。

Imagine you have a structure for nodes in a linked list, which probably is

想象一下,你有一个链表中节点的结构,它可能是

typedef struct node
{
    struct node * next;
    ....
} node;

Now you want to implement a remove_iffunction, which accepts a removal criterion rmas one of the arguments and traverses the linked list: if an entry satisfies the criterion (something like rm(entry)==true), its node will be removed from the list. In the end, remove_ifreturns the head (which may be different from the original head) of the linked list.

现在你想实现一个remove_if函数,它接受一个移除标准rm作为参数之一并遍历链表:如果一个条目满足这个标准(类似于rm(entry)==true),它的节点将从列表中移除。最后remove_if返回链表的头部(可能与原来的头部不同)。

You may write

你可以写

for (node * prev = NULL, * curr = head; curr != NULL; )
{
    node * const next = curr->next;
    if (rm(curr))
    {
        if (prev)  // the node to be removed is not the head
            prev->next = next;
        else       // remove the head
            head = next;
        free(curr);
    }
    else
        prev = curr;
    curr = next;
}

as your forloop. The message is, without double pointers, you have to maintain a prevvariable to re-organize the pointers, and handle the two different cases.

作为你的for循环。消息是,如果没有双指针,您必须维护一个prev变量来重新组织指针,并处理两种不同的情况。

But with double pointers, you can actually write

但是用双指针,你实际上可以写

// now head is a double pointer
for (node** curr = head; *curr; )
{
    node * entry = *curr;
    if (rm(entry))
    {
        *curr = entry->next;
        free(entry);
    }
    else
        curr = &entry->next;
}

You don't need a prevnow because you can directly modify what prev->nextpointed to.

您不需要prevnow 因为您可以直接修改prev->next指向.

To make things clearer, let's follow the code a little bit. During the removal:

为了使事情更清楚,让我们稍微按照代码进行操作。拆除过程中:

  1. if entry == *head: it will be *head (==*curr) = *head->next-- headnow points to the pointer of the new heading node. You do this by directly changing head's content to a new pointer.
  2. if entry != *head: similarly, *curris what prev->nextpointed to, and now points to entry->next.
  1. if entry == *head: it will be *head (==*curr) = *head->next--head现在指向新标题节点的指针。您可以通过直接将head的内容更改为新指针来实现此目的。
  2. if entry != *head: 同样,*currprev->next指向什么,现在指向entry->next.

No matter in which case, you can re-organize the pointers in a unified way with double pointers.

无论在哪种情况下,都可以使用双指针以统一的方式重新组织指针。

回答by Bhavuk Mathur

1. Basic Concept -

一、基本概念——

When you declare as follows : -

当您声明如下: -

1. char *ch - (called character pointer)
- ch contains the address of a single character.
- (*ch) will dereference to the value of the character..

1. char *ch - (称为字符指针)
- ch 包含单个字符的地址。
- (*ch) 将取消引用字符的值..

2. char **ch -
'ch' contains the address of an Array of character pointers. (as in 1)
'*ch' contains the address of a single character. (Note that it's different from 1, due to difference in declaration).
(**ch) will dereference to the exact value of the character..

2. char **ch -
'ch' 包含字符指针数组的地址。(如 1)
'*ch' 包含单个字符的地址。(请注意,由于声明的不同,它与 1 不同)。
(**ch) 将取消引用字符的确切值。

Adding more pointers expand the dimension of a datatype, from character to string, to array of strings, and so on... You can relate it to a 1d, 2d, 3d matrix..

添加更多指针会扩展数据类型的维度,从字符到字符串,再到字符串数组,等等……您可以将其与 1d、2d、3d 矩阵相关联。

So, the usage of pointer depends upon how you declare it.

因此,指针的使用取决于您如何声明它。

Here is a simple code..

这是一个简单的代码..

int main()
{
    char **p;
    p = (char **)malloc(100);
    p[0] = (char *)"Apple";      // or write *p, points to location of 'A'
    p[1] = (char *)"Banana";     // or write *(p+1), points to location of 'B'

    cout << *p << endl;          //Prints the first pointer location until it finds '
void func(char ch)
{
    ch = 'B';
}

int main()
{
    char ptr;
    ptr = 'A';
    printf("%c", ptr);

    func(ptr);
    printf("%c\n", ptr);
}
' cout << **p << endl; //Prints the exact character which is being pointed *p++; //Increments for the next string cout << *p; }

2. Another Application of Double Pointers -
(this would also cover pass by reference)

2. 双指针的另一个应用 -
(这也将涵盖通过引用传递)

Suppose you want to update a character from a function. If you try the following : -

假设你想从一个函数中更新一个字符。如果您尝试以下操作:-

void func( char *ptr)        //Passed by Reference
{
    *ptr = 'B';
}

int main()
{
    char *ptr;
    ptr = (char *)malloc(sizeof(char) * 1);
    *ptr = 'A';
    printf("%c\n", *ptr);

    func(ptr);
    printf("%c\n", *ptr);
}

The output will be AA. This doesn't work, as you have "Passed By Value" to the function.

输出将是 AA。这不起作用,因为您已“按值传递”到该函数。

The correct way to do that would be -

正确的做法是——

void func(char **str)
{
    strcpy(str, "Second");
}

int main()
{
    char **str;
    // printf("%d\n", sizeof(char));
    *str = (char **)malloc(sizeof(char) * 10);          //Can hold 10 character pointers
    int i = 0;
    for(i=0;i<10;i++)
    {
        str = (char *)malloc(sizeof(char) * 1);         //Each pointer can point to a memory of 1 character.
    }

    strcpy(str, "First");
    printf("%s\n", str);
    func(str);
    printf("%s\n", str);
}

Now extend this requirement for updating a string instead of character.
For this, you need to receive the parameter in the function as a double pointer.

现在扩展此要求以更新字符串而不是字符。
为此,您需要将函数中的参数作为双指针接收。

#include <stdlib.h>

typedef unsigned char** handle_type;

//some data_structure that the library functions would work with
typedef struct 
{
    int data_a;
    int data_b;
    int data_c;
} LIB_OBJECT;

handle_type lib_create_handle()
{
    //initialize the handle with some memory that points to and array of 10 LIB_OBJECTs
    handle_type handle = malloc(sizeof(handle_type));
    *handle = malloc(sizeof(LIB_OBJECT) * 10);

    return handle;
}

void lib_func_a(handle_type handle) { /*does something with array of LIB_OBJECTs*/ }

void lib_func_b(handle_type handle)
{
    //does something that takes input LIB_OBJECTs and makes more of them, so has to
    //reallocate memory for the new objects that will be created

    //first re-allocate the memory somewhere else with more slots, but don't destroy the
    //currently allocated slots
    *handle = realloc(*handle, sizeof(LIB_OBJECT) * 20);

    //...do some operation on the new memory and return
}

void lib_func_c(handle_type handle) { /*does something else to array of LIB_OBJECTs*/ }

void lib_free_handle(handle_type handle) 
{
    free(*handle);
    free(handle); 
}


int main()
{
    //create a "handle" to some memory that the library functions can use
    handle_type my_handle = lib_create_handle();

    //do something with that memory
    lib_func_a(my_handle);

    //do something else with the handle that will make it point somewhere else
    //but that's invisible to us from the standpoint of the calling the function and
    //working with the handle
    lib_func_b(my_handle); 

    //do something with new memory chunk, but you don't have to think about the fact
    //that the memory has moved under the hood ... it's still pointed to by the "handle"
    lib_func_c(my_handle);

    //deallocate the handle
    lib_free_handle(my_handle);

    return 0;
}

In this example, method expects a double pointer as a parameter to update the value of a string.

在这个例子中,method 需要一个双指针作为参数来更新字符串的值。

回答by Jason

Pointers to pointers also come in handy as "handles" to memory where you want to pass around a "handle" between functions to re-locatable memory. That basically means that the function can change the memory that is being pointed to by the pointer inside the handle variable, and every function or object that is using the handle will properly point to the newly relocated (or allocated) memory. Libraries like to-do this with "opaque" data-types, that is data-types were you don't have to worry about what they're doing with the memory being pointed do, you simply pass around the "handle" between the functions of the library to perform some operations on that memory ... the library functions can be allocating and de-allocating the memory under-the-hood without you having to explicitly worry about the process of memory management or where the handle is pointing.

指向指针的指针也可以用作内存的“句柄”,您希望在函数之间传递“句柄”以重新定位内存。这基本上意味着该函数可以更改句柄变量内的指针所指向的内存,并且每个使用该句柄的函数或对象都将正确指向新重新定位(或分配)的内存。图书馆喜欢用“不透明”数据类型来做这件事,也就是说,如果你不必担心他们用指向的内存做什么,你只需在两个数据类型之间传递“句柄”库的函数来对该内存执行一些操作......

For instance:

例如:

int main(int argc, char **argv)

Hope this helps,

希望这可以帮助,

Jason

杰森

回答by drysdam

Strings are a great example of uses of double pointers. The string itself is a pointer, so any time you need to point to a string, you'll need a double pointer.

字符串是使用双指针的一个很好的例子。字符串本身是一个指针,所以任何时候你需要指向一个字符串,你都需要一个双指针。

回答by plats1

Simple example that you probably have seen many times before

您之前可能已经见过很多次的简单示例

void safeFree(void** memory) {
    if (*memory) {
        free(*memory);
        *memory = NULL;
    }
}

In the second parameter you have it: pointer to pointer to char.

在第二个参数中你有它:指向字符指针的指针。

Note that the pointer notation (char* c) and the array notation (char c[]) are interchangeable in function arguments. So you could also write char *argv[]. In other words char *argv[]and char **argvare interchangeable.

请注意,指针符号 ( char* c) 和数组符号 ( char c[]) 在函数参数中可以互换。所以你也可以写char *argv[]. 换句话说,char *argv[]char **argv是可以互换的。

What the above represents is in fact an array of character sequences (the command line arguments that are given to a program at startup).

上面所代表的实际上是一个字符序列数组(在启动时提供给程序的命令行参数)。

See also this answerfor more details about the above function signature.

有关上述函数签名的更多详细信息,另请参阅此答案

回答by Jeff Foster

For example, you might want to make sure that when you free the memory of something you set the pointer to null afterwards.

例如,您可能希望确保在释放某些内容的内存后将指针设置为 null。

void* myMemory = someCrazyFunctionThatAllocatesMemory();
safeFree(&myMemory);

When you call this function you'd call it with the address of a pointer

当你调用这个函数时,你会用一个指针的地址来调用它

##代码##

Now myMemoryis set to NULL and any attempt to reuse it will be very obviously wrong.

NowmyMemory设置为 NULL,任何重用它的尝试都将是非常明显的错误。