İşaretçi veya adres yazdırmak için biçim belirteci doğru mu?


194

Bir değişkenin adresini yazdırmak için hangi format belirleyiciyi kullanmalıyım? Aşağıdaki parti arasında kafam karıştı.

% u - işaretsiz tam sayı

% x - onaltılık değer

% p - geçersiz işaretçi

Bir adres yazdırmak için en uygun biçim hangisidir?

Yanıtlar:


240

Farklı platformlar arasındaki vagaryerleri ve formattaki varyasyonları önemsemediğinizi varsayarsak, en basit cevap standart %pgösterimdir.

C99 standardı (ISO / IEC 9899: 1999) §7.19.6.1 ¶8'de diyor:

pArgüman bir gösterici olacaktır void. İşaretçinin değeri, uygulama tanımlı bir şekilde bir dizi yazdırma karakterine dönüştürülür.

(C11 - ISO / IEC 9899: 2011'de - bilgi §7.21.6.1 ¶8'de bulunmaktadır.)

Bazı platformlarda, bir satır aralığı içerecek 0xve bazılarında ise olmayacak ve harfler küçük veya büyük harf olabilir ve C standardı, bilmeme rağmen onaltılık çıktı olacağını bile tanımlamıyor olmadığı yerde uygulama yok.

İşaretçileri bir kadroyla açıkça dönüştürüp dönüştürmemeniz gerektiğini tartışmak biraz açıktır (void *). Açıktır, ki bu genellikle iyidir (bu yüzden yaptığım şeydir) ve standart 'argüman bir işaretçi olacaktır' der void. Çoğu makinede, açık bir kadroyu atlamaktan kurtulursunuz. Ancak, char *belirli bir bellek konumu için bir adresin bit sunumunun aynı bellek konumu için ' başka bir şey işaretçi ' adresinden farklı olduğu bir makinede önemli olacaktır . Bu, bayt adresli bir makine yerine bir sözcük adresli makine olacaktır. Bu tür makineler bugünlerde yaygın değil (muhtemelen mevcut değil), ancak üniversiteden sonra üzerinde çalıştığım ilk makine böyle biriydi (ICL Perq).

Uygulamasının tanımlı davranışından memnun değilseniz, %pC99 kullanın <inttypes.h>ve uintptr_tbunun yerine:

printf("0x%" PRIXPTR "\n", (uintptr_t)your_pointer);

Bu, temsilin kendinize uyacak şekilde ince ayar yapmanıza olanak tanır. Büyük harfle onaltılık basamakları seçtim, böylece sayı eşit olarak aynı yüksekliktedir ve başlangıcındaki karakteristik daldırma , sayı boyunca yukarı ve aşağı doğru daldığı 0xA1B2CDEFgibi görünmez 0xa1b2cdef. Seçiminiz olsa da, çok geniş sınırlar içinde. (uintptr_t)Derleme zamanında biçim dizesi okuması yokken dökme açıkça GCC tarafından tavsiye edilmektedir. Uyarıyı istemenin doğru olduğunu düşünüyorum, ancak uyarıyı görmezden gelip çoğu zaman ondan kaçacak olanlardan eminim.


Kerrek yorumlarda:

Standart promosyonlar ve değişken argümanlar hakkında biraz kafam karıştı. Tüm işaretçiler void * standardına yükseltilir mi? Aksi takdirde, int*diyelim ki, iki bayt ve void*4 bayt olsaydı, argümandan dört bayt okumak açıkça bir hata olurdu, değil mi?

Ben C standart tüm nesne işaretçileri yüzden, aynı boyutta olması gerektiğini söylüyor yanılsama altında void *ve int *farklı boyutlarda olamaz. Ancak, C99 standardının ilgili bölümü olduğunu düşündüğüm şey o kadar vurgulayıcı değil (yine de önerdiğim şeyin doğru olduğu bir uygulamayı bilmiyorum):

§6.2.5 Türler

¶26 Geçersiz bir işaretçi, bir karakter türüne işaretçi ile aynı temsil ve hizalama gereksinimlerine sahip olmalıdır. 39) Benzer şekilde, uyumlu tiplerin nitelikli veya niteliksiz versiyonlarına işaretçiler aynı temsil ve hizalama gerekliliklerine sahip olacaktır. Yapı türlerine yönelik tüm işaretçiler, birbirleriyle aynı temsil ve hizalama gereksinimlerine sahip olmalıdır. Birlik tiplerine yönelik tüm göstergeler, birbirleriyle aynı temsil ve hizalama gereksinimlerine sahip olacaktır. Diğer türlere işaretçiler aynı gösterim veya hizalama gereksinimlerine sahip olmak zorunda değildir.

39) Aynı temsil ve hizalama gereklilikleri, işlevlere bağımsız değişkenler, işlevlerden değer döndürme ve sendika üyeleri olarak değiştirilebilirlik anlamına gelir.

