Bir print_t değişkenini printf ailesini kullanarak taşınabilir biçimde nasıl yazdırabilirim?


404

Bir tür değişkenim var size_tve bunu kullanarak yazdırmak istiyorum printf(). Taşınabilir bir şekilde yazdırmak için hangi biçim belirticisini kullanırım?

32 bit makinede %udoğru görünüyor. Ben derledim g++ -g -W -Wall -Werror -ansi -pedanticve hiçbir uyarı yoktu. Ama bu kodu 64 bit makinede derlediğimde uyarı veriyor.

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

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

Uyarı, beklediğim gibi, ben bunu değiştirirsem gider %lu.

Soru, kodu hem 32 hem de 64 bit makinelerde ücretsiz uyarı derlemesi için nasıl yazabilirim?

Düzenleme: Geçici bir çözüm olarak, bir cevap "değişken" yeterince büyük bir tamsayı, demek unsigned longve kullanarak yazdırmak döküm olabilir sanırım %lu. Bu her iki durumda da işe yarar. Başka bir fikir olup olmadığına bakıyorum.


4
unsigned longlibc uygulamanız zdeğiştiriciyi desteklemiyorsa, döküm yapmak en iyi seçenektir ; C99 standardı size_t, tamsayı dönüşüm sıralamasının daha yüksek olmamasını önerir long, bu nedenle oldukça güvenlisiniz
Christoph



1
Windows platformunda size_t uzuntan büyük olabilir. Uyumluluk nedenleriyle uzun daima 32 bit, ancak size_t 64 bit olabilir. Bu nedenle, imzasız uzun süre döküm bitlerin yarısını kaybedebilir. Üzgünüm :-)
Bruce Dawson

Yanıtlar:


482

zDeğiştiriciyi kullanın :

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

7
+1. Bu bir C99 ilavesi mi yoksa C ++ için de geçerli mi (C90 kullanışlı değilim)?
avakar

6
bu bir C99 ekidir ve printf()2009-11-09 arasındaki C ++ 0x taslağının uzunluk değiştiricileri listesinde yer almamaktadır (sayfa 672, tablo 84)
Christoph

3
@Christoph: En son taslakta değil, n3035.
GManNickG

11
@avakar @Adam Rosenfield @Christoph @GMan: Bununla birlikte, n3035 §1.2 Normatif referanslarda sadece C99 standardına atıfta bulunulur ve aynı eyaletlerin §17.6.1.2 / 3'ü "C standart kütüphanesinin olanakları sağlanır." Bunu, aksi belirtilmedikçe, C99 standart kitaplığındaki her şeyin C99'daki ek format belirteçleri de dahil olmak üzere C ++ 0x standart kitaplığının bir parçası olduğu anlamına gelir .
James McNellis

9
@ArunSaha: Sadece C99'un bir özelliği, C ++ değil. Derlemesini istiyorsanız -pedantic, C ++ 1x taslağını destekleyen bir derleyici almanız gerekir (çok düşüktür) veya kodunuzu C99 olarak derlenmiş bir dosyaya taşımanız gerekir. Aksi takdirde, tek seçeneğiniz değişkenlerinizi maksimum taşınabilir yapmak unsigned long longve kullanmaktır %llu.
Adam Rosenfield

88

Görünüşe göre kullandığınız derleyiciye (blech) bağlı olarak değişir:

... ve elbette, C ++ kullanıyorsanız, coutbunun yerine AraK'nın önerdiği gibi kullanabilirsiniz .


3
zayrıca newlib (örn. cygwin) tarafından desteklenmektedir
Christoph

7
%zdiçin yanlış size_t; karşılık gelen işaretli tür için doğrudur size_t, ancak size_tkendisi imzalanmamış bir türdür.
Keith Thompson

1
@ KeithThompson: Ben de bahsetmiştim %zu(ve %zxonaltılık istedikleri durumda). %zuMuhtemelen listede ilk olması gereken yeterince doğru . Sabit.
TJ Crowder

