Bagaimana kita bisa mencetak variabel size_t dengan mudah menggunakan keluarga printf?

405

Saya memiliki variabel tipe size_t, dan saya ingin mencetaknya menggunakan printf(). Penentu format apa yang saya gunakan untuk mencetaknya dengan mudah?

Di mesin 32-bit, %usepertinya benar. Saya kompilasi dengan g++ -g -W -Wall -Werror -ansi -pedantic, dan tidak ada peringatan. Tetapi ketika saya mengkompilasi kode itu dalam mesin 64-bit, itu menghasilkan peringatan.

size_t x = <something>;
printf("size = %u\n", x);

warning: format '%u' expects type 'unsigned int', 
    but argument 2 has type 'long unsigned int'

Peringatan akan hilang, seperti yang diharapkan, jika saya mengubahnya menjadi %lu.

Pertanyaannya adalah, bagaimana saya bisa menulis kode, sehingga mengkompilasi peringatan gratis pada mesin 32-dan 64-bit?

Sunting: Sebagai solusinya, saya kira satu jawaban mungkin untuk "melemparkan" variabel ke integer yang cukup besar, katakanlah unsigned long, dan cetak menggunakan %lu. Itu akan bekerja dalam kedua kasus. Saya mencari apakah ada ide lain.

Arun
sumber
4
casting ke unsigned longadalah pilihan terbaik jika implementasi libc Anda tidak mendukung zpengubah; standar C99 merekomendasikan size_tuntuk tidak memiliki peringkat konversi integer yang lebih besar daripada long, jadi Anda cukup aman
Christoph
kemungkinan duplikat penentu format Platform_t size independent dalam c?
maxschlepzig
1
Pada platform Windows size_t bisa lebih besar dari panjang. Untuk alasan kompatibilitas, panjang selalu 32-bit tetapi size_t dapat 64-bit. Jadi, casting ke unsigned long mungkin kehilangan setengah dari bit. Maaf :-)
Bruce Dawson

Jawaban:

483

Gunakan zpengubah:

size_t x = ...;
ssize_t y = ...;
printf("%zu\n", x);  // prints as unsigned decimal
printf("%zx\n", x);  // prints as hex
printf("%zd\n", y);  // prints as signed decimal
Adam Rosenfield
sumber
7
+1. Apakah ini tambahan C99 atau apakah ini berlaku untuk C ++ juga (saya tidak punya C90 berguna)?
avakar
6
ini merupakan tambahan C99 dan tidak ditampilkan dalam daftar printf()pengubah panjang konsep C ++ 0x dari 2009-11-09 (tabel 84 pada halaman 672)
Christoph
3
@Christoph: Tidak juga dalam konsep terbaru, n3035.
GManNickG
11
@avakar @Adam Rosenfield @Christoph @GMan: Namun, dalam n3035 §1.2 Referensi normatif, hanya standar C99 yang dirujuk, dan §17.6.1.2 / 3 dari negara yang sama "Fasilitas perpustakaan standar C disediakan." Saya akan menafsirkan ini berarti bahwa, kecuali ditentukan lain, segala sesuatu di perpustakaan standar C99 adalah bagian dari perpustakaan standar C ++ 0x, termasuk penentu format tambahan dalam C99.
James McNellis
9
@ArunSaha: Ini hanya fitur C99, bukan C ++. Jika Anda ingin mengkompilasinya -pedantic, Anda harus mendapatkan kompiler yang mendukung konsep C ++ 1x (sangat tidak mungkin), atau Anda harus memindahkan kode Anda ke file yang dikompilasi sebagai C99. Jika tidak, satu-satunya pilihan Anda adalah melemparkan variabel Anda ke unsigned long longdan gunakan %lluuntuk menjadi portabel secara maksimal.
Adam Rosenfield
88

Sepertinya itu bervariasi tergantung pada kompiler yang Anda gunakan (blech):

... dan tentu saja, jika Anda menggunakan C ++, Anda dapat menggunakannya coutsebagai gantinya yang disarankan oleh AraK .

