C ++ 'da 32'ye 64 bit belirlenmesi


136

Güvenilir bir şekilde C ++ kodu 32 vs 64 bit derleniyor olup olmadığını belirlemek için bir yol arıyorum. Makroları kullanarak makul bir çözüm olduğunu düşündüğümüz şeyleri bulduk, ancak insanların bunun başarısız olabileceği durumları düşünüp düşünemeyeceğini veya bunu yapmanın daha iyi bir yolu olup olmadığını merak ediyorduk. Bunu, platformlar arası, çoklu derleyici ortamında yapmaya çalıştığımızı lütfen unutmayın.

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

#ifdef IS64BIT
DoMy64BitOperation()
#else
DoMy32BitOperation()
#endif

Teşekkürler.


8
Mimarinizin kelime boyutunun ne olduğunu gerçekten önemsiyorsanız, 32 veya 64 bit olma olasılığını göz ardı etmeyin. Orada 16 ve 128 bit mimariler var.
alex tingle

64 bit ve 32 bit işlem arasındaki fark nedir?
peterchen

2
Bunu gerçekten hedef platformun kelime genişliği üzerinde koşullandırmamalısınız. Bunun yerine, ne yapılacağını belirlemek için doğrudan ilgili veri türlerinin boyutunu kullanın. stdint.harkadaşınız olabilir veya kendinize ait bazı uygun tanımları geliştirmeniz gerekebilir.
Phil Miller

Bu sınama Visual Studio 2008 SP1 üzerinde çalışmıyor gibi görünüyor. 32-bit ve 64-bit için "IS64BIT" takılı kalır.
Contango

Yanıtlar:


99

Ne yazık ki, büyük derleyiciler arasında 32/64 bit tanımlayan çapraz platform makrosu yoktur. Bunu yapmanın en etkili yolunu buldum.

Önce kendi temsilciliğimi seçiyorum. ÇEVRE64 / ÇEVRE32'yi tercih ederim. Sonra tüm büyük derleyicilerin 64 bit ortam olup olmadığını belirlemek için ne kullandığını öğreniyorum ve bunu değişkenlerimi ayarlamak için kullanıyorum.

// 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

Başka bir kolay yol da bu değişkenleri derleyici komut satırından ayarlamaktır.


3
GCC ve VS dışında başka derleyiciler de var. Örneğin QNX ve GHS akla geliyor (her ne kadar QNX'in GCC'ye benzer benzer zaman tanımları olduğundan şüpheleniyorum). Ayrıca GCC çekinizde MIPS64 ve IA64 mimarilerini unuttun
Rom

14
@Rom, kesinlikle 2'den fazla derleyici ve mimari. Bu sadece tam bir çözüm değil, bu soruna nasıl yaklaşılacağının bir örneği olmalıdır.
JaredPar

2
"Genellikle" diyorum. "İdeal" muhtemelen daha gerçekçi.
Steve Jessop

7
" #İf tanımlı ( WIN32 ) || tanımlı (_WIN64)" vb. Kullanmanız gerektiğini düşünüyorum
KindDragon

3
#if _WIN32 || _WIN64... #elif __GNUC__... #else # error "Missing feature-test macro for 32/64-bit on this compiler."?
Davislor

100
template<int> 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<sizeof(size_t)>(); }

int main()
{
  // appropriate function will be selected at compile time 
  DoMyOperation(); 

  return 0;
}

2
Size_t 4 veya 8 değilse ne olur?
Jesper

16
@Jesper, Sonra yukarıdaki örnekte bağlantı hatası alırsınız. Veya bu dava için DoMyOperation'ı uygulayabilirsiniz
Kirill V. Lyadvinsky

1
Bir korelasyon yerine neyin önemli olduğunu (belirli bir türün boyutu) test etmek için şablonların ve kudosların kaygan kullanımı.
Phil Miller

2
Bunun için size_t kullanarak dikkatli olun. Örneğin, işaretçi boyutuna karşılık gelmediğinde sorun yaşayabilirsiniz (örneğin, birden fazla işaretçi boyutuna sahip platformlarda).
Logan Capaldo

