Come si può stampare una variabile size_t in modo portabile usando la famiglia printf?


405

Ho una variabile di tipo size_te voglio stamparla usando printf(). Quale identificatore di formato devo usare per stamparlo in modo portabile?

Nella macchina a 32 bit, %usembra giusto. Ho compilato g++ -g -W -Wall -Werror -ansi -pedantice non ci sono stati avvisi. Ma quando compilo quel codice in una macchina a 64 bit, viene generato un avviso.

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

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

L'avvertimento scompare, come previsto, se lo cambio a %lu.

La domanda è: come posso scrivere il codice, in modo che venga compilato senza avvisi su macchine a 32 e 64 bit?

Modifica: Per ovviare al problema, suppongo che una risposta potrebbe essere quella di "trasmettere" la variabile in un numero intero abbastanza grande, diciamo unsigned long, e stamparlo usando %lu. Funzionerebbe in entrambi i casi. Sto cercando se c'è qualche altra idea.


4
casting to unsigned longè l'opzione migliore se l'implementazione di libc non supporta il zmodificatore; lo standard C99 raccomanda di size_tnon avere un rango di conversione intero maggiore di long, quindi sei ragionevolmente sicuro
Christoph



1
Sulla piattaforma Windows size_t può essere più grande che lungo. Per motivi di compatibilità, long è sempre a 32 bit ma size_t può essere a 64 bit. Quindi, lanciare a lungo senza segno può perdere la metà dei bit. Siamo spiacenti :-)
Bruce Dawson il

Risposte:


483

Usa il zmodificatore:

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. È un'aggiunta C99 o si applica anche a C ++ (non ho C90 a portata di mano)?
avakar,

6
è un'aggiunta C99 e non è presente nell'elenco dei printf()modificatori di lunghezza della bozza C ++ 0x del 2009-11-09 (tabella 84 a pagina 672)
Christoph

3
@Christoph: Né è nell'ultima bozza, n3035.
GManNickG,

11
@avakar @Adam Rosenfield @Christoph @GMan: Tuttavia, in n3035 §1.2 Riferimenti normativi, viene fatto riferimento solo allo standard C99 e §17.6.1.2 / 3 degli stessi stati "Vengono fornite le strutture della libreria standard C." Interpreterei questo per dire che, se non diversamente specificato, tutto nella libreria standard C99 fa parte della libreria standard C ++ 0x, inclusi gli identificatori di formato aggiuntivi in ​​C99.
James McNellis,

9
@ArunSaha: è una funzionalità di solo C99, non C ++. Se vuoi che si compili -pedantic, dovrai ottenere un compilatore che supporti la bozza C ++ 1x (altamente improbabile), oppure dovrai spostare il tuo codice in un file compilato come C99. Altrimenti, la tua unica opzione è quella di trasmettere le tue variabili unsigned long longe utilizzarle %lluper essere al massimo portatile.
Adam Rosenfield,

88

Sembra che varia a seconda del compilatore che stai utilizzando (blech):

... e ovviamente, se usi C ++, puoi usare coutinvece come suggerito da AraK .


3
zè anche supportato da newlib (ovvero cygwin)
Christoph,

7
%zdnon è corretto per size_t; è corretto per il tipo con segno corrispondente size_t, ma size_tè di per sé un tipo senza segno.
Keith Thompson,

1
@KeithThompson: ho anche menzionato %zu(e %zxnel caso in cui vogliono un hex). Abbastanza vero che %zuprobabilmente avrebbe dovuto essere il primo nella lista. Fisso.
TJ Crowder,

10
@TJCrowder: non credo che %zddovrebbe essere nella lista. Non riesco a pensare a nessun motivo per utilizzare %zdpiuttosto che %zuper stampare un size_tvalore. Non è nemmeno valido (ha un comportamento indefinito) se il valore supera SIZE_MAX / 2. (Per completezza, potresti menzionare %zoper ottale.)
Keith Thompson

2
@FUZxxl: POSIX non richiede che ssize_til tipo firmato sia corrispondente size_t, quindi non è garantito che corrisponda "%zd". ( Probabilmente è sulla maggior parte delle implementazioni.) Pubs.opengroup.org/onlinepubs/9699919799/basedefs/…
Keith Thompson,

59

Per C89, utilizzare %lue trasmettere il valore per unsigned long:

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

Per C99 e versioni successive, utilizzare %zu:

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

7
Considerando il 2013, suggerisci "Per C99 e successivi" e "Per pre C99:". Migliore risposta.
chux - Ripristina Monica il

8
Non farlo. Non funzionerà su Windows a 64 bit dove size_t è 64 bit e lungo è 32 bit.
Yttrill

1
@Yttrill: qual è la risposta per Windows a 64 bit, allora?
John Bode,

1
@JohnBode Forse unsigned long long?
James Ko,

