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

最近我遇到了这个问题,我自己也不能理解。

这三个短语 真的是什么意思?

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

我联系过 Ritchie 但不幸的是,我无法理解他对这三个手术的描述。

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

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

给我这个输出:

ello

但我的预期是它将打印 Hello。 最后一个请求——请举例说明每个表达式在给定代码片段中的工作方式。因为大多数时候,只有一小段理论在我头上飞过。

98928 次浏览

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

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

  1. *ptr++计算为 arr[i],并将 ptr设置为指向 arr的(i + 1)-th 元素。它等价于 *(ptr++)

  2. *++ptrptr设置为指向 arr的(i + 1)-th 元素并计算为 arr[i+1]。它等价于 *(++ptr)

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

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

  1. (*ptr)++arr[i]增加1,并在增加之前计算其值; 指针 ptr仍然保持不变。

其余的你可以自己搞清楚; 这个问题也有@Jaguar 的回答。

关于优先级,您是对的,请注意,*的优先级高于前缀增量,但不高于后缀增量。下面是这些分解过程:

*ptr++-从左到右,取消引用指针,然后递增指针值(不是它指向的值,因为后缀优先于取消引用)

*++ptr-递增指针然后取消引用,这是因为前缀和取消引用具有相同的优先级,所以它们是按照从右到左的顺序计算的

++*ptr-在优先级方面与上面类似,同样是从右到左依次取消对指针的引用,然后递增指针所指向的值。请注意,在您的情况下,这将导致未定义的行为,因为您试图修改一个只读变量(char* p = "Hello";)。

您的循环中的情况很糟糕:

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;
++p;
printf("%c", value); // will print 'e'

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

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


++p;
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)

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

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

第一个声明:

char* p = "Hello";

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

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

然后设置一个循环:

while (*p++)

循环条件 *p++是什么意思?有三个因素使得这个问题令人困惑(至少在熟悉感出现之前) :

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

1.优先级。快速浏览一下运算符的优先级表,就会发现后缀增量的优先级(16)高于取消引用/间接(15)。这意味着复杂表达式 *p++将被分组为: *(p++)。也就是说,*部分将应用于 p++部分的值。让我们先看看 p++的部分。

2. 后缀表达式值 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'。因此,表达式 *p++的计算结果为 'H'

等等,你是说。如果 *p++的计算结果是 'H',那么为什么 'H'不能在上面的代码中打印出来呢?这就是 副作用的用武之地。

3.后缀表达式副作用 。后缀 ++具有当前操作数的 价值,但是它具有递增该操作数的 副作用。啊?再看看 int代码:

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

如前所述,产出将是:

7
8

当在第一个 printf()中计算 i++时,它的计算结果为7。但是 C 标准保证在第二个 printf()开始执行之前的某个时刻,++操作符的 副作用将已经发生。也就是说,在第二个 printf()发生之前,i将由于第一个 printf()中的 ++操作符而增加。顺便说一句,这是该标准为数不多的关于副作用发生时间的保证之一。

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

printf ("%c", *p)

麻烦的副作用已经发生了。p已增加。哇!它不再指向 'H',而是指向超过 'H'的一个字符: 指向 'e',换句话说。这就解释了你那套自以为是的说辞:

ello

因此,在其他答案中有一大堆有用的(而且准确的)建议: 要打印公认发音字体的 abc 0,而不是伦敦腔的对应字体,你需要像这样的东西

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

其他的呢? 你问这些的含义:

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

我们刚刚讨论了第一个,让我们看看第二个: *++ptr

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

前缀增量的优先级低于后缀; 它的优先级为15。换句话说,它具有与解引用/间接操作符 *相同的优先级。就像这样

*++ptr

重要的不是优先级: 这两个运算符在优先级上是相同的。联想性启动了。前缀增量和间接运算符具有左右结合性。由于这种结合性,操作数 ptr将与最右边的操作符 ++组合在一起,然后是更左边的操作符 *。换句话说,表达式将被分组为 *(++ptr)。因此,与 *ptr++一样,但是出于不同的原因,这里也将 *部分应用于 ++ptr部分的值。

那么这个值是多少呢?前缀递增表达式的值是操作数 在增量之后的值。这使它与后缀增量操作符截然不同。假设你有:

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

产出将是:

8
8

... 不同于我们看到的后缀操作符。类似地,如果你有:

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 e e l                // good dog

你知道为什么吗?

现在我们来看看你问的第三个表达式 ++*ptr。实际上,这是最棘手的。两个运算符具有相同的优先级和左右结合性。这意味着表达式将被分组为 ++(*ptr)++部分将应用于 *ptr部分的值。

因此,如果我们有:

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

令人惊讶的自我主义产出将是:

I

什么? !好的,那么 *p部分将被评估为 'H'。然后 ++开始起作用,在这一点上,它将被应用到 'H',而不是指针!当你把1加到 'H'时会发生什么?得到1加上 ASCII 值 'H',72,得到73。将它表示为 char,就会得到 ASCII 值为73: 'I'char

这就解决了你在问题中提到的三个表达式。这里还有一个问题,在第一条评论中提到过:

(*ptr)++

这个也很有趣,如果你有:

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

它会给你带来这种热情的输出:

HI
    

