指针表达式:*ptr ++,*++ ptr和++*ptr

all*_*ted 117 c c++ pointers increment

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

这三个表达式真正意味着什么?

*ptr++
*++ptr
++*ptr
Run Code Online (Sandbox Code Playgroud)

我试过里奇.但不幸的是,他无法按照他讲述的这三项行动.

我知道它们都是为了递增指针/指向的值而执行的.我还可以猜测可能有很多关于优先级和评估顺序的事情.就像一个指针首先递增指针然后取出指针的内容,一个简单地取出内容然后递增指针等等.正如你所看到的,我对他们的实际操作我没有清楚的理解,我想尽快明确.但是当我有机会将它们应用到程序中时,我真的迷失了.例如:

int main()
{
    const char *p = "Hello";
    while(*p++)
         printf("%c",*p);
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

给我这个输出:

ello
Run Code Online (Sandbox Code Playgroud)

但我的期望是它会印刷Hello.最后一个请求 - 请给出一些示例,说明每个表达式在给定的代码段中的工作原理.因为大多数时候只有一段理论飞过我的脑海.

ver*_*ose 254

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

int main()
{
    const char *p = "Hello";
    while(*p++)
        printf("%c",*p);
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

第一个声明:

const char* p = "Hello";
Run Code Online (Sandbox Code Playgroud)

声明p为指向char.当我们说"指向一个char"时,这是什么意思?这意味着值p是a的地址char; p告诉我们在记忆中哪里留有一些空间来容纳一个char.

该语句还初始化p为指向字符串文字中的第一个字符"Hello".为了这个练习,重要的是理解p为不指向整个字符串,而只指向第一个字符'H'.毕竟,p是一个指针char,而不是整个字符串.值p'H'in 的地址"Hello".

然后你设置一个循环:

while (*p++)
Run Code Online (Sandbox Code Playgroud)

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

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

1.优先权.快速浏览一下运算符的优先级表会告诉您postfix增量的优先级(16)高于dereference/indirection(15).这意味着复杂表达式*p++将被分组为:*(p++).也就是说,该*零件将应用于零件的值p++.所以让我们p++先来看看这个部分.

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

int i = 7;
printf ("%d\n", i++);
printf ("%d\n", i);
Run Code Online (Sandbox Code Playgroud)

输出将是:

7
8
Run Code Online (Sandbox Code Playgroud)

因为在增量之前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);
Run Code Online (Sandbox Code Playgroud)

如前所述,输出将是:

7
8
Run Code Online (Sandbox Code Playgroud)

i++在第一评估printf()中,评估为7.但C标准保证在之前所述第二某点printf()处开始执行,在副作用的的++操作员将已经发生.也就是说,在第二次printf()发生之前,i由于++第一次运算符的结果将增加printf().顺便说一下,这是标准给出的关于副作用时间的少数保证之一.

然后,在您的代码中,当计算表达式时*p++,它的计算结果为'H'.但到了这个时候:

printf ("%c", *p)
Run Code Online (Sandbox Code Playgroud)

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

ello
Run Code Online (Sandbox Code Playgroud)

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

while (*p)
    printf ("%c", *p++);
Run Code Online (Sandbox Code Playgroud)

这么多.剩下的呢?你问这些意义:

*ptr++
*++ptr
++*ptr
Run Code Online (Sandbox Code Playgroud)

我们刚才谈到了第一个,所以让我们来看看第二个:*++ptr.

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

前缀增量的优先级低于后缀; 它具有优先级15.换句话说,它具有与解除引用/间接运算符相同的优先级*.在表达式中

*++ptr
Run Code Online (Sandbox Code Playgroud)

重要的是不优先:两个运营商的优先权相同.因此,关联性开始发生.前缀增量和间接运算符具有左右关联性.由于该关联性,操作数ptr++在操作符更靠左边之前与最右边的操作符分组*.换句话说,表达式将被分组*(++ptr).因此,与*ptr++但不同的原因一样,这*部分也将应用于++ptr零件的价值.

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

int i = 7;
printf ("%d\n", ++i);
printf ("%d\n", i);
Run Code Online (Sandbox Code Playgroud)

输出将是:

8
8
Run Code Online (Sandbox Code Playgroud)

...与我们在postfix运算符中看到的不同.同样,如果你有:

const 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++
Run Code Online (Sandbox Code Playgroud)

输出将是:

H e e l                // good dog
Run Code Online (Sandbox Code Playgroud)

你明白为什么吗?

现在我们进入您询问的第三个表达式,++*ptr.实际上,那是最狡猾的.两个运算符具有相同的优先级和左右关联性.这意味着表达式将被分组++(*ptr).的++部分将被应用到的值*ptr的部分.

所以如果我们有:

char q[] = "Hello";
char* p = q;
printf ("%c", ++*p);
Run Code Online (Sandbox Code Playgroud)

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

I
Run Code Online (Sandbox Code Playgroud)

什么?!好的,所以该*p部分将评估为'H'.然后++进入游戏,此时,它将被应用于'H',而不是指针!添加1时会发生什么'H'?你得到1加上ASCII值'H'72; 你得到73.表示为a char,你得到charASCII值为73 : 'I'.

这会照顾你在问题中提到的三个表达方式.这是您的问题的第一条评论中提到的另一条:

(*ptr)++ 
Run Code Online (Sandbox Code Playgroud)

那个也很有意思.如果你有:

char q[] = "Hello";
char* p = q;
printf ("%c", (*p)++);
printf ("%c\n", *p);
Run Code Online (Sandbox Code Playgroud)

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

HI
Run Code Online (Sandbox Code Playgroud)

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

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

好的,但在最后两个案例中,我为什么需要

char q[] = "Hello";
char* p = q;
Run Code Online (Sandbox Code Playgroud)

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

/*const*/ char* p = "Hello";
printf ("%c", ++*p);   // attempting to change string literal!
Run Code Online (Sandbox Code Playgroud)

因为"Hello"是字符串文字.如果您尝试++*p,则尝试'H'将字符串中的内容更改'I'为整个字符串"Iello".在C中,字符串文字是只读的; 尝试修改它们会调用未定义的行为."Iello"在英语中也是不确定的,但这只是巧合.

相反,你不能拥有

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

为什么不?因为在这个例子中,p是一个数组.数组不是可修改的l值; 你不能通过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
Run Code Online (Sandbox Code Playgroud)

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

(*ptr)++ // effectively forces a dereference, then increments dereferenced value
Run Code Online (Sandbox Code Playgroud)

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

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

  • +1我认为这是我读过的最好的答案.我想每个人都可以从这个答案中学到很多东西. (21认同)
  • 在来到这个论坛之前,我已经搜索了我拥有的3本"C"书.我也尝试了一些值得注意的在线教程.但是它们都没有接近你的解释(特别是你把它们放在一起的方式).你不仅回答了我提出的问题,而且你已经从基层讨论了很多事情.其实你今天教过我很多基本的东西,这是我以前所缺乏的.我忍不住切换了我接受的答案.:) 再次感谢. (20认同)
  • 你先生,应该写一本关于C的书. (7认同)
  • 我希望两次能够回答这个问题! (6认同)
  • @verbose你先生,辜负了你的名字.. :) (5认同)

