指针expression式:* ptr ++,* ++ ptr和++ * ptr

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

这三个expression式真的意味着什么?

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

我试过里奇。 但不幸的是,他不能跟随他所说的这三项行动。

我知道,他们都执行增加指针/指向的值。 我也可以猜测评估的优先顺序和顺序可能有很多。 就像一个指针递增指针,然后获取该指针的内容,一个简单的提取内容,然后增加指针等等。正如你所看到的,我不清楚他们的实际操作,我想尽快明确。 但是当我有机会将它们应用于节目时,我真的迷失了方向。 例如:

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

给我这个输出:

 ello 

但我的期望是打印出Hello 。 最后一个请求 – 请给我举例说明每个expression式如何在给定的代码片段中工作。 大多数时候,只有一小段理论会飞过我的脑海。

这里有一个详细的解释,我希望会有所帮助。 让我们从你的程序开始,因为这是最简单的解释。

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

第一个说法:

 const char* p = "Hello"; 

声明p为指向char的指针。 当我们说“指向一个char ”时,这是什么意思? 这意味着p的值是char的地址; p告诉我们在内存中哪里有一些空间放置一个char

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

然后你build立一个循环:

 while (*p++) 

循环条件*p++是什么意思? 这里有三件事情令人费解(至less直到熟悉):

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

1.优先 。 快速浏览一下运算符的优先级表,会告诉你后缀增量比解引用/间接(15)具有更高的优先级(16)。 这意味着复杂expression式*p++将被分组为: *(p++) 。 也就是说, *部分将应用于p++部分的值。 所以我们先来看看p++部分。

2.后缀expression式值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' 。 所以expression式*p++评估为'H'

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

3.后缀expression的副作用 。 后缀++具有当前操作数的 ,但是具有递增该操作数的副作用 。 咦? 再次看看这个int代码:

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

如前所述,输出将是:

 7 8 

i++在第一个printf()评估i++ ,它的计算结果为7.但是C标准保证在第二个printf()开始执行之前的某个点, ++运算符的副作用将会发生。 也就是说,在第二个printf()发生之前,由于第一个printf()中的++操作符, i将被增加。 顺便说一句,这是标准给出的副作用时间的less数保证之一。

在你的代码中,当expression式*p++被评估时,它的计算结果为'H' 。 但是到了你这个时候:

 printf ("%c", *p) 

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

 ello 

因此,在其他答案中提供有用的(和准确的)build议的合唱:打印接收发音"Hello"而不是它的对手,你需要像

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

那么多。 那剩下的呢? 你问这些的含义:

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

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

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

前缀增量优先于后缀; 它具有优先权15.换句话说,它具有与解引用/间接运算符*相同的优先权。 在像expression式

 *++ptr 

重要的不是优先:两个运营商的优先级相同。 所以结合性就会起作用。前缀增量和间接运算符具有左右联合性。 由于这种关联性,操作数ptr将在操作符更靠左的位置之前与最右边的运算符++分组。 换句话说,expression式将被分组*(++ptr) 。 所以,就像使用*ptr++但是由于不同的原因,这里的*部分也会被应用到++ptr部分的值。

那么这个价值是什么? 前缀增量expression式的值是增量后操作数的值。 这使得后缀增量运算符成为一个非常不同的野兽。 假设你有:

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

输出将是:

 8 8 

…与我们用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++ 

输出将是:

 H eel // good dog 

你明白为什么?

现在我们得到你问的第三个expression式, ++*ptr 。 实际上,这是最棘手的问题。 两个操作符具有相同的优先级和左右关联性。 这意味着expression式将被分组++(*ptr)++部分将应用于*ptr部分的值。

所以如果我们有:

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

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

 I 

什么?! 好的,所以*p部分将评估为'H' 。 然后++进入游戏,在这一点上,它将被应用到'H' ,而不是指针! 当你给'H'加1时会发生什么? 你得到1加上'H'的ASCII值72, 你得到73.把它表示为一个char ,然后你得到ASCII值为73的字符: 'I'

这照顾了你在问题中提出的三个表情。 这是另一个,在你的问题的第一个评论中提到:

 (*ptr)++ 

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

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

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

 HI 

