2013-05-03 4 views
6

In C99 standard Sezione 7.18.4.1 "Macro per costanti in ordine di larghezza minima", alcune macro definite come [U]INT[N]_C(x) per il cast di interi costanti su tipi di dati minime dove N = 8, 16, 32, 64. Perché sono definite queste macro poiché posso utilizzare i modificatori L, UL, LL o ULL? Ad esempio, quando voglio utilizzare almeno numero intero senza segno a 32 bit, posso semplicemente scrivere 42UL anziché UINT32_C(42). Poiché il tipo di dati lunghi ha una larghezza di almeno 32 bit, è anche portatile.Qual è lo scopo di "Macro per costanti intere di larghezza minima"

Quindi, qual è lo scopo di queste macro?

+0

Penso che siano così che non devi preoccuparti di cose come quanto è grande un 'int' o un' lungo' quando si creano i propri tipi di numeri interi fissi. –

risposta

7

Tu li avresti bisogno in luoghi in cui si desidera fare in modo che non diventino troppo largo

#define myConstant UINT32_C(42) 

e successivamente

printf("%" PRId32 " is %s\n", (hasproperty ? toto : myConstant), "rich"); 

qui, se la costante sarebbe UL il espressione potrebbe essere ulong e la funzione variadic potrebbe mettere in pila un valore a 64 bit che sarebbe erroneamente interpretato da printf.

+0

Quindi, stai suggerendo che lungo può essere 64-bit su alcune piattaforme a 32-bit? Pertanto sarebbe problematico in alcune funzioni come printf. In tal caso, avrebbe anche un impatto sulle prestazioni, poiché la manipolazione di numeri interi a 64 bit si verifica quando 32 bit sono sufficienti. – obareey

+1

sì, questo dipende dai progettisti della piattaforma.E non sovrastimare la differenza di prestazioni per operazioni su interi di larghezza diversa, su macchine moderne non ce ne sono per l'aritmetica stessa. L'unica differenza potrebbe essere se si eseguono molte di tali operazioni in un ciclo e l'accesso alla memoria sarebbe un collo di bottiglia. Al contrario, i moderni processori avx eseguono operazioni a 256 bit veloci quanto operazioni a 32 bit, quindi in genere è una cattiva idea scegliere tipi di larghezza specificata. Usa 'size_t',' ptrdiff_t' e cose del genere che si adattano alla semantica di ciò che stai facendo. –

+0

Esistono compilatori a 16 bit in cui 'const uint32_t var = 0xFFFF0000;' potrebbe risultare in var = 0x0000? Cosa dicono le specifiche C99 e C89 sulla dimensione di una costante? – Samuel

3

usano il tipo intero più piccolo con una larghezza di almeno N, quindi UINT32_C(42) equivale solo 42UL nei sistemi in cui int è inferiore a 32 bit. Sui sistemi in cui int è uguale o maggiore di 32 bit, UINT32_C(42) equivale a 42U. Si potrebbe anche immaginare un sistema in cui uno short ha una larghezza di 32 bit, nel qual caso UINT32_C(42) equivale a (unsigned short)42.

MODIFICA: @obareey Sembra che la maggior parte, se non tutte, le implementazioni della libreria standard non siano conformi a questa parte dello standard, forse perché è impossibile. [glibc bug 2841] [glibc commit b7398be5]

+0

Ma su quelle piattaforme non importa se 32 bit è breve, perché sarebbe il più veloce intero con almeno 32 bit, o pensavo così. Ora sono confuso. Perché scrive "La macro UINTN_C (valore) si espanderà ad un'espressione costante intera corrispondente al tipo uint_leastN_t" nello standard, ma il compilatore ARM a 32 bit definisce "#define UINT8_C (x) (x ## u)" e " #define UINT16_C (x) (x ## u) "senza alcun casting. – obareey

+0

Il più piccolo, non il più veloce. – Oktalist

+0

Mi chiedo quanto spesso una di queste macro venga effettivamente utilizzata per rendere il codice più portatile? La semantica di cose come i confronti firmati contro i non firmati ha così tanti casi angusti e bizzarri che non riesco a vedere come si possa sperare di far fronte a tutti. Ad esempio, dato 'uint32_t n', quale sarebbe il comportamento di' (n-1) supercat

0

Le macro essenzialmente eventualmente aggiungere un numero intero costante suffisso come L, LL, U, UL, o UL la loro tesi, che rende praticamente loro quasi equivalente alla corrispondente cast, tranne che il il suffisso non sarà mai abbattuto.

esempio UINT32_C(42000000000) (42 miliardi) su un'architettura LLP64 diventerà 42000000000U, che avrà tipo UL soggetto alla rules explained here. Il cast corrispondente, d'altra parte ((uint32_t)42000000000), lo troncerebbe fino a uint32_t (unsigned int su LLP64).

Non riesco a pensare a un buon caso d'uso, ma immagino che potrebbe essere utilizzabile in alcune macro generiche a bit-twid che necessitano almeno di X bit per funzionare, ma non voglio rimuovere alcun bit in più se il l'utente passa in qualcosa di più grande.