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

- Creating FB bindings for C libraries - How to translate C data types to FB
- Standard Data Type Limits
- Integer
- Long
- LongInt
- Type (Alias)