Comparison of FreeBASIC and C integer data types
 

This page is a guide for writing FreeBASIC declarations for C libraries.
It is not a promise that every C type name has one universal FreeBASIC
spelling.

C has two different concerns that are easy to mix up:

  • ABI compatibility: the argument has the same size, signedness, alignment,
and calling convention.
  • C spelling compatibility: the generated declaration uses the same C type
spelling, which can matter for C name mangling, GCC/Clang builtin
prototype checks, and compiler diagnostics.

For ordinary Extern "C" calls, ABI compatibility is often enough. For C
bindings and compiler builtins, exact C spelling can matter.

The most important rule is that Long in FreeBASIC is
always 32-bit. It is not the same thing as C long on all targets.
Integer is pointer-sized: 32-bit on 32-bit targets and
64-bit on 64-bit targets.

Common C data models

C data model Example targets in this tree C int C long C long long Pointer size
ILP32 DOS, Win32, 32-bit Linux/BSD/Android, Wii, Xbox, WebAssembly/JavaScript 32 bits 32 bits 64 bits 32 bits
LLP64 Win64, Windows ARM64 32 bits 32 bits 64 bits 64 bits
LP64 64-bit Linux/BSD/Android/macOS/Solaris/illumos/Haiku/Cygwin 32 bits 64 bits 64 bits 64 bits


All targets supported by this tree use a 32-bit C int. This is a statement
about the targets supported here, not about every possible C implementation.

FreeBASIC integer sizes

FreeBASIC type Signedness Size Good ABI match for
Byte signed 8 bits signed char
UByte unsigned 8 bits unsigned char or raw bytes
Short signed 16 bits short
UShort unsigned 16 bits unsigned short
Long signed 32 bits int, int32_t on most supported targets
ULong unsigned 32 bits unsigned int, uint32_t on most supported targets
LongInt signed 64 bits long long
ULongInt unsigned 64 bits unsigned long long
Integer signed pointer size intptr_t or pointer-sized indexes
UInteger unsigned pointer size uintptr_t, size/count values when the target typedef agrees
Any Ptr pointer pointer size void * or opaque handles


The table above is about ABI-sized values. It is not enough when the exact C
spelling matters. For example, on LP64 targets C long and C long long
are both 64-bit, but they are still different C types.

Recommended binding choices

C type or typedef Use in FreeBASIC Why Notes
signed char Byte exact 8-bit signed storage Plain C char is a different C spelling
unsigned char UByte exact 8-bit unsigned storage Also useful for raw byte buffers
char ZString Ptr for C strings, otherwise a deliberate Byte/UByte based alias C char signedness is target dependent Plain char is distinct from signed char and unsigned char in C++
short Short exact 16-bit signed storage
unsigned short UShort exact 16-bit unsigned storage
int Long C int is 32-bit on supported targets
unsigned int ULong C unsigned int is 32-bit on supported targets
long target c_long alias C long is 32-bit on ILP32/LLP64 and 64-bit on LP64 Do not blindly use FB Long
unsigned long target c_ulong alias same data model issue as C long Do not blindly use FB ULong
long long LongInt natural 64-bit signed long long spelling Not the exact spelling of LP64 C long
unsigned long long ULongInt natural 64-bit unsigned long long spelling Not the exact spelling of LP64 unsigned long
int32_t, uint32_t crt/stdint.bi typedefs fixed-width C typedefs are already translated Use Long/ULong only when the actual header spelling does not matter
int64_t, uint64_t crt/stdint.bi typedefs fixed-width 64-bit typedef spelling can differ by target Do not assume every int64_t is C long long
intptr_t, uintptr_t crt/stdint.bi typedefs pointer-sized integer typedefs Integer/UInteger are the underlying FB-sized types
ptrdiff_t, size_t, ssize_t crt/stddef.bi typedefs spelling differs between LP64, LLP64 and some 32-bit targets Avoid hand-written mappings unless needed
wchar_t, wint_t crt/stddef.bi typedefs width and signedness vary by target Never assume wchar_t is always 16-bit
enum usually Long for ordinary C enums ordinary C enum ABI is normally int-sized on these targets verify packed enums, compiler flags, and C++ scoped enums


For target-specific C long, define a small alias once and use it in the
binding:

Example
' C long follows the target C data model.
#if defined( __FB_64BIT__ ) And defined( __FB_UNIX__ )
    Type c_long  As Integer  Alias "long"
    Type c_ulong As UInteger Alias "long"
#else
    Type c_long  As Long  Alias "long"
    Type c_ulong As ULong Alias "long"
#endif

Extern "C"
    Declare Function c_function( ByVal value As c_long ) As c_long
End Extern


For standard C typedefs, prefer the translated CRT headers instead of spelling
the mapping by hand:

#include Once "crt/stddef.bi"   ' size_t, ptrdiff_t, wchar_t
#include Once "crt/stdint.bi"   ' int32_t, uint64_t, intptr_t


Common mistakes

  • Do not translate C long as FreeBASIC Long without checking the
target data model.
  • Do not translate LP64 C long as LongInt when exact C spelling
matters. The size matches, but the C type is long, not long long.
  • Do not use Integer as a universal replacement for C long.
Integer is pointer-sized, while C long is ABI-model-sized.
  • Do not assume size_t is always the same C spelling as
unsigned long or unsigned long long. Use the target typedef.
  • Do not assume plain C char has the same signedness on every target.
Use signed char or unsigned char in the C API when signedness
matters.
  • Do not assume standard typedefs such as time_t, wchar_t, or
ssize_t are the same across all operating systems.

See also