Determinazione di 32 vs 64 bit in C ++

Sto cercando un modo per determinare in modo affidabile se il codice C ++ viene compilato in 32 vs 64 bit. Abbiamo ideato quella che pensiamo sia una soluzione ragionevole usando le macro, ma era curioso di sapere se le persone potessero pensare a casi in cui ciò potrebbe fallire o se c’è un modo migliore per farlo. Si prega di notare che stiamo cercando di farlo in un ambiente multipiattaforma multiplo.

#if ((ULONG_MAX) == (UINT_MAX)) # define IS32BIT #else # define IS64BIT #endif #ifdef IS64BIT DoMy64BitOperation() #else DoMy32BitOperation() #endif 

Grazie.

Sfortunatamente non esiste una macro cross platform che definisce 32/64 bit tra i principali compilatori. Ho trovato il modo più efficace per fare questo è il seguente.

Per prima cosa scelgo la mia rappresentazione. Preferisco ENVIRONMENT64 / ENVIRONMENT32. Poi scopro cosa usano tutti i principali compilatori per determinare se si tratta di un ambiente a 64 bit o meno e usarlo per impostare le mie variabili.

 // Check windows #if _WIN32 || _WIN64 #if _WIN64 #define ENVIRONMENT64 #else #define ENVIRONMENT32 #endif #endif // Check GCC #if __GNUC__ #if __x86_64__ || __ppc64__ #define ENVIRONMENT64 #else #define ENVIRONMENT32 #endif #endif 

Un altro percorso più semplice è semplicemente impostare queste variabili dalla riga di comando del compilatore.

 template void DoMyOperationHelper(); template<> void DoMyOperationHelper<4>() { // do 32-bits operations } template<> void DoMyOperationHelper<8>() { // do 64-bits operations } // helper function just to hide clumsy syntax inline void DoMyOperation() { DoMyOperationHelper(); } int main() { // appropriate function will be selected at compile time DoMyOperation(); return 0; } 

Sfortunatamente, in un ambiente cross-compiler, non esiste un singolo metodo affidabile per farlo semplicemente al momento della compilazione.

  • Sia _WIN32 che _WIN64 possono talvolta essere entrambi indefiniti, se le impostazioni del progetto sono difettose o danneggiate (in particolare su Visual Studio 2008 SP1).
  • Un progetto con l’etichetta “Win32” potrebbe essere impostato su 64 bit, a causa di un errore di configurazione del progetto.
  • In Visual Studio 2008 SP1, a volte l’intellisense non oscura le parti corrette del codice, in base al #define corrente. Questo rende difficile vedere esattamente quale #define viene usato in fase di compilazione.

Pertanto, l’ unico metodo affidabile è quello di combinare 3 semplici controlli :

  • 1) Impostazione del tempo di compilazione , e;
  • 2) Controllo runtime , e;
  • 3) Controllo robusto del tempo di compilazione .

Controllo semplice 1/3: impostazione del tempo di compilazione

Scegli qualsiasi metodo per impostare la variabile #define richiesta. Suggerisco il metodo da @JaredPar:

 // Check windows #if _WIN32 || _WIN64 #if _WIN64 #define ENV64BIT #else #define ENV32BIT #endif #endif // Check GCC #if __GNUC__ #if __x86_64__ || __ppc64__ #define ENV64BIT #else #define ENV32BIT #endif #endif 

Semplice controllo 2/3: controllo di runtime

In main (), controlla per vedere se sizeof () ha senso:

 #if defined(ENV64BIT) if (sizeof(void*) != 8) { wprintf(L"ENV64BIT: Error: pointer should be 8 bytes. Exiting."); exit(0); } wprintf(L"Diagnostics: we are running in 64-bit mode.\n"); #elif defined (ENV32BIT) if (sizeof(void*) != 4) { wprintf(L"ENV32BIT: Error: pointer should be 4 bytes. Exiting."); exit(0); } wprintf(L"Diagnostics: we are running in 32-bit mode.\n"); #else #error "Must define either ENV32BIT or ENV64BIT". #endif 

Controllo semplice 3/3: controllo accurato del tempo di compilazione

La regola generale è “ogni #define deve terminare con un #else che genera un errore”.

 #if defined(ENV64BIT) // 64-bit code here. #elif defined (ENV32BIT) // 32-bit code here. #else // INCREASE ROBUSTNESS. ALWAYS THROW AN ERROR ON THE ELSE. // - What if I made a typo and checked for ENV6BIT instead of ENV64BIT? // - What if both ENV64BIT and ENV32BIT are not defined? // - What if project is corrupted, and _WIN64 and _WIN32 are not defined? // - What if I didn't include the required header file? // - What if I checked for _WIN32 first instead of second? // (in Windows, both are defined in 64-bit, so this will break codebase) // - What if the code has just been ported to a different OS? // - What if there is an unknown unknown, not mentioned in this list so far? // I'm only human, and the mistakes above would break the *entire* codebase. #error "Must define either ENV32BIT or ENV64BIT" #endif 

Aggiornamento 2017-01-17

Commento di @AI.G :

