+ (+ k--) นิพจน์ใน C


9

ฉันเห็นคำถามนี้ในการทดสอบที่เราต้องบอกผลลัพธ์ของรหัสต่อไปนี้

#include<stdio.h>

int main(){
    int k = 0;
    while(+(+k--)!=0)
    k=k++;
    printf("%d\n", k);  
    return 0;
}

-1เอาท์พุทเป็น ฉันไม่แน่ใจว่าทำไมนี่คือคำตอบ

การแสดงออก+(+k--)หมายถึงอะไรใน C?


4
มันถูกจัดรูปแบบเช่นนี้หรือไม่? นั่นคือค่าเฉลี่ย ;-)
ปีเตอร์ - Reinstate Monica

3
คำแนะนำ: อย่าเขียนโค้ด BS แบบนี้ในชีวิตจริง
Jabberwocky

5
นี่ไม่ใช่ UB ดูคำตอบของฉัน
dbush

@ Peter-ReinstateMonica ใช่มันถูกจัดรูปแบบเช่นนี้
Ankur Gautam

2
เนื่องจากลักษณะที่คิดค้นขึ้นและบิดแปลกประหลาด - k=k++ไม่ได้กำหนด แต่ไม่ได้กำหนดเพราะไม่เคยดำเนินการเนื่องจากมีสภาพที่สับสน - ฉันลงคะแนนให้ใกล้เคียงกับคำถามนี้
Steve Summit

คำตอบ:


10

[สำหรับบันทึกฉันได้แก้ไขคำตอบนี้ค่อนข้างมากเนื่องจากได้รับการยอมรับและโหวตให้ มันยังบอกว่าโดยพื้นฐานแล้วสิ่งเดียวกันแม้ว่า]

รหัสนี้มีความลึกและอาจทำให้เกิดความสับสน มันมีตัวอย่างหวุดหวิดหันไปของความหวาดกลัวไม่ได้กำหนดพฤติกรรม โดยทั่วไปแล้วเป็นไปไม่ได้ที่จะตัดสินว่าคนที่สร้างคำถามนี้เป็นคนที่ฉลาดเฉลียวหรือโง่มาก และ "บทเรียน" รหัสนี้อาจอ้างถึงการสอนหรือตอบคำถามเกี่ยวกับคุณ - นั่นคือผู้ดำเนินการยูนารีบวกไม่ได้ทำอะไรมาก - แน่นอนว่ามันไม่สำคัญพอที่จะสมควรได้รับทิศทางที่ถูกโค่นล้มแบบนี้

โค้ดมีสองด้านที่ทำให้เกิดความสับสนเงื่อนไขแปลก ๆ :

while(+(+k--)!=0)

และคำสั่งที่บ้าคลั่งมันควบคุม:

k=k++;

ฉันจะครอบคลุมส่วนที่สองก่อน

หากคุณมีตัวแปรแบบkที่คุณต้องการเพิ่มขึ้น 1, C จะให้คุณไม่ใช่หนึ่งไม่ใช่สองไม่ใช่สาม แต่สี่วิธีที่แตกต่างกัน:

  1. k = k + 1
  2. k += 1
  3. ++k
  4. k++

แม้จะมีความโปรดปรานนี้ (หรืออาจเป็นเพราะมัน) แต่โปรแกรมเมอร์บางคนก็สับสน

k = k++;

หากคุณไม่สามารถเข้าใจได้ว่าควรทำอย่างไรไม่ต้องกังวล: ไม่มีใครทำได้ นิพจน์นี้มีความพยายามที่แตกต่างกันสองครั้งเพื่อแก้ไขkค่าของ ( k =ส่วนและk++ส่วน) และเนื่องจากไม่มีกฎใน C ที่จะบอกว่าการปรับเปลี่ยนใดที่พยายาม "ชนะ" การแสดงออกเช่นนี้ไม่ได้กำหนดอย่างเป็นทางการซึ่งไม่เพียง มันไม่มีความหมายที่กำหนด แต่โปรแกรมทั้งหมดที่มีมันเป็นผู้ต้องสงสัย

ตอนนี้ถ้าคุณมองมากอย่างคุณจะเห็นว่าในโปรแกรมนี้โดยเฉพาะอย่างยิ่งสายk = k++ไม่จริงจะได้รับการดำเนินการเพราะ (ในขณะที่เรากำลังจะดู) สภาพการควบคุมเป็นเท็จแรกดังนั้นห่วงวิ่ง 0 ครั้ง . ดังนั้นโปรแกรมเฉพาะนี้อาจไม่ได้ไม่ได้กำหนดจริง ๆแต่ก็ยังมีความสับสนทางพยาธิวิทยา