TJ Crowder
sumber
3
zjuga didukung oleh newlib (yaitu cygwin)
Christoph
7
%zdsalah untuk size_t; itu benar untuk jenis bertanda tangan yang sesuai size_t, tetapi size_titu sendiri adalah jenis yang tidak ditandatangani.
Keith Thompson
1
@KeithThompson: Saya memang menyebutkan %zujuga (dan %zxkalau-kalau mereka ingin hex). Cukup benar yang %zuseharusnya menjadi yang pertama dalam daftar. Tetap.
TJ Crowder
10
@TJCrowder: Saya pikir %zdseharusnya tidak ada dalam daftar sama sekali. Saya tidak bisa memikirkan alasan untuk menggunakan %zddaripada %zumencetak size_tnilai. Bahkan tidak valid (memiliki perilaku yang tidak terdefinisi) jika nilainya melebihi SIZE_MAX / 2. (Untuk kelengkapan, Anda dapat menyebutkan %zountuk oktal.)
Keith Thompson
2
@FUZxxl: POSIX tidak mengharuskan ssize_tjenis yang ditandatangani sesuai size_t, jadi tidak dijamin cocok "%zd". ( Mungkin pada sebagian besar implementasi.) Pubs.opengroup.org/onlinepubs/9699919799/basedefs/…
Keith Thompson
59

Untuk C89, gunakan %ludan berikan nilainya ke unsigned long:

size_t foo;
...
printf("foo = %lu\n", (unsigned long) foo);

Untuk C99 dan yang lebih baru, gunakan %zu:

size_t foo;
...
printf("foo = %zu\n", foo);
John Bode
sumber
7
Mempertimbangkan 2013, sarankan "Untuk C99 dan selanjutnya" & "Untuk pra C99:". Jawaban Terbaik.
chux - Reinstate Monica
8
Jangan lakukan ini. Ini akan gagal pada 64 bit Windows di mana size_t adalah 64 bit dan panjangnya adalah 32 bit.
Yttrill
1
@ Ytrtrill: Lalu apa jawaban untuk windows 64-bit?
John Bode
1
@ JohnBode Mungkin unsigned long long?
James Ko
2
Atau: Anda bisa melakukan cast ke uint64_tdan kemudian menggunakan PRIu64makro dari inttypes.h, yang berisi penentu format.
James Ko
9

Memperluas jawaban Adam Rosenfield untuk Windows.

Saya menguji kode ini dengan pratinjau VS2013 Update 4 dan VS2015:

// test.c

#include <stdio.h>
#include <BaseTsd.h> // see the note below

int main()
{
    size_t x = 1;
    SSIZE_T y = 2;
    printf("%zu\n", x);  // prints as unsigned decimal
    printf("%zx\n", x);  // prints as hex
    printf("%zd\n", y);  // prints as signed decimal
    return 0;
}

VS2015 menghasilkan keluaran biner:

1
1
2

sedangkan yang dihasilkan oleh VS2013 mengatakan:

zu
zx
zd

Catatan: ssize_tadalah ekstensi POSIX dan SSIZE_Thal serupa di Windows Data Type , maka saya menambahkan <BaseTsd.h>referensi.

Selain itu, kecuali untuk header C99 / C11 ikuti, semua header C99 tersedia dalam pratinjau VS2015:

C11 - <stdalign.h>
C11 - <stdatomic.h>
C11 - <stdnoreturn.h>
C99 - <tgmath.h>
C11 - <threads.h>

Juga, C11 <uchar.h>sekarang termasuk dalam pratinjau terbaru.

Untuk detail lebih lanjut, lihat daftar lama dan baru ini untuk kesesuaian standar.

gagak vulcan
sumber
VS2013 Pembaruan 5 menghasilkan hasil yang sama dengan Pembaruan 4 memberi Anda.
Nathan Kidd
6

Bagi mereka yang berbicara tentang melakukan ini di C ++ yang tidak selalu mendukung ekstensi C99, maka saya sungguh-sungguh merekomendasikan boost :: format. Ini membuat pertanyaan ukuran size_t bisa diperdebatkan:

std::cout << boost::format("Sizeof(Var) is %d\n") % sizeof(Var);

Karena Anda tidak memerlukan penentu ukuran dalam boost :: format, Anda bisa khawatir tentang bagaimana Anda ingin menampilkan nilainya.

