【问题标题】:Can someone explain how pointer to pointer works?有人可以解释指针的工作原理吗?
【发布时间】:2014-05-30 20:52:46
【问题描述】:

我不太明白指向指针的指针是如何工作的。有什么方法可以在不使用指针的情况下完成相同的工作?

struct customer
{
    char name[20];
    char surname[20];
    int code;
    float money;
};
typedef struct customer customer;
void inserts(customer **tmp)
{
    *tmp = (customer *)malloc(sizeof(customer));
    puts("Give me a customer name, surname code and money");
    scanf("%s %s %d %f", (*tmp)->name, (*tmp)->surname, &(*tmp)->code, &(*tmp)->money);
}

【问题讨论】:

  • OT:不要转换 malloc 的结果:stackoverflow.com/questions/605845/…
  • 你可以在函数中创建customer* tmp并返回指针而不是void。
  • 指针是一个变量,它告诉您在哪里可以找到另一个变量。如果您了解“指向变量的指针”的概念,那么您必须了解“指向指针的指针”,因为这是它的一种特殊情况。

标签: c pointers struct malloc


【解决方案1】:

指向指针 101 的指针

假设您有一个 int 变量 x: int x;

  • 部分内存将分配给x,其大小足以容纳int
  • 分配给x 的内存在进程内存映射中有一个地址。
  • 要查看x 在内存中的地址,请使用:
    printf("x's memory address = %p\n", (void*)&x);
  • &x 表示x 的地址。
  • 要查看存储在x 中的整数值,请使用:
    printf("x=%d\n", x);
  • x 可以在其存在范围内直接操作。例如,可以在声明它的函数中对其进行操作:
    x = 42;
  • 在其定义范围之外,如果知道其内存地址,则可以操纵x 的值。
  • 如果将x 的值(即:42)传递给函数(x),则该函数无法操作x 的值。
  • 如果将x 的地址传递给函数(&x),则该函数可以操作x 的值。

现在,假设您有一个指针变量p,它假定它指向intint *p;

  • 一部分内存将分配给p,其大小足以容纳内存地址。
  • 分配给p 的内存在进程内存映射中有一个地址。
  • &p 表示address of p。
  • 要查看p 在内存中的地址,请使用:
    printf("p's memory addr = %p\n", &p);
  • 要查看p 指向的地址,请使用:
    printf("Address where p is pointing: %p\n", p);
  • 要查看指向的所谓整数,请使用:
    printf("int = %d\n", *p);
  • p的值是内存中的一个地址;并且p 可以设置为指向任何地址,无论该地址是否实际存在于进程内存映射中。
  • 对象的地址是指向该对象的指针。因此,要使 p 指向 x:
    p = &x
  • p 指向的任何东西都可以通过指针类型(p 的类型为 int)来引用。
  • 分配给p 的内存量会有所不同,具体取决于机器的体系结构;以及它如何表示地址。
  • p 可以在其存在范围内直接操作。例如,可以在声明它的函数中对其进行操作:
    p = &x;p = NULL
  • 在其存在范围之外,如果知道其内存地址,则可以操纵p 的值。
  • 如果将 p(地址)的值传递给函数 (p),则该函数无法操纵“p”来更改其指向的位置。
  • 如果将p 的地址传递给函数(&p),则该函数可以操作p 指向的内容。

现在,假设您有一个指向指针变量 pp 的指针,假设它指向一个指向“int”的指针:int **pp; ...或:void inserts(customer **tmp ):

  • 指针的地址是指向指针的指针。
  • ...

回到问题

问:有什么方法可以在不使用指针的情况下完成相同的工作?

没有。假设如下:

void inserts(customer **tmp);

