Home > Articles > Programming > C/C++

C++ Reference Guide

Hosted by

Toggle Open Guide Table of ContentsGuide Contents

Close Table of ContentsGuide Contents

Close Table of Contents

C99 Core Features in C++0X

Last updated Jan 1, 2003.

The latest C standard ISO/IEC 9899 (known as C99) introduced quite a few core and library extensions that aren't supported in ISO C++ yet. The C++ standards committee is working with WG14 (the ISO C standards committee) to resolve these incompatibilities. The aim is to adopt C99 features that can be useful or necessary in C++, while rejecting features and library extensions that C+ doesn't need. Here, I present two of these C99 features: long long and intptr_t, and discuss their future in C++.

A long long Story

The C99 long long type and its unsigned counterpart aren't really needed in core C++. Since K&R days, C has always had at least one redundant integral type which is identical to at least one more integral type. In 32-bit systems, int and long are identical; in 16-bit systems, short and int were interchangeable, and so on. Therefore, it was possible to redefine long as a 64-bit integral type in 64-bit systems, while retaining int as a 32-bit integral type in 32/64-bit systems. Indeed, this is exactly what many 64-bit systems have done, including Solaris and Win64.

The main reason for introducing long long into C99 was the C99 <math.h> library. For example, take this minimal pair:

#include <math.h>
long int lrint(double x); //32-bit
long long llrint(double x);//64-bit

If you omit one long from each prototype, a 32-bit platform sees these two functions as having the same return type.

However, long long is incompatible with C++98. If you try to compile a C program that uses long long, you get a compilation error. Look at the following program:

int main()
{
 long long x=0LL;
 x++;
}

Using Comeau C/C++ 4.3.3, it compiles successfully in C mode. When compiled in strict C++ mode, you get a compilation error:

Line 3: error: the type "long long" is nonstandard,
    Consideration: Use --long_long for this to work in strict mode
 long long x=0LL;
    ^

The proposed recommendation to use long_long is problematic because other compilers don't recognize this token. Furthermore, if you fix the code as recommended and compile it once more as a C program, you're in trouble again:

int main()
{
long_long x=0LL; //compiled in C mode
x++;
}
"ComeauTest.c", line 3: error: identifier "long_long" is undefined
 long_long x=0LL;
 ^

This is a serious compatibility issue, especially for programmers who use both C++ and C99. The only workaround, at present, is conditional compilation. To solve this problem, the C++ standards committee has decided to add long long as well as their corresponding LL and ULL affixes to C++.

intptr_t and uintptr_t

The C99 header <inttypes.h> declares sets of typedefs for portable integral types, such as fastest minimum-width integer types. For example:

int_fast8_t int_fast16_t int_fast32_t int_fast64_t

exact-width signed integer types such as:

int8_t   int16_t   int32_t   int64_t

And more. In addition, this header also declares the following typedef name:

intptr_t

Any valid pointer to an object (the original C99 text speaks of "any valid pointer to void") can be converted to this type, and then converted back safely to void *. The result must compare equal to the original pointer. In other words, intptr_t legalizes a popular yet non-portable tradition of converting data pointers to integral types, and vice versa. The POSIX API defines several functions that rely on such conversions; similarly, Windows frameworks often store the this pointer as an integer. C++0x will support intptr_t, too.

The introduction of intptr_t is chiefly important in 64-bit computing. The problem is that most 64-bit systems use the LP64 model, whereby long and data pointers have the same size of 64-bits, whereas int has only 32-bits. If you want to write dually-targeted code for 32/64-bit platforms, intptr_t is guaranteed to work correctly on both.

uintptr_t is the unsigned counterpart of this typedef.

intptr_t and GC

Interestingly, intptr_t has recently drawn the standards committee's attention with reference to a standard garbage collector (GC). A typical GC keeps track of pointers in the program, ensuring that the memory to which they point is released when they are no longer referenced in the program. In simples cases such as

void f()
{
S * ptr = new S;
}//ptr's memory can now be collected

The GC can easily recognize that ptr cannot be referenced after f() returns, so it knows that the memory to which ptr points can be reclaimed. However, if you use an intptr_t variable to disguise a pointer, the GC might not recognize that the object to which ptr is bound is still referenced outside f():

void f(intptr_t & alias)
{
 S * ptr = new S;
 alias=(intptr_t)ptr;
}

The caller can still access the dynamically allocated object through the disguised pointer alias. The GC can't know that; after all, alias might be used as an ordinary integer, too. If the GC decides to reclaim the memory of ptr after f() returns, the caller might still access it via alias. This is just as bad as accessing an object to a pointer after deleting it. By contrast, if the GC decides to mark ptr's value as referenced (i.e., its memory shouldn't be reclaimed), this program might cause a memory leak.

That said, intptr_t is here to stay. It was introduced to permit commonplace C programming practices. C++ will have to follow suit. GC challenges notwithstanding, intptr_t is one of the smallest problems that any standard C++ GC will have to deal with.