Jawaban paling sederhana, dengan asumsi Anda tidak keberatan dengan keanehan dan variasi dalam format antara platform yang berbeda, adalah %p
notasi standar .
Standar C99 (ISO / IEC 9899: 1999) mengatakan dalam §7.19.6.1 ¶8:
p
Argumen harus menjadi penunjuk void
. Nilai pointer dikonversi ke urutan karakter pencetakan, dengan cara yang ditentukan implementasi.
(Dalam C11 - ISO / IEC 9899: 2011 - informasinya ada di §7.21.6.1 ¶8.)
Pada beberapa platform, itu akan mencakup yang terdepan 0x
dan yang lain tidak, dan huruf-hurufnya bisa dalam huruf kecil atau huruf besar, dan standar C bahkan tidak mendefinisikan bahwa itu akan menjadi output heksadesimal meskipun saya tahu tidak ada implementasi di mana tidak.
Ini agak terbuka untuk diperdebatkan apakah Anda harus secara eksplisit mengkonversi pointer dengan (void *)
pemain. Sedang eksplisit, yang biasanya baik (jadi itu yang saya lakukan), dan standar mengatakan 'argumen akan menjadi penunjuk ke void
'. Pada sebagian besar mesin, Anda bisa menghilangkan gips eksplisit. Namun, itu akan menjadi masalah pada mesin di mana representasi bit dari char *
alamat untuk lokasi memori yang diberikan berbeda dari pointer ' apa pun alamat ' ' untuk lokasi memori yang sama. Ini akan menjadi mesin yang dialamatkan kata, bukan byte yang dialamatkan. Mesin seperti itu tidak umum (mungkin tidak tersedia) hari ini, tetapi mesin pertama yang saya kerjakan setelah universitas adalah salah satunya (ICL Perq).
Jika Anda tidak puas dengan perilaku yang ditentukan implementasi %p
, gunakan C99 <inttypes.h>
dan uintptr_t
sebagai gantinya:
printf("0x%" PRIXPTR "\n", (uintptr_t)your_pointer);
Ini memungkinkan Anda menyempurnakan representasi agar sesuai dengan diri Anda. Saya memilih untuk memiliki angka heksa dalam huruf besar sehingga jumlahnya seragam tinggi yang sama dan kemiringan karakteristik pada awal 0xA1B2CDEF
muncul demikian, tidak seperti 0xa1b2cdef
yang naik turun di sepanjang nomor juga. Pilihan Anda, dalam batas yang sangat luas. Para (uintptr_t)
pemain tidak diragukan lagi direkomendasikan oleh GCC ketika dapat membaca string format pada waktu kompilasi. Saya pikir itu benar untuk meminta para pemeran, meskipun saya yakin ada beberapa yang akan mengabaikan peringatan dan lolos begitu saja sepanjang waktu.
Kerrek bertanya dalam komentar:
Saya agak bingung tentang promosi standar dan argumen variadik. Apakah semua petunjuk dipromosikan standar untuk dibatalkan *? Kalau tidak, jika int*
, katakanlah, dua byte, dan void*
4 byte, maka jelas akan menjadi kesalahan untuk membaca empat byte dari argumen, bukan?
Saya berada di bawah ilusi bahwa standar C mengatakan bahwa semua pointer objek harus memiliki ukuran yang sama, jadi void *
dan int *
tidak dapat menjadi ukuran yang berbeda. Namun, apa yang saya pikirkan adalah bagian yang relevan dari standar C99 tidak begitu tegas (meskipun saya tidak tahu implementasi di mana apa yang saya sarankan benar sebenarnya salah):
§6.2.5 Jenis
¶26 Suatu pointer ke void harus memiliki persyaratan representasi dan perataan yang sama dengan pointer ke tipe karakter. 39) Demikian pula, pointer ke versi yang memenuhi syarat atau tidak memenuhi syarat dari jenis yang kompatibel harus memiliki persyaratan representasi dan penyelarasan yang sama. Semua pointer ke tipe struktur harus memiliki persyaratan representasi dan perataan yang sama satu sama lain. Semua pointer ke tipe serikat harus memiliki persyaratan representasi dan penyelarasan yang sama satu sama lain. Pointer ke tipe lain tidak perlu memiliki representasi atau persyaratan penyelarasan yang sama.
39) Persyaratan representasi dan penyelarasan yang sama dimaksudkan untuk mengimplikasikan interchangeability sebagai argumen untuk fungsi, mengembalikan nilai dari fungsi, dan anggota serikat pekerja.
(C11 mengatakan persis sama di bagian §6.2.5, ¶28, dan catatan kaki 48.)
Jadi, semua pointer ke struktur harus memiliki ukuran yang sama satu sama lain, dan harus berbagi persyaratan pelurusan yang sama, meskipun struktur yang ditunjuk oleh pointer mungkin memiliki persyaratan pelurusan yang berbeda. Demikian pula untuk serikat pekerja. Pointer karakter dan pointer kosong harus memiliki ukuran dan persyaratan pelurusan yang sama. Pointer ke variasi pada int
(makna unsigned int
dan signed int
) harus memiliki ukuran dan persyaratan perataan yang sama satu sama lain; sama untuk jenis lainnya. Tetapi standar C tidak secara resmi mengatakan itu sizeof(int *) == sizeof(void *)
. Oh well, SO bagus untuk membuat Anda memeriksa asumsi Anda.
Standar C secara definitif tidak mensyaratkan pointer fungsi memiliki ukuran yang sama dengan pointer objek. Itu perlu untuk tidak merusak model memori yang berbeda pada sistem seperti DOS. Di sana Anda bisa memiliki pointer data 16-bit tetapi pointer fungsi 32-bit, atau sebaliknya. Inilah sebabnya mengapa standar C tidak mengamanatkan bahwa pointer fungsi dapat dikonversi ke pointer objek dan sebaliknya.
Untungnya (untuk pemrogram yang menargetkan POSIX), langkah-langkah POSIX ke dalam pelanggaran dan mengamanatkan bahwa fungsi pointer dan pointer data memiliki ukuran yang sama:
§2.12.3 Jenis Pointer
Semua tipe penunjuk fungsi harus memiliki representasi yang sama dengan penunjuk tipe untuk dibatalkan. Konversi dari pointer fungsi ke void *
tidak akan mengubah representasi. Sebuah void *
nilai yang dihasilkan dari konversi tersebut dapat dikonversi kembali ke jenis pointer fungsi asli, menggunakan cast yang eksplisit, tanpa kehilangan informasi.
Catatan: Standar ISO C tidak memerlukan ini, tetapi diperlukan untuk kesesuaian POSIX.
Jadi, sepertinya cast eksplisit void *
sangat disarankan untuk keandalan maksimum dalam kode ketika meneruskan pointer ke fungsi variadic seperti printf()
. Pada sistem POSIX, aman untuk melemparkan penunjuk fungsi ke penunjuk kosong untuk dicetak. Pada sistem lain, itu tidak selalu aman untuk melakukan itu, juga tidak selalu aman untuk melewati pointer selain void *
tanpa gips.
void*
? Kalau tidak, jikaint*
, katakanlah, dua byte, danvoid*
4 byte, maka jelas akan menjadi kesalahan untuk membaca empat byte dari argumen, bukan?dlsym()
fungsi. Suatu hari saya akan menulis perubahan ... tetapi 'satu hari' bukan 'hari ini'.void *
? Hmm saya melihat komentar Anda di sini . Karena hanya konversi satu-wat yang diperlukan (penunjuk fungsi kevoid *
), apakah itu berfungsi?void *
dan kembali tanpa kehilangan informasi. Secara pragmatis, ada beberapa mesin yang ukuran pointer fungsi tidak sama dengan ukuran pointer objek. Saya tidak berpikir standar menyediakan metode pencetakan pointer fungsi pada mesin di mana konversi itu bermasalah.p
adalah penentu konversi untuk mencetak pointer. Gunakan ini.Ingatlah bahwa menghilangkan para pemain adalah perilaku yang tidak terdefinisi dan bahwa pencetakan dengan
p
specifier konversi dilakukan dengan cara yang ditentukan implementasi.sumber
Gunakan
%p
, untuk "pointer", dan jangan gunakan yang lain *. Anda tidak dijamin oleh standar bahwa Anda diizinkan memperlakukan pointer seperti tipe integer tertentu, sehingga Anda akan benar-benar mendapatkan perilaku tidak terdefinisi dengan format integral. (Misalnya,%u
mengharapkanunsigned int
, tetapi bagaimana jikavoid*
memiliki ukuran atau persyaratan penyelarasan yang berbeda dariunsigned int
?)*) [Lihat jawaban baik-baik saja Jonathan!] Atau
%p
, Anda bisa menggunakan makro spesifik-pointer dari<inttypes.h>
, ditambahkan dalam C99.Semua pointer objek secara implisit dapat dikonversi ke
void*
dalam C, tetapi untuk meneruskan pointer sebagai argumen variadic, Anda harus melemparkannya secara eksplisit (karena pointer objek yang berubah-ubah hanya dapat dikonversi , tetapi tidak identik dengan void pointer):sumber
void *
(meskipun untukprintf()
Anda secara teknis membutuhkan pemeran eksplisit, karena itu adalah fungsi variadik). Pointer fungsi tidak harus dapat dikonversivoid *
.void *
dan kembali ke pointer fungsi tanpa kehilangan; Untungnya, POSIX memang secara eksplisit mensyaratkan itu (mencatat bahwa itu bukan bagian dari standar C). Jadi, dalam praktiknya, Anda bisa lolos begitu saja (mengonversivoid (*function)(void)
kevoid *
dan kembali kevoid (*function)(void)
), tetapi sebenarnya itu tidak diamanatkan oleh standar C.%u
!%u
dan%lu
salah pada semua mesin , bukan beberapa mesin. Spesifikasiprintf
sangat jelas bahwa ketika tipe yang dikirimkan tidak cocok dengan tipe yang diperlukan oleh penspesifikasi format, perilaku tidak terdefinisi. Apakah ukuran jenis cocok (yang bisa benar atau salah, tergantung pada mesin) tidak relevan; itu adalah tipe yang harus cocok, dan mereka tidak akan pernah.Sebagai alternatif dari jawaban lain (sangat baik), Anda dapat menggunakan
uintptr_t
atauintptr_t
(daristdint.h
/inttypes.h
) dan menggunakan specifier konversi integer yang sesuai. Ini akan memungkinkan lebih banyak fleksibilitas dalam bagaimana pointer diformat, tetapi secara tegas implementasi tidak diperlukan untuk memberikan typedef ini.sumber
#include <stdio.h> int main(void) { int p=9; int* m=&s; printf("%u",m); }
apakah itu perilaku tidak terdefinisi untuk mencetak alamat variabel menggunakan%u
penentu format? Alamat variabel dalam banyak kasus adalah positif, jadi bisakah saya menggunakan%u
bukan%p
?%u
adalah format untukunsigned int
tipe dan tidak dapat digunakan dengan argumen pointerprintf
.Anda dapat menggunakan
%x
atau%X
atau%p
; semuanya benar.%x
, alamat diberikan sebagai huruf kecil, misalnya:a3bfbc4
%X
, alamat diberikan sebagai huruf besar, misalnya:A3BFBC4
Keduanya benar.
Jika Anda menggunakan
%x
atau%X
mempertimbangkan enam posisi untuk alamat tersebut, dan jika Anda menggunakannya%p
mempertimbangkan delapan posisi untuk alamat tersebut. Sebagai contoh:sumber