คำถามของคุณอาจไม่ "ทำไมสิ่งเหล่านี้สร้างพฤติกรรมที่ไม่ได้กำหนดใน 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และแก้ไขมัน
นั่นคือคำตอบ: ตรวจสอบให้แน่ใจว่าในนิพจน์ใด ๆ ที่คุณเขียนตัวแปรแต่ละตัวจะถูกแก้ไขอย่างมากที่สุดหนึ่งครั้งและหากมีการแก้ไขตัวแปรคุณจะไม่พยายามใช้ค่าของตัวแปรนั้นที่อื่น