(C11, §6.2.5, ¶28 ve 48. dipnotta tamamen aynıdır.)

Dolayısıyla, yapılara yönelik tüm işaretçiler birbiriyle aynı boyutta olmalı ve işaretçilerin işaret ettiği yapılar farklı hizalama gereksinimlerine sahip olsa da aynı hizalama gereksinimlerini paylaşmalıdır. Sendikalar için de benzer şekilde. Karakter işaretçileri ve boş işaretçiler aynı boyut ve hizalama gereksinimlerine sahip olmalıdır. int(Anlam unsigned intve signed int) üzerindeki değişikliklere işaretçiler , birbirleriyle aynı boyut ve hizalama gereksinimlerine sahip olmalıdır; benzer şekilde diğer tipler için. Ancak C standardı resmi olarak bunu söylemez sizeof(int *) == sizeof(void *). Oh, SO varsayımlarınızı incelemeniz için iyidir.

C standardı, kesin olarak fonksiyon işaretleyicilerinin nesne işaretçileriyle aynı boyutta olmasını gerektirmez. DOS benzeri sistemlerde farklı bellek modellerini kırmamak gerekiyordu. Burada 16-bit veri işaretçileri olabilir, ancak 32-bit işlev işaretçileri olabilir veya tersi de geçerlidir. Bu nedenle C standardı, işlev işaretleyicilerinin nesne işaretleyicilerine dönüştürülebileceğini ve tam tersini zorunlu kılmaz.

