143

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

这三个表达式的真正含义是什么?

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

我试过里奇。但遗憾的是无法按照他所说的关于这 3 个操作的内容进行说明。

我知道它们都是为了增加指针/指向的值而执行的。我也可以猜到可能有很多关于优先级和评估顺序的事情。就像一个先增加指针然后获取该指针的内容一样,一个简单地获取内容然后增加指针等等等。如您所见,我对它们的实际操作没有清楚的了解,我想尽快清除。但是当我有机会将它们应用到程序中时,我真的迷失了。例如:

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

给我这个输出:

ello

但我的期望是它会打印Hello。最后一个请求——请举例说明每个表达式在给定代码段中的工作方式。因为大多数时候只有一段理论在我脑海中飘过。

4

11 回答 11

309

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

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

第一个声明:

char* p = "Hello";

声明p为指向char. 当我们说“指向 a 的指针char”时,这是什么意思?这意味着 的值p是 a 的地址charp告诉我们在内存中的哪个位置留出一些空间来保存char.

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

然后你设置一个循环:

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

i++在第一个中求值时printf(),它的求值为 7。但是 C 标准保证在第二个printf()开始执行之前的某个时间点,操作符的副作用将会++发生。也就是说,在第二个printf()发生之前,由于第一个运算符的作用,i将已经递增。顺便说一句,这是该标准为副作用时间提供的为数不多的保证之一。++printf()

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

printf ("%c", *p)

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

ello

因此,其他答案中的有用(和准确)建议的合唱:要打印 Received Pronunciation"Hello"而不是它的 cockney 对应物,您需要类似的东西

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,你得到charASCII 值为 73: 的'I'

这会处理您在问题中提出的三个表达方式。这是另一个,在您的问题的第一条评论中提到:

(*ptr)++ 

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

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

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

HI
    

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

的价值是'H'++多少?如果你说'I',你已经(已经!)忘记了我们关于后缀增量的价值与副作用的讨论。请记住,'H'++计算为 的当前值 'H'。所以首先printf()要打印'H'。然后,作为副作用,它将'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是一个数组。数组不是可修改的左值;您不能通过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指向字符串文字,第三个和第四个将崩溃。

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

于 2013-08-28T10:05:08.547 回答
46

假设ptr指向 array 的第 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]一并评估其增加的值;指针ptr保持不变。它相当于++(*ptr)

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

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

剩下的你自己想办法;@Jaguar 也回答了这个问题。

于 2013-08-28T07:38:05.890 回答
14

*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;
}
于 2013-08-28T07:32:10.467 回答
7

循环中的条件很糟糕:

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)
于 2013-08-28T07:41:50.690 回答
4

关于优先级,您是对的,请注意*优先于前缀增量,但不超过后缀增量。以下是这些细分的方式:

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

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

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

于 2013-08-28T07:38:21.077 回答
3

我要补充一下,因为虽然其他答案是正确的,但我认为他们遗漏了一些东西。

 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,那么写++ptrnot ptr++。如果你的意思是temp = ptr, ptr += 1, temp然后写ptr++

于 2013-08-28T13:08:20.420 回答
0
*ptr++    // 1

这与以下内容相同:

    tmp = *ptr;
    ptr++;

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

*++ptr    // 2

这与以下内容相同:

    ++ptr;
    tmp = *ptr;

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

++*ptr    // 3

这与以下内容相同:

    ++(*ptr);

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

于 2013-09-04T00:05:55.690 回答
0

指针表达式:*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 种语法都是相似的,address gets incremented但地址如何增加是不同的。

注意:求解任何表达式,先找出表达式中有多少个运算符,然后找出运算符的优先级。我有多个具有相同优先级的运算符,然后检查可能从右(R)到左(L)或从左到右的进化顺序或关联性。

*ptr++:这里有 2 个运算符,即 de-reference(*) 和 ++(increment)。两者都具有相同的优先级,然后检查 R 到 L 的关联性。所以从右到左开始求解,无论运算符先到。

*ptr++:第一个 ++ 在从 R 到 L 求解时出现,因此地址增加但其后增加。

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

*(ptr++):这里有 3 个运算符,其中 grouping () 具有最高优先级,所以首先 ptr++ 解决了,即地址增加但发布。

*(++ptr):与上述情况相同,这里的地址也增加但预增加。

案例 2++*ptr、++(*ptr)、(*ptr)++:

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

++*ptr:第一个 * 在从 R 到 L 求解时出现,因此值发生了变化,但它的预增量。

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

(*ptr)++:这里有 3 个运算符,其中 grouping () 具有最高优先级, Inside () *ptr 在那里,所以首先 *ptr 被解决,即值增加但 post。

注意: ++*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 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' ie 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);
}
于 2017-11-12T19:17:12.800 回答
0
  • Postfix++的优先级高于 unary *
  • 前缀 ++ 和一元*具有相同的优先级,两者的表达式具有从右到左的运算符关联性,这意味着右侧的操作数在左侧的操作数之前绑定到操作数。

所以:

  • *ptr++将指针增加 1 项,然后取消引用它在增加之前的内存位置。
  • *++ptr将指针增加 1 项,然后取消引用它现在指向的内存位置。
  • ++*ptr取消引用内存位置,然后将那里的内容(值)增加 1。
于 2022-01-19T13:40:38.620 回答
-1

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

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

*++ptr 此处 Pre Increment 拳头然后指向 ptr 的新值

++*ptr 这里首先获取 ptr 指向的值并递增该值

于 2016-02-10T05:36:55.337 回答
-1
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表示您正在尝试增加其 ASCII*p

   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);
于 2017-11-11T17:23:14.693 回答