为什么要使用双重间接?或者为什么要使用指针指针?

时间:2011-04-07 12:08:56

标签: c pointers

何时应该在C中使用双重间接?任何人都可以用一个例子来解释吗?

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

18 个答案:

答案 0 :(得分:440)

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

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

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

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

如果您需要传记列表(生物库),可以使用char *****biolibrary

如果你想要一个生物库列表(一个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

答案 1 :(得分:160)

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

简单来说,当你想在函数调用之外保留(或保留更改)Memory-Allocation或Assignment时使用**(所以,传递这样的函数用双指针arg。)

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

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

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

答案 2 :(得分:72)

这是一个简单的答案!!!!

  • 假设你有一个指针,指出它的值是一个地址。
  • 但现在您要更改该地址。
  • 你可以通过执行pointer1 = pointer2,而pointer1现在可以拥有指针2的地址。
  • BUT!如果你想要一个函数为你做这个,并且你希望结果在函数完成后保持,你需要做一些额外的工作,你需要一个新的指针3只是指向pointer1,并将指针3传递给函数。 / p>

  • 这是一个有趣的例子(首先看一下输出,了解!):

#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);
}
  • ,这是输出:
 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!!!. 

答案 3 :(得分:42)

添加到Asha's响应,如果使用指向示例下的单个指针(例如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;
}

它出现的原因是alloc1指针是按值传入的。因此,当它被重新分配给mallocalloc1调用的结果时,更改不属于不同范围内的代码。

答案 4 :(得分:21)

<强> 1。基本概念 -

当您声明如下: -

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

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

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

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

这是一个简单的代码..

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

在此示例中,方法需要双指针作为参数来更新字符串的值。

答案 5 :(得分:20)

我今天从this blog post看到了一个很好的例子,我在下面总结一下。

想象一下,链接列表中的节点结构可能是

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指向的内容。

为了让事情变得更清楚,让我们稍微遵循一下代码。在移除期间:

  1. 如果entry == *head:它将*head (==*curr) = *head->next - head现在指向新标题节点的指针。您可以通过直接将head的内容更改为新指针来完成此操作。
  2. 如果entry != *head:类似地,*currprev->next所指向的,现在指向entry->next
  3. 无论在哪种情况下,您都可以使用双指针以统一的方式重新组织指针。

答案 6 :(得分:15)

指针的指针也可以作为内存的“句柄”派上用场,你想要在函数之间传递一个“句柄”来重新定位内存。这基本上意味着函数可以更改句柄变量内指针所指向的内存,并且使用句柄的每个函数或对象都将正确指向新重定位(或分配)的内存。图书馆喜欢用“不透明”的数据类型来做这件事,那就是数据类型你不必担心他们正在做的事情与你所做的事情有关,你只需要绕过“处理器”之间的“句柄”。库的函数在该内存上执行一些操作......库函数可以在内部分配和解除分配内存,而无需明确担心内存管理过程或句柄指向的位置。

例如:

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

希望这有帮助,

杰森

答案 7 :(得分:6)

您可能在

之前看过很多次的简单示例
int main(int argc, char **argv)

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

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

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

有关上述功能签名的详细信息,另请参阅this answer

答案 8 :(得分:5)

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

答案 9 :(得分:5)

例如,您可能希望确保在释放内存时将指针设置为null。

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

当您调用此函数时,您将使用指针的地址调用它

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

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

答案 10 :(得分:2)

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

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

您可以根据需要制作尽可能多的排序指针数组,然后在排序指针数组上使用二进制搜索来访问您拥有的数据所需的对象。原始对象数组可以保持未排序,但每个指针数组将按其指定字段排序。

答案 11 :(得分:2)

例如,如果您想随机访问非连续数据。

p -> [p0, p1, p2, ...]  
p0 -> data1
p1 -> data2

- 在C

T ** p = (T **) malloc(sizeof(T*) * n);
p[0] = (T*) malloc(sizeof(T));
p[1] = (T*) malloc(sizeof(T));

存储指向指针数组的指针p。每个指针都指向一段数据。

如果sizeof(T)很大,则可能无法分配sizeof(T) * n字节的连续块(​​即使用malloc)。

