【问题标题】:Address of a pointer is equal to a pointer to pointer?指针的地址是否等于指向指针的指针?
【发布时间】:2013-06-14 00:48:32
【问题描述】:

我试图了解这段代码中的指针是如何工作的:

void allocateArray( int **arr, size_t size, int value )
{
    *arr = malloc( size * sizeof( int ));

    if ( *arr != NULL )
        for ( size_t i = 0; i < size; ++i )
            *( *arr + i ) = value;
}

int main( void )
{
    int *vector = NULL;

    allocateArray(&vector,5,45);

    free( vector );

    return 0;
}

如果我声明了一个指向 int ( *vector ) 的指针,那么传递它的地址如何使它成为指向 int ( **arr ) 的指针的指针?我的意思是,它是如何工作的,现在内存中向量的地址将被覆盖!

【问题讨论】:

  • 向量的地址不会被覆盖 - vector(最初是 NULL)的值将被覆盖。
  • @RichardJ.RossIII - 如果是这样,为什么我不能像这样将指针本身传递给函数:allocateArray(vector, 5, 45);
  • @KaeLL:因为allocateArray 需要int**,而vectorint*;类型不兼容。
  • @KaeLL:如果你这样做,那么你只是传递了vector 的值,即NULL,你不能在allocateArray() 中使用它。如果你真的想传递vector 的值,它应该有一个指向在调用allocateArray() 之前预分配的内存的值,删除allocateArray() 内的malloc(),并进行适当的取消引用。
  • If I declared a pointer to an int ( *vector ), how passing its address makes it a pointer to a pointer to int ( **arr )?: 因为声明int *vector 使vector 成为指向int 的指针,所以它的地址&amp;vector 也是一个指针,使它成为pointer to an int pointer(指向a指向 int) 的指针。

标签: c pointers malloc


【解决方案1】:

C 按值传递函数参数。因此,要允许函数修改调用者提供的变量,必须传递指向它的指针。并且函数必须取消引用指针才能进行修改。

void foo_1 (int a) {
    a += 1;   /* caller will not see any change to a */
}

void foo_2 (int *ap) {
    *ap += 1; /* caller will see value has changed */
}

int a = 0;
foo_1(a);  /* a is still 0 after return */
foo_2(&a); /* a becomes 1 after return */

&amp; 运算符产生一个值,该值表示应用它的对象的地址,结果类型是“指向(对象类型)的指针”。在上面的例子中,&amp;a 的结果是“指向int 的指针”。

如果变量是指针类型,则根本没有什么不同。

void foo_1 (int *a) {
    a = malloc(sizeof(int));   /* caller will not see any change to a */
}

void foo_2 (int **ap) {
    *ap = malloc(sizeof(int)); /* caller will see value has changed */
}

int *a = 0;
foo_1(a);  /* a is still 0 after return */
foo_2(&a); /* a becomes initialized to memory allocated by malloc */

在上面的例子中,由于a是一个指向int的指针,所以&amp;a的类型是“指向int的指针”。


指针 是一个用来指代对象地址的术语。对象的地址是一个值,表示对象在内存中的位置。知道该地址意味着可以读取和修改对象。 指针变量是可以存储对象地址的变量。

通常,变量的名称用于表示对象。通过对象,我只是指变量使用的内存,以及它的语义表示,也就是它的类型(通常,术语变量和对象可以互换使用,但对我来说,区别在于变量有一个名称)。读取和修改对象是通过名称完成的。获得指向对象的指针的一种方法是将一元 &amp; 运算符应用于变量的名称。因此,保存该地址的指针变量是指向该对象的指针。现在,可以使用一元 * 运算符通过指针取消引用指针来读取和修改同一个对象。

int a = 0;
int *ap = &a;
a += 1;       /* a now has the value 1 */
*ap += 1;     /* a now has the value 2 */

动态创建的对象,即通过malloc(),没有名称。但是,malloc() 返回一个指针,通过该指针可以读取和修改对象。

int *ap = 0;              /* ap initialized to NULL */
ap = malloc(sizeof(int)); /* ap points to dynamically allocated int */
*ap = 0;                  /* int pointed to by ap now holds value 0 */
*ap += 1;                 /* int pointed to by ap now holds value 1 */

您的allocateArray() 函数将指针的这两种用法结合到一个函数中。

int *vector = NULL;          /* vector is pointer to int variable initialized to NULL */
allocateArray(&vector,5,45); /* the address of vector is passed to allocateArray */

由于向量的地址被传递给allocateArray(),该函数现在可以通过引用它收到的指针值来修改名为vector 的对象。在arr 参数中接收到指针值:

void allocateArray( int **arr, size_t size, int value )

并且,通过取消引用arr,它正在使用malloc() 返回的值更新vector 对象:

