Yanıtlar:
Farklı platformlar arasındaki vagaryerleri ve formattaki varyasyonları önemsemediğinizi varsayarsak, en basit cevap standart %p
gösterimdir.
C99 standardı (ISO / IEC 9899: 1999) §7.19.6.1 ¶8'de diyor:
p
Argüman bir gösterici olacaktırvoid
. İş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 0x
ve 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, %p
C99 kullanın <inttypes.h>
ve uintptr_t
bunun 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ığı 0xA1B2CDEF
gibi 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 vevoid*
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 int
ve 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 .
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.
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?
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.
p
iş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 p
dönüşüm belirleyici ile yazdırmanın uygulama tanımlı bir şekilde yapıldığını unutmayın.
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);
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 *
.
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.
%u
!
%u
ve bazı makinelerde değil %lu
, tüm makinelerde yanlış . 'Nin belirtimi, printf
geç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.
Diğer (çok iyi) cevaplara alternatif olarak , karşılık gelen tamsayı dönüşüm belirleyicilerini kullanabilir uintptr_t
veya 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.
#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 %u
Biçim belirteci? Değişkenin adresi çoğu durumda pozitiftir, %u
bunun yerine kullanabilir miyim %p
?
%u
için bir biçimdir unsigned int
ve için işaretçi bağımsız değişkeni ile kullanılamaz printf
.
Sen kullanabilirsiniz %x
ya %X
ya %p
; hepsi doğru.
%x
, adres, örneğin küçük harflerle olarak verilir:a3bfbc4
%X
, örneğin, adres büyük harf olarak verilir:A3BFBC4
Her ikisi de doğrudur.
Kullanırsanız %x
veya %X
adres için altı konum düşünürseniz ve kullanırsanız %p
adres için sekiz konum düşünür. Örneğin:
void*
mu? Aksi takdirde,int*
diyelim ki, iki bayt vevoid*
4 bayt olsaydı, argümandan dört bayt okumak açıkça bir hata olurdu, değil mi?