10
@TJCrowder: %zdListede olması gerektiğini hiç sanmıyorum . Bir değer yazdırmak %zdyerine kullanmak için herhangi bir neden düşünemiyorum . Değer aşılırsa bile geçerli değildir (tanımlanmamış davranışa sahiptir) . (Tamlık için sekizlikten bahsedebilirsiniz .)%zusize_tSIZE_MAX / 2%zo
Keith Thompson

2
@FUZxxl: POSIX, ssize_tkarşılık gelen imzalı tür olmasını gerektirmediği size_tiçin eşleşmesi garanti edilmez "%zd". ( Muhtemelen çoğu uygulamadadır.) Pubs.opengroup.org/onlinepubs/9699919799/basedefs/…
Keith Thompson

59

C89 için, %ludeğeri şu şekilde kullanın ve yayınlayın unsigned long:

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

C99 ve üstü için %zuşunları kullanın :

size_t foo;
...
printf("foo = %zu\n", foo);

7
2013 göz önüne alındığında, "C99 ve sonrası için" & "C99 öncesi için:" önerisini yapın. En iyi cevap.
chux - Monica

8
Bunu yapma. Size_t 64 bit ve uzun 32 bit olan 64 bit Windows'ta başarısız olacaktır.
Yttrill

1
@Yttrill: Peki 64 bit pencerelerin cevabı nedir?
John Bode

1
@JohnBode Belki unsigned long long?
James Ko

2
Veya: biçim belirtecini içeren inttypes.h dosyasından bir makroya yayınlayabilir uint64_tve kullanabilirsiniz PRIu64.
James Ko

9

Adam Rosenfield'ın Windows cevabını genişletmek.

Bu kodu VS2013 Güncelleme 4 ve VS2015 önizlemesinde test ettim:

// 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 üretilen ikili çıkışlar:

1
1
2

VS2013 tarafından üretilen model şöyle diyor:

zu
zx
zd

Not: ssize_tPOSIX uzantısıdır ve Windows Veri TürlerindeSSIZE_T benzer bir şeydir , bu nedenle referans ekledim .<BaseTsd.h>

Ayrıca, aşağıdaki C99 / C11 başlıkları dışında tüm C99 başlıkları VS2015 önizlemesinde kullanılabilir:

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

Ayrıca, C11 en <uchar.h>son önizlemeye dahil edilmiştir.

Daha fazla ayrıntı için, standart uyumluluk için bu eski ve yeni listeye bakın.


VS2013 Güncelleme 5, Güncelleme 4 ile aynı sonuçları verir.
Nathan Kidd

6

Bunu mutlaka C99 uzantılarını desteklemeyen C ++ 'da yapanlar için, o zaman yürekten boost :: formatını tavsiye ederim. Bu, size_t tipinde soru sorusunu tartışır:

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

Boost :: formatında boyut belirleyicilerine ihtiyacınız olmadığından, değeri nasıl görüntülemek istediğinizi düşünebilirsiniz.


4
Muhtemelen %uo zaman istiyorum .
GManNickG

5
std::size_t s = 1024;
std::cout << s; // or any other kind of stream like stringstream!

8
Evet, ama soru soran, özellikle bir printfbelirteç ister . Sanırım std::coutbir problemin kullanılmasını sağlayan bazı başka kısıtlamalara sahiplerdi .
Donal Fellows

1
@ Donal C ++ projelerinde C ++ akışlarının ne tür bir sorun yaratabileceğini merak ediyorum!
AraK

