Fact-checked by Grok 2 weeks ago

C data types

In , data types classify the information that variables can store, dictating the permissible operations, storage requirements, and representation in memory. As outlined in the ISO/IEC 9899:2024 standard (commonly known as C23), these types are broadly categorized into basic types—including arithmetic types for integers and floating-point numbers, as well as —and derived (or compound) types such as pointers, arrays, structures, unions, and enumerations. Type qualifiers (const, volatile, and ) can be applied to refine access rules, immutability, and optimization opportunities for these types. This system ensures portability across implementations while providing low-level control essential to C's design. The core arithmetic types underpin most computations in C. Integer types include signed variants like char (at least 8 bits), short, int (typically 32 bits, ranging from -2,147,483,648 to 2,147,483,647), long, long long (at least 64 bits, added in C99), and _Bool (boolean type with values 0 or 1, added in C99), alongside unsigned counterparts for non-negative values up to twice the signed range. C23 extends this with bit-precise integers (_BitInt(N) and unsigned _BitInt(N)), allowing exact-width specification from 1 to an implementation-defined maximum of at least 128 bits for specialized needs like cryptography. Floating-point types comprise float (single precision, about 6 decimal digits), double (double precision, about 15 digits), and long double (extended precision, implementation-defined), representing approximate real numbers in binary format; C23 introduces decimal floating-point types (_Decimal32, _Decimal64, _Decimal128) for precise decimal arithmetic in applications like finance. Complex types (float _Complex, etc., since C99) support real and imaginary components, while the incomplete void type denotes absence of value, commonly used for function return types without results or generic pointers (**void ***). Sizes and exact ranges remain implementation-defined, queryable via sizeof and header macros like INT_MAX. Derived types enable construction of sophisticated data representations from basic ones. Pointers store memory addresses, facilitating dynamic allocation and indirection, with dereferencing via the *** operator to access pointed-to data. Arrays are fixed- or variable-length (VLAs since C99) sequences of identical elements stored contiguously, accessed by index starting at 0. Structures aggregate heterogeneous fields (e.g., struct point { int x, y; }) for modeling entities like coordinates, supporting member access via . or ->. Unions permit multiple interpretations of the same memory block, useful for type punning but requiring careful management to avoid undefined behavior. Enumerations (enum) define sets of named integer constants (e.g., enum color { RED = 1, GREEN }), promoting readability and often underlying switch statements. Together, these features, refined over standards from C89 to C23, balance efficiency, expressiveness, and portability in systems programming.

Fundamental Arithmetic Types

Integer Types

In , integer types represent and are categorized into signed and unsigned variants. The standard signed integer types are signed char, short int (or short), int, long int (or long), and long long int (or long long). Corresponding unsigned types include unsigned char, unsigned short int, unsigned int, unsigned long int, and unsigned long long int. These types form the basic arithmetic integers, with signed variants capable of representing both positive and negative values, while unsigned variants handle only nonnegative values. The storage sizes for these types are implementation-defined but must meet minimum requirements specified by the . Specifically, char requires at least 8 bits, short at least 16 bits, int at least 16 bits, long at least 32 bits, and long long at least 64 bits. The exact number of bits, denoted as n, may exceed these minima depending on the target platform, and signed and unsigned variants of the same type share the same size. For signed integer types, the representation is two's complement. In two's complement, arithmetic operations treat the sign bit as part of the value, enabling efficient negation via bitwise complement and addition of one. Unsigned integers employ a pure binary representation with modulo arithmetic for overflows, wrapping around silently from the maximum value to zero. Character types are a special case of integer types: char is defined as a character type but behaves as either a signed or unsigned , with its signedness implementation-defined. Distinct types signed char and unsigned char provide explicit signedness, and all three are suitable for storing small or characters from the basic execution set, which are nonnegative. Plain char objects promote to int in expressions unless larger than int, aligning with promotion rules. Declarations of variables specify the type and optional initial value, such as int x = 42; for a signed or unsigned long y = 0UL; for an unsigned long with a literal . The representable for an n-bit signed is from -(2^{n-1}) to $2^{n-1} - 1, assuming no padding bits beyond the sign and value bits. For an n-bit unsigned , the spans from 0 to $2^n - 1. These bounds ensure compatibility with standard arithmetic while allowing implementation flexibility in representation details.

Floating-Point Types

In , floating-point types are used to represent approximate real numbers, including those with fractional parts, as specified in the ISO/IEC 9899 standard. The standard defines three basic floating-point types: float for single precision, double for double precision, and long double for extended precision. These types provide varying levels of precision and range, with float typically occupying 32 bits, double 64 bits, and long double an implementation-defined size often 80 bits on x86 systems or 128 bits on others. The internal representation of these types follows a format consisting of a , an exponent field, and a (also called ) field, as outlined in the IEC 60559 standard (equivalent to for binary floating-point). For normalized numbers, the value is calculated as (-1)^s \times m \times 2^{e - bias}, where s is the (0 for positive, 1 for negative), m is the with an implicit leading 1 (1 ≤ m < 2), and e is the biased exponent. Denormalized numbers, used for values near zero, lack the implicit leading 1, allowing representation of smaller magnitudes but with reduced precision; they occur when the exponent field is zero and the significand is non-zero. Since C99, implementations are required to support IEC 60559 for float and double, ensuring consistent behavior across compliant systems, while long double support is optional and may extend beyond basic formats. Floating-point types include special values to handle edge cases in computations. Positive and negative infinity (\infty and -\infty) are represented by a non-zero exponent with all significand bits zero, resulting from operations like division by zero. Not-a-Number (NaN) values indicate invalid operations, such as $0/0, and are encoded with a non-zero exponent and non-zero significand; NaNs are further classified as quiet (propagating without signaling) or signaling (trapping on use). Zero is represented with all bits zero, but signed zeros (+0 and -0) exist, where the sign bit differs despite equal numerical value, affecting operations like division. Examples of declarations include float f = 3.14f; for single precision (noting the f suffix), double d = 1.0; for double precision (default for literals without suffix), and long double ld = 1.0L; using the L suffix. Arithmetic operations on floating-point types adhere to IEC 60559 rules, with default rounding to nearest (ties to even) but support for other modes like toward zero or away from zero via functions such as fesetround. Overflow produces infinity and sets the overflow flag, while underflow to a denormalized value or zero sets the underflow flag, with gradual underflow preserving small values when possible. These behaviors ensure predictable handling of exceptional conditions in compliant implementations.

Boolean Type

