3

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

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

4

5 回答 5

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

运算符产生一个值,该&值表示它所应用到的对象的地址,结果类型是“指向(对象类型)的指针”。在上面的例子中,结果&a是“pointer to 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,所以类型&a是“指向指针的指针int”。


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

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

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

动态创建的对象,即 through 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 */
}
于 2013-06-14T01:04:53.120 回答
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

主函数将值 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不改变其参数的值;它改变了一些其他值——它的参数指向的变量。所以规则是:

如果你想要一个函数来改变一个变量,你传递那个变量的地址

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


应用到您的代码:

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

需要明确的是:您没有更改vector;的类型 你只是传递了它的地址。所以 inside allocateArray()arris of type int ***arris of type int *。从语法上讲,您使用*arr的方式与使用vector.

于 2013-06-14T01:14:48.763 回答
0

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

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

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

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

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

于 2013-06-14T01:16:54.240 回答
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) 现在只是将向量的值传递给函数。

于 2013-06-14T03:32:13.600 回答
0

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

于 2013-06-14T01:05:16.353 回答