【问题标题】:When to use * in pointer assignment? [closed]何时在指针赋值中使用 *? [关闭]
【发布时间】:2016-05-10 12:50:52
【问题描述】:

当我学习 C 时,我经常看到指针。

我知道一个指针保存着内存中不同位置的十六进制值。所以指针就是例如:0x7fff5fbff85c

每个指针也是不同的类型。

int var = 10;
int *ptr = &var;

这里的ptr指向var的位置。要获得 var 的值,我必须使用 *ptr 取消引用指针。 喜欢

printf("Var = %d", *ptr);

将打印 `Var = 10;

但是,如果我对指针进行非内联声明,例如:

int var = 10;
int *ptr;
ptr = &var;

当我实际将内存地址分配给指针时,我不必在第三行中使用*。 但是当我得到一个需要指针的函数时:

int var = 10;
void assignPointer(int *ptr) {
*ptr = 10;
}

哦,等等!在我写这篇文章时,我认识到指针有两种不同的分配:

*ptr = 10;

ptr = &var;

有什么区别?在第一种情况下,我是否首先取消引用指针,将 10 分配给其持有的位置? 在第二种情况下,我将实际位置分配给指针。

我有点困惑何时使用 * 以及何时不使用分配。

如果我正在使用数组,我为什么需要指针?

int array[];

这里的“array”已经保存了十六进制的内存位置。这不是一个指针吗?所以如果我想给数组赋值,我不会写:

*array = [10, 2];

首先我要取消引用,然后我要分配。

我迷路了:(

编辑:也许有点不清楚。 我不知道在使用指针时何时必须使用 * 何时不需要。 带有十六进制的所有内容都是指针,对吗? 数组的变量名带有它的十六进制内存位置。那为什么不是指针呢?

EDIT2:谢谢你们帮了我很多!

【问题讨论】:

  • 我在这里看到了一个很长的指针介绍。有什么问题?数组不是指针。
  • 请从一本好的 C 书开始……你需要它。
  • 可能的答案太多,或者对于这种格式来说,好的答案太长了。请添加详细信息以缩小答案集或隔离可以在几段中回答的问题。
  • 混淆来自int *ptr = &var;,它定义了int *ptr并将其值设置为&var。如果您已经定义了指针,则将其分配给ptr = &var。您使用* 取消引用指针,即访问它所指向的内容。所以ptr 可能是0xffeebbcc*ptr10
  • “所有带有十六进制的都是指针,对吗?” 不——机器内部没有十六进制。十六进制是一种人类可读的格式,用于打印%p 格式的指针,但您也可以使用%x 格式打印十六进制的int

标签: c arrays variables pointers memory


【解决方案1】:
我不知道在使用指针时何时必须使用 *,何时不用。带有十六进制的所有内容都是指针,对吗?数组的变量名带有它的十六进制内存位置。那为什么不是指针呢?

最后一件事-数组的名称​​不是指针;它不会在任何地方存储地址。当您定义一个数组时,它的布局或多或少类似于以下内容:

+---+ 回复:| | arr[0] 递增地址 +---+ | | | arr[1] | +---+ | ... | +---+ | | | arr[n-1] V +---+

没有为arr 与数组元素arr[0]arr[n-1] 分开的对象预留存储空间。 C 不会将任何元数据(例如长度或起始地址)存储为数组对象的一部分。

相反,有一条规则规定,如果数组表达式出现在您的代码中并且该表达式不是sizeof 或一元& 运算符的操作数,它将被转换( "decay") 指向一个指针表达式,指针表达式的值将是数组第一个元素的地址。

所以给定声明

T arr[N]; // for any type T

那么以下是正确的:

    Expression         Type        Decays to        Value
    ----------         ----        ---------        -----
           arr         T [N]       T *              Address of first element
          &arr         T (*)[N]    n/a              Address of array (same value
                                                      as above
          *arr         T           n/a              Value of arr[0]
        arr[i]         T           n/a              Value of i'th element
       &arr[i]         T *         n/a              Address of i'th element
    sizeof arr         size_t                       Number of storage units (bytes)
                                                      taken up by arr

表达式arr&arr&arr[0]都产生相同的(数组的第一个元素的地址与数组的地址),但它们的类型并不完全相同; arr&arr[0] 具有 T * 类型,而 &arr 具有 T (*)[N] 类型(指向 N-T 的元素数组的指针)。

所有带有十六进制的都是指针,对吗?

十六进制只是二进制数据的一种特殊表示;它本身不是一种类型。并不是所有可以用十六进制写入或显示的东西都是指针。我可以将值 0xDEADBEEF 分配给任何 32 位整数类型;这不会使它成为一个指针。

指针的精确表示可能因架构而异;它甚至可以在同一架构上的不同指针类型之间变化。对于平面内存模型(如任何现代桌面架构),它将是一个简单的整数值。对于分段架构(如旧的 8086/DOS 时代),它可能是页面 # 和偏移量的一对值。

指针 可能没有用于存储它的类型那么宽。例如,旧的摩托罗拉 68000 只有 24 条地址线,所以任何指针值都只有 24 位宽。然而,为了让生活更轻松,大多数编译器使用 32 位类型来表示指针,而没有使用高 8 位(2 的幂很方便)。

我不知道在使用指针时何时必须使用 *,何时不用。

很简单 - 当你想引用 pointed-to 实体时,使用*;当您想引用指针本身时,请不要使用它。

另一种看待方式——表达式*ptr等价于表达式var,所以任何时候你想引用@的contents 987654342@ 你会使用*ptr

一个更具体的例子可能会有所帮助。假设如下:

void bar( T *p )
{
  *p = new_value();  // write new value to *p
}

void foo( void )
{
  T var;
  bar( &var );  // write a new value to var
}

在上面的例子中,以下是正确的:

 p == &var
*p == var

如果我给*p 写东西,我实际上是在更新var。如果我给p 写一些东西,我会将它设置为指向var 以外的东西。

上面的这段代码实际上是为什么指针首先存在的主要原因。在 C 中,所有的函数参数都是通过按值传递;也就是说,函数定义中的形参是与函数调用中的实参分开的对象。对形式参数的任何更新都不会反映在实际参数中。如果我们把代码改成如下:

void bar( T p )
{
  p = new_value();  // write new value to p
}

void foo( void )
{
  T var;
  bar( var );  // var is not updated
}

p 的值已更改,但由于pvar 是不同的内存对象,var 中的值保持不变。函数更新实际参数的唯一方法是通过指针。

所以,如果你想更新p 指向的东西,写信给*p。如果要设置p 指向不同的对象,请写信给p

int x = 0, y = 1;
int *p = &x; // p initially points to x
printf( "&x = %p, x = %d, p = %p, *p = %d\n", (void *) &x, x, (void *) p, *p );
*p = 3;
printf( "&x = %p, x = %d, p = %p, *p = %d\n", (void *) &x, x, (void *) p, *p );
p = y; // set p to point to y
printf( "&y = %p, y = %d, p = %p, *p = %d\n", (void *) &y, y, (void *) p, *p );

此时您可能会问,“为什么我在int *p = &x 中使用星号而不在p = y 中使用星号?”在第一种情况下,我们声明 p 为指针并在同一操作中对其进行初始化,并且声明语法需要*。在这种情况下,我们写信给p,而不是*p。相当于写了

int *p;
p = &x;

还要注意,在声明中* 绑定到变量名,而不是类型说明符;它被解析为int (*p);

C 声明基于表达式 的类型,而不是对象。如果p 是一个指向int 的指针,并且我们想要引用 pointed-to 值,我们使用* 操作符来取消引用它,如下所示:

x = *p;

表达式*p的类型是int,所以声明写成

int *p;

【讨论】:

  • 哇,这是我见过的最好的解释!谢谢!
  • “如果我向 *p 写入内容,我实际上是在更新 var。如果我向 p 写入内容,我会将其设置为指向 var 以外的其他内容。” - 这是一个很好的解释
【解决方案2】:

C 语法就像这样很奇怪。当您声明一个变量时,* 仅用于指示指针类型。它实际上并没有取消引用任何东西。因此,

int *foo = &bar;

就像你写的一样

int *foo;
foo = &bar;

【讨论】:

    【解决方案3】:

    指针的声明类似于常规变量。星号字符在声明期间位于指针名称之前以将其区分为指针。在声明时您不是取消引用,例如:

    int a = 0;
    int *p = &a // here the pointer of type int is declared and assigned the address of the variable a
    

    在声明语句之后,要为指针分配地址或值,请使用不带星号的名称,例如:

    int a;
    int *p;
    p = &a;
    

    要为指针的目标分配一个值,您可以通过在指针名称前加上* 来取消引用它:

    int a = 0;
    int *p;
    p = &a;
    *p = 1;
    

    【讨论】:

      【解决方案4】:

      取消引用的指针是它指向的内存。只是不要混淆声明指针和使用它。

      如果你在类型附近的声明中写*可能会更容易理解:

      int* p;
      

      int some_int = 10;
      int* p = &some_int; // the same as int *p; p = &some_int;
      *p = 20; // actually does some_int = 20;
      

      【讨论】:

        【解决方案5】:

        你说的很对。

        在第一种情况下,我是否首先取消引用指针,将 10 分配给它所持有的位置?在第二种情况下,我将实际位置分配给指针。

        没错。如您所见,这是两个逻辑上不同的操作。

        这里的“array”已经保存了十六进制的内存位置。这不是让它成为一个指针吗?

        您也在这里掌握了它。为了您的理解,我会说数组是指针。然而实际上它并不是那么简单——在大多数情况下,数组只会衰减为指针。如果你真的很感兴趣,你可以在这里找到一些很棒的帖子。

        但是,由于它只是一个指针,你不能“分配给数组”。如何在指针上下文中处理数组通常在任何 C 书籍的“字符串”部分下都有很好的解释。

        【讨论】:

        • 为了令人讨厌的迂腐,数组表达式在大多数情况下都会衰减为指针;数组 objects 总是数组。而且您不能分配给数组,因为语言定义是这样说的;编译器足够聪明,可以知道赋值的目标何时是数组表达式,并相应地抱怨。
        【解决方案6】:

        您对分配和取消引用之间的区别是正确的。

        您需要了解的是,您的数组变量是指向连续内存区第一个元素的指针

        所以你可以通过取消引用指针来访问第一个元素:

        *array = 10;
        

        您可以通过取消引用指向第 n 个元素的指针来访问第 n 个元素:

        *(array + (n * sizeof(my_array_type)) ) = 10;
        

        其中地址是指向第一个元素的指针加上指向第 n 个元素的偏移量(使用此数组中元素的大小乘以 n 计算)。

        您也可以使用等效语法访问第 n 个元素:

        array[n] = 10; 
        

        【讨论】:

        • “数组是指针这一事实也是正确的。”不,对不起,那是错误的。数组不是指针,但它可以衰减到它。当您在数组上使用sizeof 运算符时可以看到不同之处:返回值不是指针的大小。请参阅stackoverflow.com/questions/1641957/… 了解更多信息。
        • 不错不错,我解释得很糟糕。编辑;]
        【解决方案7】:

        您的其中一个示例无效。 *ptr = 10;。原因是 10 是一个值,但没有分配内存。

        您可以将您的示例视为“分配某物以指向地址”或“某物的地址是”。所以,

        int *ptr 是指向某物地址的指针。所以ptr = &val; 表示ptr 等于val 的地址。然后你可以说*ptr = 10;val = 10; 因为 *ptr 和 val 都在查看相同的内存位置,因此是相同的值。 (注意我没有说“指向”)。

        【讨论】:

        • *ptr = 10;这是获取 ptr 指向的位置,然后将 10 放入该位置,对吗?那为什么无效呢?它为指针指向的地方赋值。
        • @user148013 不。使用 C,一切都是手动的。您必须自己分配内存位置及其值。这就是为什么在您的示例中设置int val = 10。您声明一个 int 内存位置并将其设置为 10。
        • @Rob,OP 示例的上下文是 ptr 是一个函数参数,因此通常可以安全地假设分配了内存来保存一个值。对于初学者。
        • @HighPredator 否。您不能单独声明指向“10”值的指针。或者至少这是危险的原因,然后你指向一个未知的内存地址并将 10 放在那里。
        • 我基本上是这样假设的:int var = 3; int *ptr = &var; *ptr = 9;var ist first 3,然后ptr指向var的位置,然后var通过指针设置为9。我正是这个意思。我只是没有在那里写 var 赋值,因为我在前几行就写好了。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2018-03-27
        • 2013-05-16
        • 2011-10-27
        • 2011-10-10
        • 1970-01-01
        相关资源
        最近更新 更多