最近我遇到了这个我自己无法理解的问题。
这三个表达式的 真正 含义是什么?
*ptr++
*++ptr
++*ptr
我试过里奇。但遗憾的是无法按照他所说的关于这 3 个操作的内容进行说明。
我知道它们都是为了增加指针/指向的值而执行的。我也可以猜到可能有很多关于优先级和评估顺序的事情。就像一个先增加指针然后获取该指针的内容一样,一个简单地获取内容然后增加指针等等等。如您所见,我对它们的 实际 操作没有清楚的了解,我想尽快清除。但是当我有机会将它们应用到程序中时,我真的迷失了。例如:
int main()
{
char *p = "Hello";
while(*p++)
printf("%c",*p);
return 0;
}
给我这个输出:
ello
但我的期望是它会打印 Hello
。最后一个请求——请举例说明每个表达式在给定代码段中的工作方式。因为大多数时候只有一段理论在我脑海中飘过。
原文由 allocated 发布,翻译遵循 CC BY-SA 4.0 许可协议
这是一个详细的解释,希望对您有所帮助。让我们从您的程序开始,因为它是最容易解释的。
第一个声明:
声明
p
作为指向char
的指针。当我们说“指向char
的指针”时,这是什么意思?表示p
的值是一个char
的地址;p
告诉我们在内存中的哪个位置留出一些空间来保存char
。该语句还初始化
p
以指向字符串文字中的第一个字符"Hello"
。在本练习中,重要的是要理解p
不是指向整个字符串,而是指向第一个字符'H'
。毕竟,p
是指向一个char
的指针,而不是整个字符串。 —的值是p
中的'H'
"Hello"
的地址。然后你设置一个循环:
循环条件
*p++
是什么意思?这里有三件事使这令人费解(至少在熟悉之前):++
和间接*
1. 优先级。快速浏览运算符的优先级表会告诉您后缀增量的优先级 (16) 高于取消引用/间接 (15)。这意味着复杂的表达式
*p++
将被分组为:*(p++)
。也就是说,*
部分将应用于p++
部分的值。所以让我们p++
部分。2.后缀表达式值。
p++
的值是p
在增量之前的 值。如果你有:输出将是:
因为
i++
--- 在增量之前计算为i
。同样p++
将评估为p
的当前值。众所周知,—的当前值是p
'H'
地址。所以现在
p++
部分*p++
已经被评估;这是p
的当前值。然后*
部分发生。*(current value of p)
意思是:访问p
持有的地址的值。我们知道那个地址的值是'H'
。所以表达式*p++
计算结果为'H'
。现在等一下,你是说。如果
*p++
计算结果为'H'
,为什么没有'H'
在上面的代码中打印?这就是 副作用 出现的地方。3. 后缀表达式的副作用。后缀
++
具有当前操作数的 _值_,但它具有增加该操作数的 _副作用_。嗯?再看一下int
代码:如前所述,输出将是:
当
i++
在第一个printf()
中进行评估时,它的评估结果为 7。但是 C 标准保证在第二个printf()
开始执行之前 的 某个时刻++
运算符的 效果 将发生。 That is to say, before the secondprintf()
happens,i
will have been incremented as a result of the++
operator in the firstprintf()
。顺便说一句,这是该标准为副作用时间提供的为数不多的保证之一。那么,在您的代码中,当评估表达式
*p++
时,它的评估结果为'H'
。但是当你得到这个时:这种讨厌的副作用已经发生。
p
已增加。哇!它不再指向'H'
,而是指向过去的一个字符'H'
:指向'e'
,换句话说。这解释了你的 cockneyfied 输出:因此,其他答案中的有用(和准确)建议的合唱:打印收到的发音
"Hello"
而不是它的伦敦对应物,你需要类似的东西这么多。其余的呢?你问这些的含义:
我们刚刚讲了第一个,让我们看看第二个:
*++ptr
。我们在前面的解释中看到后缀增量
p++
具有一定的 _优先级_、 值 和 _副作用_。前缀增量++p
与其后缀对应物具有相同 _的副作用_:它将其操作数增加1。但是,它具有不同的 优先级 和不同的 _值_。前缀增量的优先级低于后缀;它的优先级为 15。换句话说,它与取消引用/间接操作符
*
具有相同的优先级。在像这样的表达中重要的不是优先级:两个运算符的优先级相同。所以 关联性 开始了。前缀增量和间接运算符具有左右关联性。由于这种关联性,操作数
ptr
将与最右边的运算符++
在运算符更左侧之前分组*
。换句话说,表达式将被分组*(++ptr)
。因此,与*ptr++
但出于不同的原因,这里*
部分将应用于++ptr
部分的值。那么这个值是多少?前缀增量表达式的值是 增量后 操作数的值。这使它成为与后缀增量运算符截然不同的野兽。假设您有:
输出将是:
…与我们在后缀运算符中看到的不同。同样,如果您有:
输出将是:
你明白为什么吗?
现在我们得到您询问的第三个表达式
++*ptr
。实际上,这是最棘手的。两个运算符具有相同的优先级和左右结合性。这意味着表达式将被分组++(*ptr)
。++
部分将应用于*ptr
部分的值。所以如果我们有:
令人惊讶的自负输出将是:
什么?!好的,所以
*p
部分将评估为'H'
。然后++
开始发挥作用,此时,它将应用于'H'
,而不是指针!当您将 1 添加到'H'
时会发生什么?你得到 1 加上'H'
的 ASCII 值, 72;你得到 73。将其表示为char
,你得到char
ASCII 值为 73:'I'
。这会处理您在问题中提出的三个表达方式。这是另一个,在您的问题的第一条评论中提到:
那个也很有趣。如果你有:
它会给你这个热情的输出:
这是怎么回事?同样,这是 _优先级_、 表达式值 和 副作用 的问题。由于括号,
*p
部分被视为主要表达式。主要表达胜过其他一切;他们首先得到评估。如您所知,*p
评估为'H'
。表达式的其余部分++
部分应用于该值。因此,在这种情况下,(*p)++
变为'H'++
。'H'++
的值是多少?如果您说'I'
,那么您已经(已经!)忘记了我们关于后缀增量的价值与副作用的讨论。请记住,'H'++
评估为'H'
的 _当前值_。所以首先printf()
将打印'H'
。然后,作为 _副作用_,'H'
将增加到'I'
。第二个printf()
打印出'I'
。你有你愉快的问候。好吧,但在最后两种情况下,我为什么需要
为什么我不能有类似的东西
因为
"Hello"
是字符串文字。 If you try++*p
, you’re trying to change the'H'
in the string to'I'
, making the whole string"Iello"
.在 C 中,字符串文字是只读的;试图修改它们会调用未定义的行为。"Iello"
在英语中也未定义,但这只是巧合。相反,你不能拥有
为什么不?因为在这种情况下,
p
是一个数组。数组不是可修改的左值;您无法更改p
指向前或后递增或递减的位置,因为数组的名称就像一个常量指针一样工作。 (实际上并非如此;这只是一种方便的查看方式。)总而言之,这是您询问的三件事:
这是第四个,和其他三个一样有趣:
如果
ptr
实际上是数组标识符,则第一个和第二个将崩溃。如果ptr
指向字符串文字,第三个和第四个将崩溃。你有它。我希望现在一切都是水晶。你是一个很棒的观众,我整个星期都在这里。