ฉันควรใช้ตัวระบุรูปแบบใดเพื่อพิมพ์ที่อยู่ของตัวแปร ฉันสับสนระหว่างล็อตด้านล่าง
% u - จำนวนเต็มไม่ได้ลงนาม
% x - ค่าเลขฐานสิบหก
% p - ตัวชี้โมฆะ
รูปแบบใดที่เหมาะสมที่สุดในการพิมพ์ที่อยู่
ฉันควรใช้ตัวระบุรูปแบบใดเพื่อพิมพ์ที่อยู่ของตัวแปร ฉันสับสนระหว่างล็อตด้านล่าง
% u - จำนวนเต็มไม่ได้ลงนาม
% x - ค่าเลขฐานสิบหก
% p - ตัวชี้โมฆะ
รูปแบบใดที่เหมาะสมที่สุดในการพิมพ์ที่อยู่
คำตอบ:
คำตอบที่ง่ายที่สุดสมมติว่าคุณไม่คำนึงถึงความหลากหลายและรูปแบบในรูปแบบระหว่างแพลตฟอร์มที่แตกต่างกันคือ%p
สัญลักษณ์มาตรฐาน
มาตรฐาน C99 (ISO / IEC 9899: 1999) ระบุไว้ใน§7.19.6.1¶8:
p
void
อาร์กิวเมนต์ที่จะเป็นตัวชี้ไปยัง ค่าของตัวชี้ถูกแปลงเป็นลำดับของอักขระการพิมพ์ในลักษณะที่กำหนดการนำไปใช้งาน
(ใน C11 - ISO / IEC 9899: 2011 - ข้อมูลอยู่ใน§7.21.6.1¶8)
ในบางแพลตฟอร์มนั้นจะรวมถึงผู้นำ0x
และอื่น ๆ ซึ่งจะไม่มีและตัวอักษรอาจเป็นตัวพิมพ์เล็กหรือตัวใหญ่และมาตรฐาน C ไม่ได้กำหนดว่ามันจะเป็นผลลัพธ์เลขฐานสิบหกแม้ว่าฉันจะรู้ ไม่มีการติดตั้งใช้งาน
มันค่อนข้างเปิดให้อภิปรายว่าคุณควรแปลงพอยน์เตอร์ด้วยการ(void *)
ร่าย มันเป็นการชัดเจนซึ่งโดยปกติจะดี (ดังนั้นจึงเป็นสิ่งที่ฉันทำ) และมาตรฐานบอกว่า 'ข้อโต้แย้งจะเป็นตัวชี้ไปvoid
' ในเครื่องส่วนใหญ่คุณจะไม่ต้องสนใจกับนักแสดงที่ชัดเจน อย่างไรก็ตามมันจะสำคัญกับเครื่องที่การแสดงบิตของที่char *
อยู่สำหรับตำแหน่งหน่วยความจำที่กำหนดนั้นแตกต่างจากที่อยู่ ' สิ่งอื่นสำหรับตัวชี้ ' สำหรับตำแหน่งหน่วยความจำเดียวกัน นี่จะเป็นคำที่ได้รับการแก้ไขแทนที่จะเป็นแบบไบต์ เครื่องเหล่านี้ไม่ธรรมดา (อาจไม่มี) วันนี้ แต่เครื่องแรกที่ฉันทำงานหลังจากมหาวิทยาลัยเป็นหนึ่ง (ICL Perq)
ถ้าคุณไม่ได้มีความสุขกับพฤติกรรมการดำเนินงานที่กำหนดไว้%p
แล้วใช้ C99 <inttypes.h>
และuintptr_t
แทน:
printf("0x%" PRIXPTR "\n", (uintptr_t)your_pointer);
วิธีนี้ช่วยให้คุณปรับการแสดงที่เหมาะกับตัวคุณเองได้ ฉันเลือกที่จะมีเลขฐานสิบหกในตัวพิมพ์ใหญ่เพื่อให้ตัวเลขมีความสูงเท่ากันและมีลักษณะการจุ่มที่จุดเริ่มต้นของการ0xA1B2CDEF
ปรากฏดังนั้นไม่เหมือน0xa1b2cdef
ที่ dips ขึ้นและลงตามจำนวนเกินไป ทางเลือกของคุณแม้ว่าภายในขอบเขตที่กว้างมาก (uintptr_t)
หล่อขอแนะนำอย่างไม่น่าสงสัยโดย GCC เมื่อมันสามารถอ่านสตริงรูปแบบที่รวบรวมเวลา ฉันคิดว่ามันถูกต้องที่จะขอนักแสดง แต่ฉันแน่ใจว่ามีบางคนที่จะเพิกเฉยต่อคำเตือนและหลบไปกับมันเกือบตลอดเวลา
Kerrek ถามในความคิดเห็น:
ฉันสับสนเล็กน้อยเกี่ยวกับการเลื่อนระดับมาตรฐานและการโต้แย้งแบบแปรปรวน ตัวชี้ทั้งหมดได้รับการเลื่อนระดับเป็นโมฆะ * หรือไม่ มิฉะนั้นถ้า
int*
เคยพูดว่าสองไบต์และvoid*
เป็น 4 ไบต์แล้วมันจะชัดเจนว่าเป็นข้อผิดพลาดในการอ่านสี่ไบต์จากการโต้แย้งไม่ใช่?
ฉันถูกภายใต้ภาพลวงตาว่ามาตรฐาน C กล่าวว่าทุกตัวชี้วัตถุต้องเป็นขนาดเดียวกันดังนั้นvoid *
และint *
ไม่สามารถขนาดแตกต่างกัน อย่างไรก็ตามสิ่งที่ฉันคิดว่าเป็นส่วนที่เกี่ยวข้องของมาตรฐาน C99 นั้นไม่ได้เน้นย้ำมากนัก (แม้ว่าฉันจะไม่ทราบถึงการใช้งานในสิ่งที่ฉันแนะนำคือความจริงเป็นเท็จจริง ๆ ):
§6.2.5ประเภท
¶26ตัวชี้ไปที่โมฆะจะต้องมีการเป็นตัวแทนและความต้องการการจัดตำแหน่งเช่นเดียวกับตัวชี้ไปยังประเภทตัวละคร 39)ในทำนองเดียวกันพอยน์เตอร์สำหรับประเภทที่เข้ากันได้หรือไม่ผ่านการรับรองของประเภทที่เข้ากันได้จะต้องมีข้อกำหนดการเป็นตัวแทนและการจัดตำแหน่งเดียวกัน ตัวชี้ทั้งหมดไปยังประเภทโครงสร้างจะต้องมีการเป็นตัวแทนและความต้องการการจัดตำแหน่งเช่นเดียวกับแต่ละอื่น ๆ พอยน์เตอร์ทั้งหมดไปยังประเภทยูเนี่ยนจะต้องมีข้อกำหนดการเป็นตัวแทนและการจัดตำแหน่งที่เหมือนกัน ตัวชี้ไปยังประเภทอื่นไม่จำเป็นต้องมีข้อกำหนดการเป็นตัวแทนหรือการจัดตำแหน่งเดียวกัน
39)ข้อกำหนดการเป็นตัวแทนและการจัดตำแหน่งเดียวกันนั้นหมายถึงการบ่งบอกถึงความสามารถในการแลกเปลี่ยนซึ่งเป็นข้อโต้แย้งของฟังก์ชันค่าส่งคืนจากฟังก์ชันและสมาชิกของสหภาพ
(C11 พูดเหมือนกันในส่วน§6.2.5, ¶28และเชิงอรรถ 48)
ดังนั้นตัวชี้ไปยังโครงสร้างทั้งหมดต้องมีขนาดเท่ากันและต้องใช้ข้อกำหนดการจัดตำแหน่งเดียวกันแม้ว่าโครงสร้างที่ตัวชี้จะชี้อาจมีข้อกำหนดการจัดตำแหน่งที่แตกต่างกัน ในทำนองเดียวกันสำหรับสหภาพ พอยน์เตอร์พอยน์เตอร์และพอยน์เตอร์พอยน์เตอร์ต้องมีขนาดและข้อกำหนดการจัดตำแหน่งเดียวกัน ตัวชี้ไปยังการเปลี่ยนแปลงในint
(ความหมายunsigned int
และsigned int
) ต้องมีขนาดและข้อกำหนดการจัดตำแหน่งเดียวกัน ในทำนองเดียวกันสำหรับประเภทอื่น ๆ แต่มาตรฐาน C sizeof(int *) == sizeof(void *)
ไม่ได้อย่างเป็นทางการกล่าวว่า โอ้ดีดังนั้นจึงเป็นสิ่งที่ดีสำหรับการทำให้คุณตรวจสอบสมมติฐานของคุณ
มาตรฐาน C อย่างแน่นอนไม่ต้องการพอยน์เตอร์ของฟังก์ชั่นให้มีขนาดเท่ากันกับพอยน์เตอร์พอยน์เตอร์ นั่นเป็นสิ่งที่จำเป็นที่จะไม่ทำลายหน่วยความจำรุ่นต่างๆ คุณสามารถมีพอยน์เตอร์ข้อมูลแบบ 16 บิตได้ แต่พอยน์เตอร์ฟังก์ชั่น 32 บิตหรือในทางกลับกัน นี่คือเหตุผลที่มาตรฐาน C ไม่ได้บังคับให้พอยน์เตอร์ฟังก์ชันสามารถแปลงเป็นพอยน์เตอร์พอยน์เตอร์และในทางกลับกัน
โชคดีที่ (สำหรับโปรแกรมเมอร์ที่กำหนดเป้าหมาย POSIX) POSIX จะก้าวเข้าสู่การฝ่าฝืนและกำหนดหน้าที่ของพอยน์เตอร์และพอยน์เตอร์ของข้อมูลว่ามีขนาดเท่ากัน:
§2.12.3 ประเภทตัวชี้
ฟังก์ชั่นประเภทตัวชี้ทั้งหมดจะต้องมีการแสดงเช่นเดียวกับตัวชี้ประเภทที่จะเป็นโมฆะ การแปลงตัวชี้ฟังก์ชั่นที่
void *
จะไม่เปลี่ยนการเป็นตัวแทนvoid *
คุ้มค่าที่เกิดจากการแปลงดังกล่าวสามารถแปลงกลับไปที่เดิมประเภทตัวชี้ฟังก์ชั่นที่ใช้หล่ออย่างชัดเจนโดยไม่ต้องสูญเสียข้อมูลหมายเหตุ: มาตรฐาน ISO C ไม่ต้องการสิ่งนี้ แต่เป็นสิ่งจำเป็นสำหรับความสอดคล้อง POSIX
ดังนั้นจึงดูเหมือนว่าบรรยากาศที่ชัดเจนในการvoid *
เป็นอย่างมากแนะนำให้เลือกสำหรับความน่าเชื่อถือสูงสุดในรหัสเมื่อผ่านตัวชี้ไปยังฟังก์ชั่น variadic printf()
เช่น บนระบบ POSIX จะปลอดภัยที่จะใช้ตัวชี้ฟังก์ชันเป็นตัวชี้โมฆะสำหรับการพิมพ์ ในระบบอื่น ๆ มันไม่จำเป็นต้องปลอดภัยที่จะทำเช่นนั้นและไม่จำเป็นต้องปลอดภัยในการส่งพอยน์เตอร์ที่นอกเหนือจากที่void *
ไม่ได้รับการร่าย
dlsym()
ฟังก์ชันแทน วันหนึ่งฉันจะเขียนการเปลี่ยนแปลง ... แต่ 'วันหนึ่ง' ไม่ใช่ 'วันนี้'
void *
? อืมผมเห็นความคิดเห็นของคุณที่นี่ เนื่องจากต้องการการแปลงเพียงหนึ่งวัตต์ (ตัวชี้ฟังก์ชันเป็นvoid *
) จึงใช้งานได้หรือไม่
void *
และกลับโดยไม่สูญเสียข้อมูล ในทางปฏิบัติมีเครื่องน้อยมากที่ขนาดของตัวชี้ฟังก์ชั่นไม่เหมือนกับขนาดของตัวชี้วัตถุ ฉันไม่คิดว่ามาตรฐานจะให้วิธีการพิมพ์ตัวชี้ฟังก์ชั่นบนเครื่องที่การแปลงท่ามีปัญหา
p
เป็นตัวระบุการแปลงสำหรับพิมพ์พอยน์เตอร์ ใช้สิ่งนี้
int a = 42;
printf("%p\n", (void *) &a);
โปรดจำไว้ว่าการไม่ใช้งานการโยนนั้นเป็นพฤติกรรมที่ไม่ได้กำหนดและการพิมพ์ด้วยตัวp
ระบุการแปลงจะกระทำในลักษณะที่กำหนดโดยการนำไปปฏิบัติ
ใช้%p
สำหรับ "ตัวชี้" และอย่าใช้สิ่งอื่น * คุณไม่ได้รับการรับรองตามมาตรฐานที่คุณได้รับอนุญาตให้ใช้ตัวชี้เหมือนกับจำนวนเต็มใด ๆ ดังนั้นคุณจะได้รับพฤติกรรมที่ไม่ได้กำหนดด้วยรูปแบบที่สมบูรณ์ (ตัวอย่างเช่น%u
คาดว่าจะมีunsigned int
แต่จะเกิดอะไรขึ้นหากvoid*
มีข้อกำหนดขนาดหรือการจัดตำแหน่งที่แตกต่างกว่าunsigned int
)
*) [ดูคำตอบที่ดีของโจนาธาน!] หรือ%p
คุณสามารถใช้มาโครเฉพาะสำหรับตัวชี้จาก<inttypes.h>
เพิ่มใน C99
ทั้งหมดชี้วัตถุโดยปริยายแปลงvoid*
ใน C แต่เพื่อที่จะผ่านตัวชี้เป็นอาร์กิวเมนต์ variadic คุณต้องโยนมันทิ้งอย่างชัดเจน (ตั้งแต่ตัวชี้วัตถุพลเป็นเพียงแปลงสภาพแต่ไม่เหมือนกันที่จะชี้เป็นโมฆะ):
printf("x lives at %p.\n", (void*)&x);
void *
(แต่สำหรับprintf()
คุณแล้วในทางเทคนิคคุณจำเป็นต้องใช้แคสต์ที่ชัดเจนเนื่องจากเป็นฟังก์ชัน Variadic) void *
คำแนะนำการทำงานไม่จำเป็นต้องแปลงสภาพให้แก่
void *
และกลับเป็นพอยน์เตอร์ของฟังก์ชันได้โดยไม่สูญเสีย โชคดีที่ POSIX ต้องการสิ่งนั้นอย่างชัดเจน (สังเกตว่ามันไม่ได้เป็นส่วนหนึ่งของมาตรฐาน C) ดังนั้นในทางปฏิบัติคุณสามารถหลีกเลี่ยงได้ (แปลงvoid (*function)(void)
เป็นvoid *
กลับไปvoid (*function)(void)
) แต่อย่างเคร่งครัดไม่ได้รับคำสั่งจากมาตรฐาน C
%u
!
%u
และ%lu
เป็นสิ่งที่ผิดในทุกเครื่องไม่ใช่เครื่องบางอย่าง ข้อมูลจำเพาะของprintf
มีความชัดเจนมากว่าเมื่อประเภทที่ส่งผ่านไม่ตรงกับประเภทที่ระบุโดยตัวระบุรูปแบบพฤติกรรมจะไม่ได้กำหนด ไม่ว่าขนาดของประเภทที่ตรงกัน (ซึ่งอาจเป็นจริงหรือเท็จขึ้นอยู่กับเครื่อง) นั้นไม่เกี่ยวข้อง มันเป็นประเภทที่ต้องจับคู่และพวกเขาจะไม่ทำ
อีกทางเลือกหนึ่งสำหรับคำตอบอื่น ๆ (ดีมาก) คุณสามารถส่งไปยังuintptr_t
หรือintptr_t
(จากstdint.h
/ inttypes.h
) และใช้ตัวระบุการแปลงจำนวนเต็มที่สอดคล้องกัน สิ่งนี้จะช่วยให้มีความยืดหยุ่นมากขึ้นในการจัดรูปแบบตัวชี้ แต่การพูดอย่างเข้มงวดไม่จำเป็นต้องมีการใช้งานเพื่อให้เป็นตัวพิมพ์เหล่านี้
#include <stdio.h> int main(void) { int p=9; int* m=&s; printf("%u",m); }
พฤติกรรมที่ไม่ได้กำหนดเพื่อพิมพ์ที่อยู่ของตัวแปรโดยใช้ตัว%u
ระบุรูปแบบหรือไม่ ที่อยู่ของตัวแปรในกรณีส่วนใหญ่เป็นค่าบวกดังนั้นฉันจึงสามารถใช้%u
แทนได้%p
หรือไม่
%u
เป็นรูปแบบสำหรับชนิดและไม่สามารถใช้ร่วมกับข้อโต้แย้งที่ชี้ไปยังunsigned int
printf
คุณสามารถใช้%x
หรือ%X
หรือ%p
; ทั้งหมดถูกต้อง
%x
ที่อยู่จะได้รับเป็นตัวพิมพ์เล็กตัวอย่างเช่นa3bfbc4
%X
ที่อยู่จะได้รับเป็นตัวพิมพ์ใหญ่ตัวอย่างเช่น:A3BFBC4
ทั้งสองอย่างนี้ถูกต้อง
หากคุณใช้%x
หรือ%X
กำลังพิจารณาที่อยู่หกตำแหน่งสำหรับที่อยู่และหากคุณใช้งาน%p
จะพิจารณาตำแหน่งที่อยู่แปดตำแหน่งสำหรับที่อยู่ ตัวอย่างเช่น:
void*
หรือไม่? มิฉะนั้นถ้าint*
เคยพูดว่าสองไบต์และvoid*
เป็น 4 ไบต์แล้วมันจะชัดเจนว่าเป็นข้อผิดพลาดในการอ่านสี่ไบต์จากการโต้แย้งไม่ใช่?