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

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


当前回答

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

假设您有一个链表中节点的结构,可能是这样

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

现在您想实现一个remove_if函数,它接受删除条件rm作为参数之一,并遍历链表:如果一个条目满足该条件(例如rm(entry)==true),它的节点将从链表中删除。最后,remove_if返回链表的头(可能与原始头不同)。

你可以写信

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

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

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

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

你现在不需要一个prev,因为你可以直接修改什么prev->next指向。

为了使事情更清楚,让我们稍微跟随一下代码。拆卸过程中:

如果entry ==* head:它将是*head (==*curr) =* head->next - head现在指向新标题节点的指针。您可以通过直接将head的内容更改为一个新的指针来实现这一点。 如果entry != *head:类似地,*curr是prev->next所指向的,现在指向entry->next。

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

其他回答

例如,你可能想要确保当你释放某个东西的内存时,你将指针设置为空。

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

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

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

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

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

1. 基本概念-

当你申报如下:-

1. Char *ch -(称为字符指针) - ch为单个字符的地址。 - (*ch)将解引用字符的值。

2. Char **ch - 'ch'包含字符指针数组的地址。(如1) '*ch'包含单个字符的地址。(注意它与1不同,因为声明不同)。 (**ch)将解引用到字符的确切值..

添加更多指针将扩展数据类型的维度,从字符扩展到字符串,再扩展到字符串数组,等等……你可以把它和一维,二维,三维矩阵联系起来。

指针的用法取决于你如何声明它。

这是一个简单的代码..

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 '\0'
    cout << **p << endl;         //Prints the exact character which is being pointed
    *p++;                        //Increments for the next string
    cout << *p;
}

2. 双指针的另一个应用 (这也包括引用传递)

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

void func(char ch)
{
    ch = 'B';
}

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

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

输出为AA。这是行不通的,因为您已经将“按值传递”传递给了函数。

正确的做法是-

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

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

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

在本例中,method使用双指针作为参数来更新字符串的值。

简单的例子,你可能已经见过很多次了

int main(int argc, char **argv)

在第二个参数中有它:指向char的指针的指针。

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

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

有关上述函数签名的更多详细信息,请参见此回答。

有点晚了,但希望这能帮助到一些人。

在C语言中,数组总是在堆栈上分配内存,因此函数不能返回 一个(非静态)数组,因为内存分配在堆栈上 当执行到达当前块的末尾时自动释放。 当你想处理二维数组时,这真的很烦人 (即矩阵),并实现一些可以改变和返回矩阵的函数。 要实现这一点,可以使用指针对指针来实现矩阵 动态分配内存:

/* Initializes a matrix */
double** init_matrix(int num_rows, int num_cols){
    // Allocate memory for num_rows float-pointers
    double** A = calloc(num_rows, sizeof(double*));
    // return NULL if the memory couldn't allocated
    if(A == NULL) return NULL;
    // For each double-pointer (row) allocate memory for num_cols floats
    for(int i = 0; i < num_rows; i++){
        A[i] = calloc(num_cols, sizeof(double));
        // return NULL if the memory couldn't allocated
        // and free the already allocated memory
        if(A[i] == NULL){
            for(int j = 0; j < i; j++){
                free(A[j]);
            }
            free(A);
            return NULL;
        }
    }
    return A;
} 

这里有一个例子:

double**       double*           double
             -------------       ---------------------------------------------------------
   A ------> |   A[0]    | ----> | A[0][0] | A[0][1] | A[0][2] | ........ | A[0][cols-1] |
             | --------- |       ---------------------------------------------------------
             |   A[1]    | ----> | A[1][0] | A[1][1] | A[1][2] | ........ | A[1][cols-1] |
             | --------- |       ---------------------------------------------------------
             |     .     |                                    .
             |     .     |                                    .
             |     .     |                                    .
             | --------- |       ---------------------------------------------------------
             |   A[i]    | ----> | A[i][0] | A[i][1] | A[i][2] | ........ | A[i][cols-1] |
             | --------- |       ---------------------------------------------------------
             |     .     |                                    .
             |     .     |                                    .
             |     .     |                                    .
             | --------- |       ---------------------------------------------------------
             | A[rows-1] | ----> | A[rows-1][0] | A[rows-1][1] | ... | A[rows-1][cols-1] |
             -------------       ---------------------------------------------------------

The double-pointer-to-double-pointer A points to the first element A[0] of a memory block whose elements are double-pointers itself. You can imagine these double-pointers as the rows of the matrix. That's the reason why every double-pointer allocates memory for num_cols elements of type double. Furthermore A[i] points to the i-th row, i.e. A[i] points to A[i][0] and that's just the first double-element of the memory block for the i-th row. Finally, you can access the element in the i-th row and j-th column easily with A[i][j].

下面是一个完整的例子来演示它的用法:

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

/* Initializes a matrix */
double** init_matrix(int num_rows, int num_cols){
    // Allocate memory for num_rows double-pointers
    double** matrix = calloc(num_rows, sizeof(double*));
    // return NULL if the memory couldn't allocated
    if(matrix == NULL) return NULL;
    // For each double-pointer (row) allocate memory for num_cols
    // doubles
    for(int i = 0; i < num_rows; i++){
        matrix[i] = calloc(num_cols, sizeof(double));
        // return NULL if the memory couldn't allocated
        // and free the already allocated memory
        if(matrix[i] == NULL){
            for(int j = 0; j < i; j++){
                free(matrix[j]);
            }
            free(matrix);
            return NULL;
        }
    }
    return matrix;
}

/* Fills the matrix with random double-numbers between -1 and 1 */
void randn_fill_matrix(double** matrix, int rows, int cols){
    for (int i = 0; i < rows; ++i){
        for (int j = 0; j < cols; ++j){
            matrix[i][j] = (double) rand()/RAND_MAX*2.0-1.0;
        }
    }
}


/* Frees the memory allocated by the matrix */
void free_matrix(double** matrix, int rows, int cols){
    for(int i = 0; i < rows; i++){
        free(matrix[i]);
    }
    free(matrix);
}

/* Outputs the matrix to the console */
void print_matrix(double** matrix, int rows, int cols){
    for(int i = 0; i < rows; i++){
        for(int j = 0; j < cols; j++){
            printf(" %- f ", matrix[i][j]);
        }
        printf("\n");
    }
}


int main(){
    srand(time(NULL));
    int m = 3, n = 3;
    double** A = init_matrix(m, n);
    randn_fill_matrix(A, m, n);
    print_matrix(A, m, n);
    free_matrix(A, m, n);
    return 0;
}