2
Oppure: è possibile eseguire il cast in a uint64_te quindi utilizzare la PRIu64macro da inttypes.h, che contiene l'identificatore di formato.
James Ko,

9

Estensione della risposta di Adam Rosenfield per Windows.

Ho testato questo codice con sia su VS2013 Update 4 che su VS2015 preview:

// 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 generato uscite binarie:

1
1
2

mentre quello generato da VS2013 dice:

zu
zx
zd

Nota: ssize_tè un'estensione POSIX ed SSIZE_Tè simile nei tipi di dati di Windows , quindi ho aggiunto un <BaseTsd.h>riferimento.

Inoltre, ad eccezione delle seguenti intestazioni C99 / C11, tutte le intestazioni C99 sono disponibili nell'anteprima VS2015:

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

Inoltre, C11 <uchar.h>è ora incluso nell'ultima anteprima.

Per ulteriori dettagli, vedere questo vecchio e il nuovo elenco per la conformità standard.


L'aggiornamento 5 VS2013 produce gli stessi risultati dell'aggiornamento 4.
Nathan Kidd,

6

Per coloro che parlano di farlo in C ++ che non supporta necessariamente le estensioni C99, allora consiglio vivamente boost :: format. Questo rende discutibile la domanda sulla dimensione del tipo size_t:

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

Poiché non hai bisogno di identificatori di dimensione in formato boost ::, puoi solo preoccuparti di come visualizzare il valore.


4
Probabilmente voglio %uallora.
GManNickG,

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

8
Sì, ma l'interrogante chiede specificamente un printfidentificatore. Immagino che abbiano altri vincoli non dichiarati che rendono l'utilizzo di std::coutun problema.
Donal Fellows il

1
@Donal Mi chiedo che tipo di problema potrebbero creare i flussi C ++ in un progetto C ++!
AraK,

9
@AraK. Sono molto lenti? Aggiungono MOLTI byte per non molte ragioni. ArunSaha vuole solo sapere per le sue conoscenze personali? Preferenza personale (preferisco stdio a seguire me stesso). Ci sono molte ragioni.
KitsuneYMG,

1
@TKCrowder: Bene, la richiesta originale diceva che era richiesta una soluzione C (tramite tag) e ci sono buone ragioni per non usare flussi in C ++, ad esempio se il descrittore del formato di output viene estratto da un catalogo di messaggi. (Potresti scrivere un parser per i messaggi e utilizzare gli stream se lo desideri, ma è un sacco di lavoro quando puoi semplicemente sfruttare il codice esistente.)
Donal Fellows

1
@Donal: i tag erano C e C ++. Non sto in alcun modo sostenendo le cose dello stream I / O di C ++ (non ne sono un fan), sto solo sottolineando che la domanda non originariamente * "... chiedi le specifiche per uno printfspecificatore."
TJ Crowder,


2

Come ha detto AraK, l'interfaccia di flussi c ++ funzionerà sempre in modo portabile.

std :: size_t s = 1024; std :: cout << s; // o qualsiasi altro tipo di stream come stringstream!

Se si desidera C stdio, non esiste una risposta portatile a questa in alcuni casi di "portatile". E diventa brutto da quando hai visto, scegliere i flag di formato errati può produrre un avviso del compilatore o fornire un output errato.

C99 ha provato a risolvere questo problema con formati inttypes.h come "%" PRIdMAX "\ n". Ma proprio come con "% zu", non tutti supportano c99 (come MSVS prima del 2013). Ci sono file "msinttypes.h" che fluttuano intorno per risolvere questo problema.

Se si esegue il cast di un tipo diverso, a seconda dei flag, è possibile che venga visualizzato un avviso del compilatore per il troncamento o una modifica del segno. Se segui questo percorso, scegli un tipo di dimensione fissa rilevante più grande. Uno di unsigned long long e "% llu" o unsigned long "% lu" dovrebbe funzionare, ma llu può anche rallentare le cose in un mondo a 32 bit eccessivamente grande. (Modifica: il mio mac emette un avviso a 64 bit per% llu che non corrisponde a size_t, anche se% lu,% llu e size_t hanno tutte le stesse dimensioni. E% lu e% llu non hanno le stesse dimensioni sul mio MSVS2012. potrebbe essere necessario eseguire il cast + utilizzare un formato corrispondente.)

In questo caso, puoi scegliere tipi di dimensioni fisse, come int64_t. Ma aspetta! Ora torniamo a c99 / c ++ 11 e MSVS precedente fallisce di nuovo. Inoltre hai anche cast (ad esempio map.size () non è un tipo di dimensione fissa)!

Puoi utilizzare un'intestazione o una libreria di terze parti come boost. Se non ne stai già utilizzando uno, potresti non voler gonfiare il tuo progetto in quel modo. Se sei disposto ad aggiungerne uno solo per questo problema, perché non utilizzare flussi c ++ o compilazione condizionale?