The _Bool type, introduced in the standard, is an unsigned integer type capable of representing the values 0 (false) and 1 (true). It promotes to int in expressions, with a size of at least 1 bit. When assigning a value to a _Bool object, any integer scalar is converted such that 0 becomes 0 and any nonzero value becomes 1; other types undergo standard integer conversion before this step. In C23, the keyword bool is defined as an alias for _Bool, and the keywords true and false represent the constants 1 and 0 of type bool, respectively. Prior to C23, the header <stdbool.h>, introduced in C99, provides portable macros bool (expanding to _Bool), true (expanding to 1), and false (expanding to 0) to facilitate boolean usage without relying on implementation-defined typedefs. Variables of boolean type are declared as follows:
c
_Bool flag = 1;  // C99
#include <stdbool.h>
bool condition = true;  // Pre-C23 with header
bool enabled = true;    // C23, no header needed
The logical operators ! (negation), && (AND), and || (OR) operate on scalar types, including _Bool, yielding results of type int (0 or 1). The ! operator has the highest precedence among them, followed by && (left-to-right associativity), then || (left-to-right associativity). Both && and || perform short-circuit evaluation: for &&, the right operand is not evaluated if the left is 0; for ||, the right is not evaluated if the left is nonzero. For output, _Bool values are typically printed using the integer format specifiers %d or %i in printf, as there is no dedicated specifier; the value prints as 0 or 1 after promotion to int.

Void Type

In the C programming language, the void type is defined as an incomplete type that cannot be used to declare objects, representing an empty set of values with no storage or alignment requirements. It serves primarily to specify the absence of a type or value, particularly in function return types and as a basis for generic pointers. As an incomplete type, void cannot be completed and is incompatible with all other types except in limited pointer conversion contexts. The is commonly used in declarations and definitions to indicate that a function returns no value. For example, a such as void func(void); specifies a named func that takes no arguments and produces no return value, with the empty parameter list explicitly denoted by void to distinguish it from an unspecified parameter list. In such functions, a return statement must omit any expression, as attempting to return a value results in ; the statement simply terminates execution. This usage ensures clear signaling of functions intended solely for side effects, such as operations. The void type also forms the basis for the generic pointer type void *, which can hold the address of any object without specifying its type, facilitating type-agnostic operations like memory allocation. A void * pointer is compatible with pointers to any incomplete or object type, allowing assignment from or to such pointers without explicit casting, though dereferencing requires an explicit cast to the target type. Pointer arithmetic on void * is undefined, except for addition or subtraction with integer values, which adjusts the pointer by the specified byte offset. For instance, the standard library function malloc returns a void * to allocated memory, which must be cast before use, as in int *p = (int *)malloc(10 * sizeof(int));, and subsequently freed with free(p);. Regarding compatibility, the itself is only compatible with another void type and cannot participate in arithmetic operations or conversions beyond its specialized roles. Void expressions, such as those from void function calls, are evaluated only for their side effects and cannot be used as values in larger expressions, leading to if their value is accessed. This design promotes while enabling flexible, constructs in C.

Extended Types

Fixed-Width Integer Types

Fixed-width integer types in C, introduced by the standard, offer a mechanism for specifying s with precisely defined bit widths, independent of the underlying platform's native types. These types are declared in the <stdint.h> header and include signed variants such as int8_t, int16_t, int32_t, and int64_t, each representing a signed with an exact width of 8, 16, 32, or 64 bits, respectively, using representation and containing no padding bits. Corresponding unsigned types, uint8_t, uint16_t, uint32_t, and uint64_t, provide unsigned s of the same exact widths. Implementations must define these types only if they support types meeting these exact specifications; otherwise, the corresponding macros are not defined, ensuring portability by avoiding assumptions about unavailable widths. To address varying implementation constraints, C99 also defines related minimum-width types: int_leastN_t and uint_leastN_t (for N = 8, 16, 32, 64), which denote the smallest signed or unsigned types with at least N bits. These are always available, as every C implementation guarantees a type of at least 8 bits (such as char), making int_least8_t and uint_least8_t universally defined. Additionally, fastest minimum-width types int_fastN_t and uint_fastN_t specify the signed or unsigned types with at least N bits that offer the fastest computation on the target architecture, balancing width guarantees with performance optimization. For example, a declaration might use these as follows:
c
#include <stdint.h>

int32_t value = 42;  // Exactly 32-bit signed [integer](/page/Integer)
uint64_t large = UINT64_MAX;  // Maximum value for 64-bit unsigned [integer](/page/Integer)
int_least16_t small = -100;  // At least 16 bits, signed
These types enhance code portability by allowing developers to select widths explicitly, reducing reliance on architecture-specific sizes like int or long. In the C23 standard, the fixed-width types from <stdint.h> remain unchanged in their definitions and requirements, maintaining with C99. However, C23 introduces bit-precise types via _BitInt(N), which can complement fixed-width types by supporting arbitrary exact widths beyond the standard 8, 16, 32, and 64 bits, such as _BitInt(128) for 128-bit , when the implementation provides hardware or software support. This integration allows for greater flexibility in portable code targeting specialized or high-precision needs, while format specifiers like PRId32 (from <inttypes.h>) can be used for I/O with these types.

Format Specifiers for Extended Integers

In C99, the header <inttypes.h> defines a set of macros that expand to string literals suitable for use as format specifiers in the printf and scanf families of functions, ensuring portable input and output of extended integer types such as fixed-width integers. These macros are prefixed with PRI for output functions (e.g., printf, fprintf) and SCN for input functions (e.g., scanf, fscanf), followed by a conversion specifier (like d for signed decimal) and a width indicator (e.g., 8 for 8-bit types). For instance, PRId8 and PRId16 provide specifiers for printing signed 8-bit and 16-bit integers in decimal, while SCNd32 and SCNu64 handle scanning signed 32-bit decimals and unsigned 64-bit decimals, respectively. These macros are used by concatenating them within the format string, typically with the % operator, to match the corresponding integer types from <stdint.h>. A common example for output is printf("%" PRId32 "\n", (int32_t)42);, which portably prints the value 42 as a 32-bit signed decimal, avoiding platform-dependent assumptions about int size. For input, int64_t var; scanf("%" SCNd64, &var); safely reads a 64-bit signed decimal value into var. This approach promotes portability across systems where basic integer types like long may vary in width. The macros integrate with standard length modifiers such as hh, h, l, and ll when applied to basic types, but for extended integers, the width-specific macros are preferred to ensure exact matching without relying on implementation-defined behavior. Signed variants include PRIdN and PRIiN for decimal output of signed types (where i treats the input as signed regardless of base), while unsigned variants offer PRIuN for decimal, PRIoN for octal, and PRIxN/PRIXN for hexadecimal (lowercase/uppercase). Corresponding SCN macros follow the same pattern for input, such as SCNuN for unsigned decimal and SCNxN for hexadecimal. These specifiers apply to functions beyond basic printf and scanf, including snprintf for safe string formatting and sscanf for string parsing, as in snprintf(buf, sizeof(buf), "%" PRIu64, (uint64_t)123);. Mixing these macros with basic specifiers like %d for extended types is discouraged, as it can lead to or truncation on platforms where type widths differ. In C23, the PRI and SCN macros remain unchanged from , maintaining backward compatibility, though the new typeof keyword enables safer type-generic expressions for format string construction in advanced scenarios. These specifiers are particularly useful for fixed-width types like int64_t from the fixed-width integer types section.

