2010-03-25 10 views
16

Codice di porta da 32 bit a 64 bit. Un sacco di posti conint vs size_t su 64 bit

int len = strlen(pstr); 

Questi tutti generano avvisi ora perché strlen() restituisce size_t, che è a 64 bit e int è ancora a 32 bit. Così Li ho sostituendoli con

size_t len = strlen(pstr); 

Ma ho appena reso conto che questo non è sicuro, come size_t non è firmato e può essere considerato come sottoscritto dal codice (io in realtà sono imbattuto in un caso in cui ha causato una problema, grazie, unit test!).

Il lancio di strind di ritorno a (int) si sente sporco. O forse non dovrebbe?
Quindi la domanda è: c'è una soluzione elegante per questo? Probabilmente ho mille righe di codice come quello nella base di codice; Non riesco a controllare manualmente ognuno di essi e la copertura del test è attualmente tra 0,01 e 0,001%.

+1

Avete un esempio in cui questa lunghezza è trattato come firmato? – kroimon

+0

L'esempio è probabilmente qualcosa sulla falsariga di: 'len--; se (len <0) {break} ' – Tim

risposta

5

Come compromesso, è possibile utilizzare ssize_t (se disponibile). Falla finita se non lo è, usando long long, int_fast64_t, intmax_t o disponi di un'intestazione di porting della piattaforma che consente di specificare un tipo adatto per una piattaforma. ssize_t è in POSIX non standard C o C++, ma se si colpisce una piattaforma che non ha un tipo firmato della stessa dimensione di size_t, allora sympathize.

Un cast a int è quasi sicuro (supponendo 32 bit int sulla piattaforma a 64 bit, che sembra ragionevole), perché è improbabile che una stringa sia lunga più di 2^31 byte. Un cast per un tipo di firma più grande è ancora più sicuro. I clienti che possono permettersi di 2^63 byte di memoria sono ciò che è noto in commercio come "un buon problema di avere" ;-)

Naturalmente, potreste controllare che:

size_t ulen = strlen(pstr); 
if (ulen > SSIZE_MAX) abort(); // preferably trace, log, return error, etc. 
ssize_t len = (ssize_t) ulen; 

sicuro che c'è un overhead , ma se hai 1000 istanze, non tutte possono essere critiche dal punto di vista delle prestazioni. Per quelli che sono (se ce ne sono), puoi fare il lavoro per verificare se la firma dello len è effettivamente importante. In caso contrario, passare a size_t. Se lo fa, riscrivi o rischi solo di non incontrare mai un oggetto così assurdamente enorme. Il codice originale avrebbe quasi sicuramente fatto la cosa sbagliata sulla piattaforma a 32 bit, se len era risultato negativo a causa di strlen restituendo un valore superiore a INT_MAX.

+0

Concordo sul fatto che il cast su int sia quasi sicuro, ma non capisco qual è il punto di ssize_t: è anche * nerly * sicuro. È leggermente più sicuro di int, ma comunque - size_t può essere più grande di ssize_t. –

+0

@MK, 'ssize_t' deve essere uguale a' size_t' – osgx

+2

@MK: Penso che l'intenzione generale di 'ssize_t' è che, in pratica, le implementazioni POSIX non permetteranno che singoli oggetti superino la metà della dimensione del spazio di indirizzi disponibile. È abbastanza facile far rispettare questo è 'malloc', anche se non penso che sia garantito. È utile avere un tipo di dimensione firmata per rappresentare gli offset che possono essere negativi. –

1

Nella maggior parte dei casi è possibile trattare il sito in modo sicuro. Le dimensioni unsigned_t verranno considerate negative solo quando (o i risultati intermedi nelle espressioni) sono maggiori di 2^31 (per 32 bit) o ​​2^63 per 64 bit.

UPDATE: Siamo spiacenti, size_t non sarà sicuro in costruzioni come while ((size_t)t >=0). Quindi la risposta giusta è usare ssize_t.

+1

intendevo il caso in cui poi decremento la len in un punto in cui diventa negativo. Come in un loop while (len> 0) –

+0

loop 'while (len> 0)' dovrebbe fermarsi a 'len == 0'. Per favore, mostraci il tuo esempio, problema in cui è stato rilevato con test unitari. – osgx

+2

Blah, scusa, volevo dire se (len <0). Ho avuto un ciclo con quel controllo inverso di "se (len <0) salta qualcosa;" anziché "if (len> = 0) fa qualcosa;" –

5

L'impostazione degli avvisi del compilatore al livello massimo dovrebbe fornire un report di ogni conversione di segno non corretta. In gcc, '-Wall -Wextra' dovrebbe fare.

È anche possibile utilizzare un analizzatore di codice statico come cppcheck per verificare se tutto è corretto.

+0

e -wall troverà tutti i luoghi in cui size_t viene utilizzato in un contesto firmato. Dovresti davvero usare size_t – pm100

4

È possibile utilizzare ssize_t (la variante firmata di size_t).

7

Qualche tempo fa ho postato una breve nota su questo tipo di problemi sul mio blog e la risposta breve è:

Always use proper C++ integer types

Risposta lunga: Quando si programma in C++, è una buona idea usare i tipi interi corretti relativi a un determinato contesto. Un po 'di rigore ripaga sempre. Non è raro vedere una tendenza a ignorare i tipi di integrale definiti come specifici ai contenitori standard, ovvero size_type. È disponibile per il numero di contenitori standard come std :: string o std :: vector. Tale ignoranza può ottenere facilmente la sua vendetta.

Di seguito è riportato un semplice esempio di tipo utilizzato in modo non corretto per rilevare il risultato della funzione std :: string :: find. Sono abbastanza sicuro che molti si aspetterebbero che non ci sia nulla di sbagliato nell'int unsigned qui. Ma in realtà questo è solo un bug. Eseguo Linux su architettura a 64 bit e quando compilo questo programma così com'è, funziona come previsto. Tuttavia, quando ho sostituire la stringa in linea con 1 abc, funziona ancora, ma non come ci si aspetta :-)

#include <iostream> 
#include <string> 
using namespace std; 
int main() 
{ 
    string s = "a:b:c"; // "abc" [1] 
    char delim = ':'; 
    unsigned int pos = s.find(delim); 
    if(string::npos != pos) 
    { 
    cout << delim << " found in " << s << endl; 
    } 
} 

Fix è molto semplice. Sostituisci solo unsigned int con std :: string :: size_type. Il problema potrebbe essere evitato se qualcuno che ha scritto questo programma si è preso cura dell'uso del tipo corretto. Per non parlare del fatto che il programma sarebbe immediatamente portatile.

Ho visto questo tipo di problemi un bel po 'di volte, specialmente nel codice scritto da ex programmatori C che non amano indossare la museruola di rigore che il sistema di tipi C++ impone e richiede. L'esempio sopra è banale, ma credo che presenti bene la radice del problema.

Raccomando l'articolo brillante 64-bit development scritto da Andrey Karpov dove potete trovare molto di più sull'argomento.

+2

Anche se generalmente sono d'accordo con "usa i tipi corretti", 'std :: some_container :: size_type' si riduce a' size_t' in tutte le implementazioni decenti.Per quanto posso vedere, almeno 'std :: bitset :: size_type',' std :: array :: size_type', 'std :: initializer_list' e' std :: allocator :: size_type' sono typedefs per ' size_t'. Quindi, a meno che tu non stia usando un allocatore pazzo o parametri di template molto speciali, 'size_t' è sufficiente. – rubenvb

1

Se il compilatore supporta C++ 0x:

auto len = strlen(pstr); 
Problemi correlati