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 modelExample targets in this treeC intC longC long longPointer size
ILP32DOS, Win32, 32-bit Linux/BSD/Android, Wii, Xbox, WebAssembly/JavaScript32 bits32 bits64 bits32 bits
LLP64Win64, Windows ARM6432 bits32 bits64 bits64 bits
LP6464-bit Linux/BSD/Android/macOS/Solaris/illumos/Haiku/Cygwin32 bits64 bits64 bits64 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 typeSignednessSizeGood ABI match for
Bytesigned8 bitssigned char
UByteunsigned8 bitsunsigned char or raw bytes
Shortsigned16 bitsshort
UShortunsigned16 bitsunsigned short
Longsigned32 bitsint, int32_t on most supported targets
ULongunsigned32 bitsunsigned int, uint32_t on most supported targets
LongIntsigned64 bitslong long
ULongIntunsigned64 bitsunsigned long long
Integersignedpointer sizeintptr_t or pointer-sized indexes
UIntegerunsignedpointer sizeuintptr_t, size/count values when the target typedef agrees
Any Ptrpointerpointer sizevoid * 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 typedefUse in FreeBASICWhyNotes
signed charByteexact 8-bit signed storagePlain C char is a different C spelling
unsigned charUByteexact 8-bit unsigned storageAlso useful for raw byte buffers
charZString Ptr for C strings, otherwise a deliberate Byte/UByte based aliasC char signedness is target dependentPlain char is distinct from signed char and unsigned char in C++
shortShortexact 16-bit signed storage
unsigned shortUShortexact 16-bit unsigned storage
intLongC int is 32-bit on supported targets
unsigned intULongC unsigned int is 32-bit on supported targets
longtarget c_long aliasC long is 32-bit on ILP32/LLP64 and 64-bit on LP64Do not blindly use FB Long
unsigned longtarget c_ulong aliassame data model issue as C longDo not blindly use FB ULong
long longLongIntnatural 64-bit signed long long spellingNot the exact spelling of LP64 C long
unsigned long longULongIntnatural 64-bit unsigned long long spellingNot the exact spelling of LP64 unsigned long
int32_t, uint32_tcrt/stdint.bi typedefsfixed-width C typedefs are already translatedUse Long/ULong only when the actual header spelling does not matter
int64_t, uint64_tcrt/stdint.bi typedefsfixed-width 64-bit typedef spelling can differ by targetDo not assume every int64_t is C long long
intptr_t, uintptr_tcrt/stdint.bi typedefspointer-sized integer typedefsInteger/UInteger are the underlying FB-sized types
ptrdiff_t, size_t, ssize_tcrt/stddef.bi typedefsspelling differs between LP64, LLP64 and some 32-bit targetsAvoid hand-written mappings unless needed
wchar_t, wint_tcrt/stddef.bi typedefswidth and signedness vary by targetNever assume wchar_t is always 16-bit
enumusually Long for ordinary C enumsordinary C enum ABI is normally int-sized on these targetsverify packed enums, compiler flags, and C++ scoped enums

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

' 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