Bit-Precise Integer Types

Bit-precise integer types , introduced in the C23 (ISO/IEC 9899:2024), provide programmers with exact control over the number of bits used for representations through the _BitInt(N) type specifier, where N is a positive expression denoting the bit width. These types come in signed (_BitInt(N)) and unsigned (unsigned _BitInt(N)) variants, allowing for representations ranging from 1 bit up to an implementation-defined maximum BITINT_MAXWIDTH, which must be at least 64 and is often 128 in practice. Unlike integer types, _BitInt(N) ensures a contiguous sequence of exactly N bits with no padding, making them suitable for low-level in systems or performance-critical applications requiring precise memory usage. The signed _BitInt(N) uses two's complement representation, supporting values from -2^{N-1} to $2^{N-1} - 1, while the unsigned variant handles non-negative values from 0 to $2^N - 1. For example, a declaration such as _BitInt(8) byte; defines an 8-bit signed integer, and unsigned _BitInt(1) flag; creates a single-bit unsigned type ideal for boolean-like flags. Arithmetic operations on _BitInt types behave like those on standard integers, including addition, subtraction, multiplication, division, modulus, and bitwise operations (&, |, ^, <<, >>), with the result type determined by usual arithmetic conversions that promote to the wider or sufficient type without unnecessary sign extension. Overflow for unsigned _BitInt(N) wraps around modulo $2^N, while signed overflow is implementation-defined. In terms of integer promotions, a _BitInt(N) value is promoted to int if N is less than or equal to the width of int (typically 16 bits via INT_WIDTH), otherwise it remains or promotes to a compatible larger type; notably, these types are exempt from broader integer promotion rules that apply to narrower standard types. This contrasts with fixed-width integer types from <stdint.h>, which are constrained to byte-aligned widths like 8, 16, 32, or 64 bits and have direct equivalents in the standard library, whereas _BitInt(N) supports arbitrary N not tied to byte boundaries and lacks predefined library macros. For instance, _BitInt(32) exact32 = 0b1010; initializes a 32-bit signed integer with a binary literal, enabling operations like exact32 <<= 2; for bit shifts or exact32 & 0b1111; for masking, all preserving the exact bit precision. Limitations of _BitInt include restrictions on its use as the underlying type for enumerations and an implementation-defined maximum width, which may vary by compiler (e.g., GCC supports up to 128 bits on certain architectures). Additionally, there are no standardized format specifiers in <stdio.h> for printing _BitInt values, requiring programmers to cast to compatible standard types (e.g., printf("%d", (int)my_bitint);) or use custom formatting via <stdbit.h> utilities for bit-level operations like counting zeros. Integer literals for these types use the wb or WB suffix, such as 3wb for a signed _BitInt(3) or -3uwb for an unsigned variant with wraparound.

Additional Floating-Point Types

The C11 standard, through its associated technical specification ISO/IEC TS 18661-3:2015, introduced optional interchange floating-point types to support higher precision and improved portability in numerical computations, particularly for applications requiring consistent representation across diverse hardware platforms. These types extend beyond the basic float, double, and long double by providing explicit bit widths aligned with IEEE 754 formats, enabling developers to specify precision without relying on implementation-defined behaviors of standard types. The header <stdfloat.h>, formalized in C23 (ISO/IEC 9899:2024), declares these types and associated macros, making them part of the core language for compliant implementations. Key interchange types include _Float16, _Float32, _Float64, and _Float128, which serve as aliases for specific binary formats with fixed storage sizes: _Float16 occupies 16 bits, _Float32 32 bits, _Float64 64 bits, and _Float128 128 bits. More generally, _FloatN denotes a type with an exact N-bit (where N is typically a multiple of 16 and at least 16), while _FloatNx provides an extended exponent range for the same size, enhancing for scientific computing. These types are real floating types, supporting the same operations as standard floating-point types, including arithmetic, comparisons, and mathematical functions from <math.h>. Support for these types is implementation-defined, with availability queried via macros in <stdfloat.h>, such as FLT16_HAS_SUBNORM (indicating support for _Float16, with values of -1 for indeterminable, 0 for absent, or 1 for present) and similar macros like FLT32_DIG for digits of . Where supported, all types conform to representations, featuring a , biased exponent, and normalized , with capabilities for infinities, NaNs, and subnormals depending on the macro flags. For instance, _Float16 follows the binary16 format with 5 exponent bits and 10 bits (11 including the implicit leading 1), offering approximately 3-4 digits of .
TypeStorage Size (bits)Significand BitsExponent BitsApproximate Decimal Precision
_Float161610 (11 effective)53-4
_Float323223 (24 effective)86-9
_Float646452 (53 effective)1115-17
_Float128128112 (113 effective)1533-36
In usage, these types facilitate precise control over floating-point storage and computation; for example, _Float32 f32 = 3.14f32; declares a 32-bit initialized with a hexadecimal floating-point literal, while automatic conversions occur between interchange types and standard types during arithmetic, promoting narrower types to wider ones as needed (e.g., _Float16 to _Float32). Conversions preserve as much precision as possible, with rounding modes controllable via <fenv.h>. C23 enhances integration of these types by standardizing the typeof and typeof_unqual operators, allowing in declarations and expressions involving interchange types—for instance, typeof(_Float32(1.0)) x; infers _Float32 for x, preserving qualifiers with typeof or stripping them with typeof_unqual. This supports more flexible in <tgmath.h>, where type-generic macros like sin can resolve to functions such as sinf32 for _Float32 arguments.

Size and Pointer Difference Types

