การจัดเก็บอักขระ EOF (สิ้นสุดไฟล์) ในประเภท char


11

ผมอ่านในเดนนิสริตชี่ภาษา C Programmingหนังสือที่intจะต้องนำมาใช้สำหรับตัวแปรที่จะถือ EOF - เพื่อทำให้มันมีขนาดใหญ่พอที่จะสามารถเก็บค่า EOF - charไม่ได้ แต่รหัสต่อไปนี้ทำงานได้ดี:

#include<stdio.h> 

main()  { 
  char c; 
  c=getchar(); 
  while(c!=EOF)  { 
    putchar(c); 
    c=getchar(); 
  } 
} 

เมื่อไม่มีอินพุตให้getcharส่งคืน EOF และในโปรแกรมข้างต้นตัวแปรที่cมีประเภทถ่านสามารถเก็บไว้ได้สำเร็จ

ทำไมจึงใช้งานได้ ตามคำอธิบายในหนังสือข้างต้นรหัสไม่ควรทำงาน



5
0xffรหัสนี้มีแนวโน้มที่จะล้มเหลวถ้าคุณอ่านตัวอักษรที่มีค่า การจัดเก็บผลลัพธ์getchar()ในการintแก้ปัญหานั้น คำถามของคุณเหมือนกับคำถาม 12.1 ในคำถามที่พบบ่อย comp.lang.cซึ่งเป็นแหล่งข้อมูลที่ยอดเยี่ยม ( main()ควรจะเป็นint main(void)และจะไม่เจ็บที่จะเพิ่มreturn 0;ก่อนปิด})
Keith Thompson

1
@delnan: บทความที่เชื่อมโยงค่อนข้างไม่ถูกต้องเกี่ยวกับวิธีที่ Unix ปฏิบัติต่อ control-D ไม่ปิดอินพุตสตรีม มันเป็นสาเหตุให้ fread () ใด ๆ ที่กำลังบล็อกบนคอนโซลให้กลับมาทันทีพร้อมกับข้อมูลที่ยังไม่ได้อ่าน หลายโปรแกรมตีความกลับเป็นศูนย์ไบต์จาก fread () ตามที่ระบุ EOF แต่ในความเป็นจริงไฟล์จะยังคงเปิดอยู่และสามารถป้อนข้อมูลเพิ่มเติมได้
supercat

คำตอบ:


11

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

getchar()ส่งกลับค่าintด้วยค่าที่เหมาะกับช่วงunsigned charหรือEOF(ซึ่งต้องเป็นค่าลบโดยปกติคือ -1) โปรดทราบว่าEOFตัวเองไม่ใช่ตัวละคร แต่เป็นสัญญาณว่าไม่มีตัวละครมากขึ้น

เมื่อเก็บผลลัพธ์จากgetchar()ในcมีความเป็นไปได้สองอย่าง ทั้งสองชนิดสามารถเป็นตัวแทนของค่าซึ่งในกรณีที่มีค่าของchar cหรือชนิดchar ไม่สามารถแทนค่าได้ ในกรณีนั้นมันไม่ได้กำหนดว่าจะเกิดอะไรขึ้น โปรเซสเซอร์ของ Intel เพียงสับบิตสูงที่ไม่เหมาะกับรูปแบบใหม่ (ลดค่า modulo 256 สำหรับอย่างมีประสิทธิภาพchar) แต่คุณไม่ควรพึ่งพาสิ่งนั้น

ขั้นตอนต่อไปคือการเปรียบเทียบกับc EOFในฐานะที่EOFเป็นint, cจะถูกแปลงไปเป็นเช่นเดียวกับการรักษาค่าที่เก็บไว้ในint cหากcสามารถเก็บค่าของEOFแล้วเปรียบเทียบจะประสบความสำเร็จ แต่ถ้าcจะไม่เก็บค่าแล้วเปรียบเทียบจะล้มเหลวเพราะมีการสูญเสียเรียกคืนไม่ได้ข้อมูลขณะที่การแปลงชนิดEOFchar