...
   {
   customer cust;
   custPtr  custPtr = &cust;

   inserts(&custPtr);
   ... 

inserts() 函数需要指针的地址才能操作custPtr 指向的位置。

如果改为:

void inserts2(customer *tmp);

...
   {
   customer cust;
   custPtr  custPtr = &cust;

   inserts2(custPtr);
   ... 

insert2() 将获得custPtr 值的副本,即cust 的地址。因此,insert2() 可以修改cust 的值,但不能更改custPtr 指向的位置。

【讨论】:

  • 感谢您的回复,真的很有帮助。
【解决方案2】:

对于T 类型的任何参数,如果您想修改参数的值并将该更改反映在调用者中,则必须传递一个指针:

void foo( T *ptr ) 
{
  *ptr = new_value();
}

void bar( void )
{
  T var;
  foo( &var ); // writes to var
}

如果T 是指针类型Q *,那么你会得到一个指向指针的指针:

void foo( Q **ptr )
{
  *ptr = new_value();
}

void bar( void )
{
  Q *var;
  foo( &var ); // writes to var
}

您可以使用 typedefs 来隐藏变量的指针,但根据我的经验,将指针隐藏在 typedefs 后面是不好的。

您可以更改函数以返回指针值,而不是像@Namfuak 建议的那样写入参数:

Q *foo( void )
{
  Q *val = new_value();
  return val;
}

void bar( void )
{
  Q *var;
  var = foo();
}

【讨论】:

  • +1 建议不要将指针隐藏在 typedef 后面。对于那些必须在开发人员之后阅读此类代码的人来说,这是可怕的。
【解决方案3】:

多级间接性可能会导致混淆。一个好的策略是通过降低间接级别来减少混淆。

除了使用scanf() 的合法问题之外,以下是实现此目的的一种方法:

void inserts(customer **tmp)
{
    customer *new_cust = malloc(sizeof *new_cust);
    if ( !new_cust ) {
        fprintf(stderr, "Couldn't allocate memory.\n");
        exit(EXIT_FAILURE);
    }

    puts("Give me a customer name, surname code and money");
    scanf("%s %s %d %f", new_cust->name, new_cust->surname,
                         new_cust->code, new_cust->money);

    /*  We delay using multiple levels of indirection
        until we get to the very last line, here.      */

    *tmp = new_cust;
}

正如其他人所提到的,在这种特殊情况下,仅返回指针可能会更容易。

【讨论】:

    【解决方案4】:

    如果指针是您的电话簿条目,则可以认为指向该指针的指针会告诉您该条目所在的电话簿。

    查看您的代码:

    struct customer {
        char name[20];
        char surname[20];
        int code;
        float money;
    };
    

    首先,you should not use a float type for money

    您的问题与C FAQ 4.8 有关。基本上,您有一个插入客户记录的功能。你想在哪里插入记录?显然保存记录的东西,电话簿,数据库等。所以,你需要一个指向保存指针的东西的指针,这些指针指向你要插入的对象。

    现在,对于您的其余代码,首先,请注意 you should not cast the return value of malloc。第二,using scanf is a serious security risk。第三,您在 inserts 函数中分配了一条新的客户记录,但您无法将任何故障传达给调用您的函数的代码。

    可以说,函数的名称应该类似于create_customer_interactive,以表明它不仅仅是在记录列表中插入一条记录。

    以下是您的代码结构:

    #include <stdio.h>
    #include <stdlib.h>
    
    struct customer {
        char *name;
        char *surname;
        int code;
        int money; /* in cents */
    };
    
    typedef struct customer *PCustomer;
    
    int read_customer_record(FILE *fp, PCustomer customer) {
        /* dummy implementation */
        customer->name = "A. Tester";
        customer->surname = "McDonald";
        customer->code = 123;
        customer->money = 100 * 100;
        return 1;
    }
    
    PCustomer insert_record_interactive(PCustomer *db, FILE *fp) {
        PCustomer tmp = malloc(sizeof(*tmp));
        if (!tmp) {
            return NULL;
        }
        if (!read_customer_record(fp, tmp)) {
            free(tmp);
            return NULL;
        }
        *db = tmp;
        return tmp;
    }
    
    int main(void) {
        PCustomer new_customer;
        PCustomer *db = malloc(3 * sizeof(*db));
        if (!db) {
            perror("Failed to allocate room for customer records");
            exit(EXIT_FAILURE);
        }
    
        /* insert a record in the second slot */
        new_customer = insert_record_interactive(&db[1], stdin);
        if (!new_customer) {
            perror("Failed to read customer record");
            exit(EXIT_FAILURE);
        }
    
        printf(
                "%s %s (%d) : $%.2f\n",
                new_customer->name,
                new_customer->surname,
                new_customer->code,
                ((double)new_customer->money) / 100.0
                );
        return 0;
    }
    

    【讨论】:

      【解决方案5】:
      void inserts(customer *tmp[])
      {
          *tmp = malloc(sizeof(customer));
          puts("Give me a customer name, surname code and money");
          scanf("%s %s %d %f", (*tmp)->name, (*tmp)->surname, &(*tmp)->code, &(*tmp)->money);
      }
      

      这是等价的。指向指针的指针也可以表示为指向未知大小数组的指针。

      发生的情况是 *tmp 被分配给类型为“customer”的 1 个元素的数组。 tmp 本身以两种方式(**tmp 和 *tmp[])引用一个未知大小的“客户”元素数组的指针。

      【讨论】:

      • tmp 仍然是指向指针的指针,和在参数列表中写**tmp*tmp[] 完全一样。 (这是一个仅适用于参数列表的语法怪癖)。你的第二段是假的,*tmp 没有被分配给任何东西。 tmp 仍然指向调用函数时所提供的参数所指向的任何内容。
      • *tmp 在这两种情况下都被分配给 malloc(sizeof(customer)) 。当然,tmp 指向同一个位置。
      • 哦,你是在倒退使用“gets assigned”。在这段代码中,malloc 的结果被分配给*tmp(而不是相反)。
      • 不,我不明白。 *tmp 分配给 malloc 的结果,而 **tmp / *tmp[] 当然指向 *tmp。你的观点是什么?
      • 可能是语言问题 - 你可以说 *tmp分配 malloc() 的结果,但你不能说 *tmp 是分配 tomalloc() 的结果。例如,“学生被分配了项目”,而不是“教授将项目分配给学生”。
      猜你喜欢
      • 2021-04-11
      • 2016-09-11
      • 1970-01-01
      • 1970-01-01
      • 2021-08-02
      • 2012-02-14
      • 2014-11-28
      • 1970-01-01
      • 2020-06-16
      相关资源
      最近更新 更多