นิพจน์ตัวชี้: * ptr ++, * ++ ptr และ ++ * ptr


128

เมื่อเร็ว ๆ นี้ฉันพบปัญหานี้ซึ่งฉันไม่สามารถเข้าใจได้ด้วยตัวเอง

ทำอะไรที่สามเหล่านี้แสดงออกจริงๆหมายถึง?

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

ฉันได้ลอง Ritchie แล้ว แต่น่าเสียดายที่ไม่สามารถทำตามสิ่งที่เขาบอกเกี่ยวกับปฏิบัติการทั้ง 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++);Q: fnพิมพ์อักขระเดียวกันสองครั้งหรือไม่? คุณจะประหลาดใจว่ามีอาจารย์กี่คนที่ตอบคำถามนั้นผิด
WhozCraig

1
เนื่องจาก p ชี้ไปที่ตัวอักษรสตริงคุณควรเขียนconst char* p = "Hello";
heteperfan

คำตอบ:


275

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

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

คำสั่งแรก:

const char* p = "Hello";

ประกาศเป็นตัวชี้ไปยังp charเมื่อเราพูดว่า "pointer to a char" หมายความว่าอย่างไร หมายความว่าค่าของpคือที่อยู่ของ a char; pบอกเราว่าในหน่วยความจำมีพื้นที่ว่างไว้ให้เก็บไฟล์char.

คำสั่งยังเริ่มต้นpเพื่อชี้ไปที่อักขระตัวแรกในสตริงลิเทอรั"Hello"ล เพื่อประโยชน์ของแบบฝึกหัดนี้สิ่งสำคัญคือต้องเข้าใจpว่าไม่ได้ชี้ไปที่สตริงทั้งหมด แต่เป็นเพียงอักขระตัวแรก'H'เท่านั้น หลังจากที่ทุกคนpเป็นตัวชี้ไปหนึ่งcharไม่สตริงทั้งหมด ค่าของpที่อยู่ของใน'H'"Hello"

จากนั้นคุณตั้งค่าลูป:

while (*p++)

เงื่อนไขการวนซ้ำ*p++หมายถึงอะไร? สามสิ่งกำลังทำงานอยู่ที่นี่ซึ่งทำให้งง (อย่างน้อยก็จนกว่าความคุ้นเคยจะเข้ามา):

  1. ลำดับความสำคัญของตัวดำเนินการสองตัวคือ postfix ++และ indirection*
  2. ค่าของนิพจน์ส่วนเพิ่ม postfix
  3. ผลข้างเคียงของนิพจน์ส่วนเพิ่ม postfix

1. ความเป็นผู้นำ การดูตารางลำดับความสำคัญของตัวดำเนินการอย่างรวดเร็วจะบอกคุณว่าการเพิ่มขึ้นของ postfix มีลำดับความสำคัญสูงกว่า (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. Postfix ผลข้างเคียงการแสดงออก postfix ++มีค่าของตัวถูกดำเนินการปัจจุบัน แต่มีผลข้างเคียงของการเพิ่มตัวถูกดำเนินการนั้น ฮะ? ดู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

ดังนั้นการขับร้องของข้อเสนอแนะที่เป็นประโยชน์ (และถูกต้อง) ในคำตอบอื่น ๆ : ในการพิมพ์การออกเสียงที่ได้รับ"Hello"และไม่ใช่คู่หูของมันคุณต้องมีบางอย่างเช่น

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

มากสำหรับสิ่งนั้น ส่วนที่เหลือล่ะ? คุณถามเกี่ยวกับความหมายของสิ่งเหล่านี้:

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

*++ptrเราเพียงแค่พูดคุยเกี่ยวกับครั้งแรกเพื่อให้รูปลักษณ์ที่สอง:

เราเห็นในการอธิบายก่อนหน้านี้ของเราที่เพิ่มขึ้น postfix p++มีบางอย่างที่มีความสำคัญเป็นมูลค่าและผลข้างเคียง การเพิ่มขึ้นของคำนำหน้า++pมีเดียวกันผลข้างเคียงที่เป็นของคู่ postfix ของมันเพิ่มขึ้นถูกดำเนินการโดยการ 1. แต่ก็มีความแตกต่างกันมีความสำคัญและที่แตกต่างกันค่า

การเพิ่มคำนำหน้ามีความสำคัญต่ำกว่า postfix มันมีความสำคัญ 15 ในคำอื่น ๆ *ก็มีความสำคัญเช่นเดียวกับผู้ประกอบการ ในนิพจน์เช่น

*++ptr

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

แล้วค่านั้นคืออะไร? มูลค่าของการแสดงออกคำนำหน้าเพิ่มขึ้นคือค่าของตัวถูกดำเนินการหลังจากที่เพิ่มขึ้น สิ่งนี้ทำให้มันเป็นสัตว์ร้ายที่แตกต่างจากตัวดำเนินการส่วนเพิ่มของ postfix สมมติว่าคุณมี:

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 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และคุณจะได้รับcharกับค่า ASCII 'I'73:

ที่ดูแลสามสำนวนที่คุณถามในคำถามของคุณ นี่คืออีกสิ่งหนึ่งที่กล่าวถึงในความคิดเห็นแรกสำหรับคำถามของคุณ:

(*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
ก่อนที่จะมาที่ฟอรัมนี้ฉันได้ค้นหาหนังสือ "C" 3 เล่มซึ่งฉันเป็นเจ้าของ ฉันได้ลองใช้แบบฝึกหัดออนไลน์ที่น่าสนใจแล้ว แต่ไม่มีคำอธิบายใดที่ใกล้เคียงกับคำอธิบายของคุณ (โดยเฉพาะวิธีที่คุณรวบรวมไว้ทั้งหมด) คุณไม่เพียง แต่ตอบคำถามที่ฉันถามเท่านั้น แต่คุณยังได้พูดคุยอีกมากมายตั้งแต่ระดับรากหญ้า ที่จริงวันนี้คุณได้สอนฉันเรื่องพื้นฐานมากมายซึ่งเมื่อก่อนฉันยังขาดอยู่ ฉันอดไม่ได้ที่จะสลับคำตอบที่ยอมรับ :) ขอบคุณอีกครั้ง.
จัดสรร

26
+1 ฉันคิดว่านี่เป็นคำตอบที่ยาวที่สุดที่ฉันเคยอ่านใน SO ฉันคิดว่าทุกคนสามารถเรียนรู้ได้มากจากคำตอบนี้
Shafik Yaghmour

9
คุณชายควรเขียนหนังสือเรื่อง C.
Dillon Burton

1
ช่างเป็นคำตอบที่ดีสำหรับคำถามที่ดี! ทำได้ดีมาก @verbose!
benka

7
@verbose คุณชายมีชีวิตสมชื่อ .. :)
sleeping_dragon