发生什么事了?同样,这是 优先级表达式值副作用的问题。由于使用了括号,因此 *p部分被视为主要表达式。主表达式胜过一切; 它们首先被求值。如你所知,*p等于 'H'。表达式的其余部分,即 ++部分,将应用于该值。因此,在这种情况下,(*p)++变成 'H'++

'H'++的值是多少?如果你说的是 'I',你已经忘记了(已经!)我们讨论后缀增量的价值与副作用。记住,'H'++的计算结果是 'I'0 'H'。第一个 printf()将打印 'H'。然后,作为一个 'I'1,这个 'H'会增加到 'I'。第二个 printf()打印 'I'。你有你愉快的问候。

好吧,但是在最后两个案子里,为什么我需要

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

为什么我就不能有这样的东西呢

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

因为 "Hello"是字符串文字。如果尝试使用 ++*p,则尝试将字符串中的 'H'更改为 'I',使整个字符串成为 "Iello"。在 C 语言中,字符串文字是只读的,尝试修改它们会调用未定义行为。"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指向一个字符串文字,第三个和第四个将崩溃。

就是这样。我希望现在都是水晶。你们是很棒的观众,我整周都会在这里。

我要加上我的观点,因为当其他答案是正确的时候,我认为他们遗漏了一些东西。

 v = *ptr++

手段

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

在哪里

 v = *++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 + + 中,尽管 ptr可能是一个类似迭代器的复杂类型。比如说

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

在这种情况下,因为 it是一个复杂的类型 it++可能有副作用,因为 temp的创建。当然,如果你幸运的话,编译器会尝试扔掉不需要的代码,但是如果迭代器的构造函数或析构函数执行了任何操作,那么 it++会在创建 temp时显示这些效果。

简而言之,我想说的是 写出你的意思。如果你的意思是 增量,那么写 ++ptr而不是 ptr++。如果你的意思是 temp = ptr, ptr += 1, temp,那么写 ptr++

*ptr++    // 1

这与:

    tmp = *ptr;
ptr++;

因此,检索 ptr指向的对象的值,然后增加 ptr

*++ptr    // 2

这与:

    ++ptr;
tmp = *ptr;

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

++*ptr    // 3

这与:

    ++(*ptr);

因此,ptr指向的对象是递增的; 而 ptr本身是不变的。

后缀和前缀的优先级高于取消引用,因此

* ptr + + 在这里发送增量 ptr,然后指向 ptr 的新值

这里是 * + + ptr 首先指向 ptr 的新值

在这里,+ + * 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++结束,其中有一个 '\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);

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

注意 : 指针必须初始化并且必须有有效的地址。因为在内存中,除了我们的程序(a.out) ,还有很多程序同时运行,比如,如果你想访问一些没有为你预留的内存,操作系统会通过内存区段错误。

在解释这个之前让我们考虑一个简单的例子?

#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意味着我们讨论的是 价值/data。

案例1 : < strong > * ptr + + ,* + + ptr,* (ptr + +)和 * (+ + ptr) :

上面提到的所有4种语法都是相似的,在所有的 address gets incremented中,但是地址是如何增量的就不一样了。

注意 : 对于求解任意表达式,找出表达式中有多少个运算符,然后找出运算符的 优先权。我多个运算符具有相同的优先级,然后检查进化顺序或者 联想性,它可以从右(R)到左(L)而不是从左到右。

* ptr + + : 这里有两个运算符,分别是 de- 引用(*)和 + + (增量)。两者具有相同的优先级,然后检查结合性,它是 R 到 L。所以开始从右到左求解,不管是哪个运算符先出现。

* ptr + + : first + + 是在从 R 到 L 的求解过程中出现的,所以地址是递增的,而帖子是递增的。

* + + ptr : 与这里的第一个相同,地址也会增加,但它的前增量。

* (ptr + +) : 这里有3个操作符,其中分组()具有最高的优先级,所以第一个 ptr + + 解决的即地址增加但发送。

* (+ + ptr) : 与上面的情况相同,这里的地址也是递增的,但是是前递增的。

案例2 : < strong > + + * ptr,+ + (* ptr) ,(* ptr) + + :

上面提到的所有4种语法都是相似的,在 所有值/数据都会增加中,但是值是如何被改变的,这是不同的。

+ + * ptr : 从 R 到 L 的求解过程中首先出现,所以值改变了,但是它的前增量。

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

(* ptr) + + : 这里有3个操作符,其中分组()具有最高的优先级,Inside () * ptr 在这里,所以首先 * ptr 被解决了,即值被增加但发送。

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

要理解以上所有关于指针增量的不同语法,让我们考虑一下简单的代码:

#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);
}

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

作为常量的指针 : 没有任何方法可以使指针作为常量,我在这里提到的很少。

1) Const int * p 或 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 = %d p =%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 i.e its pointing to some address(here its pointing to garbage), it should point to same address(i.e 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 = %d p =%p and *p = %d\n",num,p,*p);
}

那么这个问题的解决方案是什么呢?

     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);
}
  • 后缀 ++的优先级高于一元 *
  • 前缀 + + 和一元 *具有相同的优先级,两者的表达式都具有从右向左的 算符结合性算符结合性,这意味着右边的表达式绑定到左边的表达式之前的操作数。

因此:

  • 将指针递增1个项,然后取消引用递增之前的内存位置。
  • 增加指针1个项目,然后取消引用现在指向的内存位置。
  • 取消引用内存位置,然后将那里的 内容(值)增加1。