8
Standart, boyutunun size_tsistemde ayrılmış herhangi bir nesnenin boyutunu tutacak kadar büyük olduğunu söylüyor . Koşullu derleme sırasında genellikle bilmek istediğiniz şeydir. İstediğiniz gibi değilse, bu snippet'i yerine başka bir türle kullanabilirsiniz size_t. Örneğin, olabilir void*.
Kirill V. Lyadvinsky

44

Ne yazık ki, bir çapraz platformda, çapraz derleyici ortamında, bunu yalnızca derleme zamanında yapmak için tek bir güvenilir yöntem yoktur.

  • Proje ayarları hatalı veya bozuksa (özellikle Visual Studio 2008 SP1'de) hem _WIN32 hem de _WIN64 bazen tanımsız olabilir .
  • "Win32" etiketli bir proje, bir proje yapılandırma hatası nedeniyle 64-bit olarak ayarlanabilir.
  • Visual Studio 2008 SP1'de, intellisense, geçerli #define göre bazen kodun doğru kısımlarını grileştirmez. Bu, derleme zamanında hangi #define kullanıldığını görmeyi zorlaştırır.

Bu nedenle, tek güvenilir yöntem 3 basit kontrolü birleştirmektir :

  • 1) Derleme zamanı ayarı ve;
  • 2) Çalışma zamanı kontrolü ve;
  • 3) Sağlam derleme zamanı kontrolü .

Basit kontrol 1/3: Derleme zamanı ayarı

Gerekli #define değişkenini ayarlamak için herhangi bir yöntem seçin . @JaredPar yöntemini öneririm:

// 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

Basit kontrol 2/3: Çalışma zamanı kontrolü

Main () öğesinde sizeof () yönteminin anlamlı olup olmadığını görmek için iki kez kontrol edin:

#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

Basit kontrol 3/3: Sağlam derleme zamanı kontrolü

Genel kural "her #define hata üreten bir #else ile bitmelidir" dir.

#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

Güncelleme 2017-01-17

Yorum yapan @AI.G:

4 yıl sonra (daha önce mümkün olup olmadığını bilmiyorum) çalışma zamanı kontrolünü statik assert kullanarak derleme zamanı kontrolüne dönüştürebilirsiniz: static_assert (sizeof (void *) == 4) ;. Şimdi her şey derleme zamanında yapılır :)

Ek Bölüm A

Gizli olarak, yukarıdaki kurallar tüm kod tabanınızı daha güvenilir hale getirmek için uyarlanabilir:

  • Her if () ifadesi, uyarı veya hata oluşturan bir "else" ile biter.
  • Her switch () ifadesi, bir uyarı veya hata oluşturan bir "default:" ile biter.

Bunun iyi çalışmasının nedeni, sizi her bir vakayı önceden düşünmeye ve doğru kodu yürütmek için "başka" kısımdaki (bazen hatalı) mantığa güvenmemeye zorlamasıdır.

Bu tekniği (diğerlerinin yanı sıra) ilk üretime verildiği günden (12 ay önce) 30.000 satırlık bir proje yazmak için kullandım.


sizeof(void*)derleme zamanında veya çalışma zamanında çözüldü mü? derleme zamanında ise, çalışma zamanında kontrol her zaman olacaktır if(8!=8){...}.
Ameen

@ameen Çalışma zamanında çözüldü. Bu kontrolün amacı, eğer bitite beklendiği gibi değilse, programın uygun bir hata ile çıkmasını sağlamaktır. Bu, geliştiricinin daha sonra açılan küçük hataları teşhis etmeye çalışmak yerine bu hatayı hemen düzeltebileceği anlamına gelir.
Contango

3
4 yıl sonra statik assert kullanarak tek seferlik derlemeye çalışma zamanı kontrol dönüştürebilir (daha önce mümkün olduğunu bilmiyorum): static_assert(sizeof(void*) == 4);. Şimdi her şey derleme zamanında yapıldı :)
Al.G.

