วิธีเพิ่มที่อยู่ตัวชี้และค่าของตัวชี้


96

ให้เราถือว่า

int *p;
int a = 100;
p = &a;

โค้ดต่อไปนี้จะทำอะไรได้จริงและอย่างไร?

p++;
++p;
++*p;
++(*p);
++*(p);
*p++;
(*p)++;
*(p)++;
*++p;
*(++p);

ฉันรู้ว่านี่เป็นเรื่องยุ่งในแง่ของการเข้ารหัส แต่ฉันอยากรู้ว่าจะเกิดอะไรขึ้นเมื่อเราเขียนโค้ดแบบนี้

หมายเหตุ: ให้คิดว่าที่อยู่ของa=5120300มันจะถูกเก็บไว้ในตัวชี้ที่มีอยู่คือp หลังจากการดำเนินการของแต่ละคำสั่ง3560200จะมีมูลค่าp & aเท่าใด


3
ทำไมคุณไม่เรียกใช้มันในดีบักเกอร์ล่ะ
AndersK

24
เอาล่ะ.. ทำไมไม่ลองดูล่ะ? printfจะพิมพ์ตัวชี้ด้วย% p
Brian Roach

หากคุณอยากรู้เกี่ยวกับพฤติกรรมเพียงแค่ลองเล่นกับมัน เพียงแค่เขียนโปรแกรม c ง่ายๆที่อธิบายถึงกรณีการใช้งานเหล่านี้ทั้งหมดและดูว่าเหมาะสมกับคุณหรือไม่
Cyrus

@AndersK. บางที OP อาจคาดหวังพฤติกรรมที่ไม่ได้กำหนด? ... หรืออาจจะไม่
Mateen Ulhaq

คำตอบ:


178

ขั้นแรกให้ตัวดำเนินการ ++ มีความสำคัญเหนือตัวดำเนินการ * และตัวดำเนินการ () มีความสำคัญเหนือสิ่งอื่นใด

ประการที่สองตัวดำเนินการหมายเลข ++ จะเหมือนกับโอเปอเรเตอร์ number ++ หากคุณไม่ได้กำหนดให้อะไรเลย ความแตกต่างคือ number ++ จะส่งกลับตัวเลขจากนั้นเพิ่มจำนวนและเพิ่มจำนวน ++ ก่อนแล้วจึงส่งกลับ

ประการที่สามโดยการเพิ่มค่าของตัวชี้คุณจะเพิ่มค่าตามขนาดของเนื้อหานั่นคือคุณกำลังเพิ่มขึ้นราวกับว่าคุณกำลังทำซ้ำในอาร์เรย์

ดังนั้นเพื่อสรุปทั้งหมด:

ptr++;    // Pointer moves to the next int position (as if it was an array)
++ptr;    // Pointer moves to the next int position (as if it was an array)
++*ptr;   // The value of ptr is incremented
++(*ptr); // The value of ptr is incremented
++*(ptr); // The value of ptr is incremented
*ptr++;   // Pointer moves to the next int position (as if it was an array). But returns the old content
(*ptr)++; // The value of ptr is incremented
*(ptr)++; // Pointer moves to the next int position (as if it was an array). But returns the old content
*++ptr;   // Pointer moves to the next int position, and then get's accessed, with your code, segfault
*(++ptr); // Pointer moves to the next int position, and then get's accessed, with your code, segfault

เนื่องจากมีหลายกรณีในที่นี้ฉันอาจทำผิดพลาดไปบ้างโปรดแก้ไขฉันหากฉันผิด

แก้ไข:

ดังนั้นฉันคิดผิดลำดับความสำคัญซับซ้อนกว่าที่ฉันเขียนเล็กน้อยดูได้ที่นี่: http://en.cppreference.com/w/cpp/language/operator_precedence