45

สมมติว่าptrจุดที่จะต้อง i-TH arrองค์ประกอบของอาร์เรย์

  1. *ptr++ประเมินarr[i]และชุดptrให้ชี้ไปที่ (i + 1) arrองค์ประกอบของ เทียบเท่ากับ*(ptr++).

  2. *++ptrชุดptrที่จะชี้ไป (i + 1) องค์ประกอบของ -th และประเมินผลการ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

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)

ฉันจะเห็นด้วยกับส่วนแรกของคำตอบ ในส่วนที่สองการเริ่มต้นพอยน์เตอร์ (เป็นจำนวนเต็ม!) ด้วยจำนวนเต็มจะสร้างความสับสนให้กับผู้ที่พยายามทำความเข้าใจการใช้พอยน์เตอร์
nickie

4

คุณถูกต้องเกี่ยวกับลำดับความสำคัญโปรดทราบว่า*ลำดับความสำคัญมีความสำคัญมากกว่าการเพิ่มคำนำหน้า แต่ไม่เกินส่วนเพิ่มหลังการแก้ไข รายละเอียดเหล่านี้มีดังนี้:

*ptr++ - ไปจากซ้ายไปขวาหักล้างตัวชี้จากนั้นเพิ่มค่าตัวชี้ (ไม่ใช่สิ่งที่ชี้ไปเนื่องจากลำดับความสำคัญของ postfix มากกว่า dereference)

*++ptr - เพิ่มตัวชี้แล้วหักล้างมันเนื่องจากคำนำหน้าและ dereference มีความสำคัญเหมือนกันดังนั้นจึงได้รับการประเมินตามลำดับจากขวาไปซ้าย

++*ptr- คล้ายกับข้างต้นในแง่ของลำดับความสำคัญอีกครั้งไปจากขวาไปซ้ายเพื่อยกเลิกการอ้างอิงตัวชี้จากนั้นเพิ่มสิ่งที่ตัวชี้ชี้ไป โปรดทราบว่าในกรณีของคุณสิ่งนี้จะนำไปสู่พฤติกรรมที่ไม่ได้กำหนดเนื่องจากคุณกำลังพยายามแก้ไขตัวแปรแบบอ่านอย่างเดียว ( char* p = "Hello";)


3

ฉันจะเพิ่ม Take ของฉันเพราะในขณะที่คำตอบอื่น ๆ ถูกต้องฉันคิดว่ามันขาดอะไรไป

 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สร้าง แน่นอนว่าถ้าคุณโชคดีคอมไพเลอร์จะพยายามทิ้งโค้ดที่ไม่จำเป็นออกไป แต่ถ้าตัวสร้างหรือตัวทำลายของ iterator ทำอะไรก็it++จะแสดงเอฟเฟกต์เหล่านั้นเมื่อสร้างtempขึ้น

