有人可以解释指针指针的工作原理吗?

时间:2014-05-30 20:52:46

标签: c pointers struct malloc

我真的不明白指针的指针是如何工作的。没有使用指向指针的任何方法做同样的工作?

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

5 个答案:

答案 0 :(得分:6)

指针101的指针

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

     
  • 一部分内存将分配给大小足以容纳`int`的`x`。
  •  
  • 分配给`x`的内存在进程内存映射中有一个地址。
  •  
  • 要查看“x”位于内存中的地址,请使用:
    printf("x's memory address = %p\n", &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`表示`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 ):

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

enter image description here

回到问题

问:没有使用指向指针的方法可以做同样的工作吗?

没有。假设如下:

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指向的位置。

答案 1 :(得分:4)

对于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
}

你可以使用typedef来隐藏变量的指针,但是根据我的经验,隐藏在typedef后面的指针是糟糕的juju。

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

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

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

答案 2 :(得分:0)

如果指针是您的电话簿条目,则可以将指向该指针的指针告知您存在该条目的电话簿。

查看您的代码:

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

答案 3 :(得分:0)

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

关于使用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 :(得分:-2)

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被分配给1个“customer”类型元素的数组。 tmp本身以两种方式(** tmp和* tmp [])指向未知大小的“customer”元素数组的指针。