6
* ptr ++ ค่าจะไม่เพิ่มขึ้นตัวชี้คือ ตัวดำเนินการยูนารีเหล่านี้มีลำดับความสำคัญเหมือนกัน แต่มีการประเมินจากขวาไปซ้าย รหัสหมายถึง "นำเนื้อหาจากจุดที่ ptr ชี้ที่แล้วเพิ่ม ptr" เป็นรหัส C ที่พบบ่อยมาก (และใช่ค่อนข้างสับสน) โปรดแก้ไขและฉันจะลบการลงคะแนนให้ เช่นเดียวกับ * (ptr) ++ วงเล็บไม่ทำอะไรเลย
Lundin

ขอบคุณมาก Lundin ฉันคิดถึงอะไรอีกไหม?
felipemaia

@Lundin สวัสดีคำตอบข้างบนได้รับการแก้ไขแล้วหรือยัง? ขอบคุณ.
Unheilig

4
@Unheilig ประโยคแรกยังคงผิดอยู่อย่างสมบูรณ์ postfix ++ มีความสำคัญเหนือยูนารี * ซึ่งมีความสำคัญเช่นเดียวกับคำนำหน้า ++ นอกเหนือจากนั้นก็ดูโอเค
Lundin

4
@felipemaia แน่ใจเหรอว่าจะ segfault? อาจเป็นเพียงพฤติกรรมที่ไม่ได้กำหนด?
jotik

14

ตรวจสอบโปรแกรมและผลลัพธ์เป็น

p++;    // use it then move to next int position
++p;    // move to next int and then use it
++*p;   // increments the value by 1 then use it 
++(*p); // increments the value by 1 then use it
++*(p); // increments the value by 1 then use it
*p++;   // use the value of p then moves to next position
(*p)++; // use the value of p then increment the value
*(p)++; // use the value of p then moves to next position
*++p;   // moves to the next int location then use that value
*(++p); // moves to next location then use that value

2
@alex ใช้หมายถึงตัวอย่างเช่นพิจารณาคำสั่ง 'int * a = p ++;' ที่นี่ค่าแรกของตัวชี้ 'p' จะถูกใช้และหลังจากนั้น p จะย้ายไปยังตำแหน่งถัดไป ดังนั้นหลังจากดำเนินการคำสั่งข้างต้น 'a' จะมีที่อยู่ของตำแหน่งก่อนหน้าซึ่งชี้โดย 'p' และ 'p' จะชี้ไปที่ตำแหน่งถัดไป ขั้นแรกให้ใช้ค่า 'p' สำหรับนิพจน์การกำหนดตามด้านบนจากนั้นเพิ่มค่า 'p' เพื่อชี้ไปยังตำแหน่งถัดไป
Sujith R Kumar

ในระยะสั้นฉันคิดว่าเขาใช้วลี "use it" สำหรับคำที่เป็นทางการมากกว่า "มอบหมาย" นั้นคือทั้งหมด.
Apekshik Panigrahi

4

ต่อไปนี้คือการสร้างอินสแตนซ์ของคำแนะนำ "เพียงแค่พิมพ์" ต่างๆ ฉันพบว่ามันให้คำแนะนำ

#include "stdio.h"

int main() {
    static int x = 5;
    static int *p = &x;
    printf("(int) p   => %d\n",(int) p);
    printf("(int) p++ => %d\n",(int) p++);
    x = 5; p = &x;
    printf("(int) ++p => %d\n",(int) ++p);
    x = 5; p = &x;
    printf("++*p      => %d\n",++*p);
    x = 5; p = &x;
    printf("++(*p)    => %d\n",++(*p));
    x = 5; p = &x;
    printf("++*(p)    => %d\n",++*(p));
    x = 5; p = &x;
    printf("*p++      => %d\n",*p++);
    x = 5; p = &x;
    printf("(*p)++    => %d\n",(*p)++);
    x = 5; p = &x;
    printf("*(p)++    => %d\n",*(p)++);
    x = 5; p = &x;
    printf("*++p      => %d\n",*++p);
    x = 5; p = &x;
    printf("*(++p)    => %d\n",*(++p));
    return 0;
}

