In diversi esempi di C ++ vedo un uso del tipo in size_t
cui avrei usato un semplice int
. Qual è la differenza e perché size_t
dovrebbe essere migliore?
In diversi esempi di C ++ vedo un uso del tipo in size_t
cui avrei usato un semplice int
. Qual è la differenza e perché size_t
dovrebbe essere migliore?
Risposte:
I file di intestazione stdlib.h e stddef.h definiscono un tipo di dati chiamato size_t che viene utilizzato per rappresentare la dimensione di un oggetto. Le funzioni di libreria che accettano dimensioni si aspettano che siano di tipo size_t e l'operatore sizeof restituisce size_t.
Il tipo effettivo di size_t dipende dalla piattaforma; un errore comune è supporre che size_t sia uguale a unsigned int, il che può portare a errori di programmazione, in particolare quando le architetture a 64 bit diventano più diffuse.
Inoltre, controlla Perché size_t è importante
/usr/include/stdlib.h
ottiene la definizione /usr/lib/gcc/x86_64-redhat-linux/5.3.1/include/stddef.h
e in essa è predefinita a long unsigned int
meno che qualche altro file di intestazione non dica diversamente.
size_t è il tipo utilizzato per rappresentare le dimensioni (come suggerisce il nome). La sua piattaforma (e anche potenzialmente l'implementazione) dipende e dovrebbe essere utilizzata solo per questo scopo. Ovviamente, rappresentando una dimensione, size_t non è firmato. Molte funzioni stdlib, tra cui malloc, sizeof e varie funzioni operative delle stringhe utilizzano size_t come tipo di dati.
Un int è firmato per impostazione predefinita e anche se le sue dimensioni dipendono anche dalla piattaforma, saranno 32 bit fissi sulla maggior parte delle macchine moderne (e sebbene size_t sia 64 bit su un'architettura a 64 bit, int rimarrà a 32 bit su quelle architetture).
Per riassumere: usa size_t per rappresentare la dimensione di un oggetto e int (o long) in altri casi.
Il size_t
tipo è definito come il tipo integrale senza segno sizeof
dell'operatore. Nel mondo reale, vedrai spesso int
definiti come 32 bit (per compatibilità con le versioni precedenti) ma size_t
definiti come 64 bit (in modo da poter dichiarare matrici e strutture di dimensioni superiori a 4 GiB) su piattaforme a 64 bit. Se a long int
è anche 64 bit, questa è chiamata convenzione LP64; se long int
è 32 bit ma i long long int
puntatori sono 64 bit, è LLP64. Potresti anche ottenere il contrario, un programma che utilizza istruzioni a 64 bit per la velocità, ma puntatori a 32 bit per risparmiare memoria. Inoltre, int
è firmato e size_t
non firmato.
Storicamente c'erano diverse altre piattaforme in cui gli indirizzi erano più larghi o più brevi delle dimensioni native di int
. In effetti, negli anni '70 e all'inizio degli anni '80, questo era più comune che no: tutti i popolari microcomputer a 8 bit avevano registri a 8 bit e indirizzi a 16 bit e la transizione tra 16 e 32 bit produceva anche molte macchine che avevano indirizzi più ampi dei loro registri. Di tanto in tanto vedo ancora domande su Borland Turbo C per MS-DOS, la cui modalità di memoria enorme aveva indirizzi a 20 bit memorizzati in 32 bit su una CPU a 16 bit (ma che potevano supportare il set di istruzioni a 32 bit dell'80386); il Motorola 68000 aveva un ALU a 16 bit con registri e indirizzi a 32 bit; c'erano mainframe IBM con indirizzi a 15, 24 o 31 bit. Si vedono anche diverse ALU e dimensioni del bus di indirizzo nei sistemi integrati.
Ogni volta che int
è più piccolo di size_t
e si tenta di memorizzare la dimensione o l'offset di un file o oggetto molto grande in un unsigned int
, c'è la possibilità che potrebbe traboccare e causare un bug. Con un int
, c'è anche la possibilità di ottenere un numero negativo. Se un int
o unsigned int
è più ampio, il programma verrà eseguito correttamente ma sprecherà memoria.
In genere è necessario utilizzare il tipo corretto per lo scopo se si desidera la portabilità. Molte persone raccomandano di usare la matematica firmata anziché quella non firmata (per evitare bug cattivi e sottili come 1U < -3
). A tal fine, le definisce libreria standard ptrdiff_t
in <stddef.h>
quanto il tipo firmata del risultato della sottrazione un puntatore da un altro.
Detto questo, una soluzione alternativa potrebbe essere quella di controllare i limiti di tutti gli indirizzi e gli offset rispetto ae INT_MAX
uno 0
o INT_MIN
come appropriato e attivare gli avvisi del compilatore sul confronto tra quantità firmate e non firmate in caso di perdita. In ogni caso, dovresti sempre, sempre, controllare sempre gli accessi al tuo array per overflow in C.
È perché size_t può essere qualcosa di diverso da un int (forse uno struct). L'idea è che disaccoppia il suo lavoro dal tipo sottostante.
size_t
è specificato come tipo intero senza segno . C11 §6.5.3.4 5 "Il valore del risultato di entrambi gli operatori ( sizeof
_Alignof
) è definito dall'implementazione e il suo tipo (un tipo intero senza segno) è size_t
,".
La definizione di SIZE_T
si trova su:
https://msdn.microsoft.com/en-us/library/cc441980.aspx e https://msdn.microsoft.com/en-us/library/cc230394.aspx
Incollando qui le informazioni richieste:
SIZE_T
è una ULONG_PTR
rappresentazione del numero massimo di byte a cui può puntare un puntatore.
Questo tipo è dichiarato come segue:
typedef ULONG_PTR SIZE_T;
A ULONG_PTR
è un tipo lungo senza segno utilizzato per la precisione del puntatore. Viene usato quando si lancia un puntatore su un tipo lungo per eseguire l'aritmetica del puntatore.
Questo tipo è dichiarato come segue:
typedef unsigned __int3264 ULONG_PTR;
SIZE_T
non è size_t
quello che l'OP ha chiesto.
SIZE_T
è totalmente diverso da size_t
. Non puoi dichiarare una variabile di tipo SIZE_T
.