- Overview
- Table of Contents
- Special Member Functions: Constructors, Destructors, and the Assignment Operator
- Operator Overloading
- Memory Management
- Templates
- Namespaces
- Time and Date Library
- Streams
- Object-Oriented Programming and Design Principles
- The Standard Template Library (STL) and Generic Programming
- Exception Handling
- Runtime Type Information (RTTI)
- Signal Processing
- Creating Persistent Objects
- Bit Fields
- New Cast Operators
- Environment Variables
- Variadic Functions
- Pointers to Functions
- Function Objects
- Pointers to Members
- Lock Files
- Design Patterns
- Dynamic Linking
- Tips and Techniques
- A Tour of C99
- C++0X: The New Face of Standard C++ New
- C++0x Concurrency
- The Reflecting Circle
- We Have Mail
- The Soapbox
- Numeric Types and Arithmetic
- Careers
- Locales and Internationalization
Migrating to 64-Bit Environments
Created Apr 11, 2003.
32-bit architectures will remain predominant in the near future. However, developers should bear in mind that their applications may sooner or later be ported to 64-bit environments, as these are becoming increasingly widespread. In this article, I discuss the ramifications of migrating to 64-bit environments and explain the major differences between the object models of these two environments.
Compatibility Issues
The good news is that, in the majority of cases, migrating your 32-bit apps to a 64-bit environment should be simple. All that it would take is recompilation and re-linking. In practice, however, a few issues need special attention.
When speaking of portability of C++ code, one usually means source file portability. By contrast, the binaries are rarely portable even among different compilers on the same platform. Porting 32-bit code to a 64-bit environment means that there could be substantial differences between the binaries produced from the same source file under the two environments. Let's look at some of these differences more closely.
Built-in Types
C++ programmers who started their career in the DOS heyday learned to use long whenever they needed a 32-bit integer. However, in certain 64-bit systems long is now a 64-bit integer. If your code depends on the actual number of bits that a datatype occupies, you should use the standard typedef names:
//defined in the C99 <inttypes.h> int8_t int16_t int32_t int64_t
uint8_t uint16_t uint32_t uint64_t
Unfortunately, these typedefs are defined by C99 but not by ISO C++. In practice, however, most C++ compilers support C99 extensions including <inttypes.h>. Alternatively, you may define your own typedef names and use conditional compilation to ensure platform-independence.
Another problem that may arise is that new processors sometimes don't support 8-bit types at all. C++ requires that the smallest addressable datatype should be char. Such platforms represent char as a 16-bit type. Again, if the underlying number of bytes is not an issue, you can safely ignore this difference since all the standard char * functions and data structures will work as expected. If you truly need an 8-bit datatype, you have to use bit fields or other kludges. Should that be the case, it's always advisable to hide the low-level details in a class such as std::bistet or a similar custom class.
Pointers
32-bit systems represent data pointers as a 32-bit integer. 64-bit systems have 64 bit pointers in addition to, or instead of, 32-bit pointers. This duality enables users to run 32-bit programs under an emulator. Ideally, however, programs should use the default addressing mode which relies on 64-bit pointers. This difference affects pointers to freestanding functions as well.
Pointers to members are a different story, though. Under 32-bit architectures, they may have various sizes, depending on compiler settings, the member function in question and their class's inheritance lattice. In most cases, a typical pointer to member is represented as a struct that packs two ordinary pointers. This means that under a 64-bit system a typical pointer to member will have twice as many bits compared to its 32-bit counterpart. There could also be differences with respect to the actual values of pointers to members due to the alignment scheme of 64-bit environments.
Member Alignment
Member alignment is affected by the addressing mode of the target platform, the hardware's alignment requirements and compiler switches. In 64-bit systems you should expect a quadword (8 bytes or 64 bits) alignment scheme or higher, as opposed to the typical dword (4 bytes) alignment of 32-bit systems. Consequently, the sizes of user-defined types i.e., structs, classes, unions and enumerations could increase due the extra padding bytes inserted between members.
ABI
A platform's ABI defines a binary protocol for naming symbols and other layout issues. These symbols include the decorated names of functions, external objects, static data members and so on. The decorated names of functions with C++ linkage encode, among other things, their parameter types and their stack offset. Because the parameters' size may change when migrating to a 64-bit environment, the decorated names of functions, member functions, templates, structs, classes and so on may change accordingly.
Let's look at a concrete example:
struct S
{
void * p; // 4/8 bytes under 32/64 bit systems
int n; // 4 bytes
}; //sizeof (S)==8/16 under 32/64 bit envs, respectively
void func (S& );
The decorated name produced by a 32-bit compiler for the function func() could look like this:
v__func@@struct_S8@
A 64-bit compiler may produce this decorated name instead:
v__func@@struct_S16@
This difference eliminates the risk of inadvertently linking incompatible binaries. However, it also means that if you have a library or a dll only as a binary file, you will have to get an updated 64-bit version thereof. Tools that convert pure .exe and other binary formats are available on some 64-bit platforms. However, the best solution is to recompile the original source files.
Summary
The advent of 64-bit systems demonstrates once more the importance of separating low-level, platform dependent implementation details from the interface. Code that doesn't rely on hard-coded magic numbers and specific sizes of datatypes is easier to port. Therefore, even if the migration to a 64-bit environment still seems remote, it's best to design your new apps so that this migration would be as smooth and automatic as possible, necessitating only recompilation and re-linking.


Account Sign In
View your cart