มันกลับมา

(int) p   => 256688152
(int) p++ => 256688152
(int) ++p => 256688156
++*p      => 6
++(*p)    => 6
++*(p)    => 6
*p++      => 5
(*p)++    => 5
*(p)++    => 5
*++p      => 0
*(++p)    => 0

ฉันส่งที่อยู่ตัวชี้เป็นints เพื่อให้เปรียบเทียบได้ง่าย

ฉันรวบรวมด้วย GCC


1
ฉันจะเปลี่ยนสิ่งนี้เพื่อรวมค่า x และ p หลังการดำเนินการ
NetJohn

3

เกี่ยวกับ"วิธีการเพิ่มที่อยู่ตัวชี้และค่าของตัวชี้" ฉันคิดว่า++(*p++);มันถูกกำหนดไว้อย่างดีและทำในสิ่งที่คุณต้องการเช่น:

#include <stdio.h>

int main() {
  int a = 100;
  int *p = &a;
  printf("%p\n",(void*)p);
  ++(*p++);
  printf("%p\n",(void*)p);
  printf("%d\n",a);
  return 0;
}

มันไม่ได้แก้ไขสิ่งเดียวกันสองครั้งก่อนจุดลำดับ ฉันไม่คิดว่ามันเป็นสไตล์ที่ดีสำหรับการใช้งานส่วนใหญ่ - มันคลุมเครือเกินไปสำหรับความชอบของฉัน


ที่จริงแล้ววงเล็บไม่จำเป็น: ++*p++จะเพิ่มทั้งค่าและตัวชี้ได้สำเร็จ (postfix ++ผูกแน่นกว่า dereference *และเกิดขึ้นก่อนคำนำหน้า++เนื่องจากลำดับ) (*p++)++วงเล็บมีความจำเป็นเฉพาะเมื่อคุณต้องการความคุ้มค่าก่อนที่เพิ่มขึ้นมัน หากคุณไปที่คำนำหน้าทั้งหมด++*++pจะทำงานได้ดีโดยไม่มีวงเล็บเช่นกัน (แต่เพิ่มค่าที่ชี้ไปหลังจากการเพิ่มตัวชี้)
cmaster - คืนสถานะโมนิกา

1
        Note:
        1) Both ++ and * have same precedence(priority), so the associativity comes into picture.
        2) in this case Associativity is from **Right-Left**

        important table to remember in case of pointers and arrays: 

        operators           precedence        associativity

    1)  () , []                1               left-right
    2)  *  , identifier        2               right-left
    3)  <data type>            3               ----------

        let me give an example, this might help;

        char **str;
        str = (char **)malloc(sizeof(char*)*2); // allocate mem for 2 char*
        str[0]=(char *)malloc(sizeof(char)*10); // allocate mem for 10 char
        str[1]=(char *)malloc(sizeof(char)*10); // allocate mem for 10 char

        strcpy(str[0],"abcd");  // assigning value
        strcpy(str[1],"efgh");  // assigning value

        while(*str)
        {
            cout<<*str<<endl;   // printing the string
            *str++;             // incrementing the address(pointer)
                                // check above about the prcedence and associativity
        }
        free(str[0]);
        free(str[1]);
        free(str);

Asociativity คืออะไร?
71GA

1
ในโค้ดคุณสามารถเห็น * str ++ ตอนนี้ทั้ง * และ ++ มีลำดับความสำคัญเหมือนกัน (ลำดับความสำคัญเท่ากันในคำของคนธรรมดา) และ * str ++ ไม่ได้แยกโดยใช้วงเล็บเช่น * (str ++) หรือ (* str) ++ ดังนั้น มีความจำเป็นว่าควรจะประเมินอย่างไร ขวาไปซ้ายหมายถึง (x = str ++) แล้ว (y = * x)
Abhishek DK
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.