指针表达式: \*ptr 、 \* ptr 和 \*ptr

新手上路,请多包涵

最近我遇到了这个我自己无法理解的问题。

这三个表达式的 真正 含义是什么?

 *ptr++
*++ptr
++*ptr

我试过里奇。但遗憾的是无法按照他所说的关于这 3 个操作的内容进行说明。

我知道它们都是为了增加指针/指向的值而执行的。我也可以猜到可能有很多关于优先级和评估顺序的事情。就像一个先增加指针然后获取该指针的内容一样,一个简单地获取内容然后增加指针等等等。如您所见,我对它们的 实际 操作没有清楚的了解,我想尽快清除。但是当我有机会将它们应用到程序中时,我真的迷失了。例如:

 int main()
{
    char *p = "Hello";
    while(*p++)
         printf("%c",*p);
    return 0;
}

给我这个输出:

 ello

但我的期望是它会打印 Hello 。最后一个请求——请举例说明每个表达式在给定代码段中的工作方式。因为大多数时候只有一段理论在我脑海中飘过。

原文由 allocated 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 542
2 个回答

这是一个详细的解释,希望对您有所帮助。让我们从您的程序开始,因为它是最容易解释的。

 int main()
{
    char *p = "Hello";
    while(*p++)
        printf("%c",*p);
    return 0;
}

第一个声明:

 char* p = "Hello";

声明 p 作为指向 char 的指针。当我们说“指向 char 的指针”时,这是什么意思?表示 p 的值是一个 char 的地址; p 告诉我们在内存中的哪个位置留出一些空间来保存 char

该语句还初始化 p 以指向字符串文字中的第一个字符 "Hello" 。在本练习中,重要的是要理解 p 不是指向整个字符串,而是指向第一个字符 'H' 。毕竟, p 是指向一个 char 的指针,而不是整个字符串。 —的值是 p 中的 'H' "Hello" 的地址。

然后你设置一个循环:

 while (*p++)

循环条件 *p++ 是什么意思?这里有三件事使这令人费解(至少在熟悉之前):

  1. 两个运算符的优先级,后缀 ++ 和间接 *
  2. 后缀增量表达式的值
  3. 后缀增量表达式的副作用

1. 优先级。快速浏览运算符的优先级表会告诉您后缀增量的优先级 (16) 高于取消引用/间接 (15)。这意味着复杂的表达式 *p++ 将被分组为: *(p++) 。也就是说, * 部分将应用于 p++ 部分的值。所以让我们 p++ 部分。

2.后缀表达式值p++ 的值是 p 在增量之前的 值。如果你有:

 int i = 7;
printf ("%d\n", i++);
printf ("%d\n", i);

输出将是:

 7
8

因为 i++ --- 在增量之前计算为 i 。同样 p++ 将评估为 p 的当前值。众所周知,—的当前值是 p 'H' 地址。

所以现在 p++ 部分 *p++ 已经被评估;这是 p 的当前值。然后 * 部分发生。 *(current value of p) 意思是:访问 p 持有的地址的值。我们知道那个地址的值是 'H' 。所以表达式 *p++ 计算结果为 'H'

现在等一下,你是说。如果 *p++ 计算结果为 'H' ,为什么没有 'H' 在上面的代码中打印?这就是 副作用 出现的地方。

3. 后缀表达式的副作用。后缀 ++ 具有当前操作数的 _值_,但它具有增加该操作数的 _副作用_。嗯?再看一下 int 代码:

 int i = 7;
printf ("%d\n", i++);
printf ("%d\n", i);

如前所述,输出将是:

 7
8

i++ 在第一个 printf() 中进行评估时,它的评估结果为 7。但是 C 标准保证在第二个 printf() 开始执行之前 某个时刻 ++ 运算符的 效果 将发生。 That is to say, before the second printf() happens, i will have been incremented as a result of the ++ operator in the first printf() 。顺便说一句,这是该标准为副作用时间提供的为数不多的保证之一。

那么,在您的代码中,当评估表达式 *p++ 时,它的评估结果为 'H' 。但是当你得到这个时:

 printf ("%c", *p)

这种讨厌的副作用已经发生。 p 已增加。哇!它不再指向 'H' ,而是指向过去的一个字符 'H' :指向 'e' ,换句话说。这解释了你的 cockneyfied 输出:

 ello

因此,其他答案中的有用(和准确)建议的合唱:打印收到的发音 "Hello" 而不是它的伦敦对应物,你需要类似的东西

while (*p)
    printf ("%c", *p++);

这么多。其余的呢?你问这些的含义:

 *ptr++
*++ptr
++*ptr

我们刚刚讲了第一个,让我们看看第二个: *++ptr

我们在前面的解释中看到后缀增量 p++ 具有一定的 _优先级_、 和 _副作用_。前缀增量 ++p 与其后缀对应物具有相同 _的副作用_:它将其操作数增加1。但是,它具有不同的 优先级 和不同的 _值_。

前缀增量的优先级低于后缀;它的优先级为 15。换句话说,它与取消引用/间接操作符 * 具有相同的优先级。在像这样的表达中

*++ptr