In , size_t and ptrdiff_t are specialized types defined to handle sizes of objects and differences between pointers, respectively, ensuring portability across implementations. These types are essential for operations, as they are designed to match the capabilities of the underlying without assuming fixed widths. They are declared in the standard header <stddef.h>, with size_t being an unsigned type returned by the , capable of representing the size in bytes of any object, including arrays. The size_t type originates from the sizeof unary operator, which yields the size of a type or expression in bytes as a value of type size_t; for instance, produces a size_t result. According to the (ISO/IEC 9899:2018, section 7.19.1), size_t must be able to store the maximum size of a theoretically possible object, with a minimum width of 16 bits, though it is typically implemented as unsigned long or unsigned long long on 32-bit or 64-bit systems, respectively. This unsigned nature reflects that object sizes cannot be negative, promoting safe arithmetic for allocation and indexing. A common usage is in dynamic memory allocation, such as the malloc function, which takes a size_t argument specifying the number of bytes to allocate: void *malloc(size_t size);. Here, passing an integer literal like malloc(100) implicitly converts it to size_t, but explicit use prevents portability issues on systems where is smaller than size_t. Complementing size_t, ptrdiff_t is the signed type for the result of subtracting two pointers to elements of the same , allowing representation of both positive and negative differences. Defined in the (ISO/IEC 9899:2018, section 7.19.1), it serves as the signed counterpart to size_t, with a minimum bit width of 17 bits to accommodate potential negative values exceeding the range of 16-bit unsigned types. For example, in pointer arithmetic, the difference between two element pointers yields a ptrdiff_t: ptrdiff_t diff = &array - &array;, resulting in 10, which indicates the number of elements between them. This type ensures that operations like array indexing with offsets remain well-defined even when negative indices are conceptually involved, though actual pointer subtraction is only valid within the same object. For portability, both types are also accessible via <stdint.h>, where they relate to uintptr_t—an unsigned integer type capable of holding any valid pointer value converted to an integer—which often matches the size of size_t but is distinct in purpose for pointer-to-integer conversions. Implementations must ensure size_t can represent at least the size of the largest possible object, but exceeding this limit, such as through arithmetic overflow, results in ; for instance, adding two size_t values that exceed SIZE_MAX (defined in <limits.h> or <stdint.h>) does not wrap around predictably. Thus, programmers must validate sizes before operations to avoid undefined outcomes, particularly in large-scale allocations.

Derived Types

Arrays

In C, an array type is a derived type that describes a contiguously allocated nonempty set of objects, all of the same type known as the element type, along with a specified number of elements. The element type must be a complete object type other than a variable-length (VLA) type in certain contexts, and the array type itself may be incomplete if the size is unspecified. Arrays provide a means to store and access homogeneous sequences of data in contiguous memory, facilitating efficient indexing and traversal. Fixed-size arrays have a compile-time constant size greater than zero, while variable-length (VLAs), introduced as an optional feature in ISO/IEC 9899:1999 () and retained as conditional in later standards, allow the size to be determined by a runtime-evaluated expression. For VLAs, the size expression must yield a positive value representable in the , or the is undefined. Array declarations specify the element type followed by the identifier and size in square brackets. For a fixed-size array, the size is a constant expression, as in int arr[10];, which allocates space for 10 integers. For a VLA, the size is a non-constant expression evaluated when the declaration is reached, such as int n = 5; int vla[n]; within a block scope, where n could be determined by user input or computation. The size of a VLA instance remains fixed throughout its lifetime, and VLAs have automatic storage duration, allocated upon entering their scope and deallocated upon exit. VLAs cannot have static or thread storage duration, nor can they appear at file scope or as members of structures or unions. Initialization of arrays occurs at declaration using a brace-enclosed list of initializers matching the element type. For example, int arr[5] = {1, 2, 3}; initializes the first three elements to 1, 2, and 3, respectively, with the remaining two implicitly set to zero if the array has static or storage (or indeterminate otherwise for ). If the array size is omitted, it is inferred from the number of initializers, as in int arr[] = {1, 2, 3};, resulting in an array of size 3. Partial initialization zeros unpadded elements in static contexts, promoting predictable behavior. For arrays, string literals provide convenient initialization, such as char str[] = "hello";, which includes the and infers the size as 6. In expressions, an array decays to a pointer to its first element, except when it is the operand of the sizeof or unary & operators, the _Alignof operator, or a string literal initializing another array; this decay means the array name serves as &arr[0] in most contexts, enabling pointer arithmetic for access. Notably, sizeof(arr) yields the total byte size of the array (e.g., 40 for int arr[10] on a 32-bit int), whereas sizeof(&arr[0]) yields the size of a pointer (typically 4 or 8 bytes). This distinction underscores arrays as objects with fixed layout, distinct from pointers. Multidimensional arrays are arrays of arrays, declared as int matrix[3][4];, representing 3 rows each of 4 integers, stored in row-major order with contiguous allocation across the entire structure. Initialization follows nested braces, like int matrix[2][3] = {{1, 2, 3}, {4, 5, 6}};, and access uses multiple indices, equivalent to *((matrix + i) + j) after decay. VLAs carry specific limitations to ensure portability and safety: they are optional in C11 (checkable via __STDC_NO_VLA__), cannot nest in ways that create variably modified types incompatible with composite types, and no bounds checking is performed—accessing elements outside the defined range invokes undefined behavior. While multidimensional VLAs like int a[n][m] are permitted with runtime n and m, VLAs cannot serve as elements of other VLAs if the inner size varies independently per instance, as array types require uniform element types; deeper nesting may exceed implementation limits on recursion or stack depth.

Pointers

In , a pointer type derived from an object type T, denoted as T* , is an object whose value provides a to an entity of type T . Such pointers store the of an object of type T or one past the end of such an object, enabling indirect access to the referenced data. Pointers are essential for , array traversal, and implementing data structures like linked lists, as they allow programs to manipulate addresses rather than values directly. The primary operations on pointers include the address-of operator &, which yields the of its as a pointer of type T* if the operand is of type T . The indirection operator * dereferences a pointer of type T* to access or modify the object at that , producing an lvalue of type T . For structures, the arrow operator -> accesses a member of the structure pointed to by the ; if p is a pointer to a structure with member m , then p->m is equivalent to (*p).m and yields an lvalue of the member's type. Pointer arithmetic supports and of an n to a pointer p of type T* , resulting in a pointer to an element offset by n positions, where the offset is scaled by sizeof(T) to account for the size of the referenced type; the result type is T* . between two pointers to elements of the same yields a difference of type ptrdiff_t representing the number of elements between them. A is the value obtained by converting a null pointer constant—such as the integer constant expression 0 or (void *)0—to a pointer type, and it compares unequal to any pointer to an object. Equality comparisons (== and !=) between pointers are defined such that two pointers compare equal if both are , both point to the same object, or both point to one past the last element of the same array object. Relational comparisons (<, >, <=, >=) are defined only for pointers to elements of the same array (or one past the end), ordering them by their relative positions. An expression of type decays to a pointer to the initial element of the array, except when it is the operand of the sizeof operator or the unary & operator, or when it is a used to initialize another array. This equivalence treats an array name as a pointer to its first element in most contexts, facilitating uniform handling in functions and loops, though sizeof on an array yields the total size while on a pointer yields the pointer's size. Pointer conversions preserve under specific rules: a pointer to any object type may be converted to void * and back to the original type, yielding a value equal to the original pointer. Qualified pointer types, such as const T * or volatile T *, are compatible only if they have the identically qualified versions of compatible types; for instance, const int * is not compatible with int * due to differing qualifiers on the pointed-to type. The following example illustrates basic pointer usage:
c
int x = 10;
int *p = &x;  // p holds the [address](/page/Address) of x
*p = 5;       // Dereference to assign 5 to x
Here, &x produces a pointer to x, and *p modifies the value at that . For array equivalence:
c
int arr[3] = {1, 2, 3};
int *arr_ptr = arr;  // arr decays to pointer to first element
The pointer arr_ptr can be used interchangeably with arr for indexing, except in sizeof(arr) which gives 3 * sizeof(int).

