指针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直到熟悉):
- 两个运算符的优先级,后缀
++和间接* - 后缀增量expression式的值
- 后缀增量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个元素。
-
*ptr++计算为arr[i],并将ptr设置为指向arr第(i + 1)个元素。 它相当于*(ptr++)。 -
*++ptr将*++ptr设置为指向arr的(i + 1)个元素,并计算为arr[i+1]。 它相当于*(++ptr)。 -
++*ptr将arr[i]增加1并评估其增加值; 指针ptr保持不变。 它相当于++(*ptr)。
还有一个,但你需要括号来写它:
-
(*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 = # 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 = # 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 = # 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 = #//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 = #//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);