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

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


当前回答

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.

例如:

#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;
}

希望这能有所帮助,

杰森

其他回答

I have used double pointers today while I was programming something for work, so I can answer why we had to use them (it's the first time I actually had to use double pointers). We had to deal with real time encoding of frames contained in buffers which are members of some structures. In the encoder we had to use a pointer to one of those structures. The problem was that our pointer was being changed to point to other structures from another thread. In order to use the current structure in the encoder, I had to use a double pointer, in order to point to the pointer that was being modified in another thread. It wasn't obvious at first, at least for us, that we had to take this approach. A lot of address were printed in the process :)).

当你处理在应用程序其他地方被更改的指针时,你应该使用双指针。在处理返回和寻址给您的硬件时,您可能还会发现双指针是必须的。

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

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

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

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

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

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

……

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


一个非常非常非常无聊的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);
}

输出:

total words in my lol: 243

我经常使用它们的一件事是,当我有一个对象数组,我需要根据不同的字段对它们执行查找(二进制搜索)。 我保留原始数组…

int num_objects;
OBJECT *original_array = malloc(sizeof(OBJECT)*num_objects);

然后创建一个指向对象的排序指针数组。

int compare_object_by_name( const void *v1, const void *v2 ) {
  OBJECT *o1 = *(OBJECT **)v1;
  OBJECT *o2 = *(OBJECT **)v2;
  return (strcmp(o1->name, o2->name);
}

OBJECT **object_ptrs_by_name = malloc(sizeof(OBJECT *)*num_objects);
  int i = 0;
  for( ; i<num_objects; i++)
    object_ptrs_by_name[i] = original_array+i;
  qsort(object_ptrs_by_name, num_objects, sizeof(OBJECT *), compare_object_by_name);

您可以根据需要创建任意数量的已排序指针数组,然后对已排序指针数组使用二进制搜索,根据已有的数据访问所需的对象。对象的原始数组可以保持无序,但是每个指针数组将按照它们指定的字段进行排序。

下面是一个非常简单的c++示例,说明如果要使用函数将指针设置为指向对象,则需要一个指针指向指针。否则,指针将继续返回null。

(一个c++的答案,但我相信在C中也是一样的)

(同样,供参考:谷歌("pass by value c++") = "默认情况下,c++中的参数是按值传递的。当实参按值传递时,实参的值被复制到函数的形参中。”)

我们想让指针b等于字符串a。

#include <iostream>
#include <string>

void Function_1(std::string* a, std::string* b) {
  b = a;
  std::cout << (b == nullptr);  // False
}

void Function_2(std::string* a, std::string** b) {
  *b = a;
  std::cout << (b == nullptr);  // False
}

int main() {
  std::string a("Hello!");
  std::string* b(nullptr);
  std::cout << (b == nullptr);  // True

  Function_1(&a, b);
  std::cout << (b == nullptr);  // True

  Function_2(&a, &b);
  std::cout << (b == nullptr);  // False
}

// Output: 10100

在Function_1(&a, b);这条线上会发生什么?

The "value" of &main::a (an address) is copied into the parameter std::string* Function_1::a. Therefore Function_1::a is a pointer to (i.e. the memory address of) the string main::a. The "value" of main::b (an address in memory) is copied into the parameter std::string* Function_1::b. Therefore there are now 2 of these addresses in memory, both null pointers. At the line b = a;, the local variable Function_1::b is then changed to equal Function_1::a (= &main::a), but the variable main::b is unchanged. After the call to Function_1, main::b is still a null pointer.

在函数_2(&a, &b);这一行发生了什么?

The treatment of the a variable is the same: within the function, Function_2::a is the address of the string main::a. But the variable b is now being passed as a pointer to a pointer. The "value" of &main::b (the address of the pointer main::b) is copied into std::string** Function_2::b. Therefore within Function_2, dereferencing this as *Function_2::b will access and modify main::b . So the line *b = a; is actually setting main::b (an address) equal to Function_2::a (= address of main::a) which is what we want.

如果你想用一个函数来修改一个东西,无论是一个对象还是一个地址(指针),你必须传递一个指向那个东西的指针。您实际传入的内容不能被修改(在调用范围内),因为创建了本地副本。

(一个例外是如果形参是一个引用,例如std::string& a.但通常这些是const。一般来说,如果你调用f(x),如果x是一个对象,你应该能够假设f不会修改x。但如果x是一个指针,那么你应该假设f可能修改x指向的对象。)

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

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

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;
}

发生这种情况的原因是在alloc1中,指针是按值传入的。因此,当它被重新分配给alloc1内部的malloc调用的结果时,更改不属于不同作用域中的代码。