Lungo lungo nel c99
Nello standard C99 hanno introdotto long long
. Qual è lo scopo di questo? Nella mia (limitata) esperienza di programmazione in C, ho visto solo un int di 4 byte e uno di 8 byte di lunghezza. Ad esempio, da Compiler Explorer:
Se long
è già 8
allora, perché è necessario aggiungere un altro long long
tipo? Cosa fa questo al compilatore / architettura?
Risposte
Se long è già 8, perché è necessario aggiungere un altro tipo long long? Cosa fa questo al compilatore / architettura?
"Se long è già 8" non è sempre vero in quanto esiste molto codice che si basa su 32 bit long
e int
32 o 16 bit.
Richiedere long
come 64 bit romperebbe le basi del codice. Questa è una delle principali preoccupazioni.
Tuttavia, la richiesta long
di rimanere a 32 bit (e no long long
) non renderebbe l'accesso a interi standard a 64 bit, quindi una logica per long long
.
Consentire long
come 32 bit o 64 bit (o altri) consente la transizione.
Diverse funzioni passano / ritornano long
come fseek(), ftell()
. Beneficiano di long
essere più di 32 bit per il supporto di file di grandi dimensioni.
La pratica consigliata incoraggia una più ampia long
: "I tipi utilizzati per size_t
e ptrdiff_t
non dovrebbero avere un rango di conversione intero maggiore di quello di a signed long int
meno che l'implementazione non supporti oggetti abbastanza grandi da renderlo necessario." Ciò si riferisce alle dimensioni della memoria superiori a 32 bit.
Forse in futuro un'implementazione potrebbe utilizzare int/long/long long/intmax_t
come 32/64/128/256 bit.
IAC, vedo i tipi di larghezza fissa intN_t
aumentare di popolarità oltre long
e long long
. Tendo a usare i tipi a larghezza fissa o bool
, ( unsigned
) char
, int
/ unsigned
,, size_t
( u
) intmax_t
e leave signed char
, ( unsigned
) short
, ( unsigned
) long
, ( unsigned
) long long
per casi speciali.
Lo standard C garantisce solo che an int
può essere (in senso lato) 2 byte, a long
può essere 4 byte e a long long
può essere 8 byte.
In effetti, MSVC utilizza ancora un 4 byte long
anche se ha un 4 byte int
.
L'unico requisito rilevante per int
e long
, allora e ora, è che int
deve essere di almeno 16 bit e long
deve essere di almeno 32 bit. I sistemi a 16 e 32 bit tendono entrambi ad avere 32 bit long
e le macchine a 64 bit erano molto meno comuni alla fine degli anni '90. Quindi, prima di C99, i programmatori non potevano fare affidamento in modo portabile sulla disponibilità di un tipo intero a 64 bit. Questo problema è stato risolto con l'introduzione di long long
, che deve contenere almeno 64 bit. (Credo che sia già stato fornito da GCC e forse da altri compilatori come estensione).
Oggigiorno, molti (ma non tutti) i sistemi a 64 bit utilizzano un 64 bit long
e non si preoccupano di long long
ingrandirlo, quindi è anche a 64 bit ed è in un certo senso ridondante. Questi sono presumibilmente i sistemi con cui hai familiarità, ma non rappresentano tutto là fuori.
Penso che non ti rendessi conto che stai facendo un'enorme ipotesi sbagliata su come funzionano i requisiti di larghezza del tipo C: ISO C imposta solo un intervallo di valori minimo come la grandezza minima consentita LONG_MAX
e LONG_MIN
(-2147483647, non 8 perché ISO C consente il proprio complemento e interi con segno di segno / grandezza, non solo il complemento di 2.) Le implementazioni effettive possono avere tipi più ampi, spesso per abbinare una larghezza di registro o una dimensione di operando che la macchina di destinazione può eseguire in modo efficiente.
Molto è stato scritto su questo argomento su Stack Overflow e altrove, che non cercherò di ripetere qui. Guarda anchehttps://en.cppreference.com/w/c/language/arithmetic_types
Ciò ti ha portato all'errore di guardare le scelte di larghezza del tipo nell'ABI System V x86-64 e supporre che le altre implementazioni C siano le stesse, credo. x86-64 è un ISA a 64 bit che può funzionare in modo efficiente con interi a 64 bit, quindi 64 bit è long
stata una scelta abbastanza sensata.
Nessun ABI sano di mente per una macchina a 32 bit come i386 userebbe 64 bit long
perché non è richiesto, solo 32 bit. L'uso di 64 bit significherebbe che non potrebbe essere contenuto in un singolo registro. Compilare con -m32
o compilare per ARM a 32 bit. Godbolt ha anche GCC per AVR e MSP430. Su quelle macchine a 8 e 16 bit, GCC sceglie le larghezze più piccole consentite da ISO C (2 byte int
, ecc.)
Nel 1999, x86-64 non esisteva nemmeno. (Alcuni altri ISA a 64 bit lo hanno fatto, come Alpha). Quindi guardare uno dei 2 ABI tradizionali per capire le scelte di C99 non ti porterà molto lontano.
Ovviamente C ha bisogno di un tipo che sia garantito almeno a 64 bit, per consentire alle persone di scrivere programmi che eseguono in modo efficiente la matematica dei numeri interi a 64 bit.
E a proposito, x86-64 può fare cose intere a 32 bit in modo efficiente come 64 bit, a volte in modo più efficiente. Quindi creare long
un tipo a 64 bit probabilmente non è eccezionale. Alcuni codici utilizzano long
perché desiderano un tipo che deve essere a 32 bit, ma non trae vantaggio dall'averlo più ampio. Per tale codice, 64 bit long
spreca solo il footprint della cache / larghezza di banda della memoria e la dimensione del codice (prefissi REX). In C99 la scelta ideale sarebbe int_least32_t
, ma è fastidiosamente lunga da digitare e usata raramente.
Ma OTOH, a long
volte si spera che sia "il tipo più efficiente (1 registro)", sebbene non ci sia tale garanzia e gli ABI LLP64 come Windows x64 a 32 bit long
non sono così.
Un altro intero barattolo di worm è int_fast32_t
la scarsa scelta IMO di C99 e x86-64 System V per renderlo un tipo a 64 bit. (Ho una risposta scritta a metà per Cpp uint32_fast_t si risolve in uint64_t ma è più lento per quasi tutte le operazioni rispetto a uint32_t (x86_64). Perché si risolve in uint64_t? Che dovrei finire ... int_fast32_t
solleva la domanda "veloce per cosa scopo ", e in molte implementazioni non è quello che speri in molti casi.
Guarda anche
- C ++: il tipo intero più veloce?
- Come dovrebbero essere definiti i tipi [u] int_fastN_t per x86_64, con o senza l'ABI x32?
- Perché uint32_t dovrebbe essere preferito invece di uint_fast32_t?
- Perché uint_least16_t è più veloce di uint_fast16_t per la moltiplicazione in x86_64?
- Ottimizzazioni del compilatore consentite tramite i tipi di larghezza non fissa "int", "minimo" e "veloce" C / C ++
Ci sono alcuni limiti ma l'autore del compilatore è libero di scegliere le lunghezze per i tipi di variabili C standard (char, short, int, long, long long). Naturalmente char sarà un byte per quell'architettura (la maggior parte con i compilatori C sono 8 bit). E naturalmente non puoi avere qualcosa di più piccolo più grande di qualcosa di più grande, long non può essere più piccolo di un int. Ma certamente nel 1999 abbiamo visto la transizione x86 da 16 a 32 bit e ad esempio int è cambiato da 16 a 32 con un numero di strumenti ma è rimasto a lungo 32. Successivamente è avvenuta la transizione da 32 a 64 bit x86 ea seconda dello strumento c'erano dei tipi disponibili aiutare.
Il problema esisteva molto prima e la soluzione non era quella di fissare le lunghezze dei tipi, sono, all'interno delle regole, a discrezione degli autori del compilatore per quanto riguarda la dimensione. Ma gli autori del compilatore devono creare un file stdint.h che corrisponda allo strumento e al target (stdint.h è specifico per uno strumento e target come minimo e può essere la versione dello strumento e le opzioni di compilazione per quello strumento, ecc.). Quindi, ad esempio, uint32_t è sempre a 32 bit. Alcuni autori lo convertiranno in un int, altri in long, ecc. Nel loro stdint.h. I tipi di variabile del linguaggio C rimangono limitati a char, short, int, ecc per la lingua (uint32_t non è un tipo di variabile, viene convertito in un tipo di variabile tramite stdint.h). Questa soluzione / soluzione alternativa era un modo per evitare che tutti diventassero pazzi e mantenere viva la lingua.
Gli autori sceglieranno spesso, ad esempio, se i GPR sono a 16 bit per avere int a 16 bit, e se 32 bit a 32 bit e così via, ma hanno una certa libertà.
Sì, questo significa specificamente che non c'è motivo di presumere che due strumenti qualsiasi per un particolare target (il computer su cui stai leggendo questo per esempio) usino le stesse definizioni per int e long in particolare, e se vuoi scrivere codice per questa piattaforma che può eseguire il port su questi strumenti (che supportano questa piattaforma) quindi usa i tipi stdint.h e non int, long, ecc ... Sicuramente se stai attraversando piattaforme un mcu msp430, un mcu arm, una macchina linux arm , una macchina basata su x86, che i tipi, anche per la stessa "toolchain" (gnu gcc e binutils per esempio), non hanno le stesse definizioni per int e long, ecc. char e short tendono ad essere 8 e 16 bit, int e long tendono a variare di più, a volte della stessa dimensione l'una dell'altra a volte diverse, ma il punto è non dare per scontato.
È banale rilevare le dimensioni, per una versione del compilatore / destinazione / opzioni della riga di comando, o semplicemente seguire il percorso stdint per ridurre al minimo i problemi in seguito.