ชนิดข้อมูล uintptr_t คืออะไร


252

มันคืออะไรuintptr_tและใช้ทำอะไรได้บ้าง?


5
รายละเอียดเพิ่มเติมเกี่ยวกับประเภทนี้รวมถึงประเภทอื่น ๆ ที่เกี่ยวข้องมีอยู่ที่นี่: opengroup.org/onlinepubs/000095399/basedefs/stdint.h.html
Void

en.cppreference.com/w/cpp/types/integerรายการstd::uintptr_tและstd::intptr_tตัวเลือก C ++ 11
alfC

คำตอบ:


199

uintptr_tเป็นชนิดจำนวนเต็มที่ไม่ได้ลงนามที่สามารถเก็บตัวชี้ข้อมูลได้ ซึ่งโดยทั่วไปหมายความว่ามีขนาดเท่ากับตัวชี้

มันถูกกำหนดเป็นทางเลือกใน C ++ 11 และมาตรฐานในภายหลัง

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

แก้ไข: โปรดทราบว่า Steve Jessop มีรายละเอียดเพิ่มเติมที่น่าสนใจ (ซึ่งฉันจะไม่ขโมย) ในคำตอบอื่นที่นี่สำหรับคุณประเภทที่คลั่งไคล้ :)


53
โปรดทราบว่าsize_tจะต้องมีเพียงพอที่จะเก็บขนาดที่ใหญ่ที่สุดของวัตถุและอาจมีขนาดเล็กกว่าตัวชี้ นี้คาดว่าจะมีสถาปัตยกรรมแบ่งเช่น 8086 (16 บิตsize_tแต่ 32 บิตvoid*)
MSalters

3
ptrdiff_tสำหรับตัวแทนของความแตกต่างระหว่างสองตัวชี้ที่คุณมี uintptr_tไม่ได้มีไว้สำหรับสิ่งนั้น
jalf

3
@jalf: สำหรับความแตกต่างใช่ แต่สำหรับระยะทางคุณจะต้องการประเภทที่ไม่ได้ลงนาม
Drew Dormann

คำจำกัดความ uintptr_t เห็นได้ชัดว่าไม่ได้บังคับ (ดังนั้นไม่ได้มาตรฐาน) แม้ใน C ++ 11! cplusplus.com/reference/cstdint (ฉันได้รับคำใบ้จากคำตอบของ Steve Jessop)
Antonio

2
@MarcusJ unsigned intมักจะไม่ใหญ่พอ แต่มันอาจมีขนาดใหญ่พอ ประเภทนี้มีอยู่โดยเฉพาะเพื่อลบ"สมมติ"ทั้งหมด
Drew Dormann

239

สิ่งแรกเวลาที่ถามคำถามuintptr_tไม่ได้อยู่ใน C ++ มันอยู่ใน C99 ใน<stdint.h>เป็นอุปกรณ์เสริม คอมไพเลอร์ C ++ 03 จำนวนมากให้ไฟล์นั้น นอกจากนี้ยังอยู่ใน C ++ 11 <cstdint>ที่ซึ่งเป็นตัวเลือกอีกครั้งและหมายถึง C99 สำหรับคำจำกัดความ

ใน C99 มันถูกกำหนดเป็น "ชนิดจำนวนเต็มที่ไม่ได้ลงชื่อพร้อมคุณสมบัติที่ตัวชี้ที่ถูกต้องใด ๆ เป็นโมฆะสามารถแปลงเป็นประเภทนี้จากนั้นแปลงกลับเป็นตัวชี้เป็นโมฆะและผลลัพธ์จะเปรียบเทียบเท่ากับตัวชี้ดั้งเดิม"

ใช้สิ่งนี้เพื่อหมายถึงสิ่งที่พูด มันไม่ได้พูดอะไรเกี่ยวกับขนาด

uintptr_tvoid*อาจจะมีขนาดเดียวกับ มันอาจใหญ่กว่านี้ มันอาจจะมีขนาดเล็กลงแม้ว่าการใช้งาน C ++ เช่นนั้นจะผิดเพี้ยนไป ตัวอย่างเช่นบนแพลตฟอร์มสมมุติบางตัวที่void*มี 32 บิต แต่ใช้พื้นที่ที่อยู่เสมือนเพียง 24 บิตคุณอาจมี 24 บิตuintptr_tที่ตรงกับความต้องการ ฉันไม่รู้ว่าทำไมการนำไปใช้จะทำเช่นนั้น แต่มาตรฐานอนุญาต


8
ขอบคุณสำหรับ "<stdint.h>" แอปพลิเคชันของฉันไม่ได้รวบรวมเนื่องจากมีการประกาศ uintptr_t แต่เมื่อฉันอ่านความคิดเห็นของคุณฉันเพิ่ม "#include <stdint.h>" และใช่ตอนนี้มันใช้งานได้ ขอบคุณ!
JavaRunner