nic*_*kie 43

假设ptr指向数组的第i个元素arr.

  1. *ptr++求值arr[i]并设置ptr为指向的第(i + 1)个元素arr.它相当于*(ptr++).

  2. *++ptr设置ptr为指向的第(i + 1)个元素arr并进行求值arr[i+1].它相当于*(++ptr).

  3. ++*ptr增加arr[i]1并评估其增加的价值; 指针ptr保持不变.它相当于++(*ptr).

还有一个,但你需要括号来写它:

  1. (*ptr)++增加arr[i]1并在增加之前评估其值; 指针ptr再次保持不变.

其余的你可以弄清楚自己; @Jaguar也回答了这个问题.


Jai*_*dra 13

*ptr++ : post increment a pointer ptr

*++ptr : Pre Increment a pointer ptr

++*ptr : preincrement the value at ptr location

阅读此处有关预增量和后增量运算符的信息


这将 Hello作为输出

int main()
{
    const char *p = "Hello";
    while(*p)
         printf("%c",*p++);//Increment the pointer here 
    return 0;
}
Run Code Online (Sandbox Code Playgroud)


nou*_*ney 7

循环中的条件很糟糕:

while(*p++)
    printf("%c",*p);
Run Code Online (Sandbox Code Playgroud)

是相同的

while(*p)
{
    p++;
    printf("%c",*p);
}
Run Code Online (Sandbox Code Playgroud)

这是错的,这应该是:

while(*p)
{
    printf("%c",*p);
    p++;
} 
Run Code Online (Sandbox Code Playgroud)

*ptr++是一样的*(ptr++),这是:

const char  *ptr = "example";
char  value;

value = *ptr;
++ptr;
printf("%c", value); // will print 'e'
Run Code Online (Sandbox Code Playgroud)

*++ptr是一样的*(++ptr),这是:

const char  *ptr = "example";
char  value;

++ptr;
value = *ptr;
printf("%c", value); // will print 'x'
Run Code Online (Sandbox Code Playgroud)

++*ptr是一样的++(*ptr),这是:

const char  *ptr = "example";
char  value;

value = *ptr;
++value;
printf("%c", value); // will print 'f' ('e' + 1)
Run Code Online (Sandbox Code Playgroud)