ดูเพิ่มเติมที่บัญญัติเหล่านี้เพื่อตอบคำถามทุกข้อเกี่ยวกับพฤติกรรมที่ไม่ได้กำหนดของประเภทนี้

แต่คุณไม่ได้ถามเกี่ยวกับk=k++ส่วนนี้ คุณถามเกี่ยวกับส่วนที่สับสนแรก+(+k--)!=0เงื่อนไข นี้มีลักษณะแปลกเพราะมันเป็นที่แปลกประหลาด ไม่มีใครเคยเขียนโค้ดดังกล่าวในโปรแกรมจริง ดังนั้นจึงไม่มีเหตุผลที่จะเรียนรู้วิธีทำความเข้าใจ (ใช่มันจริงการสำรวจขอบเขตของระบบสามารถช่วยให้คุณเรียนรู้เกี่ยวกับจุดที่ดีของมัน แต่มีเส้นที่ชัดเจนในหนังสือของฉันระหว่างการสำรวจเชิงจินตนาการการกระตุ้นความคิดและการสำรวจที่ผิดปกติทารุณและการแสดงออกที่ชัดเจนมาก ด้านที่ผิดของบรรทัดนั้น)

+(+k--)!=0อย่างไรก็ตามขอตรวจสอบ (และหลังจากทำเช่นนั้นมาลืมเรื่องทั้งหมด) การแสดงออกเช่นนี้จะต้องเข้าใจจากภายในสู่ภายนอก ฉันเข้าใจว่าคุณรู้อะไร

k--

ทำ. มันต้องใช้kค่าปัจจุบัน 's และ 'ผลตอบแทน' มันกับส่วนที่เหลือของการแสดงออกและมันมากหรือน้อยพร้อมกัน decrements k, ที่อยู่, จะเก็บปริมาณกลับเข้ามาk-1k

แต่แล้วจะ+ทำอย่างไร? นี่คือunary plus ไม่ใช่ไบนารีบวก มันก็เหมือนกับการลบนารี คุณรู้ว่าลบด้วยเลขฐานสองจะลบ: การแสดงออก

a - b

ลบ b จาก a และคุณรู้ว่าเอกนารีลบล้างสิ่งต่าง ๆ นั่นคือการแสดงออก

-a

ให้ลบ a สิ่งที่แตกต่างกัน+คือ ... โดยพื้นฐานแล้วไม่มีอะไร +aให้aคุณค่ากับคุณหลังจากเปลี่ยนค่าบวกเป็นค่าบวกและลบเป็นลบ ดังนั้นการแสดงออก

+k--

ให้สิ่งที่k--ให้แก่คุณนั่นคือkคุณค่าเก่า ๆ

แต่เราไม่ได้ทำเพราะเรามี

+(+k--)

สิ่งนี้จะนำสิ่งใดมา+k--ให้คุณและนำไปใช้กับ unary +อีกครั้ง ดังนั้นมันจะให้อะไรก็ตามที่+k--ให้คุณซึ่งเป็นสิ่งที่k--ให้คุณซึ่งเป็นkคุณค่าเก่าแก่

ดังนั้นในที่สุดสภาพ

while(+(+k--)!=0)

ทำสิ่งเดียวกันเป็นเงื่อนไขปกติมากขึ้น

while(k-- != 0)

จะได้ทำ (มันยังทำในสิ่งเดียวกันกับสภาพที่ดูซับซ้อนมากขึ้นwhile(+(+(+(+k--)))!=0)และวงเล็บก็ไม่จำเป็นเช่นกันมันก็ทำเช่นเดียวกันกับที่while(+ + + +k--!=0)ทำไป)

แม้แต่การหาเงื่อนไขว่า "ปกติ"

while(k-- != 0)

ไม่ยุ่งยาก มีสองสิ่งที่เกิดขึ้นในวงนี้: เมื่อวงวนอาจเกิดขึ้นหลายครั้งเราจะไปที่:

  1. ทำk--ต่อไปเพื่อให้kเล็กลงและเล็กลง แต่ยัง
  2. ทำร่างกายของห่วงไม่ว่าอะไรก็ตาม