答案 12 :(得分:2)

以下是一个非常简单的C ++示例,它显示如果要使用函数将指针设置为指向对象,需要指向指针的指针。否则,指针将继续恢复为null

(C ++答案,但我相信它在C中是一样的。)

(另外,供参考:Google(&#34;传递值c ++&#34;)=&#34;默认情况下,C ++中的参数按值传递。当参数按值传递时,参数的值被复制到函数的参数中。&#34;)

因此我们希望将指针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);会发生什么?

  • &#34;值&#34;将&main::a(地址)复制到参数std::string* Function_1::a中。因此Function_1::a是指向字符串main::a的指针(即内存地址)。

  • &#34;值&#34;将main::b(内存中的地址)复制到参数std::string* Function_1::b中。因此,现在内存中有2个这样的地址,都是空指针。在第b = a;行,然后将局部变量Function_1::b更改为等于Function_1::a(= &main::a),但变量main::b保持不变。在调用Function_1之后,main::b仍然是空指针。

Function_2(&a, &b);会发生什么?

  • a变量的处理方式相同:在函数内,Function_2::a是字符串main::a的地址。

  • 但变量b现在作为指针传递。 &#34;值&#34;将&main::b(指针的地址 main::b)复制到std::string** Function_2::b。因此,在Function_2中,取消引用此*Function_2::b将访问和修改main::b。因此,行*b = a;实际上将main::b(地址)设置为等于Function_2::a(= main::a的地址),这正是我们想要的。

如果你想使用一个函数修改一个东西,无论是一个对象还是一个地址(指针),你必须传入一个指向那个东西的指针。实际传入无法修改(在调用范围内),因为制作了本地副本。

(如果参数是参考,例如std::string& a,则例外。但通常这些是const。通常,如果您致电f(x),如果x是一个对象你应该能够假设f 赢了修改x。但如果x是一个指针,那么你应该假设{ {1}} 可能修改f指向的对象。)

答案 13 :(得分:1)

为什么选择双指针?

目标是使用函数更改studentA指向的内容。

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


typedef struct Person{
    char * name;
} Person; 

/**
 * we need a ponter to a pointer, example: &studentA
 */
void change(Person ** x, Person * y){
    *x = y; // since x is a pointer to a pointer, we access its value: a pointer to a Person struct.
}

void dontChange(Person * x, Person * y){
    x = y;
}

int main()
{

    Person * studentA = (Person *)malloc(sizeof(Person));
    studentA->name = "brian";

    Person * studentB = (Person *)malloc(sizeof(Person));
    studentB->name = "erich";

    /**
     * we could have done the job as simple as this!
     * but we need more work if we want to use a function to do the job!
     */
    // studentA = studentB;

    printf("1. studentA = %s (not changed)\n", studentA->name);

    dontChange(studentA, studentB);
    printf("2. studentA = %s (not changed)\n", studentA->name);

    change(&studentA, studentB);
    printf("3. studentA = %s (changed!)\n", studentA->name);

    return 0;
}

/**
 * OUTPUT:
 * 1. studentA = brian (not changed)
 * 2. studentA = brian (not changed)
 * 3. studentA = erich (changed!)
 */

答案 14 :(得分:1)

聚会晚了一点,但希望这会对某人有所帮助。

在C数组中总是在堆栈上分配内存,因此函数无法返回 一个(非静态)数组,原因是在栈上分配了内存 当执行到达当前块的末尾时,将自动释放。 当您要处理二维数组时,这确实很烦人 (即矩阵)并实现一些可以更改和返回矩阵的函数。 为此,您可以使用指针到指针来实现矩阵 动态分配的内存:

/* Initializes a matrix */
float** init_matrix(int num_rows, int num_cols){
    // Allocate memory for num_rows float-pointers
    float** A = calloc(num_rows, sizeof(double*));
    // return NULL if the memory couldn't allocated
    if(A == NULL) return NULL;
    // For each float-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;
} 

这是一个例子:

 float**        float*            float
             -------------       ---------------------------------------------------------
   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] |
             -------------       ---------------------------------------------------------