这是怎么回事? 再次,这是一个优先事项, expression价值副作用的问题 。 由于括号, *p部分被视为主要expression式。 主要performance胜过一切; 他们首先得到评估。 而且,如你所知, *p评估为'H' 。 expression式的其余部分,即++部分应用于该值。 所以,在这种情况下, (*p)++变成'H'++

什么是'H'++的价值? 如果你说'I' ,你已经忘记(已经!)我们讨论的价值与副作用后缀增量。 请记住, 'H'++评估为'H'++当前值 。 因此,第一个printf()将打印'H' 。 那么,作为一个副作用 ,这个'H'将会增加到'I' 。 第二个printf()打印'I' 。 你有愉快的问候。

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

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

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

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

因为"Hello"是一个string文字。 如果你尝试++*p ,你试图把string中的'H'改为'I' ,使整个string"Iello" 。 在C中,string文字是只读的; 试图修改它们调用未定义的行为。 "Iello"在英文中也没有定义,但这只是巧合。

相反,你不能有

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

为什么不? 因为在这个例子中, 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 

这里有四分之一,和其他三个一样有趣:

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

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

你有它。 我希望现在都是水晶 你已经是一个很好的观众了,我会整整一周来到这里。

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

  1. *ptr++计算为arr[i] ,并将ptr设置为指向arr第(i + 1)个元素。 它相当于*(ptr++)

  2. *++ptr*++ptr设置为指向arr的(i + 1)个元素,并计算为arr[i+1] 。 它相当于*(++ptr)

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

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

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

其余的你可以弄清楚自己; 它也被@Jaguar回答。

*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; } 

你的循环条件是不好的:

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

是相同的

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

这是错的,这应该是:

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

*ptr++*(ptr++) ,即:

 const char *ptr = "example"; char value; value = *ptr; ++ptr; printf("%c", value); // will print 'e' 

*++ptr*(++ptr) ,即:

 const char *ptr = "example"; char value; ++ptr; value = *ptr; printf("%c", value); // will print 'x' 

++*ptr++(*ptr) ,即:

 const char *ptr = "example"; char value; value = *ptr; ++value; printf("%c", value); // will print 'f' ('e' + 1) 

你有优先权,请注意, *优先于前缀增量,但不超过后缀增量。 以下是这些细节:

*ptr++ – 从左到右,取消引用指针,然后递增指针值(而不是它指向的,由于postfix的优先级超过解引用)

*++ptr – 递增指针然后解引用它,这是因为前缀和解引用具有相同的优先级,所以它们按从右到左的顺序被评估

++*ptr – 类似于上面的优先级,再次从右到左依次引用指针,然后增加指针指向的内容。 请注意,在你的情况下,这将导致未定义的行为,因为你试图修改一个只读variables( char* p = "Hello"; )。

我要添加我的,因为虽然其他答案是正确的,我认为他们错过了一些东西。

  v = *ptr++ 

手段

  temp = ptr; ptr = ptr + 1 v = *temp; 

在哪里

  *++ptr 

手段

  ptr = ptr + 1 v = *ptr 

理解后增量(和后减量)的含义是很重要的

  temp = ptr // Temp created here!!! ptr = ptr + 1 // or - 1 if decrement) v = *temp // Temp destroyed here!!! 

为什么这有关系? 那么在C这不是那么重要。 在C ++中,尽pipeptr可能是一个像迭代器一样的复杂types。 例如

  for (std::set<int>::iterator it = someSet.begin(); it != someSet.end(); it++) 

在这种情况下,因为it是一个复杂的types, it++可能因为temp创build而具有副作用。 当然,如果你幸运的话,编译器会试图抛弃那些不需要的代码,但是如果迭代器的构造函数或析构函数做了任何事情,那么it++会在创buildtemp时显示这些效果。

我想说的是写你的意思 。 如果你的意思是增加ptr然后写++ptr不是ptr++ 。 如果你的意思是temp = ptr, ptr += 1, temp写入ptr++

后缀和前缀比取消引用具有更高的优先级

* ptr ++在这里后递增ptr,然后指向ptr的新值

* ++ ptr在这里Pre Increment拳头然后指向ptr的新值

++ * ptr在这里首先得到ptr的值指向并增加vlaue

 *ptr++ // 1 

这是一样的:

  tmp = *ptr; ptr++; 

