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


128

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

这三个表达式实际上是什么意思?

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

我试过里奇。但不幸的是,他无法按照他对这3项操作的讲述进行操作。

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

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

给我这个输出:

ello

但是我希望它能打印出来Hello。最后一个要求-请提供示例,说明每个表达式在给定代码段中的工作方式。大多数时候,只有一小段理论飞过我的脑海。


6
您错过了第四个:((*ptr)++括号需要与*ptr++
加以区别

15
因为您在打印指针之前先增加了指针。您需要while(* p)和printf(“%c”,* p ++);
dcaswell

面试的好问题。有限的实际使用。我希望C没有这些指针:)
Himanshu

5
@Himanshu如果这使您的受访者感到不安,请尝试以下操作:使用全局指针char* p,指向有效的终止的唯一字符字符串。然后有一个功能fn(char ch)是打印ch参数当前焦炭指向p。现在调用fn(*p++);问:fn打印两次相同的字符吗?你会惊讶于有多少教授把这个问题弄错了。
WhozCraig 2013年

1
因为p指向字符串文字,所以您应该写const char* p = "Hello";
hetepeperfan 2013年

Answers:


275

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

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

第一条语句:

const char* p = "Hello";

声明p为的指针char。当我们说“指向char”时,是什么意思?表示的值p是a的地址charp告诉我们在内存中的位置可以保留一些空间char

该语句还初始化p为指向字符串文字中的第一个字符"Hello"。为了便于练习,重要的是要理解p为不指向整个字符串,而仅指向第一个字符'H'。毕竟p是一个指向一个char而不是整个字符串的指针。的值p'H'in 的地址"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',换句话说。这就解释了您的总结:

ello

因此,在其他答案中,有很多有用(准确)的建议合唱:要打印“接收到的发音” "Hello"而不是“老二”,您需要

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

为此。其余的呢?您询问这些含义:

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

我们只是讨论了第一个,因此让我们看一下第二个:*++ptr

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

前缀增量的优先级低于后缀;它的优先级为15。换句话说,它的优先级与取消引用/间接操作符相同*。在像这样的表达中

*++ptr

重要的不是优先级:两个运算符的优先级相同。因此,关联性开始起作用。前缀增量和间接运算符具有左右关联性。由于这种关联性,操作数ptr将与最右边的运算符组合在一起,然后再向++左移*。换句话说,该表达式将被分组*(++ptr)。因此,就像*ptr++出于不同的原因一样,这里也将*零件应用于++ptr零件的价值。

那那值多少钱呢?前缀增量表达式的值是增量之后的操作数的值。这使其与后缀增量运算符完全不同。假设您有:

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

输出将是:

8
8

...与我们在后缀运算符中看到的不同。同样,如果您有:

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 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。将其表示为a char,并且您将得到char具有ASCII值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;

为什么我不能像这样

/*const*/ 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指向字符串文字,则会崩溃。

你有它。我希望现在一切都是水晶。您的观众很好,我整周都会在这里。


22
在进入这个论坛之前,我搜索了我拥有的3本“ C”书。我还尝试了一些著名的在线教程。但是它们都不接近您的解释(尤其是您将它们组合在一起的方式)。您不仅回答了我所问的问题,而且还从基层讨论了很多事情。实际上,您今天已经教了我很多以前没有的基本知识。我忍不住切换了我接受的答案。:) 再次感谢。
分配

26
+1我认为这是我在SO上阅读的最好的长答案。我认为每个人都可以从这个答案中学到很多东西。
Shafik Yaghmour 2013年

9
先生,您应该写一本关于C的书
。– Dillon Burton

1
一个好问题的好答案!干得好@verbose!
卡(

7
@verbose你的先生,没有辜负你的名字.. :)
sleeping_dragon

44

假设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也回答了。


13

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

@ Nik-Lz是的,输出为Hello
Jainendra '18 -10-10

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)

我完全同意答案的第一部分。在第二部分中,使用整数初始化指针(到整数!)会使那些难以理解指针用法的人感到困惑。
尼克,

4

您对优先级没错,请注意,*优先级优先于前缀增量,但不优先于后缀增量。这些细分的方式如下:

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

*++ptr -递增指针,然后对其取消引用,这是因为前缀和取消引用具有相同的优先级,因此按从右到左的顺序对其求值

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


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


0
*ptr++    // 1

这与以下内容相同:

    tmp = *ptr;
    ptr++;

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

*++ptr    // 2

这与以下内容相同:

    ++ptr;
    tmp = *ptr;

因此,将指针ptr递增,然后ptr读取由指向的对象。

++*ptr    // 3

这与以下内容相同:

    ++(*ptr);

因此,由指向的对象将ptr增加。ptr本身是不变的。


0

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

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

* ++ ptr在这里,先增加拳头然后指向ptr的新值

++ * ptr首先获取指向的ptr的值并增加vlaue


1
这是不正确的。后缀具有更高的优先级,但前缀与取消引用具有相同的优先级。
详细的

0

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

注意:指针必须初始化并且必须具有有效地址。因为在RAM中,除了我们的程序(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表示我们正在谈论abbout 值。 / data。

案例1* ptr ++,* ++ ptr,*(ptr ++)和*(++ ptr):

上面提到的所有4种语法都是相似的,address gets incremented但是地址的递增方式不同。

注意:要求解任何表达式,请找出表达式中有多少个运算符,然后找出运算符的优先级。在具有相同优先级的多个运算符中,然后检查从右到左(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个运算符,其中分组()具有最高优先级,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'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);
}

-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);
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.