Lungo lungo nel c99

Jan 10 2021

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à 8allora, perché è necessario aggiungere un altro long longtipo? Cosa fa questo al compilatore / architettura?

Risposte

6 chux-ReinstateMonica Jan 10 2021 at 05:36

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 longe int32 o 16 bit.

Richiedere longcome 64 bit romperebbe le basi del codice. Questa è una delle principali preoccupazioni.


Tuttavia, la richiesta longdi 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 longcome 32 bit o 64 bit (o altri) consente la transizione.

Diverse funzioni passano / ritornano longcome fseek(), ftell(). Beneficiano di longessere 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_te ptrdiff_tnon dovrebbero avere un rango di conversione intero maggiore di quello di a signed long intmeno 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_tcome 32/64/128/256 bit.

IAC, vedo i tipi di larghezza fissa intN_taumentare di popolarità oltre longe long long. Tendo a usare i tipi a larghezza fissa o bool, ( unsigned) char, int/ unsigned,, size_t( u) intmax_te leave signed char, ( unsigned) short, ( unsigned) long, ( unsigned) long longper casi speciali.

4 dbush Jan 10 2021 at 05:26

Lo standard C garantisce solo che an intpuò essere (in senso lato) 2 byte, a longpuò essere 4 byte e a long longpuò essere 8 byte.

In effetti, MSVC utilizza ancora un 4 byte longanche se ha un 4 byte int.

3 NateEldredge Jan 10 2021 at 05:38

L'unico requisito rilevante per inte long, allora e ora, è che intdeve essere di almeno 16 bit e longdeve essere di almeno 32 bit. I sistemi a 16 e 32 bit tendono entrambi ad avere 32 bit longe 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 longe non si preoccupano di long longingrandirlo, 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.

2 PeterCordes Jan 10 2021 at 10:29

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_MAXe 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 è longstata una scelta abbastanza sensata.

Nessun ABI sano di mente per una macchina a 32 bit come i386 userebbe 64 bit longperché non è richiesto, solo 32 bit. L'uso di 64 bit significherebbe che non potrebbe essere contenuto in un singolo registro. Compilare con -m32o 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 longun tipo a 64 bit probabilmente non è eccezionale. Alcuni codici utilizzano longperché desiderano un tipo che deve essere a 32 bit, ma non trae vantaggio dall'averlo più ampio. Per tale codice, 64 bit longspreca 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 longvolte 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 longnon sono così.

Un altro intero barattolo di worm è int_fast32_tla 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_tsolleva 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 ++
old_timer Jan 11 2021 at 06:16

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.