สั้นของสิ่งที่ฉันพยายามจะบอกก็คือเขียนสิ่งที่คุณหมาย หากคุณมีค่าเฉลี่ยเพิ่มขึ้น PTRแล้วเขียนไม่ได้++ptr ptr++ถ้าคุณหมายถึงtemp = ptr, ptr += 1, tempเขียนptr++


0
*ptr++    // 1

สิ่งนี้เหมือนกับ:

    tmp = *ptr;
    ptr++;

ดังนั้นค่าของวัตถุที่ชี้ไปจะptrถูกดึงมาจากนั้นptrจะเพิ่มขึ้น

*++ptr    // 2

สิ่งนี้เหมือนกับ:

    ++ptr;
    tmp = *ptr;

ดังนั้นตัวชี้ptrจึงเพิ่มขึ้นจากนั้นวัตถุที่ชี้ไปจะptrถูกอ่าน

++*ptr    // 3

สิ่งนี้เหมือนกับ:

    ++(*ptr);

ดังนั้นวัตถุที่ชี้ไปptrจะเพิ่มขึ้น ptrตัวเองไม่เปลี่ยนแปลง


0

postfix และ prefix มีลำดับความสำคัญสูงกว่า dereference ดังนั้น

* ptr ++ ที่นี่โพสต์ ptr ที่เพิ่มขึ้นแล้วชี้ไปที่ค่าใหม่ของ ptr

* ++ ptr ที่นี่ก่อนเพิ่มกำปั้นแล้วชี้ไปที่ค่าใหม่ของ ptr

++ * ptr ที่นี่ก่อนอื่นให้รับค่าของ ptr ที่ชี้ไปและเพิ่มค่า vlaue นั้น


1
สิ่งนี้ไม่ถูกต้อง Postfix มีลำดับความสำคัญสูงกว่า แต่คำนำหน้ามีความสำคัญเช่นเดียวกับการหักล้าง
verbose

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หมายถึงเรากำลังพูดถึงค่า / ข้อมูล

กรณีที่ 1 : * ptr ++, * ++ ptr, * (ptr ++) และ * (++ ptr):

ที่กล่าวมาข้างต้นไวยากรณ์ทั้ง 4 มีความคล้ายคลึงกันaddress gets incrementedแต่วิธีการเพิ่มที่อยู่นั้นแตกต่างกัน

หมายเหตุ : สำหรับการแก้นิพจน์ใด ๆ ให้ค้นหาจำนวนตัวดำเนินการในนิพจน์จากนั้นค้นหาลำดับความสำคัญของตัวดำเนินการ ฉันตัวดำเนินการหลายตัวที่มีลำดับความสำคัญเท่ากันจากนั้นตรวจสอบลำดับของวิวัฒนาการหรือการเชื่อมโยงที่อาจจากขวา (R) ไปซ้าย (L) จากซ้ายไปขวา

* ptr ++ : ที่นี่มีตัวดำเนินการ 2 ตัว ได้แก่ de-reference (*) และ ++ (Increment) ทั้งสองมีลำดับความสำคัญเท่ากันจากนั้นตรวจสอบการเชื่อมโยงซึ่งเป็น R ถึง L ดังนั้นเริ่มต้นการแก้จากขวาไปซ้ายตัวดำเนินการใดก็ตามที่มาก่อน

* ptr ++ : ++แรกเข้ามาในขณะที่แก้จาก R ถึง L ดังนั้นแอดเดรสจึงเพิ่มขึ้น แต่จะเพิ่มขึ้นของโพสต์

* ++ ptr : เช่นเดียวกับที่อยู่แรกที่นี่ยังได้รับการเพิ่มขึ้น แต่จะเพิ่มขึ้นล่วงหน้า

* (ptr ++) : ที่นี่มีตัวดำเนินการ 3 ตัวในหมู่พวกเขาจัดกลุ่ม () มีลำดับความสำคัญสูงสุดดังนั้น 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 หรือ const int * P : ที่นี่valueคือคงที่ , ที่อยู่ไม่ได้คงที่เช่นที่พีชี้? ที่อยู่บ้าง? ตามที่อยู่นั้นค่าคืออะไร? คุณค่าบางอย่างใช่ไหม ค่านั้นคงที่คุณไม่สามารถแก้ไขค่านั้นได้ แต่ตัวชี้จะชี้ไปที่ใด? ที่อยู่บ้างใช่ไหม สามารถชี้ไปยังที่อยู่อื่นได้ด้วย

เพื่อให้เข้าใจสิ่งนี้ให้พิจารณาโค้ดด้านล่าง:

#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) const int * 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) int const * 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++ข้ามอักขระตัวแรกคุณจะได้รับผลลัพธ์ของคุณโดยเริ่มจากอักขระที่สองเท่านั้น

รหัสต่อไปนี้จะไม่แสดงผลอะไรเลยเพราะในขณะที่มีลูป '\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);
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.