ดูเหมือนว่าคอมไพเลอร์ของคุณเลือกที่จะทำให้charประเภทลงนามและค่าของพอขนาดเล็กที่จะพอดีEOF charหากcharไม่ได้ลงชื่อ (หรือถ้าคุณเคยใช้unsigned char) การทดสอบของคุณจะล้มเหลวเนื่องจากunsigned charไม่สามารถเก็บค่าEOFไว้ได้


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


การบังคับให้พิมพ์charค่าที่อยู่นอกช่วงCHAR_MIN.. CHAR_MAXจะต้องให้ค่าที่กำหนดให้กับการนำไปใช้, ให้รูปแบบบิตที่การนำไปใช้งานกำหนดเป็นการแทนแทร็บหรือเพิ่มสัญญาณที่กำหนดให้นำไปใช้ ในกรณีส่วนใหญ่การใช้งานจะต้องผ่านการทำงานพิเศษมากมายเพื่อทำสิ่งอื่นนอกเหนือจากการลดสองส่วน หากผู้คนในคณะกรรมการมาตรฐานได้สมัครรับความคิดว่าผู้รวบรวมควรได้รับการสนับสนุนให้ใช้พฤติกรรมที่สอดคล้องกับพฤติกรรมของผู้รวบรวมคนอื่น ๆ ส่วนใหญ่โดยที่ไม่มีเหตุผลที่จะทำเช่นนั้น ...
supercat

... ฉันจะถือว่าการข่มขู่เช่นนั้นน่าเชื่อถือ (ไม่ได้บอกว่ารหัสนั้นไม่ควรบันทึกความตั้งใจของมัน แต่(signed char)xมันควรจะมีความชัดเจนและปลอดภัยเท่า((unsigned char)x ^ CHAR_MAX+1))-(CHAR_MAX+1)ๆ กัน) อย่างที่มันเป็นฉันไม่เห็นโอกาสใด ๆ ของ คอมไพเลอร์ใช้พฤติกรรมอื่น ๆ ที่สอดคล้องกับมาตรฐานของวันนี้; สิ่งที่เป็นอันตรายอย่างหนึ่งคือมาตรฐานอาจเปลี่ยนไปเพื่อทำลายพฤติกรรมโดยคำนึงถึงประโยชน์ของ "การเพิ่มประสิทธิภาพ"
supercat

@supercat: มาตรฐานเขียนขึ้นโดยไม่ต้องคอมไพเลอร์ในการสร้างโค้ดที่มีพฤติกรรมที่โปรเซสเซอร์ไม่สนับสนุนตามเป้าหมาย พฤติกรรมส่วนใหญ่ที่ไม่ได้กำหนดมีเพราะ (ในขณะที่เขียนมาตรฐาน) โปรเซสเซอร์ไม่ได้ทำงานอย่างสม่ำเสมอ เมื่อคอมไพเลอร์เริ่มมีความเป็นผู้ใหญ่มากขึ้นผู้เขียนคอมไพเลอร์ก็เริ่มใช้ประโยชน์จากพฤติกรรมที่ไม่ได้กำหนดเพื่อให้เกิดประสิทธิภาพสูงสุด
Bart van Ingen Schenau

ในอดีตความตั้งใจของ Standard นั้นส่วนใหญ่จะเป็นตามที่คุณอธิบายถึงแม้ว่า Standard จะอธิบายพฤติกรรมบางอย่างในรายละเอียดที่เพียงพอว่าจะต้องใช้คอมไพเลอร์สำหรับแพลตฟอร์มทั่วไปบางแห่งในการสร้างโค้ดมากกว่าที่จะต้องใช้ภายใต้ข้อกำหนดที่หลวม การข่มขู่ประเภทint i=129; signed char c=i;เป็นหนึ่งในพฤติกรรมดังกล่าว ตัวประมวลผลค่อนข้างน้อยมีคำสั่งที่จะทำให้cเท่ากันiเมื่ออยู่ในช่วง -127 ถึง +127 และจะให้การแมปที่สอดคล้องกันของค่าอื่น ๆ ของiถึงค่าในช่วง -128 ถึง +127 ซึ่งแตกต่างจากการลดลงสองส่วนหรือ ..
supercat

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