Structures

A structure in C is a derived data type that aggregates multiple data members of potentially heterogeneous types into a single unit, providing named access to each member for organizing related data. The syntax for declaring a structure type is struct [tag] { member-declarations } ;, where the optional tag serves as an identifier for the type, allowing reuse in subsequent declarations. [6.7.2.1] This definition establishes an ordered sequence of members, with the structure type considered incomplete until fully specified. [6.7.2.1] Structures support both named and anonymous forms; since C11, an anonymous structure omits the tag and can be nested within another structure, promoting its members directly to the enclosing scope for simplified access without qualification. [6.7.2.1] For example, a point structure might be declared as struct point { int x, y; };, followed by an object instantiation struct point p = {1, 2};, which initializes the members in declaration order. [6.7.8] Member access occurs via the dot operator for direct objects (p.x) or the arrow operator for pointers (ptr->x), with the latter dereferencing the pointer to apply the former. [6.5.2.3] The offsetof macro, defined in <stddef.h>, computes the byte offset of a member from the structure's start as a size_t value, e.g., offsetof(struct point, y), facilitating portable member location without assumptions about layout. [7.17] To optimize memory access, implementations insert padding bytes between or after members to align them according to hardware requirements, ensuring the structure's size is at least the sum of member sizes and a multiple of its alignment. [6.7.2.1] Padding placement and contents are implementation-defined, with no padding before the first member, and all members occupy non-overlapping storage portions, distinguishing structures from unions that permit memory sharing. [6.7.2.1] Bit-fields within structures declare integer members with specified widths for compact representation, using syntax like unsigned int flags: 3;, where the type is typically _Bool (C99+), signed/unsigned int, or _BitInt(N) (C23), and the width ranges from 1 to the type's bit count, subject to implementation limits such as no more than INT_MAX bits total per unit. [6.7.2.1] Adjacent bit-fields may pack into shared storage units, but crossing unit boundaries or signedness is implementation-defined. Initialization of structure objects uses brace-enclosed lists matching member order, with excess initializers discarded and insufficient ones zero-initializing remainders; since , designated initializers allow explicit member targeting, e.g., struct point p = {.x = 1, .y = 2};, supporting non-sequential and nested assignments. [6.7.8] A , introduced in , permits the final member to be an array of incomplete type (e.g., char data[];), whose size is determined by the allocated memory beyond the structure's fixed portion, excluded from sizeof and requiring dynamic allocation for use, such as malloc(sizeof(struct s) + 10 * sizeof([char](/page/Char))). [6.7.2.1] This enables variable-length structures while maintaining for the fixed prefix.

Unions

A union in the C programming language is a derived type that consists of a sequence of members, each of a possibly different type, whose storage overlaps such that at any given time only one member can hold a value. This design enables memory overlay, allowing the same portion of memory to be interpreted as different types depending on the active member. The size of a union object is determined by the size of its largest member, including any necessary alignment padding, and all members share the same base address. Unions are declared using the union keyword followed by an optional identifier and a brace-enclosed list of member declarations. For example, a union can be defined as union data { int i; float f; };, which creates a type capable of storing either an integer or a floating-point value in the same memory space. Objects of union type can be initialized by designating one member, such as union data u = {.i = 42};, which sets the integer member while leaving the floating-point member uninitialized. Anonymous unions, introduced in C11, allow members to be accessed directly without a union object name, provided they are properly scoped. Accessing a union member involves reading or writing through that member's name, but the behavior is implementation-defined if a member other than the last one written is accessed, as the value is reinterpreted from the stored bits. For instance, writing to the floating-point member and then reading the integer member may yield a value derived from the representation of the float, potentially violating expectations if the types have different representations. However, for unions containing plain old data (POD) types with a common initial sequence—such as the first few members being identical scalar types—the value of those initial members is preserved regardless of which member was last written. Unions are commonly used for , where the same memory is safely reinterpreted as different types through union membership, an allowance explicitly provided by the standard to support low-level operations without invoking under strict rules. This is particularly useful in implementing types or optimizing space in data structures where only one of several possible formats is needed at a time. within a union is limited; while unnamed trailing may be added to satisfy the requirements of the largest member, there is no padding between members since they overlap completely, and bit-fields are confined to individual members without spanning boundaries. The following example demonstrates union usage for type punning:
c
#include <stdio.h>

union example {
    int i;
    float f;
};

int main(void) {
    union example u;
    u.f = 3.14f;
    printf("As float: %f\n", u.f);
    printf("As int (bit reinterpretation): %d\n", u.i);  // Value depends on float's binary representation
    return 0;
}
In this code, assigning to u.f stores the floating-point value, and reading u.i reinterprets those bits as an integer, which may produce a large or negative number due to the internal encoding of 3.14 in IEEE 754 format. Such reinterpretation is well-defined only through the mechanism and underscores the importance of tracking the active member to avoid portable issues.

Function Pointers