Quindi sei impegnato in flussi c ++, compilazione condizionale, framework di terze parti o qualcosa di portatile che sembra funzionare per te.


-1

Ti avvertirà se passi un numero intero senza segno a 32 bit in un formato% lu? Dovrebbe andare bene poiché la conversione è ben definita e non perde alcuna informazione.

Ho sentito che alcune piattaforme definiscono le macro <inttypes.h>che puoi inserire nel formato string letterale ma non vedo quell'intestazione sul mio compilatore Windows C ++, il che implica che potrebbe non essere multipiattaforma.


1
La maggior parte dei compilatori non ti avviserà se passi a printf qualcosa di sbagliato. GCC è un'eccezione. inttypes.h è stato definito in C99, quindi qualsiasi compilatore C conforme a C99 lo avrà, che dovrebbe essere ormai tutto. Tuttavia, potrebbe essere necessario attivare C99 con un flag di compilatore. In ogni caso, intttypes.h non definisce un formato specifico per size_t o ptrdiff_t, poiché sono stati decisi di essere abbastanza importanti da ottenere rispettivamente i propri identificatori di dimensione di 'z' e 't'.
Swestrup,

Se lo usi %lu, dovresti trasmettere il size_tvalore a unsigned long. Non esiste una conversione implicita (oltre alle promozioni) per gli argomenti in printf.
Keith Thompson,

-2

C99 definisce "% zd" ecc. Per questo. (grazie ai commentatori) Non esiste un identificatore di formato portatile per questo in C ++: è possibile utilizzare la %pparola woulkd in questi due scenari, ma non è nemmeno una scelta portatile e fornisce il valore in esadecimale.

In alternativa, utilizzare alcuni streaming (ad esempio stringstream) o una sostituzione di stampa sicura come Boost Format . Comprendo che questo consiglio ha solo un uso limitato (e richiede C ++). (Abbiamo implementato un approccio simile adatto alle nostre esigenze durante l'implementazione del supporto Unicode.)

Il problema fondamentale per C è che printf usando i puntini di sospensione non è sicuro in base alla progettazione: deve determinare la dimensione dell'argomento aggiuntivo dagli argomenti noti, quindi non può essere riparato per supportare "qualunque cosa tu abbia". Quindi, a meno che il compilatore non abbia implementato alcune estensioni proprietarie, sei sfortunato.


2
il zmodidfier di dimensioni è lo standard C, ma alcune implementazioni libc sono bloccate nel 1990 per vari motivi (ad es. Microsoft ha praticamente abbandonato C in favore di C ++ e - più recentemente - C #)
Christoph

3
C99 ha definito l'identificatore di dimensione 'z' come dimensione di un valore size_t e 't' come dimensione di un valore ptrdiff_t.
Swestrup,

2
%zdè sbagliato, non è firmato quindi dovrebbe essere %zu.
David Conrad,

-3

Su alcune piattaforme e per alcuni tipi sono disponibili specifici identificatori di conversione printf, ma a volte è necessario ricorrere al casting per tipi più grandi.

Ho documentato questo delicato problema qui, con il codice di esempio: http://www.pixelbeat.org/programming/gcc/int_types/ e lo aggiorno periodicamente con informazioni su nuove piattaforme e tipi.


1
Si noti che le risposte solo al collegamento sono sconsigliate, le risposte SO dovrebbero essere il punto finale di una ricerca di una soluzione (rispetto a un altro scalo di riferimento, che tende a diventare stantio nel tempo). Si prega di considerare l'aggiunta di una sinossi stand-alone qui, mantenendo il collegamento come riferimento.
Kleopatra il

-5

se vuoi stampare il valore di size_t come stringa puoi farlo:

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

il risultato è:

numero: 2337200120702199116

testo: Andiamo a pescare invece di sederci sul nostro ma !!

Modifica: rileggere la domanda a causa dei voti negativi ho notato che il suo problema non è% llu o% I64d ma il tipo size_t su macchine diverse vede questa domanda https://stackoverflow.com/a/918909/1755797
http: // www. cplusplus.com/reference/cstdio/printf/

size_t è unsigned int su una macchina a 32 bit e unsigned long long int su 64bit
ma% ll si aspetta sempre un unsigned long long int.

size_t varia in lunghezza su diversi sistemi operativi mentre% llu è lo stesso


4
Che assurdità è questa ?!
Antti Haapala,

lanciare i primi 8 byte dell'array char su un 64 bit a lungo lungo senza segno tramite il puntatore size_t e stamparli come numero con printf% I64d non è davvero spettacolare, lo so, ovviamente non ho usato il codice per impedire l'overflow del tipo ma non rientra nell'ambito della domanda.
Andre
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.