2
ยกตัวอย่างเช่นการจัดสรรหน่วยความจำที่เหมาะสม
ตำนาน2k

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

3
@JoelCunningham: ว่างาน แต่ไม่ได้จริงๆใด ๆ void*ที่แตกต่างจากการใช้ มันมีผลต่อทิศทางในอนาคตที่เป็นไปได้โดยเฉพาะอย่างยิ่งถ้าคุณอาจต้องการที่จะเปลี่ยนการใช้สิ่งที่เป็นเพียงการจัดการจำนวนเต็มไม่ใช่ตัวชี้ที่แปลงแล้ว
Steve Jessop

2
@CiroSantilli 烏坎事件 2016 六四事件法轮功กรณีการใช้งานทั่วไปคือการส่งเพียงแค่ int ไปยัง API ซึ่งคาดว่าจะเป็นโมฆะ * ไปยังข้อมูลทั่วไป typedef struct { int whyAmIDoingThis; } SeriouslyTooLong; SeriouslyTooLong whyAmNotDoneYet; whyAmINotDoneYet.whyAmIDoingThis = val; callback.dataPtr = &whyAmINotDoneYet;ช่วยให้คุณประหยัดการกดแป้นพิมพ์ แทน: callback.dataPtr = (void*)val. ในอีกด้านหนึ่งคุณแน่นอนจะได้รับและมีการโยนมันกลับไปvoid* int
Francesco Dondi

19

มันเป็นจำนวนเต็มชนิดที่ไม่ได้ลงนามตรงขนาดของตัวชี้ เมื่อใดก็ตามที่คุณต้องทำสิ่งผิดปกติด้วยตัวชี้ - เช่นกลับด้านบิตทั้งหมด (อย่าถามว่าทำไม) คุณโยนมันลงไปuintptr_tและจัดการมันให้เป็นจำนวนเต็มปกติจากนั้นก็โยนกลับ


6
แน่นอนคุณสามารถทำได้ แต่แน่นอนว่ามันจะเป็นพฤติกรรมที่ไม่ได้กำหนด ฉันเชื่อว่าสิ่งเดียวที่คุณสามารถทำได้กับผลลัพธ์ของการส่งไปยัง uintptr_t คือการส่งผ่านไปโดยไม่เปลี่ยนแปลงและส่งกลับ - ทุกสิ่งทุกอย่างคือ UB
sleske

มีบางครั้งที่คุณต้องเล่นกับบิตและมันจะสร้างข้อผิดพลาดคอมไพเลอร์ ตัวอย่างทั่วไปคือการบังคับใช้หน่วยความจำที่จัดตำแหน่งแบบ 16 ไบต์สำหรับแอปพลิเคชั่นที่สำคัญต่อวิดีโอและประสิทธิภาพ stackoverflow.com/questions/227897/…
Cloud

3
@sleske ที่ไม่เป็นความจริง บนเครื่องที่มีการจัดประเภทด้วยตนเองบิตที่มีนัยสำคัญอย่างน้อยสองบิตของตัวชี้จะเป็นศูนย์ (เนื่องจากที่อยู่เป็นทวีคูณของ 4 หรือ 8) ฉันเคยเห็นโปรแกรมที่ใช้ประโยชน์จากข้อมูลนี้ในการบีบอัดข้อมูล ..
saadtaame

3
@saadtaame: ฉันเพียงแค่ชี้ให้เห็นว่านี้จะ UB ตามมาตรฐานซี นั่นไม่ได้หมายความว่ามันไม่สามารถกำหนดได้ในบางระบบ - คอมไพเลอร์และรันไทม์มีอิสระที่จะกำหนดพฤติกรรมที่เฉพาะเจาะจงสำหรับบางสิ่งที่เป็น UB ในมาตรฐาน C ดังนั้นจึงไม่มีความขัดแย้ง :-)
sleske

2
ไม่จำเป็นต้องมีขนาดเท่าของตัวชี้ การรับประกันมาตรฐานทั้งหมดคือการแปลงvoid*ค่าตัวชี้ไปเป็นuintptr_tกลับมาอีกครั้งจะให้void*ค่าที่เปรียบเทียบเท่ากับตัวชี้ดั้งเดิม uintptr_tมักจะมีขนาดเท่าvoid*กัน แต่ไม่รับประกันและไม่มีการรับประกันใด ๆ ว่าบิตของค่าที่แปลงมีความหมายเฉพาะใด ๆ และไม่รับประกันว่าจะสามารถเก็บค่าตัวชี้ไปยังฟังก์ชันที่แปลงแล้วโดยไม่สูญเสียข้อมูล ในที่สุดก็ไม่รับประกันว่าจะมีอยู่
Keith Thompson