swestrup
sumber
4
Mungkin menginginkannya %u.
GManNickG
5
std::size_t s = 1024;
std::cout << s; // or any other kind of stream like stringstream!
AraK
sumber
8
Ya, tetapi si penanya meminta printfspecifier secara spesifik. Saya kira mereka memiliki beberapa kendala tidak tercatat yang membuat std::coutmasalah.
Donal Fellows
1
@ Donal Saya bertanya-tanya masalah seperti apa yang dapat dibuat aliran C ++ dalam proyek C ++!
AraK
9
@Ak. Mereka sangat lambat? Mereka menambahkan BANYAK byte karena tidak banyak alasan. ArunSaha hanya ingin tahu untuk pengetahuan pribadinya sendiri? Preferensi pribadi (saya lebih suka stdio daripada melakukan streaming sendiri). Ada banyak alasan.
KitsuneYMG
1
@TKCrowder: Ya, permintaan asli memang mengatakan bahwa solusi C diinginkan (melalui penandaan) dan ada alasan bagus untuk tidak menggunakan stream di C ++, misalnya, jika format output descriptor ditarik dari katalog pesan. (Anda bisa menulis parser untuk pesan dan menggunakan stream jika Anda mau, tapi itu banyak pekerjaan ketika Anda bisa memanfaatkan kode yang ada.)
Donal Fellows
1
@Donal: Tag-nya adalah C dan C ++. Saya sama sekali tidak mengadvokasi aliran I / O C ++ (saya bukan penggemar), hanya menunjukkan bahwa pertanyaannya bukan pada awalnya * "... tanyakan spesifikasi untuk printfspecifier."
TJ Crowder
5
printf("size = %zu\n", sizeof(thing) );
nategoose
sumber
2

Seperti yang dikatakan AraK, antarmuka c ++ stream akan selalu bekerja dengan baik.

std :: size_t s = 1024; std :: cout << s; // atau aliran lain seperti stringstream!

Jika Anda menginginkan C stdio, tidak ada jawaban portabel untuk ini untuk kasus "portabel" tertentu. Dan itu menjadi jelek karena seperti yang Anda lihat, memilih format bendera yang salah dapat menghasilkan peringatan kompiler atau memberikan output yang salah.

C99 mencoba menyelesaikan masalah ini dengan format inttypes.h seperti "%" PRIdMAX "\ n". Tetapi seperti halnya dengan "% zu", tidak semua orang mendukung c99 (seperti MSVS sebelum 2013). Ada file "msinttypes.h" yang beredar untuk menangani hal ini.

Jika Anda menggunakan tipe yang berbeda, bergantung pada flag, Anda mungkin mendapatkan peringatan kompilator untuk pemotongan atau perubahan tanda. Jika Anda memilih rute ini, pilih jenis ukuran tetap relevan yang lebih besar. Salah satu dari "% llu" panjang dan "llu" panjang yang tidak ditandatangani atau panjang harus bekerja, tetapi llu juga dapat memperlambat hal-hal di dunia 32bit sebagai terlalu besar. (Sunting - mac saya mengeluarkan peringatan dalam 64 bit untuk% llu tidak cocok dengan size_t, meskipun% lu,% llu, dan size_t semuanya berukuran sama. Dan% lu dan% llu bukan ukuran yang sama pada MSVS2012 saya. Jadi Anda mungkin perlu menggunakan + menggunakan format yang cocok.)

Untuk itu, Anda bisa menggunakan jenis ukuran tetap, seperti int64_t. Tapi tunggu! Sekarang kita kembali ke c99 / c ++ 11, dan MSVS yang lebih lama gagal lagi. Plus Anda juga memiliki gips (mis. Map.size () bukan tipe ukuran tetap)!

Anda bisa menggunakan header atau pustaka pihak ke-3 seperti boost. Jika Anda belum menggunakannya, Anda mungkin tidak ingin mengembang proyek Anda seperti itu. Jika Anda ingin menambahkan satu hanya untuk masalah ini, mengapa tidak menggunakan c ++ stream, atau kompilasi bersyarat?

Jadi Anda turun ke c ++ stream, kompilasi bersyarat, kerangka kerja pihak ke-3, atau semacam portable yang kebetulan bekerja untuk Anda.

Rick Berge
sumber
-1

Apakah ini akan memperingatkan Anda jika Anda meneruskan integer 32-bit yang tidak ditandatangani ke format% lu? Itu harus baik karena konversi didefinisikan dengan baik dan tidak kehilangan informasi apa pun.

Saya pernah mendengar bahwa beberapa platform mendefinisikan makro di <inttypes.h>mana Anda dapat memasukkan ke dalam format string literal tapi saya tidak melihat header itu di kompiler Windows C ++ saya, yang menyiratkan itu mungkin bukan cross-platform.

