Ko'rsatgichni "uintptr_t" ga almashtirishdan oldin "void *" ga o'zgartirish kerakmi yoki aksincha?

C99 standartidagi holatlarda 7.18.1.4 :

Quyidagi turdagi void ga tegishli har qanday ko'rsatgich shu turga aylantirilishi mumkin, keyin void va natija asl ko'rsatgich bilan teng taqqoslanadi:

     

uintriptr_t

ni bosing

Bu faqat void * turlarini asl nishonchaning qiymatini o'zgartirmasdan uintptr_t ga o'zgartirilishi mumkinligini anglatadimi?

Xususan, uintptr_t dan foydalanish uchun quyidagi kodni talab qilmoqchiman:

int foo = 42;
void * bar = &foo;
uintptr_t baz = bar;
void * qux = baz;
int quux = *(int *)qux; /* quux == foo == 42 */

Yoki ushbu sodda versiya C99 standarti bilan bir xil ta'sirga erishish uchun kafolatlangan bo'lsa:

int foo = 42;
uintptr_t bar = &foo;
int baz = *(int *)bar; /* baz == foo == 42 */

Pointerni uintptr_t ga almashtirishdan oldin void * ga o'tish kerakmi?

8
Ha, void * (yoki void * ning malakali versiyasi) uchun oraliq translyatsiya kerak.
qo'shib qo'ydi muallif Ian Abbott, manba
@IanAbbott Ushbu qidiruv qidiruvni void * ga talab qilish uchun standartning mantiqiy asoslarini bilasizmi?
qo'shib qo'ydi muallif Vilhelm Gray, manba

5 javoblar

Bundan tashqari, farq ob'ektiga har qanday markerni void * ga o'zgartirilishi mumkin bo'lsa, C funksiyasi ko'rsatgichlari void * <// code> va yana qaytib kelinglar!

Boshqa turdagi narsalarga ko'rsatadigan bo'lsak, C standartida har qanday belgisi bir tamsayıga aylantirilishi mumkin, va tamsayı har qanday markerga aylantirilishi mumkin, ammo bunday natijalar amalga oshirilishi mumkin. Standartda faqat void * faqat oldinga va orqaga konvertatsiya qilinadi, deb yozilgandan beri, avvalambor cast pointerni void * uchun eng xavfsiz tikishdir. uintptr_t ga o'zgartirilganda turli xil vakolatlarga ega bo'lgan ko'rsatgichlar boshqa tamsayı vakili ham keltirishi mumkin, shuning uchun quyidagicha bo'lishi mumkin:

int a;
uintptr_t up = (uintptr_t)&a;
void *p = (void *)up;
p == &a;//could be conceivably false, or it might be even that the value is indeterminate.
6
qo'shib qo'ydi

Standartlar mualliflari kompilyatorlarning bunday ishni bajarishlarini kutishgani uchun, ular kompilyatorlarning o'zini oqilona tutishi kerak deb o'ylamagan turli joylar mavjud.

Agar dasturda uintptr_t va p kodi belgilangan bo'lsa, void * q = (void * ) (uintptr_t) p; esa q == p tengligini taqqoslash. Standart q kodi p foydalanish mumkin bo'lgan har qanday maqsadlar uchun, yoki boshqa ko'rsatkichlar bilan taqqoslashdan boshqa har qanday maqsadlar uchun foydalanishga kafolat bermaydi.

Amaliy nuqtai nazardan, amalga oshirishda uintptr_t orqali o'zgartirishlarni har ikki tomonida void * dan/yoki/yoki kodirovkadan o'tishni talab qiladigan hech qanday sabab yo'q, lekin standart ilovalarni o'zboshimchalik bilan amalga oshirishga imkon beradi agar bunday qo'shimcha tashlanmalarni chiqarib yuborsa, moda. Boshqa tomondan, standart, amaliyotlar deyarli barcha amaliy holatlarda o'zboshimchalik bilan o'zini tutishlariga imkon beradi uintptr_t ni o'z ichiga oladigan bo'lsa, faqat bitta haqiqiy savol - bajaruvchilarga mutlaqo yo'l tutmaslikka tayanish kerakmi? Bunday ishonch, ba'zi tijoriy kompilyatorlar bilan oqilona bo'lishi mumkin, biroq ba'zi erkinliklar bilan emas.

2
qo'shib qo'ydi

Ha, void */ dan tovush kerak.

Ushbu paragrafning kodi void * parametr sifatida printf kodi uchun % p formati identifikatorining matniga o'xshash.

