unsigned int vs. size_t


492

Ho notato che il moderno codice C e C ++ sembra usare al size_tposto di int/ unsigned intpraticamente ovunque - dai parametri per le funzioni della stringa C allo STL. Sono curioso di sapere il motivo e i benefici che ne derivano.

Risposte:


388

Il size_ttipo è il tipo intero senza segno che è il risultato sizeofdell'operatore (e offsetofdell'operatore), quindi è garantito che sia abbastanza grande da contenere le dimensioni dell'oggetto più grande che il sistema può gestire (ad esempio, un array statico di 8 Gb).

Il size_ttipo può essere maggiore di, uguale o minore di un unsigned inte il compilatore potrebbe fare ipotesi al riguardo per l'ottimizzazione.

È possibile trovare informazioni più precise nella norma C99, sezione 7.17, una bozza di cui è disponibile su Internet in formato pdf , o nella norma C11, sezione 7.19, disponibile anche come bozza in pdf .


50
No. Pensa a x86-16 con il modello di memoria grande (non enorme): i puntatori sono lontani (32 bit), ma i singoli oggetti sono limitati a 64k (quindi size_t può essere a 16 bit).
dan04,

8
"dimensione dell'oggetto più grande" non è una formulazione scadente, ma assolutamente corretta. Il sixe di un oggetto può essere molto più limitato dello spazio degli indirizzi.
gnasher729,

3
"il tuo compilatore potrebbe ipotizzarlo": spero che il compilatore conosca l'esatto intervallo di valori che size_tpuò rappresentare! In caso contrario, chi lo fa?
Marc van Leeuwen,

4
@Marc: penso che il punto fosse più che il compilatore potesse essere in grado di fare qualcosa con quella conoscenza.

8
Vorrei solo che questo tipo sempre più popolare non richiedesse l'inclusione di un file di intestazione.
user2023370


74

In breve, size_tnon è mai negativo e massimizza le prestazioni perché è stato tipizzato come il tipo intero senza segno che è abbastanza grande - ma non troppo grande - per rappresentare la dimensione dell'oggetto più grande possibile sulla piattaforma di destinazione.

Le dimensioni non dovrebbero mai essere negative e in effetti size_tè un tipo senza segno. Inoltre, poiché size_tè senza segno, è possibile memorizzare numeri grandi circa il doppio rispetto al tipo con segno corrispondente, poiché è possibile utilizzare il bit di segno per rappresentare la grandezza, come tutti gli altri bit nell'intero senza segno. Quando guadagniamo un altro bit, stiamo moltiplicando l'intervallo di numeri che possiamo rappresentare per un fattore di circa due.

Quindi, chiedi, perché non usare solo un unsigned int? Potrebbe non essere in grado di contenere numeri abbastanza grandi. In un'implementazione in cui unsigned intsono presenti 32 bit, il numero più grande che può rappresentare è 4294967295. Alcuni processori, come IP16L32, possono copiare oggetti più grandi di 4294967295byte.

Quindi, chiedi, perché non usare un unsigned long int? Esegue un bilancio delle prestazioni su alcune piattaforme. Lo standard C richiede che longoccupi almeno 32 bit. Una piattaforma IP16L32 implementa ogni lunghezza a 32 bit come una coppia di parole a 16 bit. Quasi tutti gli operatori a 32 bit su queste piattaforme richiedono due istruzioni, se non di più, perché lavorano con i 32 bit in due blocchi a 16 bit. Ad esempio, lo spostamento di una lunghezza di 32 bit richiede in genere due istruzioni macchina: una per spostare ogni blocco di 16 bit.

L'utilizzo size_tevita questo bilancio delle prestazioni. Secondo questo fantastico articolo , "Tipo size_tè un typedef che è un alias per un tipo intero senza segno, in genere unsigned into unsigned long, ma forse anche unsigned long long. Ogni implementazione Standard C dovrebbe scegliere l'intero senza segno che è abbastanza grande - ma non più grande del necessario-- per rappresentare la dimensione dell'oggetto più grande possibile sulla piattaforma di destinazione. "


