เป็นพฤติกรรมที่ไม่ได้กำหนดไว้ในการพิมพ์พอยน์เตอร์ว่างด้วยตัว%p
ระบุการแปลงหรือไม่
#include <stdio.h>
int main(void) {
void *p = NULL;
printf("%p", p);
return 0;
}
คำถามนี้ใช้กับมาตรฐาน C ไม่ใช่กับการใช้งาน C
เป็นพฤติกรรมที่ไม่ได้กำหนดไว้ในการพิมพ์พอยน์เตอร์ว่างด้วยตัว%p
ระบุการแปลงหรือไม่
#include <stdio.h>
int main(void) {
void *p = NULL;
printf("%p", p);
return 0;
}
คำถามนี้ใช้กับมาตรฐาน C ไม่ใช่กับการใช้งาน C
คำตอบ:
นี่เป็นหนึ่งในกรณีมุมแปลก ๆ ที่เราอยู่ภายใต้ข้อ จำกัด ของภาษาอังกฤษและโครงสร้างที่ไม่สอดคล้องกันในมาตรฐาน อย่างดีที่สุดฉันสามารถโต้แย้งโต้แย้งที่น่าสนใจได้เนื่องจากเป็นไปไม่ได้ที่จะพิสูจน์ :) 1
รหัสในคำถามแสดงพฤติกรรมที่กำหนดไว้อย่างชัดเจน
เนื่องจาก[7.1.4]เป็นพื้นฐานของคำถามมาเริ่มกันเลย:
แต่ละคำสั่งต่อไปนี้ใช้เว้นแต่จะระบุไว้อย่างชัดเจนเป็นอย่างอื่นในคำอธิบายโดยละเอียดที่ตามมา: หากอาร์กิวเมนต์ของฟังก์ชันมีค่าที่ไม่ถูกต้อง ( เช่นค่าภายนอกโดเมนของฟังก์ชันหรือตัวชี้ที่อยู่นอกพื้นที่ที่อยู่ของโปรแกรมหรือตัวชี้โมฆะ , [ ... ตัวอย่างอื่น ๆ ... ] ) [ ... ]ลักษณะการทำงานจะไม่ได้กำหนด [... ข้อความอื่น ๆ ... ]
นี่เป็นภาษาที่เงอะงะ การตีความอย่างหนึ่งคือรายการในรายการเป็น UB สำหรับฟังก์ชันไลบรารีทั้งหมดเว้นแต่จะถูกแทนที่โดยคำอธิบายแต่ละรายการ แต่รายการจะขึ้นต้นด้วย "เช่น" ซึ่งบ่งบอกว่าเป็นเพียงตัวอย่างเท่านั้น ตัวอย่างเช่นไม่ได้กล่าวถึงการยุติสตริงที่เป็นโมฆะที่ถูกต้อง (วิกฤตสำหรับพฤติกรรมของเช่นstrcpy
)
ดังนั้นจึงชัดเจนว่าเจตนา / ขอบเขตของ 7.1.4 คือ "ค่าที่ไม่ถูกต้อง" นำไปสู่ UB ( เว้นแต่จะระบุไว้เป็นอย่างอื่น ) เราต้องดูคำอธิบายของแต่ละฟังก์ชันเพื่อพิจารณาว่าค่าใดเป็น "ค่าที่ไม่ถูกต้อง"
strcpy
[7.21.2.3]พูดเพียงสิ่งนี้:
strcpy
สำเนาฟังก์ชั่นสตริงชี้ไปตามs2
(รวมถึงตัวละครยุติโมฆะ)s1
ลงในอาร์เรย์ที่ชี้ไปตาม หากการคัดลอกเกิดขึ้นระหว่างออบเจ็กต์ที่ทับซ้อนกันพฤติกรรมนั้นจะไม่ได้กำหนดไว้
ไม่มีการกล่าวถึงตัวชี้โมฆะอย่างชัดเจน แต่ก็ไม่มีการกล่าวถึงตัวบอกเลิกว่างด้วยเช่นกัน แทนที่จะอนุมานจาก "สตริงที่ชี้โดยs2
" ว่าค่าที่ถูกต้องเท่านั้นคือสตริง (เช่นพอยน์เตอร์ไปยังอาร์เรย์อักขระที่สิ้นสุดด้วยค่า null)
อันที่จริงรูปแบบนี้สามารถเห็นได้ตลอดทั้งคำอธิบายแต่ละรายการ ตัวอย่างอื่น ๆ :
[7.6.4.1 (fenv)]เก็บสภาพแวดล้อมจุดลอยตัวปัจจุบันในวัตถุชี้ไปโดย
envp
[7.12.6.4 (frexp)]เก็บจำนวนเต็มใน int ที่วัตถุชี้ไปโดย
exp
[7.19.5.1 (fclose)] กระแสชี้ไปโดย
stream
printf
[7.19.6.1]กล่าวเกี่ยวกับ%p
:
p
-void
ข้อโต้แย้งที่จะเป็นตัวชี้ไปยัง ค่าของตัวชี้จะถูกแปลงเป็นลำดับของอักขระการพิมพ์ในลักษณะที่กำหนดไว้สำหรับการนำไปใช้งาน
Null เป็นค่าตัวชี้ที่ถูกต้องและส่วนนี้จะไม่มีการกล่าวถึงอย่างชัดเจนว่า null เป็นกรณีพิเศษและตัวชี้จะต้องชี้ไปที่วัตถุ ดังนั้นจึงเป็นพฤติกรรมที่กำหนด
1. เว้นแต่ผู้เขียนมาตรฐานจะมาข้างหน้าหรือเว้นแต่เราจะพบสิ่งที่คล้ายกับเอกสารเหตุผลที่ชี้แจงสิ่งต่างๆ
ครับ . การพิมพ์ตัวชี้ค่าว่างด้วยตัว%p
ระบุการแปลงมีพฤติกรรมที่ไม่ได้กำหนดไว้ ต้องบอกว่าฉันไม่ทราบถึงการนำไปใช้งานที่เป็นไปตามข้อกำหนดที่มีอยู่ซึ่งจะเป็นการทำงานผิด
คำตอบใช้กับมาตรฐาน C (C89 / C99 / C11)
ตัว%p
ระบุการแปลงคาดว่าอาร์กิวเมนต์ของตัวชี้ชนิดจะเป็นโมฆะการแปลงตัวชี้เป็นอักขระที่พิมพ์ได้จะถูกกำหนดให้ใช้งานได้ ไม่ระบุว่าคาดว่าจะมีตัวชี้ค่าว่าง
การแนะนำฟังก์ชันไลบรารีมาตรฐานระบุว่าพอยน์เตอร์ว่างเป็นอาร์กิวเมนต์ของฟังก์ชัน (ไลบรารีมาตรฐาน) ถือว่าเป็นค่าที่ไม่ถูกต้องเว้นแต่จะระบุไว้เป็นอย่างอื่นอย่างชัดเจน
C99
/ C11
§7.1.4 p1
[... ] ถ้าอาร์กิวเมนต์ของฟังก์ชันมีค่าที่ไม่ถูกต้อง (เช่น [... ] ตัวชี้ค่าว่าง [... ] พฤติกรรมจะไม่ได้กำหนด
ตัวอย่างสำหรับฟังก์ชัน (ไลบรารีมาตรฐาน) ที่คาดว่าพอยน์เตอร์ว่างเป็นอาร์กิวเมนต์ที่ถูกต้อง:
fflush()
ใช้ตัวชี้โมฆะเพื่อล้าง "สตรีมทั้งหมด" (ที่เกี่ยวข้อง)freopen()
ใช้ตัวชี้ค่าว่างเพื่อระบุไฟล์ "เชื่อมโยงในปัจจุบัน" กับสตรีมsnprintf()
อนุญาตให้ส่งตัวชี้ค่าว่างเมื่อ 'n' เป็นศูนย์realloc()
ใช้ตัวชี้ค่าว่างสำหรับการจัดสรรวัตถุใหม่free()
อนุญาตให้ส่งตัวชี้โมฆะstrtok()
ใช้ตัวชี้ค่าว่างสำหรับการโทรในภายหลังหากเราใช้กรณีนี้snprintf()
มันสมเหตุสมผลแล้วที่จะอนุญาตให้ส่งตัวชี้ค่าว่างเมื่อ 'n' เป็นศูนย์ แต่นี่ไม่ใช่กรณีสำหรับฟังก์ชัน (ไลบรารีมาตรฐาน) อื่น ๆ ที่อนุญาตให้มีค่าเป็นศูนย์ 'n' ที่คล้ายกัน ตัวอย่างเช่น: memcpy()
, memmove()
, strncpy()
, ,memset()
memcmp()
ไม่เพียง แต่ระบุไว้ในบทนำสู่ไลบรารีมาตรฐาน แต่ยังรวมถึงการแนะนำฟังก์ชันเหล่านี้อีกครั้ง:
C99 §7.21.1 p2
/ C11 §7.24.1 p2
โดยที่อาร์กิวเมนต์ที่ประกาศเป็น
size_t
n ระบุความยาวของอาร์เรย์สำหรับฟังก์ชัน n สามารถมีค่าเป็นศูนย์ในการเรียกใช้ฟังก์ชันนั้น เว้นแต่จะระบุไว้อย่างชัดเจนเป็นอย่างอื่นในคำอธิบายของฟังก์ชันเฉพาะในข้อย่อยนี้อาร์กิวเมนต์ตัวชี้ในการเรียกดังกล่าวจะยังคงมีค่าที่ถูกต้องตามที่อธิบายไว้ใน 7.1.4
ฉันไม่รู้ว่า UB ของ%p
ตัวชี้ null นั้นเป็นความตั้งใจจริงหรือไม่ แต่เนื่องจากมาตรฐานระบุอย่างชัดเจนว่าตัวชี้ค่าว่างถือเป็นค่าที่ไม่ถูกต้องเป็นอาร์กิวเมนต์ของฟังก์ชันไลบรารีมาตรฐานจากนั้นจะไปและระบุกรณีที่เป็นโมฆะ ชี้เป็นอาร์กิวเมนต์ที่ถูกต้อง (snprintf, ฟรี, ฯลฯ ) และจากนั้นมันจะไปอีกครั้งและซ้ำความต้องการสำหรับข้อโต้แย้งที่จะสามารถใช้งานได้แม้ในศูนย์กรณี 'n' ( memcpy
, memmove
, memset
) แล้วฉันคิดว่ามันเป็นเหตุผลที่จะสมมติว่า คณะกรรมการมาตรฐาน C ไม่ได้กังวลกับการมีสิ่งเหล่านี้ที่ไม่ได้กำหนดไว้มากเกินไป
%p
ไม่ควรเป็นพฤติกรรมที่ไม่ได้กำหนด
ผู้เขียนมาตรฐาน C ไม่ได้พยายามที่จะแสดงรายการข้อกำหนดด้านพฤติกรรมทั้งหมดอย่างละเอียดถี่ถ้วนซึ่งการดำเนินการจะต้องเป็นไปตามเพื่อให้เหมาะสมกับวัตถุประสงค์เฉพาะใด ๆ แต่พวกเขาคาดหวังว่าคนที่เขียนคอมไพเลอร์จะใช้สามัญสำนึกระดับหนึ่งไม่ว่ามาตรฐานจะต้องการหรือไม่ก็ตาม
คำถามที่ว่าสิ่งที่เรียกร้อง UB นั้นแทบจะไม่มีหรือไม่มีประโยชน์ในตัวมันเอง คำถามสำคัญที่แท้จริงคือ:
คนที่พยายามเขียนคอมไพเลอร์ที่มีคุณภาพควรทำให้มันมีพฤติกรรมที่คาดเดาได้หรือไม่? สำหรับสถานการณ์ที่อธิบายคำตอบคือใช่
โปรแกรมเมอร์ควรมีสิทธิ์คาดหวังว่าคอมไพเลอร์ที่มีคุณภาพสำหรับสิ่งที่คล้ายกับแพลตฟอร์มปกติจะทำงานในลักษณะที่คาดเดาได้หรือไม่? ในสถานการณ์ที่อธิบายฉันจะบอกว่าคำตอบคือใช่
ผู้เขียนคอมไพเลอร์ป้านบางคนอาจยืดการตีความมาตรฐานเพื่อพิสูจน์ว่าทำอะไรแปลก ๆ หรือไม่? ฉันจะไม่หวัง แต่จะไม่ออกกฎ
ควรทำความสะอาดคอมไพเลอร์เกี่ยวกับพฤติกรรมหรือไม่? ขึ้นอยู่กับระดับความหวาดระแวงของผู้ใช้ คอมไพเลอร์ที่ทำความสะอาดอาจไม่ควรตั้งค่าเริ่มต้นในการพูดคุยเกี่ยวกับพฤติกรรมดังกล่าว แต่อาจมีตัวเลือกการกำหนดค่าให้ทำในกรณีที่โปรแกรมอาจถูกพอร์ตไปยังคอมไพเลอร์ที่ "ฉลาด" / เป็นใบ้ที่มีพฤติกรรมแปลก ๆ
หากการตีความมาตรฐานอย่างสมเหตุสมผลจะบ่งบอกถึงพฤติกรรมที่กำหนดไว้ แต่ผู้เขียนคอมไพเลอร์บางคนยืดการตีความเพื่อให้เหตุผลว่าทำอย่างอื่นมันมีความสำคัญกับสิ่งที่มาตรฐานกล่าวจริงหรือไม่?
printf("%p", (void*) 0)
พฤติกรรมที่ไม่ได้กำหนดหรือไม่ตามมาตรฐาน? การเรียกฟังก์ชันที่ซ้อนกันอย่างลึกซึ้งมีความเกี่ยวข้องกับราคาชาในประเทศจีน และใช่ UB เป็นเรื่องธรรมดามากในโปรแกรมจริง - มันคืออะไร?
%p
ความหมายที่เป็นไปได้ของคำถาม