4 anni dopo (non so se fosse ansible prima) è ansible convertire il controllo in fase di esecuzione in fase di compilazione utilizzando l’asserzione statica: static_assert (sizeof (void *) == 4) ;. Ora è tutto finito in fase di compilazione 🙂

Appendice A

Per inciso, le regole di cui sopra possono essere adattate per rendere l’intera base di codici più affidabile:

  • Ogni istruzione if () termina con un “else” che genera un avvertimento o un errore.
  • Ogni istruzione switch () termina con un “default:” che genera un avvertimento o un errore.

Il motivo per cui questo funziona bene è che ti costringe a pensare a ogni singolo caso in anticipo e non a fare affidamento sulla logica (a volte imperfetta) nella parte “else” per eseguire il codice corretto.

Ho usato questa tecnica (tra molte altre) per scrivere un progetto di 30.000 linee che funzionava perfettamente dal giorno in cui è stato distribuito in produzione (12 mesi fa).

Dovresti essere in grado di utilizzare le macro definite in stdint.h . In particolare INTPTR_MAX è esattamente il valore che ti serve.

 #include  #if INTPTR_MAX == INT32_MAX #define THIS_IS_32_BIT_ENVIRONMENT #elif INTPTR_MAX == INT64_MAX #define THIS_IS_64_BIT_ENVIRONMENT #else #error "Environment not 32 or 64-bit." #endif 

Alcune (tutte?) Versioni del compilatore di Microsoft non vengono fornite con stdint.h . Non so perché, dato che è un file standard. Ecco una versione che puoi utilizzare: http://msinttypes.googlecode.com/svn/trunk/stdint.h

Quello non funzionerà su Windows per un inizio. Longs e Ints sono entrambi a 32 bit, indipendentemente dalla compilazione per windows a 32 bit o 64 bit. Penserei che controllare se la dimensione di un puntatore è 8 byte è probabilmente un percorso più affidabile.

Potresti fare questo:

 #if __WORDSIZE == 64 char *size = "64bits"; #else char *size = "32bits"; #endif 
 Try this: #ifdef _WIN64 // 64 bit code #elif _WIN32 // 32 bit code #else if(sizeof(void*)==4) // 32 bit code else // 64 bit code #endif 

“Compiled in 64 bit” non è ben definito in C ++.

C ++ imposta solo limiti inferiori per dimensioni come int, long e void * . Non vi è alcuna garanzia che int sia a 64 bit anche se compilato per una piattaforma a 64 bit. Il modello consente ad es. 23 bit int s e sizeof(int *) != sizeof(char *)

Esistono diversi modelli di programmazione per piattaforms a 64 bit.

La tua migliore scommessa è un test specifico per piattaforma. La tua seconda scelta, più portatile, deve essere più specifica in ciò che è 64 bit.

Le persone hanno già suggerito metodi che cercheranno di determinare se il programma viene compilato a 32-bit o 64-bit .

E voglio aggiungere che puoi usare la funzione static_assert di c ++ 11 per assicurarti che l’architettura sia ciò che pensi sia (“rilassarsi”).

Quindi nel posto in cui definisci i macro:

 #if ... # define IS32BIT static_assert(sizeof(void *) == 4, "Error: The Arch is not what I think it is") #elif ... # define IS64BIT static_assert(sizeof(void *) == 8, "Error: The Arch is not what I think it is") #else # error "Cannot determine the Arch" #endif 

Il tuo approccio non era troppo lontano, ma stai solo controllando se long e int hanno le stesse dimensioni. In teoria, potrebbero essere entrambi a 64 bit, nel qual caso il controllo fallirebbe, supponendo che entrambi fossero 32 bit. Ecco un controllo che controlla effettivamente la dimensione dei tipi stessi, non la loro dimensione relativa:

 #if ((UINT_MAX) == 0xffffffffu) #define INT_IS32BIT #else #define INT_IS64BIT #endif #if ((ULONG_MAX) == 0xfffffffful) #define LONG_IS32BIT #else #define LONG_IS64BIT #endif 

In linea di principio, puoi farlo per qualsiasi tipo per cui hai una macro definita dal sistema con il valore massimo.

Si noti che lo standard richiede long long per essere almeno 64 bit anche su sistemi a 32 bit.

Sotto il codice funziona bene per la maggior parte degli ambienti attuali:

  #if defined(__LP64__) || defined(_WIN64) || (defined(__x86_64__) && !defined(__ILP32__) ) || defined(_M_X64) || defined(__ia64) || defined (_M_IA64) || defined(__aarch64__) || defined(__powerpc64__) #define IS64BIT 1 #else #define IS32BIT 1 #endif 

Se è ansible utilizzare le configurazioni di progetto in tutti gli ambienti, sarebbe facile definire un simbolo a 64 e 32 bit. Quindi avresti configurazioni di progetto come questa:

Debug a 32 bit
Versione a 32 bit
Debug a 64 bit
Versione a 64 bit

EDIT: si tratta di configurazioni generiche, non di configurazioni mirate. Chiamali come vuoi

Se non puoi farlo, mi piace l’idea di Jared.

Metterei sorgenti a 32-bit e 64-bit in file diversi e quindi selezionare i file sorgente appropriati usando il sistema di compilazione.