1
static_assert(sizeof(void*) * CHAR_BIT == 32)daha etkileyici ve teknik olarak doğrudur (baytların 8'den farklı bit miktarlarına sahip herhangi bir mimari bilmememe rağmen)
Xeverous

1
Ayrıca bu mükemmel yanıtı Fluent C ++ 'dan " Better Makrolar, Better Flags " ile birleştiren aşağıdaki yanıtıma bakın .
metal

30

İçinde tanımlanan makroları kullanabilmeniz gerekir stdint.h. Özellikle INTPTR_MAXtam olarak ihtiyacınız olan değer.

#include <cstdint>
#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

Microsoft'un derleyicisinin bazı (tümü?) Sürümleri birlikte verilmez stdint.h. Neden olduğundan emin değilim, çünkü standart bir dosya. İşte kullanabileceğiniz bir sürüm:http://msinttypes.googlecode.com/svn/trunk/stdint.h


4
Microsoft için neden stdint.h yok? Çünkü C99 standardıyla tanıtıldı ve Microsoft, C99'dan en kolay şeyleri bile uygulamak için aktif bir isteksizliğe sahip gibi görünüyor. Derleyici değişikliği gerektirmeyen kolay kütüphane işleri bile. C ++ için derleme yapılırken yapılanlar bile (ifadelerden sonraki bildirimler gibi). Test vb. İhtiyacı olduğunu biliyorum, ama aynı zamanda MS'in Dinkumware / Plauger'dan kütüphanesinin adil bir parçasını aldığını (veya bir kez aldığını) ve Dinkumware'in yıllardır C99 kütüphane eşyalarını aldığını biliyorum.
Michael Burr

2
VC ++ 2010 (beta 1, neyse) <stdint.h>ve vardır <cstdint>. Mevcut işlere gelince - VC ++ kütüphanesi Dinkumware'den kaynaklanmaktadır (yine de - TR1 oradan da alınmıştır), ancak VCBlog'da okuduğumu hatırladığım kadarıyla, temiz bir şekilde derlemek için oldukça önemli bir yeniden düzenleme işleminden geçiyor, /clrtüm MSVC ile çalışıyorum standart olmayan türler __int64ve benzeri - bu yüzden sadece onu alıp bir sonraki derleyici sürümüne koymak kadar basit değil.
Pavel Minaev

2
Bu beni doğru cevaba götürdü, ama sanırım INT64_MAX değil UINT64_MAX ile karşılaştırmalısın. SIZE_MAX == UINT64_MAX - prob aynı kullandım
Arno Duvenhage

15

Bu, bir başlangıç ​​için Windows'ta çalışmaz. İster 32 bit ister 64 bit pencereler için derleme yapsanız, uzunlamasına ve ints 32 bittir. Bir işaretçinin boyutu 8 bayt olup olmadığını kontrol muhtemelen daha güvenilir bir yol olduğunu düşünüyorum.


2
Maalesef #if direktifinde sizeof yasaklanmıştır (eğer önişlemcinin anlatacak bir yolu yoktur)
EFraim

Evet, bu yüzden sizeof kullanmak yerine bir işaretçinin boyutunu kontrol etmeyi
önerdim

3
Soru (henüz) demiyor sahiptir öncesi işlemci anda yapılacak. Optimizasyonu olan birçok / en derleyici, "çalışma süresine kadar bırak" gibi bir testle bile ölü kodu ortadan kaldırmak için iyi bir iş çıkarır sizeof(void*) == 8 ? Do64Bit() : Do32Bit();. Bu hala kullanılmayan bir işlevi ikili dosyada bırakabilir, ancak ifade büyük olasılıkla "sağ" işlev çağrısına derlenir.
Steve Jessop

1
@ işlev çağrısı sorununu çözen herkes, ancak bir değişkeni platforma dayalı farklı bir tür olarak bildirmek istersem, birden çok değişkeni bildirmek ve bunları bir if ifadesine dayalı olarak kullanmak istemiyorsanız önişlemcide yapılması gerekir ( eğer kullanılmazlarsa optimize edilebilirler, ancak kodda çok hoş
olmazlar

1
Öyleyse haklısın, şartlı olarak sürekli bir ifade iyi değil. Kirill'in yaklaşımı ne istersen yapabilir:template<int> struct Thing; template<> struct Thing<4> { typedef uint32_t type; }; template<> struct Thing<8> { typedef uint64_t type; }; typedef Thing<sizeof(void*)>::type thingtype;
Steve Jessop

9

Bunu yapabilirsin:

#if __WORDSIZE == 64
char *size = "64bits";
#else
char *size = "32bits";
#endif

1
64-bit makinelerde C ve C-türetilmiş diller için birçok programlama ortamında, "int" değişkenleri hala 32 bit genişliğindedir, ancak uzun tamsayılar ve işaretçiler 64 bit genişliğindedir. Bunlar bir LP64 veri modeline sahip olarak tanımlanmaktadır. unix.org/version2/whatsnew/lp64_wp.html
Hermes

6
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

7
Bu kod doğru değil. 64 bitte hem _WIN32 hem de _WIN64 tanımlanır. Eğer ters çevirirseniz (önce _WIN64 için kontrol edin) elbette işe yarar.
BertR

4

"64 bit olarak derlendi" C ++ 'da iyi tanımlanmamıştır.

C ++, int, long ve gibi boyutlar için yalnızca alt sınırlar belirler void *. 64 bitlik bir platform için derlendiğinde bile int'in 64 bit olduğuna dair bir garanti yoktur. Model, örneğin 23 bit ints vesizeof(int *) != sizeof(char *)

Farklı programlama modelleri vardır64 bit platformlar için .

En iyi bahisiniz platforma özgü bir testtir. İkinci en iyi, taşınabilir kararınız 64 bit olan şeyde daha spesifik olmalıdır .


3

Kişisel yaklaşım çok uzakta değildi, ama yalnızca olmadığını kontrol ediyoruz longve intaynı büyüklüktedir. Teorik olarak, her ikisi de 64 bit olabilir, bu durumda kontrolünüz başarısız olur ve her ikisinin de 32 bit olduğunu varsayar. Burada, göreceli boyutlarını değil, türlerin boyutunu gerçekten kontrol eden bir kontrol vardır:

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

Prensip olarak, maksimum tanımlı sistem tanımlı makrosu olan herhangi bir tür için bunu yapabilirsiniz.

Standardın long long32 bit sistemlerde bile en az 64 bit olması gerektiğini unutmayın .


Dikkat edilmesi gereken bir nokta, UINT_MAX ve ULONG_MAX'ın tanımlanması için muhtemelen testlerinizden #include <limits.h>önce bir yere sahip olmak istersiniz #if.
Alexis Wilke

3

İnsanlar zaten programın derlenip derlenmediğini belirlemeye çalışacak yöntemler önerdi 32-bitveya 64-bit.

Ve static_assertmimarinin düşündüğünüz gibi olduğundan emin olmak için c ++ 11 özelliğini kullanabileceğinizi eklemek istiyorum ("rahatlamak").

Yani makroları tanımladığınız yerde:

#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

static_assert(sizeof(void*) * CHAR_BIT == 32)daha etkileyici ve teknik olarak doğrudur (baytların 8'den farklı bit miktarlarına sahip herhangi bir mimari bilmememe rağmen)
Xeverous

2

Aşağıdaki kod, mevcut ortamların çoğunda iyi çalışır:

  #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

3
_WIN64Önceden eklemenizi gerektiren not <windows.h>. : Visual C ++ ile, yerleşik derleyici tanımlayıp kullanmak daha iyidir _M_IX86, _M_X64, _M_ARM, _M_ARM64, vb
Chuck Walbourn

PowerPC için, sana için kontrol edilmesi gereken inanmak __ppc64__, __powerpc64__ve _ARCH_PPC64. Bu AIX ve diğer platformları da yakalar.
jww

1

Proje yapılandırmalarını tüm ortamlarınızda kullanabiliyorsanız, 64 ve 32 bitlik bir sembol tanımlamak kolaylaşır. Yani bunun gibi proje yapılandırmalarınız olur:

32 bit Hata Ayıklama
32 bit Sürüm
64 bit Hata Ayıklama
64 bit Sürüm

EDIT: Bunlar, hedeflenen yapılandırmalar değil, genel yapılandırmalardır. Onları ne istersen ara.

Bunu yapamazsan, Jared'ın fikrini seviyorum.


Ya da ikisini birleştirin: Bildiğiniz derleyicilerdeki yapılandırmayı otomatik olarak algılayın, ancak tanınmayan derleyicilerde proje / komut satırında / tanımlanmış herhangi bir #define bakmaya geri dönün.
Steve Jessop

4
VisualStudio'ya özgü çözümünüz OP'nin çapraz platform sorusuna nasıl yardımcı olacak?
alex tingle

3
@Jon: Hmm. Tanımı gereği herhangi bir çapraz platform ortamında DESTEKLENMEMEKTEDİR . MS'in çapraz platform tanımı olmadığı sürece - Windows'un yeni lezzetleri üzerinde çalışır.
EFraim

1
@EFraim: Evet, VS kullanarak 32 veya 64 bit TARGET yapabilirsiniz, ancak bahsettiğim bu değil. Genel proje yapılandırmaları ve onlara atadığım isimlerin platformla kesinlikle bir ilgisi yoktur. Proje yapılandırmaları VS'ye özgü ise, bu çok utanç verici çünkü çok kullanışlıdır.
Jon Seigel

1
Bence bu doğru cevap. Bazı şeyleri otomatik olarak algılamaya çalışmaktan daha güvenilirdir. Gördüğüm tüm IDE'ler bu özelliği bir şekilde destekliyor ve hiç görmediklerim de bunu destekliyor. Make veya jam kullanırsanız, çağrıldığında değişkenleri her zamanki gibi komut satırından ayarlayabilirsiniz.

1

32-bit ve 64-bit kaynakları farklı dosyalara yerleştirip derleme sistemini kullanarak uygun kaynak dosyaları seçerdim.


2
Bu, size böyle bir bayrak veren derleme sistemine sahip olmaya benzer -DBUILD_64BIT. Çoğu zaman, bazı şeyler hem 32 hem de 64 bit'e çok benzer, bu yüzden aynı dosyada olması oldukça pratik olabilir.
Alexis Wilke

İkiz kaynak dosyalarının bakımı hataya açıktır. IMO bile büyük bir #if bit64 .. tüm kod, 64bit #else .. tüm kod, 32bit #endif için bundan daha iyidir. (#
if's

1

Borçlanma repor 'ın üstünde mükemmel cevap ve 'birleştirerek daha iyi makrolar, daha iyi Flags Akıcı C' ++, bunu yapabilirsiniz:

// Macro for checking bitness (safer macros borrowed from 
// https://www.fluentcpp.com/2019/05/28/better-macros-better-flags/)
#define MYPROJ_IS_BITNESS( X ) MYPROJ_IS_BITNESS_PRIVATE_DEFINITION_##X()

// Bitness checks borrowed from https://stackoverflow.com/a/12338526/201787
#if _WIN64 || ( __GNUC__ && __x86_64__ )
#    define MYPROJ_IS_BITNESS_PRIVATE_DEFINITION_64() 1
#    define MYPROJ_IS_BITNESS_PRIVATE_DEFINITION_32() 0
#    define MYPROJ_IF_64_BIT_ELSE( x64, x86 ) (x64)
    static_assert( sizeof( void* ) == 8, "Pointer size is unexpected for this bitness" );
#elif _WIN32 || __GNUC__
#    define MYPROJ_IS_BITNESS_PRIVATE_DEFINITION_64() 0
#    define MYPROJ_IS_BITNESS_PRIVATE_DEFINITION_32() 1
#    define MYPROJ_IF_64_BIT_ELSE( x64, x86 ) (x86)
    static_assert( sizeof( void* ) == 4, "Pointer size is unexpected for this bitness" );
#else
#    error "Unknown bitness!"
#endif

Sonra şöyle kullanabilirsiniz:

#if MYPROJ_IS_BITNESS( 64 )
    DoMy64BitOperation()
#else
    DoMy32BitOperation()
#endif

Veya eklediğim ekstra makroyu kullanarak:

MYPROJ_IF_64_BIT_ELSE( DoMy64BitOperation(), DoMy32BitOperation() );

0

Bu yanıtı başka bir yanıtta açıklanan çalışma zamanı denetimi için bir kullanım durumu ve tam örnek olarak ekliyorum .

Programın 64 bit veya 32 bit (veya bu konuda başka) olarak derlenmiş olup olmadığını son kullanıcıya iletmek için kullandığım yaklaşım budur:

version.h

#ifndef MY_VERSION
#define MY_VERSION

#include <string>

const std::string version = "0.09";
const std::string arch = (std::to_string(sizeof(void*) * 8) + "-bit");

#endif

test.cc

#include <iostream>
#include "version.h"

int main()
{
    std::cerr << "My App v" << version << " [" << arch << "]" << std::endl;
}

Derleme ve Test Etme

g++ -g test.cc
./a.out
My App v0.09 [64-bit]
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.