1
Mi spiace commentarlo dopo così tanto tempo, ma ho dovuto confermare il numero più grande che un int senza segno può contenere - forse sto fraintendendo la tua terminologia, ma ho pensato che il numero più grande che un int senza segno può contenere sia 4294967295, essendo 65356 il massimo di un corto senza segno.
Mitch,

Se il tuo int senza segno occupa 32 bit, quindi sì, il numero più grande che può contenere è 2 ^ 32 - 1, che è 4294967295 (0xffffffff). hai un'altra domanda?
Rose Perrone,

3
@Mitch: il valore più grande che può essere rappresentato in una unsigned intlattina e varia da un sistema all'altro. Deve essere almeno 65536 , ma è comunemente 4294967295e potrebbe essere 18446744073709551615(2 ** 64-1) su alcuni sistemi.
Keith Thompson,

1
Il valore più grande che può contenere un int senza segno a 16 bit è 65535, non 65536. Una differenza piccola ma importante come 65536 è la stessa di 0 in un int senza segno a 16 bit.
Sie Raybould,

1
@ gnasher729: sei sicuro dello standard C ++? Avendo cercato per un po 'di tempo, ho l'impressione che abbiano semplicemente rimosso tutte le garanzie assolute sugli intervalli di numeri interi (escluso unsigned char). Lo standard non sembra contenere la stringa '65535' o '65536' ovunque, e '32.767' si verifica solo (1,9: 9) in una nota come possibile rappresentabile più grande numero intero int; nessuna garanzia è data nemmeno che INT_MAXnon può essere più piccola di quella!
Marc van Leeuwen,

51

Il tipo size_t è il tipo restituito dall'operatore sizeof. È un numero intero senza segno in grado di esprimere la dimensione in byte di qualsiasi intervallo di memoria supportato sul computer host. È (in genere) correlato a ptrdiff_t in quanto ptrdiff_t è un valore intero con segno tale che sizeof (ptrdiff_t) e sizeof (size_t) sono uguali.

Quando scrivi il codice C dovresti sempre usare size_t ogni volta che hai a che fare con intervalli di memoria.

D'altra parte, il tipo int è sostanzialmente definito come la dimensione del valore intero (con segno) che la macchina host può usare per eseguire in modo più efficiente l'aritmetica dei numeri interi. Ad esempio, su molti computer di tipo PC più vecchi il valore sizeof (size_t) sarebbe 4 (byte) ma sizeof (int) sarebbe 2 (byte). L'aritmetica a 16 bit era più veloce dell'aritmetica a 32 bit, sebbene la CPU potesse gestire uno spazio di memoria (logico) fino a 4 GiB.

Usa il tipo int solo quando ti interessa l'efficienza poiché la sua precisione effettiva dipende fortemente sia dalle opzioni del compilatore che dall'architettura della macchina. In particolare lo standard C specifica i seguenti invarianti: sizeof (char) <= sizeof (short) <= sizeof (int) <= sizeof (long) non ponendo altre limitazioni alla rappresentazione effettiva della precisione disponibile per il programmatore per ciascuno di questi tipi primitivi.

Nota: NON è lo stesso di Java (che in realtà specifica la precisione dei bit per ciascuno dei tipi 'char', 'byte', 'short', 'int' e 'long').


la definizione di fatto di int è che è 16 bit su 16 macchine e 32 bit su qualcosa di più grande. È stato scritto troppo codice che presuppone che int sia largo 32 bit, per cambiarlo ora e di conseguenza le persone dovrebbero sempre usare size_t o {, u} int {8,16,32,64} _t se vogliono qualcosa di specifico - - per precauzione, le persone dovrebbero semplicemente usarle sempre, anziché i tipi interi integrali.
Più chiaro il

3
"È un numero intero senza segno in grado di esprimere la dimensione in byte di qualsiasi intervallo di memoria supportato sul computer host." -> No. size_tè in grado di rappresentare la dimensione di ogni singolo oggetto (es: numero, matrice, struttura). L'intero intervallo di memoria può superaresize_t
chux - Ripristina Monica il

