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:
' 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