重要的不是优先级:两个运算符的优先级相同。所以 关联性 开始了。前缀增量和间接运算符具有左右关联性。由于这种关联性,操作数 ptr 将与最右边的运算符 ++ 在运算符更左侧之前分组 * 。换句话说,表达式将被分组 *(++ptr) 。因此,与 *ptr++ 但出于不同的原因,这里 * 部分将应用于 ++ptr 部分的值。

那么这个值是多少?前缀增量表达式的值是 增量后 操作数的值。这使它成为与后缀增量运算符截然不同的野兽。假设您有:

 int i = 7;
printf ("%d\n", ++i);
printf ("%d\n", i);

输出将是:

 8
8

…与我们在后缀运算符中看到的不同。同样,如果您有:

 char* p = "Hello";
printf ("%c ", *p);    // note space in format string
printf ("%c ", *++p);  // value of ++p is p after the increment
printf ("%c ", *p++);  // value of p++ is p before the increment
printf ("%c ", *p);    // value of p has been incremented as a side effect of p++

输出将是:

 H e e l                // good dog

你明白为什么吗?

现在我们得到您询问的第三个表达式 ++*ptr 。实际上,这是最棘手的。两个运算符具有相同的优先级和左右结合性。这意味着表达式将被分组 ++(*ptr)++ 部分将应用于 *ptr 部分的值。

所以如果我们有:

 char q[] = "Hello";
char* p = q;
printf ("%c", ++*p);

令人惊讶的自负输出将是:

 I

什么?!好的,所以 *p 部分将评估为 'H' 。然后 ++ 开始发挥作用,此时,它将应用于 'H' ,而不是指针!当您将 1 添加到 'H' 时会发生什么?你得到 1 加上 'H' 的 ASCII 值, 72;你得到 73。将其表示为 char ,你得到 char ASCII 值为 73: 'I'

这会处理您在问题中提出的三个表达方式。这是另一个,在您的问题的第一条评论中提到:

 (*ptr)++

那个也很有趣。如果你有:

 char q[] = "Hello";
char* p = q;
printf ("%c", (*p)++);
printf ("%c\n", *p);

它会给你这个热情的输出:

 HI


这是怎么回事?同样,这是 _优先级_、 表达式值副作用 的问题。由于括号, *p 部分被视为主要表达式。主要表达胜过其他一切;他们首先得到评估。如您所知, *p 评估为 'H' 。表达式的其余部分 ++ 部分应用于该值。因此,在这种情况下, (*p)++ 变为 'H'++

'H'++ 的值是多少?如果您说 'I' ,那么您已经(已经!)忘记了我们关于后缀增量的价值与副作用的讨论。请记住, 'H'++ 评估为 'H' 的 _当前值_。所以首先 printf() 将打印 'H' 。然后,作为 _副作用_, 'H' 将增加到 'I' 。第二个 printf() 打印出 'I' 。你有你愉快的问候。

好吧,但在最后两种情况下,我为什么需要

char q[] = "Hello";
char* p = q;

为什么我不能有类似的东西

char* p = "Hello";
printf ("%c", ++*p);   // attempting to change string literal!

因为 "Hello" 是字符串文字。 If you try ++*p , you’re trying to change the 'H' in the string to 'I' , making the whole string "Iello" .在 C 中,字符串文字是只读的;试图修改它们会调用未定义的行为。 "Iello" 在英语中也未定义,但这只是巧合。

相反,你不能拥有

char p[] = "Hello";
printf ("%c", *++p);  // attempting to modify value of array identifier!

为什么不?因为在这种情况下, p 是一个数组。数组不是可修改的左值;您无法更改 p 指向前或后递增或递减的位置,因为数组的名称就像一个常量指针一样工作。 (实际上并非如此;这只是一种方便的查看方式。)

总而言之,这是您询问的三件事:

 *ptr++   // effectively dereferences the pointer, then increments the pointer
*++ptr   // effectively increments the pointer, then dereferences the pointer
++*ptr   // effectively dereferences the pointer, then increments dereferenced value

这是第四个,和其他三个一样有趣:

 (*ptr)++ // effectively forces a dereference, then increments dereferenced value

如果 ptr 实际上是数组标识符,则第一个和第二个将崩溃。如果 ptr 指向字符串文字,第三个和第四个将崩溃。

你有它。我希望现在一切都是水晶。你是一个很棒的观众,我整个星期都在这里。

原文由 verbose 发布,翻译遵循 CC BY-SA 4.0 许可协议

  • 后缀 ++ 的优先级高于一元 *
  • 前缀 ++ 和一元 * 具有相同的优先级,两者的表达式具有从右到左的 _运算符关联性_,这意味着右侧的操作数在左侧的操作数之前绑定。

所以:

  • *ptr++ 将指针递增 1 项,然后取消引用它在递增之前的内存位置。
  • *++ptr 将指针增加 1 项,然后取消引用它现在指向的内存位置。
  • ++*ptr 取消引用内存位置然后将 _内容_(值)增加1。

原文由 Lundin 发布,翻译遵循 CC BY-SA 4.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题
logo
Stack Overflow 翻译
子站问答
访问
宣传栏