"Quando scrivi il codice C dovresti sempre usare size_t ogni volta che hai a che fare con intervalli di memoria." - ciò implica che ogni indice di ogni array dovrebbe essere size_t- Spero che non lo intendi. Il più delle volte non ci occupiamo di array in cui conta anche la cardinalità dello spazio degli indirizzi + portabilità. In questi casi avresti preso size_t. In ogni altro caso prendi gli indici da numeri interi (firmati). Perché la confusione (che arriva senza preavviso) derivante da comportamenti insospettati di underflow di non firmati è più comune e peggiore dei problemi di portabilità che possono insorgere negli altri casi.
johannes_lalala,

23

Digitare size_t deve essere abbastanza grande per memorizzare le dimensioni di qualsiasi oggetto possibile. Unsigned int non deve soddisfare quella condizione.

Ad esempio, nei sistemi a 64 bit int e unsigned int possono avere una larghezza di 32 bit, ma size_t deve essere abbastanza grande per memorizzare numeri maggiori di 4G


38
"oggetto" è il linguaggio utilizzato dallo standard.
R .. GitHub FERMA AIUTANDO ICE

2
Penso che size_tdovrebbe essere così grande se il compilatore potesse accettare un tipo X tale che sizeof (X) produrrebbe un valore maggiore di 4G. La maggior parte dei compilatori rifiuterebbe typedef unsigned char foo[1000000000000LL][1000000000000LL], ad esempio , e foo[65536][65536];potrebbe anche essere legittimamente rifiutata se superasse un limite documentato definito dall'implementazione.
supercat

1
@MattJoiner: la formulazione va bene. "Oggetto" non è affatto vago, ma piuttosto definito come "regione di archiviazione".
Razze di leggerezza in orbita

4

Questo estratto dal manuale di glibc 0.02 può anche essere rilevante quando si ricerca l'argomento:

Esiste un potenziale problema con il tipo size_t e le versioni di GCC precedenti alla versione 2.4. ANSI C richiede che size_t sia sempre un tipo senza segno. Per compatibilità con i file di intestazione dei sistemi esistenti, GCC definisce size_t in stddef.h' to be whatever type the system'ssys / types.h 'lo definisce. La maggior parte dei sistemi Unix che definiscono size_t in `sys / types.h ', lo definiscono un tipo con segno. Alcuni codici nella libreria dipendono dal fatto che size_t è un tipo senza segno e non funzionerà correttamente se è firmato.

Il codice della libreria GNU C che prevede che size_t non sia firmato è corretto. La definizione di size_t come tipo firmato è errata. Prevediamo che nella versione 2.4, GCC definirà sempre size_t come un tipo senza segno e fixincludes' script will massage the system'ssys / types.h 'per non essere in conflitto con questo.

Nel frattempo, risolviamo questo problema dicendo a GCC di utilizzare esplicitamente un tipo senza segno per size_t durante la compilazione della libreria GNU C. `configure 'rileverà automaticamente quale tipo GCC usa per size_t organizza sovrascriverlo se necessario.


3

Se il mio compilatore è impostato su 32 bit, size_tnon è altro che un typedef per unsigned int. Se il mio compilatore è impostato su 64 bit, size_tnon è altro che un typedef per unsigned long long.


1
Può essere appena definito come unsigned longper entrambi i casi su alcuni sistemi operativi.
StaceyGirl,

-4

size_t è la dimensione di un puntatore.

Quindi in 32 bit o il modello ILP32 (intero, lungo, puntatore) comune size_t è 32 bit. e in 64 bit o il comune modello LP64 (long, pointer) size_t è 64 bit (gli interi sono ancora 32 bit).

Esistono altri modelli ma questi sono quelli che usano g ++ (almeno per impostazione predefinita)


15
size_tnon ha necessariamente le stesse dimensioni di un puntatore, anche se comunemente lo è. Un puntatore deve essere in grado di puntare a qualsiasi posizione nella memoria; size_tdeve solo essere abbastanza grande da rappresentare la dimensione del singolo oggetto più grande.
Keith Thompson,
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.