Neyse ki (POSIX'i hedefleyen programcılar için), POSIX ihlale girer ve işlev işaretçileri ile veri işaretleyicilerinin aynı boyutta olmasını zorunlu kılar:

§2.12.3 İşaretçi Türleri

Tüm işlev işaretçisi türleri, geçersiz sayı işaretçisi ile aynı temsile sahip olmalıdır. Bir işlev işaretçisinin dönüşümü void *temsili değiştirmeyecektir. void *Böyle bir dönüşümden kaynaklanan bir değer, bilgi kaybı olmadan açık bir kadro kullanılarak orijinal işlev işaretçisi türüne geri dönüştürülebilir.

Not: ISO C standardı bunu gerektirmez, ancak POSIX uyumluluğu için gereklidir.

Bu nedenle, açık işaretlerin void *, bir işaretçi gibi değişken bir işleve geçerken koddaki maksimum güvenilirlik için şiddetle tavsiye edildiği görülmektedir printf(). POSIX sistemlerinde, bir işlev işaretçisini yazdırmak için bir boş işaretçiye atamak güvenlidir. Diğer sistemlerde, bunu yapmak her zaman güvenli değildir ve void *oyuncu olmadan başka işaretçiler geçirmek de güvenli değildir .


3
Standart promosyonlar ve değişken argümanlar hakkında biraz kafam karıştı. Tüm işaretçiler standart olarak tanıtılıyor void*mu? Aksi takdirde, int*diyelim ki, iki bayt ve void*4 bayt olsaydı, argümandan dört bayt okumak açıkça bir hata olurdu, değil mi?
Kerrek SB

POSIX'e (POSIX 2013) yapılan bir güncellemenin 2.12.3 bölümünü kaldırdığını ve dlsym()bunun yerine gereksinimlerin çoğunu işleve taşıdığını unutmayın . Bir gün değişikliği yazacağım ... ama 'bir gün' 'bugün' değil.
Jonathan Leffler

Bu cevap ayrıca işlev işaretçileri için de geçerli mi? Bunlar dönüştürülebilir void *mi? Hmm, yorumunuzu burada görüyorum . Yalnızca bir watt dönüştürme gerektiğinden (işlev işaretçisine void *), o zaman çalışır?
chux - Monica'yı eski durumuna döndür

@chux: Kesinlikle, cevap 'hayır', fakat pratikte cevap 'evet'. C standardı, işlev işaretleyicilerinin void *bilgi kaybı olmadan bir ve arkaya dönüştürülebileceğini garanti etmez . Pragmatik olarak, bir işlev işaretçisinin boyutunun bir nesne işaretçisinin boyutuyla aynı olmadığı çok az makine vardır. Standartın, dönüştürme işleminin sorunlu olduğu makinelerde bir işlev işaretçisi yazdırma yöntemi sunduğunu düşünmüyorum.
Jonathan Leffler

"ve bilgi kaybı olmadan geri" yazdırma ile ilgili değildir. Bu yardımcı olur mu?
chux - Monica'yı

50

pişaretçileri yazdırmak için dönüşüm belirtecidir. Bunu kullan.

int a = 42;

printf("%p\n", (void *) &a);

Oyuncuyu atlamanın tanımsız bir davranış olduğunu ve pdönüşüm belirleyici ile yazdırmanın uygulama tanımlı bir şekilde yapıldığını unutmayın.


2
Pardon, oyuncu kadrosunu atlamak neden "tanımsız davranış" tır? Bu, hangi değişkenin adresinin önemi var mı?
valdo

9
@valdo çünkü C diyor (C99, 7.19.6.1p8) "p Argüman geçersiz kılmak için bir işaretçi olacaktır."
ouah

12
@valdo: Tüm işaretçilerin aynı boyutta / temsilde olması zorunlu değildir.
caf

32

Kullanım %p"pointer" için, ve başka bir şey * kullanmayın. Bir işaretçiyi belirli bir tamsayı türü gibi tedavi etmenize izin verildiği standardı tarafından garanti edilmezsiniz, bu nedenle integral formatlarıyla tanımlanmamış davranışlar elde edersiniz. (Örneğin,%u bekler unsigned int, ancak void*farklı bir boyut veya hizalama gereksinimi varsa ne olur unsigned int?)

*) [Jonathan'ın iyi cevabına bakınız!] Alternatif olarak %p , sen yapabilirsiniz dan işaretçi özgü Makro kullanan <inttypes.h>C99 eklendi.

Tüm nesne işaretçileri örtük void*olarak C'ye dönüştürülebilir , ancak işaretçiyi değişken bir argüman olarak iletmek için, onu açıkça atmanız gerekir (keyfi nesne işaretçileri yalnızca dönüştürülebilir , ancak geçersiz işaretçilerle aynı değildir ):

printf("x lives at %p.\n", (void*)&x);

2
Tüm nesne işaretçileri dönüştürülebilir void *( printf()teknik olarak açık kadroya ihtiyacınız vardır, çünkü değişken bir işlevdir). İşlev işaretçileri mutlaka dönüştürülemez void *.
caf

@caf: Oh, varyasyon argümanlarını bilmiyordum - düzeltildi! Teşekkürler!
Kerrek SB

2
Standart C, void *fonksiyon göstergelerinin kayıp olmadan fonksiyon göstergesine dönüştürülmesini ve geri dönmesini gerektirmez ; neyse ki, POSIX açıkça gerektirir (standart C'nin bir parçası olmadığını belirtmek). Yani, uygulamada, siz (dönüştürme onunla uzak alabilirsiniz void (*function)(void)etmek void *ve geri void (*function)(void)), fakat kesinlikle o C standart tarafından zorunlu değildir.
Jonathan Leffler

2
Jonathan ve R. Burada ısrar etmem için biraz destek görmeyi tercih ederim %u!
Kerrek SB

2
%uve bazı makinelerde değil %lu, tüm makinelerde yanlış . 'Nin belirtimi, printfgeçirilen türün biçim belirtecinin gerektirdiği türle eşleşmediği zaman, davranışın tanımsız olduğu çok açıktır. Türlerin boyutunun eşleşip eşleşmediği (makineye bağlı olarak doğru veya yanlış olabilir) ilgisiz olup olmadığı; eşleşmesi gereken türler ve asla olmayacaklar.
R .. GitHub BUZA YARDIMCI OLMAK DURDUR

9

Diğer (çok iyi) cevaplara alternatif olarak , karşılık gelen tamsayı dönüşüm belirleyicilerini kullanabilir uintptr_tveya bunlardan intptr_t(gelen stdint.h/ inttypes.h) ve kullanabilirsiniz. Bu, işaretçinin biçimlendirilmesinde daha fazla esneklik sağlar, ancak bu typedef'leri sağlamak için kesinlikle bir uygulamaya gerek yoktur.


düşünün #include <stdio.h> int main(void) { int p=9; int* m=&s; printf("%u",m); } o kullanarak değişkenin adresini yazdırmak için tanımsız davranıştır %uBiçim belirteci? Değişkenin adresi çoğu durumda pozitiftir, %ubunun yerine kullanabilir miyim %p?
Yıkıcı

1
@Destructor: Hayır, yazım %uiçin bir biçimdir unsigned intve için işaretçi bağımsız değişkeni ile kullanılamaz printf.
R .. GitHub BUZA YARDIMCI DURDUR

-1

Sen kullanabilirsiniz %xya %Xya %p; hepsi doğru.

  • Eğer kullanırsanız %x, adres, örneğin küçük harflerle olarak verilir:a3bfbc4
  • Eğer kullanırsanız %X, örneğin, adres büyük harf olarak verilir:A3BFBC4

Her ikisi de doğrudur.

Kullanırsanız %xveya %Xadres için altı konum düşünürseniz ve kullanırsanız %padres için sekiz konum düşünür. Örneğin:


1
SO hoş geldiniz. Lütfen diğer yanıtları incelemek için biraz zaman ayırın, gözden kaçırdığınız bazı ayrıntıları açıkça açıklıyorlar.
AntoineL
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.