所以ptr指向的对象的值被检索,然后ptr递增。

 *++ptr // 2 

这是一样的:

  ++ptr; tmp = *ptr; 

所以指针ptr是递增的,然后读取由ptr指向的对象。

 ++*ptr // 3 

这是一样的:

  ++(*ptr); 

所以ptr指向的对象是递增的; ptr本身不变。

指针expression式:* ptr ++,* ++ ptr和++ * ptr:

注意 :指针必须初始化,并且必须有有效的地址。 因为在除了我们的程序(a.out)之外的RAM中还有更多的程序正在同时运行,也就是说如果你试图访问一些没有为你保留的内存,操作系统将会通过分段错误。

在说明这个之前让我们考虑简单的例子吗

 #include<stdio.h> int main() { int num = 300; int *ptr;//uninitialized pointer.. must be initialized ptr = &num; printf(" num = %d ptr = %p and data on ptr : %d \n",num,ptr,*ptr); *ptr = *ptr + 1;//*ptr means value/data on the address.. so here value gets incremented printf(" num = %d ptr = %p and data on ptr : %d \n",num,ptr,*ptr); /** observe here that "num" got changed but manually we didn't change, it got modified by pointer **/ ptr = ptr + 1;//ptr means address.. so here address got incremented /** char pointer gets incremented by 1 bytes Integer pointer gets incremented by 4 bytes **/ printf(" num = %d ptr = %p and data on ptr : %d \n",num,ptr,*ptr); } 

分析以上代码的输出,希望你能得到以上代码的输出。 从上面的代码可以清楚地看到,指针名( ptr )意味着我们正在谈论地址* ptr意味着我们正在谈论价值 /数据。

情况1* ptr ++,* ++ ptr,*(ptr ++)和*(++ ptr):

上面提到的所有4个syntex是相似的,在所有的address gets incremented但地址得到增加,这是不同的。

注意 :为了解决任何expression式,找出expression式中有多less个操作符,然后找出操作符的优先级 。 我有多个具有相同优先级的操作符,然后检查可能右(R)左(L)从左到右的进化顺序或关联性

* ptr ++ :这里有两个运算符,即去引用(*)和++(增量)。 两者都具有相同的优先级,然后检查是否为R的关联性。因此,从右向左开始解决,无论哪个操作员先来。

* ptr ++ :从R到L求解时,第一个++出现了,所以地址得到递增,但是它的后增加。

* ++ ptr :和第一个一样,地址也是递增的,但是它的前一个增量。

*(ptr ++) :这里有3个运算符,其中grouping()具有最高的优先级,所以第一个ptr ++解决了,即地址得到递增,但后。

*(++ ptr) :和上面的情况一样,地址也是递增的,但是是递增的。

情况2++ * ptr,++(* ptr),(* ptr)++:

上面提到的所有4个syntex是相似的,在所有值/数据增加,但价值如何变化是不同的。

++ * ptr :first *在从R到L求解时出现,所以value被改变,但是它的pre增量。

++(* ptr) :与上述情况相同,值被修改。

(* ptr)++ :这里有3个运算符,其中grouping()具有最高的优先级,Inside()* ptr在那里,所以first * ptr被求值,即值得到递增但后。

注意 :++ * ptr和* ptr = * ptr + 1都是相同的,在这两种情况下值都被改变。 ++ * ptr:只有1个指令(INC)被使用,直接值被改变在单枪。 * ptr = * ptr + 1:这里第一个值被增加(INC),然后被赋值(MOV)。

为了理解以上指针上增加的不同syntex让我们考虑简单的代码:

 #include<stdio.h> int main() { int num = 300; int *ptr; ptr = &num; printf(" num = %d ptr = %p and data on ptr : %d \n",num,ptr,*ptr); *ptr++;//address changed(post increment), value remains un-changed // *++ptr;//address changed(post increment), value remains un-changed // *(ptr)++;//address changed(post increment), value remains un-changed // *(++ptr);//address changed(post increment), value remains un-changed // ++*ptr;//value changed(pre increment), address remains un-changed // (*ptr)++;//value changed(pre increment), address remains un-changed // ++(*ptr);//value changed(post increment), address remains un-changed printf(" num = %d ptr = %p and data on ptr : %d \n",num,ptr,*ptr); } 