Kylotan
sumber
1
Kebanyakan kompiler tidak akan memperingatkan Anda jika Anda memasukkan sesuatu dengan ukuran yang salah ke printf. GCC adalah pengecualian. inttypes.h didefinisikan dalam C99 sehingga setiap kompiler C yang memenuhi persyaratan C99 akan memilikinya, yang seharusnya semuanya sekarang. Namun, Anda mungkin harus mengaktifkan C99 dengan flag kompiler. Bagaimanapun, intttypes.h tidak mendefinisikan format spesifik untuk size_t atau ptrdiff_t, karena mereka diputuskan cukup penting untuk mendapatkan penspesifikasi ukuran masing-masing 'z' dan 't' masing-masing.
swestrup
Jika Anda menggunakan %lu, Anda harus memberikan size_tnilainya unsigned long. Tidak ada konversi tersirat (selain promosi) untuk argumen printf.
Keith Thompson
-2

C99 mendefinisikan "% zd" dll untuk itu. (terima kasih kepada para komentator) Tidak ada specifier format portabel untuk itu di C ++ - Anda dapat menggunakan%p , yang kata woulkd dalam dua skenario ini, tetapi juga bukan pilihan portabel, dan memberikan nilai dalam hex.

Atau, gunakan streaming (misalnya stringstream) atau pengganti printf yang aman seperti Boost Format . Saya mengerti bahwa saran ini hanya untuk penggunaan terbatas (dan memang membutuhkan C ++). (Kami telah menggunakan pendekatan serupa yang sesuai dengan kebutuhan kami saat menerapkan dukungan unicode.)

Masalah mendasar untuk C adalah bahwa printf menggunakan ellipsis tidak aman secara desain - ia perlu menentukan ukuran argumen tambahan dari argumen yang diketahui, sehingga tidak dapat diperbaiki untuk mendukung "apa pun yang Anda dapatkan". Jadi kecuali jika kompiler Anda menerapkan beberapa ekstensi milik, Anda tidak beruntung.

Peterchen
sumber
2
yang zukuran modidfier standar C, tetapi beberapa implementasi libc terjebak pada tahun 1990 karena berbagai alasan (misalnya Microsoft pada dasarnya ditinggalkan C mendukung C ++ dan - baru-baru - C #)
Christoph
3
C99 mendefinisikan penentu ukuran 'z' menjadi ukuran nilai size_t, dan 't' menjadi ukuran nilai ptrdiff_t.
swestrup
2
%zdsalah, tidak ditandatangani sehingga seharusnya %zu.
David Conrad
-3

Pada beberapa platform dan untuk beberapa jenis ada specifiers konversi printf tertentu yang tersedia, tetapi kadang-kadang kita harus beralih ke tipe yang lebih besar.

Saya telah mendokumentasikan masalah rumit ini di sini, dengan kode contoh: http://www.pixelbeat.org/programming/gcc/int_types/ dan memperbaruinya secara berkala dengan info tentang platform dan tipe baru.

pixelbeat
sumber
1
Perhatikan bahwa jawaban hanya tautan tidak disarankan, jawaban SO harus menjadi titik akhir pencarian solusi (vs. persinggahan referensi lainnya, yang cenderung menjadi basi seiring waktu). Harap pertimbangkan untuk menambahkan sinopsis mandiri di sini, dengan menjaga tautan sebagai referensi.
kleopatra
-5

jika Anda ingin mencetak nilai size_t sebagai string, Anda bisa melakukan ini:

char text[] = "Lets go fishing in stead of sitting on our but !!";
size_t line = 2337200120702199116;

/* on windows I64x or I64d others %lld or %llx if it works %zd or %zx */
printf("number: %I64d\n",*(size_t*)&text);
printf("text: %s\n",*(char(*)[])&line);

hasilnya adalah:

nomor: 2337200120702199116

teks: Mari kita memancing bukannya duduk di atas tetapi !!

Sunting: membaca ulang pertanyaan karena suara turun saya perhatikan masalahnya bukan% llu atau% I64d tetapi tipe size_t pada mesin yang berbeda melihat pertanyaan ini https://stackoverflow.com/a/918909/1755797
http: // www. cplusplus.com/reference/cstdio/printf/

size_t adalah unsigned int pada mesin 32bit dan int longign unsigned pada 64bit
tapi% ll selalu mengharapkan int longign unsigned.

size_t bervariasi panjangnya pada sistem operasi yang berbeda sementara% llu adalah sama

Andre
sumber
4
Omong kosong apa ini ?!
Antti Haapala
casting 8 byte pertama dari array char ke 64bit panjang tanpa tanda yang ditandatangani melalui pointer size_t dan mencetaknya sebagai angka dengan printf% I64d tidak benar-benar spektakuler saya tahu, tentu saja saya tidak pada kode untuk mencegah tipe overflow tetapi itu bukan dalam lingkup pertanyaan.
Andre