指针的地址等于指针的指针?

时间:2013-06-14 00:48:33

标签: c pointers malloc

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

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)的指针?我的意思是,它是如何工作的,现在内存中的向量地址将被覆盖!

5 个答案:

答案 0 :(得分:3)

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 */

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

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

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的指针,因此&a的类型是&#34;指向int&#34;的指针。


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

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

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 */

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

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

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

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

答案 1 :(得分:1)

某些背景:

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

main函数将值5传递给some_funci会在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

“技巧”是foo不会改变其参数的值;它改变了一些其他值 - 其参数指向的变量。所以规则是:

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

换句话说,如果您希望功能更改foo,则将指针传递给main


应用于您的代码:

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

要明确:您没有更改allocateArray()的类型;你只是通过了它的地址。因此,在arr中,int **的类型为*arr,而int *的类型为*arr。从语法上讲,您完全按照vector使用{{1}}。

答案 2 :(得分:0)

因此vector是一个指向int的指针,因此vector的地址是指向int的指针(&amp; vector相当于int **)。 vector = * arr,not&amp; vector = * arr。因此vector获取对malloc的调用返回的地址,而不是vector的地址。我认为混淆是在向量的地址和向量指向的地址之间。

答案 3 :(得分:0)

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

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

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

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

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

答案 4 :(得分:0)

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

    allocateArray(&vector,5,45);

    free( vector );

    return 0;
}

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