在上面的代码中,尝试评论/取消注释评论和分析输出。

指针为常数 :没有什么方法可以使指针保持不变,我在这里提到的很less。

1) const int * p OR int const * p :这里的value常量地址不是常量,即p是什么地方? 有些地址? 在那个地址上有什么价值? 有些价值吧? 该值是不变的,你不能修改该值,但指针指向哪里? 有些地址对吗? 它也可以指向其他地址。

要了解这一点,可以考虑下面的代码

 #include<stdio.h> int main() { int num = 300; const int *ptr;//constant value, address is modifible ptr = &num; printf(" num = %d ptr = %p and data on ptr : %d \n",num,ptr,*ptr); *ptr++;// // *++ptr;//possible bcz you are trying to change address which is possible // *(ptr)++;//possible // *(++ptr);//possible // ++*ptr;//not possible bcz you trying to change value which is not allowed // (*ptr)++;//not possible // ++(*ptr);//not possible printf(" num = %d ptr = %p and data on ptr : %d \n",num,ptr,*ptr); } 

试着分析以上代码的输出

2) int const * p :它被称为' **constant pointe**r ',即address is constant but value is not constant 。 在这里您不能更改地址,但可以修改该值。

注意 :常量指针(大小写)在声明自身时必须初始化。

要理解这一点,可以检查简单的代码

 #include<stdio.h> int main() { int x = 300; int* const p; p = &x; printf("x = %dp =%p and *p = %d\n",num,p,*p); } 

在上面的代码中,如果你观察到没有++ * p或* p ++所以你可能会认为这很简单,因为我们没有改变地址或值,但是会产生错误。 为什么? 我在评论中提到的原因。

 #include<stdio.h> int main() { int x = 300; /** constant pointer must initialize while decaring itself **/ int* const p;//constant pointer ie its pointing to some address(here its pointing to garbage), it should point to same address(ie garbage ad dress only p = &x;// but here what we are doing ? we are changing address. we are making p to point to address of x instead of garbage address. printf("x = %dp =%p and *p = %d\n",num,p,*p); } 

那么这个问题的解决scheme是什么?

  int* const p = &x; 

关于这个案例更多的是考虑下面的例子。

 #include<stdio.h> int main() { int num = 300; int *const ptr = &num;//constant value, address is modifible printf(" num = %d ptr = %p and data on ptr : %d \n",num,ptr,*ptr); *ptr++;//not possible // *++ptr;//not possible bcz you are trying to change address which is not possible // *(ptr)++;//not possible // *(++ptr);//not possible // ++*ptr;// possible bcz you trying to change value which is allowed // (*ptr)++;// possible // ++(*ptr);// possible printf(" num = %d ptr = %p and data on ptr : %d \n",num,ptr,*ptr); } 

3) const int * const p :这里地址和值都是常量

要理解这一点,请查看下面的代码

 #include<stdio.h> int main() { int num = 300; const int* const ptr = &num;//constant value,constant address printf(" num = %d ptr = %p and data on ptr : %d \n",num,ptr,*ptr); *ptr++;//not possible ++*ptr;//not possible printf(" num = %d ptr = %p and data on ptr : %d \n",num,ptr,*ptr); } 
 const char *p = "Hello"; *p means "Hello" ^ | p *p++ means "Hello" ^ | p *++p means "Hello" ^ | (WHILE THE STATEMENT IS EXECUTED) p *++p means "Hello" ^ | (AFTER THE STATEMENT IS EXECUTED) p 

++*p表示您正在尝试增加*p的ASCII值

  is "Hello" ^ | p 

你不能增加值,因为它是一个常量,所以你会得到一个错误

至于你的while循环,循环运行,直到*p++达到string的末尾有一个'\0' (NULL)字符。

现在,由于*p++跳过第一个字符,所以只能从第二个字符开始输出。

下面的代码不会输出任何东西,因为while循环有'\0'

 const char *p = "Hello"; while('\0') printf("%c",*p); 

下面的代码会给你和下一个代码相同的输出,比如ello。

 const char *p = "Hello"; while(*++p) printf("%c",*p); 

……………………………..

 const char *p = "Hello"; while(*p++) printf("%c",*p);