คำถามของคุณอาจไม่ "ทำไมสิ่งเหล่านี้สร้างพฤติกรรมที่ไม่ได้กำหนดใน C?" อาจเป็นคำถามของคุณ "ทำไมรหัสนี้ (โดยใช้++
) ไม่ให้คุณค่าที่ฉันคาดหวัง" และมีคนทำเครื่องหมายคำถามของคุณว่าซ้ำซ้อนและส่งมาให้คุณที่นี่
คำตอบนี้พยายามตอบคำถามนั้น: เหตุใดรหัสของคุณจึงไม่ให้คำตอบที่คุณคาดหวังและคุณจะเรียนรู้วิธีการรับรู้ (และหลีกเลี่ยง) การแสดงออกที่ไม่ได้ผลตามที่คาดไว้
ฉันคิดว่าคุณเคยได้ยินคำจำกัดความพื้นฐานของ C ++
และ--
ตัวดำเนินการแล้วและแบบฟอร์ม prefix นั้น++x
แตกต่างจากแบบฟอร์ม postfix x++
อย่างไร แต่โอเปอเรเตอร์เหล่านี้ยากที่จะคิดดังนั้นเพื่อให้แน่ใจว่าคุณเข้าใจบางทีคุณอาจเขียนโปรแกรมทดสอบเล็ก ๆ ที่เกี่ยวข้องกับบางสิ่งเช่น
int x = 5;
printf("%d %d %d\n", x, ++x, x++);
แต่สำหรับความประหลาดใจของคุณโปรแกรมนี้ไม่ได้ช่วยให้คุณเข้าใจ - มันพิมพ์ผลลัพธ์ที่แปลกประหลาดคาดไม่ถึงอธิบายไม่ได้ว่ามี++
บางอย่างที่แตกต่างอย่างสิ้นเชิงไม่ใช่สิ่งที่คุณคิด
หรือบางทีคุณอาจมองว่าการแสดงออกที่เข้าใจยากเป็นเช่นนั้น
int x = 5;
x = x++ + ++x;
printf("%d\n", x);
อาจมีบางคนให้รหัสนั้นเป็นปริศนา รหัสนี้ก็ไม่มีเหตุผลโดยเฉพาะอย่างยิ่งถ้าคุณเรียกใช้ - และถ้าคุณรวบรวมและเรียกใช้ภายใต้คอมไพเลอร์สองตัวที่แตกต่างกันคุณมีแนวโน้มที่จะได้รับคำตอบที่ต่างกันสองข้อ! เกิดอะไรขึ้นกับสิ่งนั้น คำตอบใดถูกต้อง? (และคำตอบก็คือทั้งคู่เป็นหรือไม่ใช่ของทั้งคู่)
ดังที่คุณได้ยินมาแล้วการแสดงออกเหล่านี้ทั้งหมดไม่ได้ถูกกำหนดซึ่งหมายความว่าภาษา C ไม่รับประกันว่าพวกเขาจะทำอะไร นี่เป็นผลลัพธ์ที่แปลกและน่าประหลาดใจเพราะคุณอาจคิดว่าโปรแกรมใด ๆ ที่คุณสามารถเขียนได้ตราบใดที่คอมไพล์และรันจะสร้างเอาต์พุตที่ไม่ซ้ำใครและชัดเจน แต่ในกรณีของพฤติกรรมที่ไม่ได้กำหนดไม่เป็นเช่นนั้น
สิ่งที่ทำให้การแสดงออกไม่ได้กำหนด? มีการแสดงออกที่เกี่ยวข้อง++
และ--
ไม่ได้กำหนดเสมอ? ไม่แน่นอน: นี่เป็นตัวดำเนินการที่มีประโยชน์และหากคุณใช้อย่างถูกต้อง
สำหรับการแสดงออกที่เรากำลังพูดถึงสิ่งที่ทำให้พวกเขาไม่ได้กำหนดคือเมื่อมีมากเกินไปเกิดขึ้นในครั้งเดียวเมื่อเราไม่แน่ใจว่าสิ่งที่จะเกิดขึ้นในการสั่งซื้อ แต่เมื่อการสั่งซื้อมีความสำคัญต่อผลที่เราได้รับ
กลับไปที่สองตัวอย่างที่ฉันใช้ในคำตอบนี้ เมื่อฉันเขียน
printf("%d %d %d\n", x, ++x, x++);
คำถามคือก่อนที่จะโทรprintf
รวบรวมคอมไพเลอร์คำนวณค่าx
แรกหรือx++
หรืออาจจะ++x
? แต่มันกลับกลายเป็นเราไม่ทราบว่า ไม่มีกฎใน C ที่บอกว่าข้อโต้แย้งของฟังก์ชันได้รับการประเมินจากซ้ายไปขวาหรือจากขวาไปซ้ายหรือในลำดับอื่น ดังนั้นเราไม่สามารถพูดได้ว่าคอมไพเลอร์จะทำx
ก่อนจาก++x
นั้นx++
หรือx++
จาก++x
นั้นx
หรือตามลำดับอื่น ๆ printf
แต่คำสั่งอย่างชัดเจนที่สำคัญเพราะขึ้นอยู่กับที่สั่งใช้คอมไพเลอร์เราจะเห็นได้ชัดว่าได้รับผลลัพธ์ที่แตกต่างพิมพ์โดย
แล้วสีหน้าบ้าคลั่งแบบนี้ล่ะ?
x = x++ + ++x;
ปัญหาเกี่ยวกับการแสดงออกนี้คือมันมีสามพยายามที่แตกต่างกันในการปรับเปลี่ยนค่าของ x: (1) x++
ส่วนพยายามที่จะเพิ่ม 1 ถึง x, เก็บค่าใหม่ในx
และส่งกลับค่าเก่าของx
; (2) ++x
ส่วนที่พยายามเพิ่ม 1 ไปยัง x เก็บค่าใหม่ในx
และส่งคืนค่าใหม่ของx
; และ (3) x =
ส่วนหนึ่งพยายามกำหนดผลรวมของอีกสองอันกลับไปที่ x ข้อใดในสามข้อที่พยายามจะ "ชนะ" ซึ่งในสามของค่าจริงจะได้รับการมอบหมายให้x
? อีกครั้งและอาจน่าแปลกใจที่ไม่มีกฎใน C ที่จะบอกเรา
คุณอาจจินตนาการว่าลำดับความสำคัญหรือความสัมพันธ์หรือการประเมินจากซ้ายไปขวาจะบอกคุณว่าสิ่งใดที่เกิดขึ้นในการสั่งซื้อ แต่สิ่งเหล่านั้นไม่ได้ทำ คุณอาจไม่เชื่อฉัน แต่โปรดใช้คำของฉันสำหรับมันและฉันจะพูดอีกครั้ง: ความสำคัญและความสัมพันธ์ไม่ได้กำหนดทุกแง่มุมของลำดับการประเมินผลของการแสดงออกในซีโดยเฉพาะอย่างยิ่งถ้าภายในหนึ่งการแสดงออกมีหลาย จุดที่แตกต่างกันที่เราพยายามกำหนดค่าใหม่ให้กับสิ่งที่ต้องการx
ความสำคัญและความสัมพันธ์ไม่ได้บอกเราว่าความพยายามเหล่านั้นเกิดขึ้นก่อนหรือสุดท้ายหรืออะไรก็ตาม
ถ้าคุณต้องการให้แน่ใจว่าโปรแกรมทั้งหมดของคุณมีการกำหนดไว้อย่างดีคุณสามารถเขียนนิพจน์ใดได้บ้างและโปรแกรมใดที่คุณไม่สามารถเขียนได้
การแสดงออกเหล่านี้เป็นสิ่งที่ดี:
y = x++;
z = x++ + y++;
x = x + 1;
x = a[i++];
x = a[i++] + b[j++];
x[i++] = a[j++] + b[k++];
x = *p++;
x = *p++ + *q++;
การแสดงออกเหล่านี้ทั้งหมดไม่ได้กำหนด:
x = x++;
x = x++ + ++x;
y = x + x++;
a[i] = i++;
a[i++] = i;
printf("%d %d %d\n", x, ++x, x++);
และคำถามสุดท้ายคือคุณจะบอกได้อย่างไรว่านิพจน์ใดที่มีการกำหนดชัดเจนและนิพจน์ใดที่ไม่ได้กำหนด?
ดังที่ฉันได้กล่าวไว้ก่อนหน้านี้นิพจน์ที่ไม่ได้นิยามนั้นเป็นนิพจน์ที่เกิดขึ้นพร้อมกันมากเกินไปซึ่งคุณไม่สามารถมั่นใจได้ว่ามีสิ่งใดบ้างที่เกิดขึ้นในการสั่งซื้อ
- หากมีหนึ่งตัวแปรที่ได้รับการแก้ไข (มอบหมายให้) ในสองที่หรือมากกว่านั้นคุณจะรู้ได้อย่างไรว่าการดัดแปลงใดที่เกิดขึ้นก่อน
- หากมีตัวแปรที่ได้รับการแก้ไขในที่เดียวและมีค่าที่ใช้ในที่อื่นคุณจะรู้ได้อย่างไรว่ามันใช้ค่าเดิมหรือค่าใหม่
เป็นตัวอย่างของ # 1 ในการแสดงออก
x = x++ + ++x;
มีความพยายามสามประการในการแก้ไข `x
เป็นตัวอย่างของ # 2 ในการแสดงออก
y = x + x++;
เราทั้งคู่ใช้ค่าของx
และแก้ไขมัน
นั่นคือคำตอบ: ตรวจสอบให้แน่ใจว่าในนิพจน์ใด ๆ ที่คุณเขียนตัวแปรแต่ละตัวจะถูกแก้ไขอย่างมากที่สุดหนึ่งครั้งและหากมีการแก้ไขตัวแปรคุณจะไม่พยายามใช้ค่าของตัวแปรนั้นที่อื่น