9
@AraK. Çok yavaş mı? Çok fazla nedenden dolayı bir sürü bayt eklerler. ArunSaha sadece kendi kişisel bilgisini bilmek mi istiyor? Kişisel tercih (stdio'yu kendim izlemeyi tercih ederim). Bir çok neden var.
KitsuneYMG

1
@TKCrowder: Orijinal istek, bir C çözümünün (etiketleme yoluyla) istendiğini ve C ++ 'da akış kullanmamanın iyi nedenleri olduğunu söyledi, örneğin, çıktı biçimi tanımlayıcısı bir mesaj kataloğundan çekiliyorsa. (İsterseniz mesajlar için bir ayrıştırıcı yazabilir ve akışları kullanabilirsiniz, ancak mevcut koddan yararlanabileceğiniz çok iş var.)
Donal Fellows

1
@Donal: Etiketler C ve C ++ idi. Ben hiçbir şekilde C ++ 'ın I / O akışı şeyler savunmak değilim (Ben bir hayranı değilim), sadece sorunun başlangıçta * "... belirtmek için bir şartname isteyin değil işaret printf.
TJ Crowder


2

AraK'ın dediği gibi, c ++ akış arayüzü her zaman portatif olarak çalışacaktır.

std :: size_t s = 1024; std :: cout << s; // veya stringstream gibi başka herhangi bir akış!

C stdio istiyorsanız, bazı "taşınabilir" durumlar için buna taşınabilir bir cevap yoktur. Gördüğünüz gibi, yanlış format bayraklarını seçmek derleyici uyarısı verebilir veya yanlış çıktı verebilir.

C99 bu sorunu "%" PRIdMAX "\ n" gibi inttypes.h formatlarıyla çözmeye çalıştı. Ancak "% zu" ile olduğu gibi, herkes c99'u desteklemez (2013'ten önceki MSVS gibi). Bu sorunu çözmek için yüzen "msinttypes.h" dosyaları vardır.

Farklı bir türe döküm yaparsanız, bayraklara bağlı olarak kesme veya işaret değişikliği için derleyici uyarısı alabilirsiniz. Bu rotaya giderseniz, daha büyük bir ilgili sabit boyut türü seçin. İmzasız uzun uzun ve "% llu" veya imzasız uzun "% lu" dan biri çalışmalıdır, ancak llu ayrıca 32 bitlik bir dünyada işleri aşırı derecede büyük ölçüde yavaşlatabilir. (Düzenle - mac'um,% lu,% llu ve size_t öğelerinin tümü aynı boyutta olmasına rağmen% llu için size_t ile eşleşmeyen 64 bitlik bir uyarı veriyor. MSVS2012'de% lu ve% llu aynı boyutta değil. eşleşen bir biçimi yayınlamanız + kullanmanız gerekebilir.)

Bu nedenle, int64_t gibi sabit boyut türleriyle gidebilirsiniz. Fakat bekle! Şimdi c99 / c ++ 11'e geri döndük ve eski MSVS tekrar başarısız oluyor. Ayrıca, yayınlarınız da var (ör. Map.size () sabit boyutlu bir tür değildir)!

3. taraf üstbilgisi veya boost gibi bir kitaplık kullanabilirsiniz. Zaten bir tane kullanmıyorsanız, projenizi bu şekilde şişirmek istemeyebilirsiniz. Sadece bu sorun için bir tane eklemek istiyorsanız, neden c ++ akışları veya koşullu derleme kullanmıyorsunuz?

Yani c ++ akışlarına, koşullu derlemeye, 3. taraf çerçevelerine veya sizin için işe yarayacak taşınabilir bir şeye bağlısınız.


-1

% Lu biçimine 32 bit işaretsiz bir tam sayı iletirseniz sizi uyarır mı? Dönüşüm iyi tanımlandığından ve hiçbir bilgi kaybetmediğinden iyi olur.

Bazı platformların <inttypes.h>biçim dizesi değişmezine ekleyebileceğiniz makroları tanımladığını duydum, ancak Windows C ++ derleyicimde bu üstbilgiyi çapraz platform olmayabilir.


1
Çoğu derleyici printf içine yanlış boyutta bir şey iletirseniz sizi uyarmaz. GCC bir istisnadır. inttypes.h, C99'da tanımlanmıştır, bu yüzden C99 uyumlu olan herhangi bir C derleyicisi buna sahip olacaktır, ki bu şimdiye kadar hepsi olmalıdır. Yine de, derleyici bayrağıyla C99'u açmanız gerekebilir. Her durumda, intttypes.h, sırasıyla 'z' ve 't' kendi boyut belirleyicilerini alacak kadar önemli oldukları kararlaştırıldığından, size_t veya ptrdiff_t için belirli bir biçim tanımlamaz.
Mart'ta

