I tipi di larghezza variabile sono stati sostituiti da tipi fissi nella moderna C?

19

Mi sono imbattuto in un punto interessante oggi in una recensione sulla revisione del codice . @Veedrac recommened in questa risposta che i tipi di dimensione variabile (ad esempio int e long ) devono essere sostituiti con tipi di dimensioni come uint64_t e uint32_t . Citazione dai commenti di quella risposta:

The sizes of int and long (and thus the values they can hold) are platform-dependent. On the other hand, int32_t is always 32 bits long. Using int just means that your code works differently on different platforms, which is generally not what you want.

Il ragionamento dietro lo standard che non fissa i tipi comuni è parzialmente spiegato qui da @supercat. C è stato scritto per essere portabile su architetture, al contrario del montaggio che di solito veniva utilizzato per la programmazione dei sistemi al momento.

I think the design intention was originally that each type other than int be the smallest thing that could handle numbers of various sizes, and that int be the most practical "general-purpose" size that could handle +/-32767.

Per quanto mi riguarda, ho sempre usato int e non sono davvero preoccupato per le alternative. Ho sempre pensato che fosse il tipo più con le migliori prestazioni, la fine della storia. L'unico posto in cui pensavo che la larghezza fissa sarebbe utile è quando si codificano i dati per l'archiviazione o per il trasferimento su una rete. Raramente ho visto tipi di larghezza fissa nel codice scritti da altri.

Sono bloccato negli anni '70 o esiste effettivamente una motivazione per usare int nell'era di C99 e oltre?

    
posta jacwah 15.06.2015 - 00:50
fonte

2 risposte

6

Esiste un mito comune e pericoloso che digita come uint32_t salva i programmatori dal doversi preoccupare delle dimensioni di int . Mentre sarebbe utile se il comitato degli standard definisse un mezzo per dichiarare gli interi con la semantica indipendente dalla macchina, i tipi senza segno come uint32_t hanno una semantica troppo lenta per permettere che il codice sia scritto in un modo che sia sia pulito che portatile ; inoltre, i tipi firmati come int32 hanno una semantica che per molte applicazioni è definita indefinitamente e quindi preclude quelle che altrimenti sarebbero ottimizzazioni utili.

Considera, ad esempio:

uint32_t upow(uint32_t n, uint32_t exponent)
{
  while(exponent--)
    n*=n;
  return n;
}

int32_t spow(int32_t n, uint32_t exponent)
{
  while(exponent--)
    n*=n;
  return n;
}

Sulle macchine in cui int non può contenere 4294967295 o può contenere 18446744065119617025, la prima funzione sarà definita per tutti i valori di n e exponent , e il suo comportamento non sarà influenzato dalla dimensione di int ; inoltre, lo standard non richiederà che produca un comportamento diverso su macchine con qualsiasi dimensione di int Alcuni valori di n e exponent , tuttavia, causerà il richiamo di comportamento non definito su macchine in cui 4294967295 è rappresentabile come int ma 18446744065119617025 non lo è.

La seconda funzione genererà il comportamento non definito per alcuni valori di n e exponent su macchine in cui int non può contenere 4611686014132420609, ma produrrà un comportamento definito per tutti i valori di n e exponent su tutte le macchine dove può (le specifiche per int32_t implicano che il comportamento di avvolgimento del complemento a due su macchine in cui è inferiore a int ).

Storicamente, anche se lo Standard non ha detto nulla su cosa i compilatori dovrebbero fare con int di overflow in upow , i compilatori avrebbero coerentemente prodotto lo stesso comportamento come se int fosse stato abbastanza grande da non eccedere. Sfortunatamente, alcuni compilatori più recenti possono cercare di "ottimizzare" i programmi eliminando comportamenti non imposti dallo Standard.

    
risposta data 15.06.2015 - 05:01
fonte
3

Per i valori strettamente correlati ai puntatori (e quindi alla quantità di memoria indirizzabile) come le dimensioni del buffer, gli indici di array e il lParam di Windows, ha senso avere un numero intero con una dimensione dipendente dall'architettura. Quindi, i tipi di dimensioni variabili sono ancora utili. Questo è il motivo per cui abbiamo typedefs size_t , ptrdiff_t , intptr_t , ecc. hanno per essere typedefs perché nessuno dei tipi interi C incorporati deve essere di dimensioni puntatore.

Quindi la domanda è davvero se char , short , int , long e long long siano ancora utili.

IME, è ancora comune per i programmi C e C ++ utilizzare int per la maggior parte delle cose. E la maggior parte delle volte (cioè quando i tuoi numeri sono compresi nell'intervallo ± 32767 e non hai requisiti di prestazioni rigorosi), questo funziona perfettamente.

Ma se fosse necessario lavorare con i numeri nell'intervallo di 17-32 bit (come le popolazioni di grandi città)? Potresti usare int , ma sarebbe difficile codificare una dipendenza dalla piattaforma. Se si desidera aderire rigorosamente allo standard, è possibile utilizzare long , che garantisce almeno 32 bit.

Il problema è che lo standard C non specifica alcuna dimensione massima per un tipo intero. Esistono implementazioni su cui long è 64 bit, che raddoppia l'utilizzo della memoria. E se questi long s capita di essere elementi di una matrice con milioni di elementi, la memoria sarà pazzesca.

Quindi, né intlong è un tipo adatto da usare qui se vuoi che il tuo programma sia sia multipiattaforma che efficiente in termini di memoria. Inserisci int_least32_t .

  • Il compilatore I16L32 ti offre un long a 32 bit, evitando i problemi di troncamento di int
  • Il tuo compilatore I32L64 ti offre un int a 32-bit, evitando la memoria sprecata dellong a 64-bit.
  • Il tuo compilatore I36L72 ti offre un 36% di int

OTOH, supponiamo che non abbia bisogno di numeri enormi o di enormi matrici, ma hai bisogno di velocità. E int può essere abbastanza grande su tutte le piattaforme, ma non è necessariamente il tipo più veloce: i sistemi a 64 bit di solito hanno ancora il 32% diint. Ma puoi usare int_fast16_t e ottenere il tipo "più veloce", sia che sia int , long o long long .

Quindi, ci sono casi d'uso pratico per i tipi da <stdint.h> . I tipi interi standard non significano nulla. Soprattutto long , che può essere 32 o 64 bit, e può o non può essere abbastanza grande da contenere un puntatore, a seconda del capriccio degli scrittori del compilatore.

    
risposta data 26.06.2015 - 07:22
fonte

Leggi altre domande sui tag