In C, a function pointer is a derived type that refers to a , allowing indirect invocation of executable code with a specific . The declaration syntax for a follows the form return_type (*pointer_name)(parameter_list), where the parentheses around the *pointer_name ensure the * binds to the identifier rather than forming a returning a pointer. For example, int (*func_ptr)(int a); declares func_ptr as a pointer to a that takes an integer parameter and returns an integer. This syntax derives from the C standard's rules for pointer declarators combined with types. Function pointers can be initialized with the address of a compatible , using either the address-of & or the function name directly, as a function designator implicitly converts to a pointer to itself. For instance, given a int add(int x) { return x + 1; }, the declaration int (*ptr)(int) = &add; or int (*ptr)(int) = add; both validly initialize ptr. To invoke the function through the pointer, use the function call syntax (*ptr)(arg) or the shorthand ptr(arg), such as int result = ptr(5);, which executes the pointed-to function and assigns its return value. These operations adhere to the standard's expressions for postfix operators and implicit conversions. Two types are compatible if the underlying types are compatible, meaning they share the same return type (or compatible types) and parameter type lists, with adjustments for parameters (treated as pointers) and default promotions, while ignoring parameter names and top-level cv-qualifiers on parameters. For example, a pointer to int f(const int) is compatible with a pointer to int f(int), but not with one returning double or having mismatched parameters. Calling a through an incompatible pointer results in . Arrays of function pointers provide a mechanism for , often implemented as dispatch s to select and invoke functions based on an , enabling efficient handling of multiple similar operations without lengthy switch statements. An example declaration is void (*[table](/page/Table)[3])(int);, which creates an array of three pointers to functions taking an int and returning void; initialization might assign [table](/page/Table)[0] = func1; [table](/page/Table)[1] = func2;, followed by invocation as [table](/page/Table)[index](arg);. This technique is commonly used in embedded systems and event handlers for scalable code organization. Function pointers with void return types are prevalent in callback mechanisms, where the invoked function performs an action without needing to return a value, such as signal handlers or event processors. However, some standard callbacks require specific return types; for instance, the qsort function from <stdlib.h> expects a comparator callback of type int (*compar)(const void *, const void *), which returns a negative value if the first argument is less than the second, zero if equal, or positive otherwise to guide sorting. An example comparator is:
c
int cmp(const void *a, const void *b) {
    return (*(int *)a - *(int *)b);
}
Used as qsort(array, n, sizeof(int), cmp);, this enables generic sorting by providing a pluggable comparison logic via the function pointer.

Type Qualifiers

Const Qualifier

The const type qualifier in the C programming language declares an object whose value cannot be modified through an lvalue of non-const-qualified type after initialization, with any such attempt resulting in undefined behavior. This qualifier applies only to lvalues that designate objects, and implementations may place const-qualified objects in read-only memory to enforce this restriction. The semantics promote data integrity and enable compiler optimizations by assuring immutability within the current scope. Basic usage involves applying const directly to a variable, such as const int x = 5;, which initializes x to a read-only that cannot be altered via like x = 6;. For pointers, const int *q = &x; creates a pointer to a const object, permitting read access but prohibiting modification through *q = 7;, as this would invoke . In contrast, a non-const pointer to a const object, like int *r = (int *)&x;, allows modification only if the cast discards const, but doing so leads to if the object is altered. The placement of const distinguishes top-level qualification from nested qualification. A top-level const, as in const int y = 10;, makes the object itself immutable. Nested const in const int *p qualifies the pointed-to type, rendering the target object read-only while allowing the pointer p to be reassigned. Conversely, int * const s = &z; qualifies the pointer, preventing reassignment of s but allowing modification of the pointed-to object *s. This distinction is crucial for type compatibility, as a pointer to const cannot implicitly convert to a non-const pointer, but the reverse is permitted with explicit casting. In function parameters, const signals that the function will not modify the argument, enhancing interface clarity and preventing unintended changes. For example, void f(const int *p); accepts a pointer to const int, disallowing *p = 11; inside f and treating such attempts as undefined behavior. String literals are commonly handled as const char *, since modifying a string literal like char *t = "hello"; *t = 'a'; yields undefined behavior, and proper declaration uses const char *u = "hello";. For array parameters that decay to pointers, to qualify the pointer to prevent reassignment while allowing element modification unless further const-qualified, declare as void g(double * const arr);. Const-qualified objects require initialization at declaration, as post-declaration assignment is prohibited; uninitialized automatic const objects lead to upon access. For objects with static or storage duration, initializers must be expressions or literals. Thus, const [int](/page/INT) v = 15; is valid, but const [int](/page/INT) w; w = 16; compiles only if the assignment is unreachable, though the lack of initialization for w invites . Compilers enforce const by rejecting direct modifications of const lvalues at , issuing errors for violations like assigning to a const or dereferencing a pointer to const in a modifying context. Indirect modifications via or casting away const may compile but result in if executed, potentially causing runtime faults in . This enforcement aligns with the standard's intent to treat const as a compile-time for immutability.

Volatile Qualifier

The volatile type qualifier in the C programming language is applied to a type to indicate that an object of that type may be modified by external factors outside the program's control, such as hardware events, signal handlers, or concurrent threads, thereby ensuring that accesses to the object are treated as observable side effects. According to the ISO C standard, the properties of a volatile-qualified type are meaningful only when applied to lvalue expressions, and the qualifier creates a distinct type with the same representation and alignment requirements as the unqualified version. This qualifier was introduced to address scenarios where the compiler's assumptions about value stability could lead to incorrect optimizations, particularly in low-level programming contexts. In usage, volatile is commonly employed for variables representing memory-mapped or ports, where the value might change unexpectedly due to external actions. For instance, to access a at a specific , a declaration such as volatile int *reg = (volatile [int](/page/INT) *)0x1234; ensures that pointer dereferences force direct reads or writes without intermediate caching. The qualifier can also be applied to types like sig_atomic_t for variables accessed within signal handlers, guaranteeing that modifications are visible across signal invocations. Regarding behaviors, volatile disables compiler optimizations that assume the object's remains unchanged between accesses, such as , dead store removal, or instruction reordering across sequence points; instead, each read or write operation must correspond to an actual access in the order specified by the program, adhering to the semantics. However, the precise mapping of these accesses to physical operations is implementation-defined, and volatile does not provide atomicity guarantees for multi-byte objects or . The volatile qualifier can be combined with const to declare read-only objects that may still change externally, such as a status register that the program should not modify but must read freshly each time, e.g., const volatile [int](/page/INT) status; . It may also qualify structure or members individually, applying the semantics only to those fields. For function s, volatile is permitted but rarely used, as in int func(volatile [int](/page/INT) x); where the parameter itself is treated as potentially volatile, though this primarily affects pointer parameters in contexts like array decays since C99. Accessing a volatile object through a non-volatile lvalue results in , emphasizing the need for consistent qualification. In embedded systems, volatile is essential for polling status flags or controlling peripherals, such as reading a device's without optimization skipping the check:
c
volatile uint8_t *flag_reg = (volatile uint8_t *)0x4000;  
while (!(*flag_reg & 0x01)) {  
    // Wait for flag to be set by [hardware](/page/Hardware)  
}  
This ensures the loop does not optimize to a single read, potentially missing hardware updates. Similarly, writing to a like *reg = 1; guarantees the store occurs immediately to memory, preventing buffering or elision.

Restrict Qualifier