7.19.6.1-bo'limdan:

P Ushbu dalillar bekor qilish uchun marker bo'lishi kerak. Pointerning qiymati   bosma belgilar ketma-ketligiga aylanadi   amalga oshirishga mo'ljallangan usul.

Biroq, yuqorida ko'rsatilgan kamroq qidiruv parametrlarga ega bo'lishingiz mumkin:

int foo = 42;
uintptr_t baz = (void *)&foo;
int quux = *(int *)((void *)baz); /* quux == foo == 42 */
1
qo'shib qo'ydi
@AjayBrahmakshatriya muammosi ha, har qanday ko'rsatgichni aylantirishi mumkin, lekin faqat void * <=> (u) intptr_t ishlaydi ikkala tomon ham ...
qo'shib qo'ydi muallif Antti Haapala, manba
printf uchun kutilgan kod printf ning deklaratsiyasini ... . Ammo bu erda derleyici ochilganda kodni void * o'zgartirishi mumkinligini biladi. Shunday qilib, yopiq tirgak mumkin.
qo'shib qo'ydi muallif Ajay Brahmakshatriya, manba
@AnttiHaapala Men buni bilmayman. uintptr_t har qanday marker turiga erkin tarzda ko'chirilishi mumkin deb o'yladim. Rahmat
qo'shib qo'ydi muallif Ajay Brahmakshatriya, manba

Ha, bu uchun ko'chma bo'lishi uchun void * ga qidiruv o'tkazish kerak. Buning sababi shundaki, konvertatsiya qilish tamsayıni "amalga oshirish" deb nomlanadi, shuning uchun platformangiz uni hujjatlashtirgandan keyingina har qanday narsani amalga oshirishi mumkin.

BTW, sizning savolingizga "to'qimalarni" va "aylantirilishini" aralashtirasiz. "Konvertatsiya" - umumiy atama, "to'qimalar" - "aniq konvertatsiya".

1
qo'shib qo'ydi
@AjayBrahmakshatriya, yo'q, aytganlarimni aytaman. Sizning dastlabki kod zarrachalaringiz void * ga yopiq tarzda aylantirildi va shuning uchun bu yaxshi. Sizning ikkinchi parchadan faqat uintptr_t ga o'zgartirishlar kiritilgan bo'lib, void * emas, va shuning uchun u noto'g'ri.
qo'shib qo'ydi muallif Jens Gustedt, manba
"Tasvir" aniq ifodasidir. Agar siz birinchi jumlada void * -ga yozib olishni talab qilmoqchisiz?
qo'shib qo'ydi muallif Ajay Brahmakshatriya, manba
@JensGustedt emas, balki OP :)
qo'shib qo'ydi muallif Ajay Brahmakshatriya, manba
Buning sababi shundaki, asosan, uintptr_t tamsayı turi hisoblanadi, ya'ni dasturni aniqlangan xatti-harakatlarda konvertatsiya natijalarini aniqlovchi to'g'ridan-to'g'ri markerni bildiradi.
qo'shib qo'ydi muallif Vilhelm Gray, manba

Agar biz C belgisi haqida gapiradigan bo'lsak, ularning qanday vakillik qilayotgani haqida o'ylashimiz kerak. Asos sifatida ko'rsatgichning kattaligi me'moriyaga ishora qiladigan turiga bog'liq. void * qiymatining qiymati va uintptr_t qiymatining qiymati bir xil, biroq farqlanish boshqacha bo'lishi mumkin. Hujjatlarga muvofiq uintptr_t bo'lsa

Quyidagi turi belgisi bilan ishoratsiz tamsayı turini bildiradi   har qanday joriy ko'rsatgich bekor qilinadigan xususiyat ushbu turga o'tkazilishi mumkin,   keyin qaytib ko'rsatgichni bekor qilib, natijani taqqoslaydi   asl belgisi bilan teng: uintptr_t.

Bu shuni anglatadiki, bu turdagi ko'rsatkich markerni qiymatini ishonchli saqlash uchun ishlatilishi mumkin va u dasturning o'ziga xos tafsilotlari uchun unga teng, katta yoki hatto undan kamroq void * bo'lishi mumkin.

Ya'ni, void * ga o'tish orqali sarlavhalar uchun siz uintptr_t dan ishlashni joriy qilish kafolatlanganligini ta'minlaysiz.

0
qo'shib qo'ydi
"Aslida ko'rsatgichning kattaligi me'morchilikning turiga bog'liq emas" => bu noto'g'ri.
qo'shib qo'ydi muallif Antti Haapala, manba