= Using `printf` in a safe way = This page describes which type specifiers you shall use when printing special types, such as integers of fixed width or handles returned by various libraries. Do '''not''' use `%d` for everything that looks like an integer. Special types requires special handling. HelenOS system library libc provides macros that hides differences between various architectures and allows you to print special types in platform independent way. The macros have names similar to `PRIu32` (for `uint32_t` in decimal) or `PRIxn` (for native-size integer in hexadecimal) and you use them instead of the type specifier (see examples below). The `PRI*` macros are defined in two places `/common.h` (yes, in the root directory of HelenOS source) and in `/uspace/lib/c/arch/ARCHITECTURE/include/inttypes.h`. `common.h` is a generated file that appears after you configure your build. == Integers of fixed width == The macro is of form `PRI`. `` is one of 8, 16, 32 or 64 or `n` for native width. `` is summarized in following table. ||= `` =||= Meaning =|| || `u` || unsigned integer, decimal || || `d` || signed integer, decimal || || `x` || unsigned integer, hexadecimal || || `o` || unsigned integer, octal || For example, to print `uint16_t` in octal, use `PRIo16`. == Special integer types == ||= Type =||= Directive =|| || `size_t` || `"%zu"` || || `sysarg_t` || `PRIun` / `PRIxn` || || `uintptr_t` || `"%#" PRIxn` || == Handles == Although handles in HelenOS have their own `typedef`, they are usually backed by integer or pointer. Integers are usually used for handles passed from servers to clients (e.g. devices, files). Typically, they use unsigned integer of the native size. Pointers are used for structures used only locally (same application) and are used to hide implementation details. Thus, for these handles one should use `PRIun` (makes more sense for integer backed handles) or `PRIxn` (usually for pointers). The table below is not exhaustive and serves as a list of hints. ||= Type =||= Directive =|| || `fid_t` (fibril id) || `PRIxn` || || `devman_handle_t` (device handle) || `PRIun` || || `thread_id_t` (thread id) || `PRIu64` || || `ipc_callid_t` (IPC call id) || `PRIxn` || || `aid_t` (IPC message id) || `PRIxn` || == Annotating your own printf-like functions == If you are creating your own `printf`-like function, do not forget to add `PRINTF_ATTRIBUTE()` to it to force checking of the parameters. {{{ #!c void my_logging_function(int level, const char *format, ...) PRINTF_ATTRIBUTE(2, 3); // 2 .. second argument is the format string // 3 .. the variadic arguments are here (third parameter) }}} == Example == {{{ #!c devman_handle_t device_handle; printf("Handle = %" PRIun ".\n", device_handle); fid_t fibril_handle; printf("Fibril handle = %#" PRIxn ".\n", fibril_handle); uint32_t pci_register; printf("Register at 0x%08" PRIx32 ".\n", pci_register); int16_t small_value; printf("16bits: %" PRId16 ".\n", small_value); sysarg_t ipc_argument; printf("IPC arg1 = %20" PRIun ".\n", ipc_argument); size_t buffer_size; printf("Buffer has %zuB.\n", buffer_size); uintptr_t pointer_as_number; printf("Data at %p or at %#" PRIxn ".\n", (void *) pointer_as_number, pointer_as_number); void *pointer; printf("Pointer is built-in: %p.\n", pointer); }}}