The restrict qualifier, introduced in the standard, is a type qualifier that can only be applied to pointers, indicating that the pointer is the sole means by which an object or a thereof is accessed during the pointer's lifetime within its . This declaration, such as T *restrict p, serves as a from the to the that no other pointer or lvalue will access or modify the pointed-to object during that period, thereby eliminating ambiguities that could otherwise hinder . The qualifier's is limited to the block or function in which it is declared, and it enables the to make aggressive assumptions about memory access patterns without needing to conservatively handle potential overlaps. In practice, the restrict qualifier is most commonly used in function parameters to optimize memory-intensive operations, such as data copying or array processing. For example, the function declaration void copy(int *restrict dest, const int *restrict src, size_t n); asserts that the objects pointed to by dest and src do not overlap and are not accessed through any other pointers during the function's execution. This usage aligns with standard library functions like memcpy, declared as void *memcpy(void *restrict dest, const void *restrict src, size_t n);, where the qualifier allows the compiler to treat the source and destination buffers as disjoint. Without the restrict qualifier, as in a non-qualified version void copy(int *dest, const int *src, size_t n);, the compiler must assume possible aliasing between dest and src, potentially inserting conservative checks or barriers that prevent certain transformations. The primary benefit of the restrict qualifier lies in the optimizations it enables by removing concerns, such as , instruction reordering, and register caching of values, which can yield significant performance improvements—sometimes by a factor of ten or more in memory-bound code. For instance, in a copying from src to dest, the can unroll iterations or use SIMD instructions freely, assuming no interference between the pointers. However, this optimization relies on the programmer's guarantee being upheld; if occurs—such as when dest and src point to overlapping regions—the behavior is undefined, potentially leading to incorrect results or crashes at . The restrict qualifier has specific limitations in its application: it is valid only for pointers and cannot be used on objects directly or non-pointer types, and it is particularly intended for function parameters rather than general declarations. In function prototypes, the qualifier is optional and ignored for compatibility purposes, but it must match in definitions if present. Additionally, while it can combine with other qualifiers like const—as in const int *restrict src—the restrict aspect focuses solely on assumptions, not on modification restrictions.

Type Properties and Macros

Implementation Limits and Characteristics

The provides several headers containing macros that define implementation-defined limits and characteristics of fundamental data types, enabling portable code that adapts to different platforms and compilers. These macros are constant expressions suitable for use in preprocessor directives, allowing developers to query properties such as maximum values, precision, and bit widths at . The header <limits.h> specifies limits for integer types, including the number of bits in a byte via CHAR_BIT, which has a minimum value of 8, and bounds for signed and unsigned integers such as INT_MAX (maximum value for int, at least 32,767) and INT_MIN (minimum value for int, at most -32,767). For example, on many 32-bit systems, INT_MAX is 2,147,483,647, while CHAR_BIT is exactly 8, reflecting the standard's requirement for byte composition. These macros ensure that code can check for overflow risks or platform capabilities, such as using #if INT_MAX >= 0x7FFFFFFF to detect if int is at least 32 bits wide. Similarly, <float.h> defines characteristics for floating-point types, including FLT_DIG (number of decimal digits of precision for float, at least 6) and DBL_EPSILON (the smallest positive double value such that 1.0 + DBL_EPSILON ≠ 1.0, at most 1E-9). These parameters, such as FLT_DIG typically being 6 for single precision, help in determining suitable precision for numerical computations across implementations. The <stdint.h> header introduces fixed-width integer types and their limits, such as INT8_MAX (127 for 8-bit signed s) and UINT_LEAST32_MAX (at least 4,294,967,295 for the smallest unsigned type with at least 32 bits), along with INTPTR_MAX for pointer-sized integers, which varies by architecture (e.g., 32-bit on x86). This header facilitates portable programming for systems requiring specific bit widths, like embedded devices. For enhanced portability, <iso646.h> provides macros as alternative spellings for operators, such as and for && and or for ||, useful in environments with limited keyboard support. Meanwhile, <stdbool.h> defines macros for boolean handling, including bool (expanding to _Bool), true (1), and false (0), promoting standardized logical operations. In C23, bool, true, and false become keywords, reducing reliance on the <stdbool.h> macros while maintaining backward compatibility.
HeaderPurposeKey MacrosMinimum Values
<limits.h>Integer type limitsCHAR_BIT, INT_MAX, INT_MINCHAR_BIT: 8; INT_MAX: 32,767
<float.h>Floating-point characteristicsFLT_DIG, DBL_EPSILONFLT_DIG: 6; DBL_EPSILON: 1E-9
<stdint.h>Fixed-width integersINT8_MAX, UINT_LEAST32_MAX, INTPTR_MAXINT8_MAX: 127; UINT_LEAST32_MAX: 4,294,967,295
<iso646.h>Operator alternativesand, or, not_eqN/A (token expansions)
<stdbool.h> supportbool, true, falsetrue: 1; false: 0
These compile-time macros contrast with operators like sizeof, which yield actual object sizes at compile time but are addressed separately in type inquiry contexts.

Type Inquiry Operators

Type inquiry operators in the C programming language provide mechanisms to obtain metadata about types at compile time, such as their size, alignment requirements, or inferred identities, facilitating portable and generic code design. These operators yield constant expressions of type size_t (an unsigned integer type defined in <stddef.h>) and are unevaluated, meaning their operands do not produce side effects unless involving variable-length arrays (VLAs). They cannot be applied to incomplete types (except in specific cases for VLAs), function types, or bit-fields, resulting in undefined behavior if misused. The [sizeof](/page/Sizeof) operator is a operator that determines the size in bytes required to store an object of the specified type or the type of the given expression. Its syntax is sizeof unary-expression or sizeof (type-name), where the type-name must be parenthesized. The result is an integer constant expression of type size_t, representing the total bytes, including any for structures or unions. For example, size_t s = sizeof(int); assigns the size of an int to s. The operand is never evaluated except when it is a VLA type, in which case it may be evaluated to compute the array size. It yields 1 for char, signed char, or unsigned char types. Constraints prohibit its use on expressions of function type, incomplete type (such as void or arrays of unknown size), bit-fields, or the parenthesized name of such types. Introduced in C11, the _Alignof operator queries the alignment requirement of a type, specifying the maximum number of bytes between successive addresses at which objects of that type must be allocated. Its syntax is _Alignof (type-name), yielding an integer constant expression of type size_t that is always a positive . For array types, it returns the alignment of the element type. For instance, _Alignof(struct s); provides the alignment for the structure s. The minimum alignment is 1 (for char types), while the maximum is implementation-defined but at least as strict as that of max_align_t from <stdalign.h>. It applies only to complete object types; use on incomplete types (other than void), function types, or void is undefined. In C23, _Alignof is deprecated in favor of the new alignof keyword with equivalent semantics. C23 introduces the typeof for , allowing the type of an expression or type-name to be deduced and used in declarations while preserving all type qualifiers such as const, volatile, _Atomic, and restrict. Its syntax is typeof (expression) or typeof (type-name), where parentheses are required, and it functions as a type specifier. For example, int *p; typeof(*p) x; declares x as an int (the type of the expression *p). The operator cannot be applied to bit-field members and is evaluated each time a block-scope declaration is reached. It is undefined for incomplete types or void, and cannot serve as an operand to sizeof or the unary & operator. Complementing typeof, the typeof_unqual operator (also since C23) infers the type similarly but strips all qualifiers, yielding the unqualified, non-atomic base type. Its syntax mirrors typeof: typeof_unqual (expression) or typeof_unqual (type-name). For instance, const int y = 5; typeof_unqual(y) z; declares z as an unqualified int. Like typeof, it requires parentheses, excludes bit-fields, and is restricted from incomplete types or void; it also cannot be used with sizeof or unary &. This operator supports type definitions, such as typedef typeof_unqual(nullptr) nullptr_t;, aiding in generic and portable type manipulations. Both typeof operators produce constant expressions where applicable and enhance compile-time type checking without runtime overhead.