15

มีคำตอบที่ดีมากมายในส่วน "สิ่งที่เป็น uintptr_t ชนิดข้อมูล" ฉันจะพยายามพูดถึง "สิ่งที่สามารถใช้เพื่ออะไร" มีส่วนร่วมในโพสต์นี้

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

ดังนั้นเพื่อที่จะทำการดำเนินการระดับบิตบนพอยน์เตอร์คุณจะต้องส่งพอยน์เตอร์เพื่อพิมพ์ unitpr_t แล้วจึงทำการดำเนินการระดับบิต

นี่คือตัวอย่างของฟังก์ชั่นที่ฉันเพิ่งเขียนให้ทำ bitwise exclusive หรือ 2 pointers เพื่อเก็บไว้ใน XOR linked list เพื่อให้เราสามารถท่องไปในทั้งสองทิศทางเหมือนกับรายการที่เชื่อมโยงทวีคูณ แต่ไม่มีโทษของการเก็บ 2 พอยน์เตอร์ในแต่ละโหนด .

 template <typename T>
 T* xor_ptrs(T* t1, T* t2)
 {
     return reinterpret_cast<T*>(reinterpret_cast<uintptr_t>(t1)^reinterpret_cast<uintptr_t>(t2));
  }

1
นอกจากเลขคณิตบิตมันก็ดีถ้าคุณต้องการให้ซีแมนทิกส์อิงตามที่อยู่แทนการนับวัตถุ
อเล็กซ์

4

การเสี่ยงต่อการได้รับตรา Necromancer อีกครั้งฉันต้องการเพิ่มการใช้งานที่ดีมากสำหรับ uintptr_t (หรือแม้กระทั่ง intptr_t) และนั่นเป็นการเขียนโค้ดแบบฝังที่สามารถทดสอบได้ ฉันเขียนโค้ดที่ฝังตัวส่วนใหญ่มุ่งไปที่แขนหลายแขนงและตัวประมวลผลแบบดึงในปัจจุบัน สิ่งเหล่านี้มีความกว้างของบัสเนทีฟต่าง ๆ และแรงดึงเป็นสถาปัตยกรรมฮาร์วาร์ดที่มีโค้ดแยกต่างหากและบัสข้อมูลที่มีความกว้างต่างกัน ฉันใช้รูปแบบการพัฒนาที่ขับเคลื่อนด้วยการทดสอบสำหรับรหัสส่วนใหญ่ของฉันซึ่งหมายความว่าฉันทำการทดสอบหน่วยสำหรับหน่วยรหัสทั้งหมดที่ฉันเขียน การทดสอบหน่วยบนฮาร์ดแวร์เป้าหมายที่เกิดขึ้นจริงเป็นเรื่องยุ่งยากดังนั้นโดยทั่วไปฉันเขียนทุกอย่างบนพีซีที่ใช้ Intel ทั้งใน Windows หรือ Linux โดยใช้ Ceedling และ GCC ที่ถูกกล่าวว่ารหัสฝังตัวจำนวนมากที่เกี่ยวข้องกับการ twiddling บิตและการจัดการที่อยู่ เครื่อง Intel ส่วนใหญ่ของฉันคือ 64 บิต ดังนั้นหากคุณจะทดสอบรหัสการจัดการที่อยู่คุณต้องมีวัตถุทั่วไปที่จะทำคณิตศาสตร์ ดังนั้น uintptr_t จะให้วิธีที่เป็นอิสระในการดีบักรหัสของคุณกับเครื่องก่อนที่คุณจะลองปรับใช้กับฮาร์ดแวร์เป้าหมาย ปัญหาอีกประการหนึ่งสำหรับเครื่องบางรุ่นหรือแม้แต่รุ่นหน่วยความจำในคอมไพเลอร์บางตัวชี้ฟังก์ชั่นและพอยน์เตอร์ข้อมูลมีความกว้างต่างกัน บนเครื่องเหล่านั้นคอมไพเลอร์อาจไม่อนุญาตให้ทำการแคสต์ระหว่างสองคลาส แต่ uintptr_t ควรจะสามารถเก็บไว้ได้เช่นกัน


ไม่แน่ใจว่าเกี่ยวข้องหรือไม่ ... คุณลอง "oped typedefs" หรือไม่? Vide: youtube.com/watch?v=jLdSjh8oqmE
shycha

@sleske ฉันหวังว่าจะมีให้ใน C. แต่การมี stdint.h นั้นดีกว่าไม่มีอะไรเลย (ขอให้นับจำนวนที่พิมพ์ได้ดีกว่า แต่ผู้ debugger ส่วนใหญ่ทำงานได้ดีในการหาทาง)
bd2357
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.