แต่เราทำk--ส่วนทันทีก่อน (หรือในกระบวนการ) ตัดสินใจว่าจะเดินทางอีกครั้งผ่านวง และจำไว้ว่าk--"คืนค่า" ค่าเก่าของkก่อนที่จะลดค่าลง ในโปรแกรมนี้ค่าเริ่มต้นkคือ 0 ดังนั้นk--จะ "คืน" ค่าเก่า 0 จากนั้นอัพเดทkเป็น -1 แต่เงื่อนไขที่เหลือคือ!= 0- แต่อย่างที่เราเพิ่งเห็นครั้งแรกที่เราทดสอบเงื่อนไขเราได้ 0 ดังนั้นเราจะไม่ทำการเดินทางผ่านลูปดังนั้นเราจะไม่พยายามดำเนินการ คำสั่งที่มีปัญหาk=k++ที่ทุกคน

กล่าวอีกนัยหนึ่งในวงเฉพาะนี้แม้ว่าฉันจะบอกว่า "มีสองสิ่งที่เกิดขึ้น" ปรากฎว่าสิ่งที่ 1 เกิดขึ้นครั้งเดียว แต่สิ่งที่ 2 เกิดขึ้นเป็นศูนย์ครั้ง

ในอัตราใด ๆ ฉันหวังว่ามันตอนนี้เพียงพอที่ชัดเจนว่าทำไมนี้ข้ออ้างที่ดีสำหรับโปรแกรมสิ้นสุดการพิมพ์ -1 kเป็นค่าสุดท้ายของ โดยปกติแล้วฉันไม่ชอบตอบคำถามในแบบทดสอบนี้ - มันรู้สึกเหมือนการโกง - แต่ในกรณีนี้เนื่องจากฉันไม่เห็นด้วยอย่างมากกับประเด็นทั้งหมดของการออกกำลังกายฉันไม่รังเกียจ


1
ตัวอย่างที่คาดหวังนั้นเป็นเรื่องปกติในชั้นเรียนพื้นฐานใด ๆ เราจะเขียนคำถามสั้น ๆ เกี่ยวกับความสำคัญของโอเปอเรเตอร์ในการสอบได้อย่างไร ผมสอนคณิตศาสตร์และถ้าผมมีนิกเกิลสำหรับทุกครั้งที่นักศึกษาถามว่า "เมื่อฉันจะทำเช่นนี้ในจริงชีวิต?" ...
สกอตต์

4
@Scott มีที่วางแผนไว้แล้วมีcontrived สมมติว่าคุณเป็นผู้ฝึกสอนการขับรถ สมมติว่าคุณขอให้นักเรียนขับรถฝ่าการจราจรในตัวเมืองอย่างหนักพร้อมกับตีหัวเบสบอลด้วยไม้เบสบอล เนื้อหาที่นักเรียนจะได้เรียนรู้ทักษะที่อาจเป็นประโยชน์ แต่ฉันเรียกการละเมิดนี้ และฉันยืนตามความเห็นของฉันว่ารหัสในคำถามนี้ไม่เหมาะสมกับค่าการสอนเชิงลบ
Steve Summit

1
"ฉันอยู่ในความคิดเห็นของฉัน" นี่ยุติธรรม แต่คำหลักที่นี่คือ "ความคิดเห็น" คำตอบ StackOverflow อาจไม่ใช่สถานที่ที่เหมาะสมที่สุดในการแสดงความคิดเห็น :)
สกอตต์

1
สำหรับบันทึกฉันได้แก้ไขคำตอบนี้ค่อนข้างมากเนื่องจากได้รับการยอมรับและโหวตให้ มันยังบอกว่าโดยพื้นฐานแล้วสิ่งเดียวกันแม้ว่า
Steve Summit

11

ในภาพรวมครั้งแรกดูเหมือนว่ารหัสนี้จะเรียกใช้พฤติกรรมที่ไม่ได้กำหนดแต่นั่นไม่ใช่กรณี

ก่อนอื่นมาจัดรูปแบบรหัสให้ถูกต้อง:

#include<stdio.h>

int main(){
    int k = 0;
    while(+(+k--)!=0)
        k=k++;
    printf("%d\n", k);  
    return 0;
}

ตอนนี้เราสามารถเห็นได้ว่าคำสั่งk=k++;นั้นอยู่ในลูป

ตอนนี้เรามาติดตามโปรแกรม:

เมื่อเงื่อนไขลูปถูกประเมินครั้งแรกkมีค่า 0 นิพจน์k--มีค่าปัจจุบันkซึ่งคือ 0 และkลดลงเป็นผลข้างเคียง ดังนั้นหลังจากข้อความนี้ค่าของk-1