浮点指针到浮点指针A指向a的第一个元素A [0] 元素本身为浮点指针的内存块。你可以想象这些 浮点指针作为矩阵的行。这就是为什么每个 浮点指针为num_cols个浮点元素分配内存。 此外,A [i]指向第i行,即A [i]指向A [i] [0]和 那只是第i行的内存块的第一个浮点元素。 最后,您可以访问第i行中的元素 A [i] [j]可以轻松地获得第j列。

下面是一个完整的示例,演示了用法:

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

/* Initializes a matrix */
float** init_matrix(int num_rows, int num_cols){
    // Allocate memory for num_rows float-pointers
    float** matrix = calloc(num_rows, sizeof(double*));
    // return NULL if the memory couldn't allocated
    if(matrix == NULL) return NULL;
    // For each float-pointer (row) allocate memory for num_cols
    // floats
    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 float-numbers between -1 and 1 */
void randn_fill_matrix(float** matrix, int rows, int cols){
    for (int i = 0; i < rows; ++i){
        for (int j = 0; j < cols; ++j){
            matrix[i][j] = (float) rand()/RAND_MAX*2.0f-1.0f;
        }
    }
}


/* Frees the memory allocated by the matrix */
void free_matrix(float** 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(float** 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;
    float** A = init_matrix(m, n);
    randn_fill_matrix(A, m, n);
    print_matrix(A, m, n);
    free_matrix(A, m, n);
    return 0;
}

答案 15 :(得分:0)

我今天使用双指针,而我正在为工作编程,所以我可以回答为什么我们必须使用它们(这是我第一次使用双指针)。我们必须处理缓冲区中包含的帧的实时编码,这些帧是某些结构的成员。在编码器中,我们必须使用指向其中一个结构的指针。问题是我们的指针被改为指向另一个线程的其他结构。为了在编码器中使用当前结构,我不得不使用双指针,以指向在另一个线程中被修改的指针。一开始并不是很明显,至少对我们来说,我们不得不采取这种方法。在这个过程中打印了很多地址:))。

当您处理在应用程序的其他位置更改的指针时,您应该使用双指针。当您处理返回并向您发送地址的硬件时,您可能还会发现必须使用双指针。

答案 16 :(得分:0)

比较修改变量的与修改指针的

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

void changeA(int (*a))
{
  (*a) = 10;
}

void changeP(int *(*P))
{
  (*P) = malloc(sizeof((*P)));
}

int main(void)
{
  int A = 0;

  printf("orig. A = %d\n", A);
  changeA(&A);
  printf("modi. A = %d\n", A);

  /*************************/

  int *P = NULL;

  printf("orig. P = %p\n", P);
  changeP(&P);
  printf("modi. P = %p\n", P);

  free(P);

  return EXIT_SUCCESS;
}

这有助于我避免在被调用函数(用于单链接列表)修改指针时返回指针的值。

旧(坏):

int *func(int *P)
{
  ...
  return P;
}

int main(void)
{
  int *pointer;
  pointer = func(pointer);
  ...
}    

新(更好):

void func(int **pointer)
{
  ...
}

int main(void)
{
  int *pointer;
  func(&pointer);
  ...
}    

答案 17 :(得分:0)

这里的大多数答案或多或少与应用程序编程有关。这是来自嵌入式系统编程的示例。例如,以下示例摘自NXP的Kinetis KL13系列微控制器的参考手册,该代码段用于从固件运行驻留在ROM中的引导程序:

” 为了获得入口点的地址,用户应用程序读取包含指向引导加载程序矢量表的偏移量0x1C处的引导加载程序API树的指针的字。向量表位于引导加载程序的地址范围的底部,ROM的范围为0x1C00_0000。因此,API树指针位于地址0x1C00_001C。

bootloader API树是一个结构,其中包含指向其他结构的指针,这些结构具有bootloader的功能和数据地址。引导加载程序入口点始终是API树的第一个字。 “

uint32_t runBootloaderAddress;
void (*runBootloader)(void * arg);
// Read the function address from the ROM API tree.
runBootloaderAddress = **(uint32_t **)(0x1c00001c);
runBootloader = (void (*)(void * arg))runBootloaderAddress;
// Start the bootloader.
runBootloader(NULL);