Eğer kullanırsanız %lu, size_tdeğeri şuna çevirmelisiniz unsigned long. İçin bağımsız değişkenler için örtülü dönüştürme (tanıtımlar dışında) yoktur printf.
Keith Thompson

-2

C99 bunun için "% zd" vb. Tanımlar. (Yorum yapanlar sayesinde) C ++ bunun için taşınabilir biçim belirteci yoktur - verebilir kullanmak%p , bu iki senaryoda da kelime woulkd ama taşınabilir bir seçim değildir ya ve onaltılık değerini verir.

Alternatif olarak, bazı akış (örn. Stringstream) veya Boost Format gibi güvenli bir printf değiştirme kullanın . Bu tavsiyenin sadece sınırlı kullanım olduğunu (ve C ++ gerektirdiğini) anlıyorum. (Unicode desteği uygularken ihtiyaçlarımıza uygun benzer bir yaklaşım kullandık.)

C için temel sorun, bir üç nokta kullanan printf'in tasarım açısından güvensiz olmasıdır - bilinen argümanlardan ek argümanın boyutunu belirlemesi gerekir, bu nedenle "ne olursa olsun" ı destekleyecek şekilde düzeltilemez. Derleyiciniz bazı özel uzantıları uygulamazsa, şansınız kalmaz.


2
zboyut modidfier standart C, ancak bazı libc uygulamaları çeşitli nedenlerle 1990 yılında sıkışmış (C # C lehine örneğin Microsoft temelde terk C ++ ve - - son zamanlarda)
Christoph

3
C99, "z" boyut belirtecini bir size_t değerinin boyutu ve "t" yi bir ptrdiff_t değerinin boyutu olarak tanımladı.
Mart'ta

2
%zdyanlış, imzasız olduğu için olması gerekir %zu.
David Conrad

-3

Bazı platformlarda ve bazı türler için belirli printf dönüşüm belirteçleri vardır, ancak bazen daha büyük türlere döküme başvurmak gerekir.

Bu zor sorunu burada örnek kodla belgeledim: http://www.pixelbeat.org/programming/gcc/int_types/ ve yeni platformlar ve türler hakkındaki bilgilerle periyodik olarak güncelleyin.


1
Yalnızca bağlantı yanıtlarının cesaretinin kırıldığına dikkat edin, SO yanıtları bir çözüm arayışının son noktası olmalıdır (zaman içinde bayatlama eğilimi gösteren başka bir referans noktasıyla karşılaştırıldığında). Lütfen bağlantıyı referans olarak tutarak bağımsız bir özet eklemeyi düşünün.
kleopatra

-5

size_t değerini dize olarak yazdırmak istiyorsanız bunu yapabilirsiniz:

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);

sonuç:

numarası: 2337200120702199116

metin: Bizim yerine oturma yerine balığa gidelim ama !!

Düzenleme: aşağı oylar nedeniyle soruyu yeniden okuma ben onun sorunu% llu veya% I64d değil ama farklı makinelerde size_t türü bu soruya bakın https://stackoverflow.com/a/918909/1755797
http: // www. cplusplus.com/reference/cstdio/printf/

size_t 32bit makinede imzasız int ve 64bit'te imzasız uzun uzun int
ama% ll her zaman imzasız uzun uzun int bekler.

size_t, farklı işletim sistemlerinde uzunluk olarak farklılık gösterirken% llu aynı


4
Bu ne saçmalık ?!
Antti Haapala

char_ dizisinin ilk 8 baytını imzasız uzun uzun bir 64 bit'e size_t işaretçisi aracılığıyla döküm ve printf% I64d ile sayı olarak yazdırmak gerçekten muhteşem değil biliyorum, tabii ki tip taşmasını önlemek için kodda yoktu ama bu sorunun kapsamı dışındadır.
Andre
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.