การนำ+หน้านิพจน์นี้ไม่มีผลกับค่าดังนั้นให้+k--ประเมินเป็น 0 และ+(+k--)ประเมินในทำนองเดียวกันกับ 0

จากนั้น!=ผู้ประกอบการจะได้รับการประเมิน ตั้งแต่0!=0เป็นเท็จร่างกายของวงที่ไม่ได้เข้ามา หากมีการป้อนเนื้อหาคุณจะต้องเรียกใช้พฤติกรรมที่ไม่ได้กำหนดเนื่องจากk=k++ทั้งการอ่านและการเขียนkโดยไม่มีจุดต่อเนื่อง แต่ไม่ได้ใส่ลูปดังนั้นจึงไม่มี UB

ในที่สุดมูลค่าของkจะถูกพิมพ์ซึ่งเป็น -1


1
มันเป็นคำถามที่เปิดกว้างมีอยู่เป็นปรัชญาและเป็นไปไม่ได้ว่าพฤติกรรมที่ไม่ได้กำหนดซึ่งไม่ได้ดำเนินการไม่ได้ถูกกำหนดไว้ มันไม่ได้กำหนด? ฉันไม่คิดอย่างนั้น
Steve Summit

2
@SteveSummit ไม่มีสิ่งใดที่มีอยู่ปรัชญาไม่มีใครรู้เกี่ยวกับมันเลย if (x != NULL) *x = 42;สิ่งนี้ไม่ได้กำหนดเมื่อx == NULLใด ไม่แน่นอน พฤติกรรมที่ไม่ได้กำหนดไม่ได้เกิดขึ้นในส่วนของรหัสที่ไม่ได้ดำเนินการ พฤติกรรมของคำเป็นคำใบ้ รหัสที่ไม่ได้ดำเนินการไม่มีพฤติกรรมไม่ได้กำหนดหรืออย่างอื่น
n คำสรรพนาม 'm

1
@SteveSummit หากพฤติกรรมที่ไม่ได้กำหนดซึ่งไม่ได้ดำเนินการจะทำให้โปรแกรมทั้งหมดใช้ไม่ได้ไม่มีโปรแกรม C ใดที่จะมีพฤติกรรมที่กำหนดไว้ เพราะโปรแกรม C มักจะเป็นลูป / หากเงื่อนไขอยู่ห่างจากการรัน UB ใช้การทำซ้ำอาร์เรย์อย่างง่าย ๆ สำหรับตัวอย่าง: มันวนซ้ำจนกว่าการตรวจสอบที่ถูกผูกไว้จะล้มเหลว การดำเนินการวนซ้ำครั้งถัดไปจะเป็นพฤติกรรมที่ไม่ได้กำหนด แต่เนื่องจากไม่เคยดำเนินการเลยทุกอย่างก็ดี
cmaster - คืนสถานะโมนิ

3
ฉันจะไม่โต้แย้งเกี่ยวกับเรื่องนี้เพราะฉันไม่ได้เป็นทนายความด้านภาษา แต่ฉันเดิมพันถ้าคุณถามคำถามนี้และติดแท็กเป็นทนายความภาษาคุณอาจได้รับการอภิปรายที่มีชีวิตชีวา ทราบว่าเป็นในเชิงคุณภาพที่แตกต่างกว่าk=k++ *x=42หลังถูกกำหนดอย่างดีถ้าxเป็นตัวชี้ที่ถูกต้อง แต่ตัวเก่าไม่ได้กำหนดไม่ว่าอะไร (ฉันยอมรับว่าคุณอาจจะถูก แต่อีกครั้งฉันจะไม่เถียงกับมันและฉันกังวลมากขึ้นที่เราได้รับการควบคุมอย่างเชี่ยวชาญ)
Steve Summit

1
"ตัวหลังถูกกำหนดอย่างดีถ้า x เป็นตัวชี้ที่ถูกต้อง แต่ตัวหลังนั้นไม่ได้กำหนดไม่ว่าจะเกิดอะไรขึ้นก็ตาม" เฉพาะโปรแกรมทั้งหมดอาจมีพฤติกรรมที่ไม่ได้กำหนด แนวคิดนี้ใช้ไม่ได้กับการแยกส่วนของโค้ดที่ใช้แยก
n คำสรรพนาม 'm

3

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

+(+(k--))

ทั้งสองเอกผู้ประกอบการไม่ได้ทำอะไรเพื่อให้การแสดงออกตรงนี้เป็นเทียบเท่ากับ+ k--คนที่เขียนสิ่งนี้น่าจะพยายามยุ่งกับความคิดของคุณ

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.