ก่อนอื่นตามที่สัมผัสในคำตอบอื่น ๆ หลายคำ แต่ไม่ใช่ในใจของฉันสะกดชัดเจนเพียงพอ: ใช้งานได้เพื่อระบุจำนวนเต็มในบริบทส่วนใหญ่ที่ฟังก์ชันไลบรารีรับdouble
หรือfloat
อาร์กิวเมนต์ คอมไพเลอร์จะแทรกการแปลงโดยอัตโนมัติ ตัวอย่างเช่นsqrt(0)
มีการกำหนดไว้อย่างชัดเจนและจะทำงานเหมือนกันsqrt((double)0)
และเช่นเดียวกับนิพจน์ประเภทจำนวนเต็มอื่น ๆ ที่ใช้ที่นั่น
printf
แตกต่างกัน แตกต่างกันเพราะต้องใช้อาร์กิวเมนต์เป็นจำนวนตัวแปร ต้นแบบฟังก์ชันคือ
extern int printf(const char *fmt, ...);
ดังนั้นเมื่อคุณเขียน
printf(message, 0);
คอมไพเลอร์ไม่มีข้อมูลเกี่ยวกับประเภทที่printf
คาดว่าอาร์กิวเมนต์ที่สองจะเป็น มันมีเฉพาะประเภทของนิพจน์อาร์กิวเมนต์ซึ่งเป็นint
ไปตามนั้น ดังนั้นสิ่งที่แตกต่างจากฟังก์ชันไลบรารีส่วนใหญ่จะอยู่ที่คุณโปรแกรมเมอร์เพื่อให้แน่ใจว่ารายการอาร์กิวเมนต์ตรงกับความคาดหวังของสตริงรูปแบบ
(คอมไพเลอร์สมัยใหม่สามารถดูสตริงรูปแบบและบอกคุณว่าคุณมีประเภทที่ไม่ตรงกัน แต่จะไม่เริ่มแทรกการแปลงเพื่อให้บรรลุตามที่คุณหมายถึงเพราะโค้ดของคุณควรจะพังในตอนนี้เมื่อคุณสังเกตเห็น หลายปีต่อมาเมื่อสร้างใหม่ด้วยคอมไพเลอร์ที่มีประโยชน์น้อยกว่า)
ตอนนี้อีกครึ่งหนึ่งของคำถามคือ: ระบุว่า (int) 0 และ (float) 0.0 อยู่ในระบบที่ทันสมัยที่สุดทั้งคู่แสดงเป็น 32 บิตซึ่งทั้งหมดเป็นศูนย์ทำไมมันถึงไม่ทำงานโดยบังเอิญ? มาตรฐาน C บอกเพียงว่า "สิ่งนี้ไม่จำเป็นในการทำงานคุณอยู่คนเดียว" แต่ขอให้ฉันระบุสาเหตุที่พบบ่อยที่สุดสองประการที่ทำให้ไม่ได้ผล ซึ่งอาจช่วยให้คุณเข้าใจว่าเหตุใดจึงไม่จำเป็นต้องใช้
ครั้งแรกสำหรับเหตุผลทางประวัติศาสตร์เมื่อคุณผ่านการfloat
ผ่านรายการอาร์กิวเมนต์ตัวแปรที่จะได้รับการเลื่อนตำแหน่งไปdouble
ซึ่งในระบบที่ทันสมัยที่สุดเป็น64บิตกว้าง ดังนั้นจึงprintf("%f", 0)
ส่งผ่านเพียง 32 ศูนย์บิตไปยัง callee โดยคาดหวัง 64 ของพวกเขา
เหตุผลประการที่สองที่สำคัญไม่แพ้กันคืออาร์กิวเมนต์ของฟังก์ชันทศนิยมอาจถูกส่งไปในที่อื่นที่ไม่ใช่อาร์กิวเมนต์จำนวนเต็ม ตัวอย่างเช่นซีพียูส่วนใหญ่มีไฟล์รีจิสเตอร์แยกกันสำหรับจำนวนเต็มและค่าทศนิยมดังนั้นจึงอาจเป็นกฎที่อาร์กิวเมนต์ 0 ถึง 4 จะเข้าสู่การลงทะเบียน r0 ถึง r4 หากเป็นจำนวนเต็ม แต่ f0 ถึง f4 หากเป็นทศนิยม ดังนั้นprintf("%f", 0)
ดูใน register f1 สำหรับศูนย์นั้น แต่มันไม่มีเลย
printf
คาดหวังว่าจะได้รับdouble
และคุณกำลังให้ไฟล์int
.float
และint
อาจมีขนาดเท่ากันบนเครื่องของคุณ แต่0.0f
จริงๆแล้วจะถูกแปลงเป็นdouble
เมื่อผลักเข้าไปในรายการอาร์กิวเมนต์ตัวแปร (และprintf
คาดว่าจะเป็นเช่นนั้น) ในระยะสั้นคุณไม่ได้บรรลุจุดสิ้นสุดของการต่อรองโดยprintf
พิจารณาจากตัวระบุที่คุณใช้และข้อโต้แย้งที่คุณระบุ