*arr = malloc( size * sizeof( int ));

如果分配、内存初始化和vector变量的更新分多个步骤进行,也许函数会更清晰。

void allocateArray( int **arr, size_t size, int value )
{
    int *vec = malloc( size * sizeof( int ));  /* vec points to size contiguous int */

    if ( vec != NULL )
        for ( size_t i = 0; i < size; ++i )
            vec[i] = value;                    /* vec[i] set to value */

    *arr = vec;                                /* return vec into first parameter */
}

【讨论】:

  • 那么,在 foo_2 中,我可以使用 '**a' 来分配,例如,不仅是一个向量,而且是一个数组?
  • foo_2中,在调用malloc之前,*ap是0。所以**ap取消引用0,这是未定义的行为。
【解决方案2】:

一些背景:

C 中的参数总是按值传递。例如,考虑代码

void some_func(int i) { i = i + 2; printf("i = %d\n", i); }

void main_func() {
  int n = 5;
  some_func(n);
  printf("n = %d\n", n);
}

输出:

 i = 7
 n = 5

主函数将值 5 传递给 some_func,它在 i 中更改自己的本地副本,但不能在调用函数中更改 n

现在,假设您要编写一个确实改变调用者值的函数。您可以通过传递要更改的变量的地址来做到这一点:

void new_func(int *i) { *i = *i + 2; printf("*i = %d\n", *i); }

void main_func() {
  int n = 5;
  some_func(&n);
  printf(" n = %d\n", n);
}

输出:

 *i = 7
  n = 7

“诀窍”是new_func 不会更改其参数的值;它改变了一些其他值——它的参数 points 指向的变量。所以规则是:

如果您希望函数更改变量,则传递该变量的地址

换句话说,如果你想要一个函数来改变foo,你需要传递一个指针foo


应用到您的代码:

main 函数声明了一个没有分配内存的指针vector。它的值为NULL。它调用allocateArray() 分配内存并将其分配给vector。但这意味着它必须将vector 的值从NULL更改为新分配的内存地址。按照与上述相同的规则,要更改vector,您需要将其地址传递给allocateArray()。并且由于vector 的类型是pointer-to-int,它的地址是pointer-to-pointer-to-int 的类型。

要明确:你没有改变vector的类型;你只是传递了它的地址。所以在allocateArray() 内,arr 的类型为int ***arr 的类型为int *。从语法上讲,您使用 *arr 与使用 vector 完全相同。

【讨论】:

    【解决方案3】:

    所以vector是一个指向int的指针,所以vector的地址就是一个指向int指针的指针(&vector等价于int **)。向量 = *arr,而不是 &vector = *arr。因此vector获取的是malloc调用返回的地址,而不是vector的地址。我认为混淆之处在于vector的地址和vector指向的地址。

    【讨论】:

      【解决方案4】:

      在函数main() 内部,vector 是堆栈上的单个变量(可能是 4 个字节)。 这 4 个字节中第一个字节的地址是&amp;vector。当你调用allocateArray() 时,你在堆上分配了一块内存。 那些字节的地址必须存储在某个地方。该函数将该 4 字节地址存储在传递给它的内存地址中,即在堆栈上分配的字节。

      所以当我们返回main() 时,变量vector 现在指向分配块的开始。因为它被声明为指向 int 的指针,所以该内存将作为 int 数组访问。

      内存地址只是数字,就像任何其他数字一样。您在 C 中分配给它的“类型”只是告诉编译器您打算如何处理该地址处的内存。将vector 声明为int * 只是告诉编译器在看到*vector 时从内存中获取int。用于存储变量本身值的内存只是堆栈,就像它曾经是一个常规的 int 一样,并且该内存与它指向的内存无关。

      我们需要获取堆栈变量的地址,否则allocateArray() 将无法影响变量vector 的值,该变量未在其范围内定义,因此我们只需要告诉它在哪里看。

      实现这一点的更好方法可能是删除allocateArray() 的第一个参数并让它返回分配块的地址。然后我们可以将vector 分配给main() 中的返回值,它在范围内。

      【讨论】:

        【解决方案5】:
        int main( void )
        {
            int *vector = NULL;
        
            allocateArray(&vector,5,45);
        
            free( vector );
        
            return 0;
        }
        

        正如你在主函数中看到的,当语句allocateArray(&amp;vector,5,45); 执行时, 5 元素数组的内存地址将传递给向量变量;但是,如果您使用allocateArray(vector,5,45);(假设已更正修改),则内存地址不会传递给向量变量,因为函数 allocateArray(vector, 5, 45) 现在只是将向量的值传递给函数。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2013-05-09
          • 2020-12-30
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-04-24
          • 1970-01-01
          相关资源
          最近更新 更多