指针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);