References

  1. [1]
    Programming languages - ISO/IEC 9899:2024
    In stockThis document specifies the form and establishes the interpretation of programs written in the C programming language. It is designed to promote the ...
  2. [2]
    Primitive Types (GNU C Language Manual)
    11 Primitive Data Types. This chapter describes all the primitive data types of C—that is, all the data types that aren't built up from other types.
  3. [3]
    Pointers (GNU C Language Manual)
    ### Summary of Pointers as Data Types in C
  4. [4]
    Structures (GNU C Language Manual)
    A structure is a user-defined data type that holds various fields of data. Each field has a name and a data type specified in the structure’s definition.
  5. [5]
    Unions (GNU C Language Manual)
    ### Summary of Unions as Data Types in C
  6. [6]
    Enumeration Types (GNU C Language Manual)
    ### Summary of Enumeration Types in C
  7. [7]
    [PDF] ISO/IEC 9899:201x - Open Standards
    Dec 2, 2010 · ISO/IEC 9899:201x specifies the form and interpretation of C programs, promoting portability, reliability, maintainability, and efficient ...
  8. [8]
    [PDF] ISO/IEC 9899:yyyy - Open Standards
    ISO/IEC 9899 specifies the form and interpretation of C programs, promoting portability, reliability, maintainability, and efficient execution.
  9. [9]
    Built-in types (C++) - Microsoft Learn
    Aug 11, 2025 · Type double is a floating point type that is larger than or equal to type float , but shorter than or equal to the size of type long double .
  10. [10]
    What Every Computer Scientist Should Know About Floating-Point ...
    This paper is a tutorial on those aspects of floating-point arithmetic (floating-point hereafter) that have a direct connection to systems building.
  11. [11]
    Special Float Values (GNU C Language Manual)
    There are two special values, called Not-a-Number (NaN): a quiet NaN (QNaN), and a signaling NaN (SNaN). A QNaN is produced by operations for which the value is ...
  12. [12]
    [PDF] ISO/IEC 9899:1999(E) -- Programming Languages -- C
    4. International Standard ISO/IEC9899 was prepared by Joint Technical. Committee ISO/IEC JTC 1, Information technology, Subcommittee SC 22,. Programming ...
  13. [13]
    [PDF] ISO/IEC 9899:2024 (en) — N3220 working draft - Open Standards
    (This cover sheet to be replaced by ISO.) This document specifies the form and establishes the interpretation of programs expressed in the.
  14. [14]
  15. [15]
    <stdint.h>
    The <stdint.h> header shall declare sets of integer types having specified widths, and shall define corresponding sets of macros.Missing: fixed- | Show results with:fixed-<|control11|><|separator|>
  16. [16]
    Standard library header <inttypes.h> (C99) - cppreference.com
    ### Summary of Macros for printf and scanf Format Specifiers (C99)
  17. [17]
    [PDF] technical iso/iec ts specificaton 18661-‐3 - Open Standards
    The C Standard provides just three standard floating types (float, double, and long double) that are required of all implementations. C Annex F for binary ...Missing: _Float16 | Show results with:_Float16
  18. [18]
    Floating Types (Using the GNU Compiler Collection (GCC))
    The _Float32 type is supported on all systems supporting IEEE binary32; the _Float64 and _Float32x types are supported on all systems supporting IEEE binary64.Missing: C11 stdfloat. h
  19. [19]
    None
    Below is a merged summary of arrays in C based on the N1570 (C11 Draft) document, consolidating all information from the provided segments into a comprehensive response. To maximize detail and clarity, I’ll use a structured format with sections and tables where appropriate, ensuring all unique points, quotes, and details are retained. Since the system limits "thinking tokens," I’ll focus on direct synthesis and presentation without excessive deliberation.
  20. [20]
    Programming languages - ISO/IEC 9899:2018
    ISO/IEC 9899:2018 specifies the form and interpretation of C programs, including syntax, semantic rules, and data representation.
  21. [21]
  22. [22]
  23. [23]
    How to Create Jump Tables via Function Pointer Arrays in C and C++
    May 1, 1999 · Jump tables, also called branch tables, are an efficient means of handling similar events in software. Here's a look at the use of arrays of function pointers ...
  24. [24]
    qsort, qsort_s - cppreference.com
    ### Summary of qsort Comparator Details
  25. [25]
  26. [26]
    [PDF] Contents - Open Standards
    the mechanism by which input data are transformed for use by a C program ...<|control11|><|separator|>
  27. [27]
    [PDF] Rationale for International Standard— Programming Languages— C
    representation of C can be defined. ISO has defined such a standard, ISO/IEC 646, which describes an invariant subset of ASCII. Page 29. Environment. 22. The ...
  28. [28]
    Restricted Pointers (Using the GNU Compiler Collection (GCC))
    ### Summary of Restricted Pointers in GCC
  29. [29]
    restrict Pointers (GNU C Language Manual)
    You can declare a pointer as “restricted” using the restrict type qualifier, like this: int *restrict p = x; This enables better optimization of code that uses ...Missing: C99 | Show results with:C99
  30. [30]
    [PDF] Programming languages — C - Open Standards
    May 6, 2016 · (This cover sheet to be replaced by ISO.) This document specifies the form and establishes the interpretation of programs expressed in the.
  31. [31]
    ISO/ IEC C - Project status and milestones - Open Standards
    Oct 7, 2025 · All five parts were originally developed based on C11; parts 4 and 5 are being revised based on C23 (revisions pending publication as of ...
  32. [32]
    [PDF] C++26 should refer to C23 not C17 - Open Standards
    Aug 2, 2024 · In C23 the bool, true and false macros are now keywords. As a result, the header <stdbool.h> is empty in C23, except for the obsolescent ...