Native (computing)
View on WikipediaThis article possibly contains original research. (February 2023) |
Native describes a computing system as operating directly with an underlying technology; with no intervening communication or translation layers.
Native software
[edit]Native software is built to be executed directly by processors that implement a compatible instruction set.[1] A program that runs natively on one platform is runnable on another platform via an emulator if an emulator is available and, generally, with significant runtime speed degradation.[2] For example, games for a Game Boy (typically distributed as a cartridge), generally run natively on a Game Boy which is relatively incompatible with other computer platforms.[3] To run such a game on another processor, software that emulates the Game Boy hardware is required.[4]
Cross-platform software can run on multiple processors although possibly requiring it to be re-built for different target systems.
Native API
[edit]A native application programming interface (API) provides direct access to an underlying technology. For example, the Windows Native API is an application programming interface specific for the Windows NT kernel, which provides access to some kernel functions which cannot be directly accessed through the more universal Windows API.
Native VM
[edit]A native virtual machine (VM) runs directly on hardware; without virtualization or virtualization at its lowest level. For example, with multiple levels of virtualization, the lowest level operating system – the one that actually maintains direct control of the hardware – is referred to as a "Native VM".
Native data
[edit]This section needs expansion. You can help by adding to it. (February 2012) |
Applied to data, native data formats or communication protocols are those supported by a certain computer hardware or software, with maximal consistency and minimal amount of additional components.
For example, EGA and VGA video adapters natively support code page 437. This does not preclude supporting other code pages, but it requires either a font uploading or using graphic modes.
Cloud Native
[edit]Cloud native refers to the approach of building, deploying, and managing applications in cloud computing environments – for software optimized for running on a cloud-based platform.
References
[edit]- ^ "What is native code? - Definition from WhatIs.com". SearchAppArchitecture. Retrieved 2019-11-22.
- ^ "How Does Emulation Work and Why Is It So S". MakeUseOf. Retrieved 2018-11-22.
- ^ "Nintendo Game Boy - Game Console - Computing History". www.computinghistory.org.uk. Retrieved 2019-11-22.
- ^ Pot, Justin. "Why Are Video Game Emulators So Important? (Because They Preserve Our History)". How-To Geek. Retrieved 2019-11-22.
Native (computing)
View on GrokipediaCore Concepts
Definition and Scope
In computing, native refers to code, data, or processes that are specifically designed and optimized for direct execution on a particular hardware architecture, operating system, or computing environment, without relying on intermediaries such as emulators, interpreters, virtual machines, or abstraction layers. This direct compatibility ensures that the elements operate using the target platform's inherent capabilities, including its instruction set and memory model. For instance, native code is typically compiled into machine-readable binaries that the processor can execute immediately, contrasting with portable or interpreted formats that require runtime translation.[6][7] The practice of platform-specific programming and native code generation traces its roots to the 1970s and 1980s, a period marked by the proliferation of such approaches for mainframes and early personal computers. During this era, developers increasingly used assembly languages tailored to architectures like the Intel x86, introduced in 1978, to produce machine code that ran directly on the hardware without additional overhead. This approach arose alongside the development of high-level compilers, such as those for Pascal in the early 1970s, which generated native code for local machines to achieve optimal performance on diverse systems.[8][9] The scope of native computing extends beyond code to include data formats and execution models. Native binaries represent compiled machine code executable directly by the CPU, while native formats encompass platform-specific conventions like endianness—the byte order for multi-byte data such as integers—and data structures aligned to the system's memory boundaries for efficient access. Native execution involves the straightforward use of the CPU's instruction set, allowing instructions to be processed without emulation or virtualization. These elements collectively enable seamless interaction with the underlying hardware and OS resources.[7][10][11] Key benefits of native computing include superior performance efficiency through minimized overhead, optimal resource utilization by leveraging hardware directly, and tight integration with system-level features like low-latency I/O and specialized instructions. In modern contexts, concepts like cloud native extend these principles to distributed, containerized environments optimized for scalable cloud infrastructures.[12][6][13]Native vs. Cross-Platform
In computing, native software development compiles source code directly into machine instructions tailored to a specific hardware architecture, such as x86 or ARM, allowing execution with minimal runtime overhead. In contrast, cross-platform or non-native approaches employ abstraction layers, emulation, interpretation, or virtualization to enable software portability across diverse systems, though this introduces efficiency trade-offs. These methods prioritize broader compatibility over peak performance, often at the expense of execution speed and resource utilization. Non-native alternatives encompass several techniques. Emulation, exemplified by tools like QEMU, simulates an incompatible hardware environment to run code from one instruction set architecture (ISA) on another, but incurs substantial overhead; for instance, emulated execution can be up to 10 times slower than native runs due to dynamic translation of instructions.[14] Interpretation involves executing intermediate bytecode, as in the Java Virtual Machine (JVM) processing Java bytecode, where runtime translation adds overhead, resulting in slower performance compared to pre-compiled native code before just-in-time (JIT) optimizations kick in. Virtualization, via hypervisors such as VMware, partitions hardware resources to host multiple operating systems, achieving near-native speeds with typical CPU overheads of 1-5%, though full-system emulation within virtual machines amplifies costs.[15] Cross-platform frameworks, like the .NET Common Language Runtime (CLR), use managed environments with JIT compilation to generate native code at runtime, providing abstraction for code reusability across platforms. The primary trade-offs between native and cross-platform methods revolve around performance, portability, and development effort. Native execution delivers superior speed and low overhead by leveraging hardware directly, but demands recompilation and maintenance for each target platform, limiting scalability for multi-architecture support. Cross-platform solutions, through layers like the CLR or JVM, enhance portability by allowing a single codebase to deploy across ecosystems—such as Windows, Linux, and macOS—with reduced per-platform coding, yet they impose efficiency penalties; for example, early cross-platform implementations could be significantly slower than optimized native code in server applications, though modern JIT reduces this gap. Cross-platform development can reduce overall effort by 30-50% for multi-platform apps compared to separate native implementations, though it may require additional time for platform-specific adaptations and testing multiple environments.[16] Native approaches also facilitate deeper integration via platform-specific APIs, enabling optimizations unavailable in abstracted cross-platform setups. Portability challenges for native code stem from its tight coupling to specific ISAs; binaries compiled for x86 are incompatible with ARM processors because the instruction sets differ fundamentally in encoding and semantics, necessitating architecture-specific recompilation to avoid emulation or translation. This binary incompatibility hinders seamless deployment across heterogeneous hardware, such as desktops (often x86) and mobile devices (frequently ARM), forcing developers to maintain multiple builds. Comparative metrics underscore these differences. In execution speed, native code often outperforms pure emulation or early interpreted non-native variants significantly, with factors up to 10x in emulation cases; however, JIT-managed cross-platform code approaches native performance. Hybrid mobile apps using frameworks like React Native often use 10-20% more CPU than native equivalents in resource-intensive tasks.[17] Memory usage follows suit, with cross-platform abstractions adding 10-30% overhead in managed runtimes like the JVM due to object headers, metadata, and garbage collection structures. Debugging complexity rises in cross-platform scenarios, as abstraction layers obscure low-level issues, contrasting with native code's direct hardware traceability, though this benefit comes at the cost of platform-specific expertise.Native Software Development
Compilation and Execution
In native software development, the compilation process begins with source code written in languages such as C or C++, which is translated into machine code executable on a specific hardware architecture. Compilers like the GNU Compiler Collection (GCC) perform this translation through multiple stages, including preprocessing to handle directives, compilation to assembly language, assembly to object code, and linking to produce the final executable. For instance, on Linux systems, GCC generates ELF (Executable and Linkable Format) binaries, a standard format that encapsulates the machine code, metadata, and relocation information tailored to architectures like x86-64.[18] Similarly, Microsoft's Visual C++ (MSVC) compiler processes C++ source into native executables for Windows, producing portable executable (PE) format files that include the compiled code optimized for the target processor.[19] Once compiled, native executables follow a direct execution model where the operating system loads the binary into memory without intermediate runtime translation, such as just-in-time compilation seen in managed environments. The kernel parses the executable's header—e.g., the ELF header on Linux—to map sections like code, data, and stack into virtual memory, then transfers control to the program's entry point for CPU execution.[20] This process includes resolving dependencies by linking to native libraries; for dynamically linked executables, the dynamic linker (ld.so on Linux) loads shared objects at runtime, mapping them into the process's address space to resolve symbols and enable execution.[21] In contrast, statically linked executables embed all library code directly into the binary during compilation, resulting in a self-contained file that loads and runs without external dependencies, though at the cost of larger file sizes. Build workflows for native software rely on tools that automate compilation and linking for platform-specific targets. Systems like GNU Make manage dependencies and invoke the compiler via Makefiles, which specify rules for building object files and executables across Unix-like platforms. CMake, a cross-platform meta-build system, generates native build files (e.g., Makefiles or Visual Studio projects) from a platform-agnostic configuration, allowing developers to target architectures like ARM or x86 while handling static or dynamic linking options.[22] Error handling in native execution arises from direct memory and hardware access, often leading to crashes like segmentation faults when code attempts invalid operations, such as dereferencing null pointers or exceeding array bounds. Unlike managed environments with garbage collection and bounds checking, native code lacks automatic safeguards, so the operating system signals the process (e.g., SIGSEGV on Unix-like systems), typically terminating it unless a custom handler is installed via mechanisms like signal(). This direct access enables low-level control but requires explicit checks, such as validating pointers before use, to prevent system instability.Platform-Specific Optimizations
Platform-specific optimizations in native software development exploit hardware features unique to the target architecture to enhance performance, often yielding significant speedups for compute-intensive tasks. One key strategy involves the use of Single Instruction, Multiple Data (SIMD) instructions, such as SSE and AVX on x86 architectures, which enable parallel processing of multiple data elements within a single operation. For instance, AVX-512 extensions allow for 512-bit vector operations, improving throughput for floating-point computations in scientific applications. These instructions are accessed through compiler intrinsics, which map directly to hardware operations without inline assembly, facilitating fine-grained control over vectorization.[23][24] Cache-aware algorithms further optimize performance by minimizing data movement between memory hierarchies, reducing cache misses that can severely impact execution time. Seminal work on cache-oblivious algorithms demonstrates asymptotically optimal approaches for operations like matrix transposition and fast Fourier transforms (FFT), achieving near-ideal cache utilization without explicit knowledge of cache parameters. These methods recursively divide problems to ensure data locality, resulting in significant improvements in bandwidth-bound workloads compared to naive implementations. Architecture-specific intrinsics complement these by providing low-level access to features like unaligned memory loads or fused multiply-add operations, tailored to the processor's instruction set.[25] In media processing applications, vectorization via SIMD intrinsics accelerates tasks such as image filtering and audio encoding; an empirical study on the Berkeley Media Benchmark showed speedups of 1.10 to 3.39 for 23 out of 34 procedures when targeting Pentium 4's SSE2 extensions. Similarly, GPU offloading using native CUDA APIs on NVIDIA hardware enables massive parallelism for graphics and machine learning workloads, with shared memory optimizations reducing global memory accesses and yielding throughput increases of several orders of magnitude over CPU equivalents.[26][27] Native profiling tools are essential for identifying platform-specific bottlenecks, such as inefficient vector utilization or cache thrashing. GNU gprof, when invoked with the -pg compilation flag, generates call graphs and flat profiles revealing time spent in functions, helping pinpoint hotspots like recursive neighbor-counting loops in simulation code. Intel VTune Profiler extends this by analyzing threading oversubscription and memory access patterns on x86 systems, enabling optimizations in oversubscribed scenarios.[28][29] However, these optimizations often compromise portability, as reliance on architecture-specific features like AVX intrinsics or CUDA calls limits code reusability across platforms. Developers mitigate this through conditional compilation directives, such as #ifdef blocks, to include platform-tailored code paths while maintaining a unified codebase.[30]Native System Interfaces
Native APIs
Native APIs refer to the collection of functions, protocols, and interfaces provided directly by an operating system to enable applications to perform core operations such as resource allocation, input/output handling, and system management without intermediary abstraction layers.[31][32] These APIs form the foundational layer for native software development, allowing developers to interact with the kernel and system services in a platform-specific manner. For instance, on Windows, the Win32 API serves as the primary native interface for desktop and server applications, encompassing thousands of functions for system-level tasks.[31] Similarly, POSIX (Portable Operating System Interface), defined by IEEE Std 1003.1, establishes a standardized API for Unix-like systems, ensuring portability across compliant operating systems like Linux and macOS.[32][33] Key components of native APIs include low-level system calls and higher-level libraries that wrap them for ease of use. System calls, such as those invoked via the syscall instruction on Unix-like systems, provide direct kernel access for operations like file input/output (e.g., open, read, write functions under POSIX). These calls transition the processor from user mode to kernel mode to execute privileged instructions securely. Libraries like the GNU C Library (glibc), which implements the ISO C standard, act as intermediaries by encapsulating system calls into user-friendly functions, such as those in <stdio.h> for standard I/O or <stdlib.h> for dynamic memory allocation.[34] On Windows, the Win32 API includes analogous components, with functions like CreateFile for file I/O and VirtualAlloc for memory operations, often backed by underlying NT kernel calls.[31] The evolution of native APIs traces from rudimentary interrupt-based mechanisms in early operating systems to sophisticated object-oriented frameworks in modern ones. In MS-DOS, APIs were primarily accessed through software interrupts, such as INT 21h, which handled a wide range of services including file operations and program execution via subfunctions specified in the AH register.[35] This interrupt-driven model persisted into early Windows versions, which initially operated as shells atop MS-DOS. With the introduction of Windows NT in 1993, the Win32 API emerged as a comprehensive, protected-mode interface, replacing DOS interrupts with direct function calls to the NT kernel for enhanced stability and 32-bit support.[36] On Unix-like systems, POSIX standardized APIs starting with IEEE 1003.1-1988, building on earlier Unix system calls to promote interoperability.[32] Modern examples include Apple's Cocoa framework on macOS, which evolved from the NeXTSTEP API developed in 1989 and was integrated into OS X after Apple's 1997 acquisition of NeXT, providing object-oriented abstractions over POSIX and Mach kernel services.[37] Native APIs are essential for usage scenarios requiring fine-grained control over system resources, such as direct memory management and process control. For memory management, APIs allow allocation of virtual memory regions without garbage collection overhead, as seen in Win32's HeapAlloc or POSIX's mmap for mapping files or devices into address space.[31] Process control functions enable creating, suspending, or terminating processes, exemplified by Win32's CreateProcess and POSIX's fork/exec family, which facilitate multitasking and inter-process communication directly with the kernel.[31] These capabilities are particularly valuable in performance-critical applications like system utilities or embedded software, where abstraction layers would introduce latency. Native APIs may also interface with hardware access via drivers, but this remains subordinate to general OS services.[38]Native Drivers and Hardware Access
Native drivers in computing are specialized software components that operate in kernel mode to enable direct communication between the operating system and hardware devices, translating abstract OS requests into specific hardware instructions or signals. In the Windows ecosystem, the Windows Driver Model (WDM) serves as a foundational architecture for these kernel-mode drivers, ensuring source-code compatibility across Windows versions while managing device interactions through standardized interfaces. WDM drivers handle tasks such as power management and Plug and Play support, operating at a privileged level to access hardware registers and memory directly. Similarly, in Linux, device drivers are implemented as kernel modules that integrate with the kernel's device model, providing a unified framework for hardware abstraction and resource allocation, including buses like PCI and USB.[39][40] Development of native drivers involves leveraging platform-specific native APIs to manage core hardware operations, including device input/output (I/O), interrupt handling, and Direct Memory Access (DMA). For I/O, developers use kernel APIs to perform read and write operations on device registers or memory-mapped I/O spaces, ensuring efficient data exchange without unnecessary context switches. Interrupt handling is implemented through interrupt service routines (ISRs) that respond to hardware signals, such as data arrival or errors, by registering handlers with the kernel's interrupt subsystem to maintain system responsiveness. DMA support allows devices to transfer data directly to or from system memory, bypassing the CPU for performance gains; in Linux, this is facilitated by the DMA API functions likedma_map_single() for streaming mappings and dma_alloc_coherent() for persistent allocations, while Windows uses IRPs (I/O Request Packets) to coordinate DMA operations within WDM frameworks. These APIs require careful synchronization to avoid race conditions and ensure data integrity during hardware access.[41][42]
Representative examples of native drivers include graphics drivers and USB controllers, which illustrate hardware-specific implementations. Graphics drivers, such as those integrated with DirectX on Windows, rely on kernel-mode components like the DirectX Graphics Kernel Subsystem (dxgkrnl.sys) to manage GPU resources, handle rendering commands, and optimize display output through low-level hardware interactions. These drivers translate DirectX API calls into device-specific instructions, enabling features like texture mapping and shader execution directly on the GPU hardware. For USB controllers, kernel-mode drivers in Windows, built using WDM or the Kernel-Mode Driver Framework (KMDF), interface with host controllers to enumerate devices, manage bandwidth allocation, and process data transfers over the USB bus, supporting protocols from USB 2.0 to USB4. In Linux, USB drivers similarly use kernel modules to handle controller hardware, such as EHCI for high-speed USB, ensuring seamless device connectivity and power management.[43][44]
Security considerations are paramount in native driver development due to their elevated privileges, which expose the system to risks like buffer overflows that can result in arbitrary code execution or system instability. In kernel mode, improper bounds checking on input buffers from user space or hardware can overflow into adjacent memory, potentially allowing attackers to escalate privileges or crash the kernel; for instance, vulnerabilities in driver I/O handling have historically led to exploitable conditions in both Windows and Linux environments. Mitigations include rigorous input validation, use of safe memory allocation functions, and kernel protections such as address space layout randomization (KASLR) to obscure memory layouts, non-executable (NX) memory pools to prevent code injection, and driver signing requirements to verify authenticity. Frameworks like KMDF in Windows and self-protection features in the Linux kernel, including stack smashing protection and control-flow integrity checks, further reduce attack surfaces by enforcing safer coding patterns and runtime verifications. Developers must also conduct threat modeling and use tools like static analysis to identify potential overflows during development.[45][46][47]
Native Runtime Environments
Native Virtual Machines
Native virtual machines (VMs) are runtime environments designed to execute high-level code by compiling it directly to machine-native instructions, either at build time via ahead-of-time (AOT) compilation or dynamically at runtime, thereby avoiding interpretive overhead and achieving performance close to natively compiled applications.[48] A prominent example is the Java HotSpot JVM operating in server mode, which employs tiered just-in-time (JIT) compilation to identify and optimize frequently executed code paths ("hot spots") into native machine code for improved throughput on multi-processor systems.[49] Similarly, .NET Native, introduced by Microsoft for Universal Windows Platform (UWP) applications, uses AOT compilation to transform managed intermediate language (IL) code into a single native executable, eliminating the need for a JIT compiler at runtime and reducing startup latency.[50] Core components of native VMs include JIT compilers for runtime optimization and garbage collection (GC) mechanisms tailored to manage memory allocation on native-accessible heaps while maintaining language-level safety guarantees.[51] In the HotSpot JVM, the GC operates on the managed Java heap, reclaiming unused objects through algorithms like the parallel collector, even as JIT-generated native code executes directly on the hardware.[52] For .NET Native AOT deployments, the runtime incorporates a server-oriented GC that handles managed memory alongside the statically compiled native binaries, supporting features like concurrent collections to minimize pauses in performance-critical scenarios.[53] JIT compilation acts briefly as a bridge to native execution by profiling execution patterns before generating optimized assembly.[54] These environments find key applications in high-performance computing (HPC) domains, such as scientific simulations, big data analytics, and financial modeling, where the reduced VM overhead enables scalable performance comparable to traditional native languages like C++.[55] For instance, the HotSpot JVM's optimizations allow Java applications to leverage multi-core processors effectively in cluster-based HPC workloads, achieving near-native speeds after warm-up.[56] .NET Native AOT similarly supports resource-constrained HPC edge computing by producing lightweight, fast-starting binaries suitable for distributed simulations.[4] The historical evolution of native VMs traces back to the mid-1990s with the debut of the original Java VM in 1995, which initially relied on interpretation but laid the groundwork for dynamic compilation.[57] The HotSpot JVM, acquired by Sun Microsystems in 1997 and integrated as the default in Java 2 Standard Edition (J2SE) 1.3 in 2000, pioneered adaptive JIT techniques to enhance runtime performance.[58] In parallel, Microsoft's .NET Framework launched in 2002 with JIT-based execution, evolving to include .NET Native AOT in 2015 for UWP and expanding to cross-platform support in .NET 7 (2022), with further enhancements in .NET 8 (2023, adding ASP.NET Core Native AOT support) and .NET 9 (2024, improving runtime trimming and diagnostics), reflecting a shift toward hybrid JIT/AOT models in contemporary runtimes as of 2025.[59][4][60]Just-In-Time Compilation
Just-in-time (JIT) compilation is a dynamic technique that generates native machine code from bytecode or intermediate representation (IR) during program execution, enabling runtime optimization in managed environments. The process begins with loading the input code, such as Java bytecode or JavaScript, into the runtime. An interpreter or baseline compiler initially executes the code to gather profiling data on execution frequency and patterns. Once hotspots—frequently executed sections—are identified, the JIT compiler analyzes the IR, applies optimizations, and translates it into platform-specific native instructions, which are then cached for reuse. This on-the-fly compilation replaces slower interpretation with faster native execution, often using techniques like on-stack replacement to seamlessly switch from interpreted to compiled code without restarting the method.[61][62] Key algorithms in JIT compilation include hotspot detection, inlining, and adaptive optimization. Hotspot detection typically employs invocation counters that decrement with each method call until reaching a threshold, signaling the compiler to target that code for compilation; alternatively, sampling threads monitor execution to profile hotspots dynamically. Inlining replaces method calls with the method's body directly in the caller, reducing overhead and enabling further optimizations like constant folding, particularly effective for small, frequently invoked routines. Adaptive optimization builds on runtime profiles to recompile code at escalating optimization levels—such as from basic (cold) to aggressive (scorching)—incorporating data like branch probabilities or type information to refine decisions, such as devirtualizing virtual calls or unrolling loops based on observed behavior. These algorithms allow the compiler to tailor code to actual usage, improving efficiency over static analysis alone.[61][49] Compared to ahead-of-time (AOT) compilation, JIT reduces startup time by deferring most compilation until needed, allowing quick initial execution via interpretation while building optimized code incrementally. However, this introduces temporary overhead during compilation pauses, though it enables runtime adaptations that can yield superior peak performance for long-running applications by leveraging execution profiles unavailable at compile time. In benchmarks, JIT often achieves 2-10x speedups over pure interpretation for hot paths, with adaptive recompilation closing the gap to AOT in steady-state scenarios, though startup may lag by seconds in large applications.[61] Prominent examples include the V8 engine in Node.js, which compiles JavaScript to native code on-the-fly, parsing source into IR and using a tiered system starting with the Ignition interpreter for bytecode generation, followed by the baseline JIT Maglev (introduced in 2023 for faster compilation) and the optimizing TurboFan JIT to accelerate server-side execution.[63] In web browsers like Chrome, V8's JIT similarly boosts JavaScript performance for dynamic web applications. Android's ART runtime employs JIT alongside AOT, profiling Dalvik Executable (.dex) files during app runs to compile methods to native code, enabling inlining and on-stack replacement for smoother multitasking and improved responsiveness through profile-guided optimizations. JIT integrates with native virtual machines by serving as the execution engine within them, compiling managed code to leverage host hardware directly.[64][62]Native Data Handling
Native Data Types
Native data types in computing refer to the fundamental building blocks of data representation that are directly supported by a platform's hardware architecture and defined through its application binary interface (ABI). These types ensure efficient memory usage and processing by aligning with the processor's native word size and instruction set, allowing software to interact seamlessly with the underlying system without abstraction layers.[65] Primitive types form the core of native data handling, with sizes varying by architecture to match the processor's register width. For instance, on x86-64 architectures, theint type is typically 32 bits (4 bytes), while pointers are 64 bits (8 bytes) to address the expanded memory space. Structs and other composite types are aligned to the native word size—often 64 bits on modern 64-bit systems—to enable atomic operations and minimize access overhead.[65][66]
Endianness governs the byte order of multi-byte values in memory, impacting how data is interpreted across platforms. In big-endian systems, such as those using PowerPC processors, the most significant byte is stored at the lowest memory address (e.g., the value 0x4F52 appears as 4F at address 1000 and 52 at 1001). Conversely, little-endian architectures like x86 store the least significant byte first (e.g., 52 at 1000 and 4F at 1001), which can affect the portability of binary data but optimizes certain arithmetic operations.[67]
Alignment rules enforce that data begins at addresses that are multiples of their size or a specified boundary, with padding bytes inserted to achieve this for performance reasons. On modern CPUs, such as those in x86-64 systems, 64-bit alignment is common for quadword types like doubles and pointers, preventing split cache line accesses that could halve fetch efficiency. For structs, the compiler adds padding between members—e.g., 4 bytes after a 32-bit int followed by a 16-bit short—to ensure each aligns properly, though reordering members from largest to smallest can reduce overall padding.[66][68]
ABI specifications standardize these layouts to guarantee compatibility between compiled code, libraries, and the operating system. The System V ABI for AMD64, for example, mandates natural alignments for scalars (e.g., 4 bytes for int, 8 bytes for long) and requires struct sizes to be multiples of their alignment, including undefined padding at the end if needed. These rules, detailed in platform-specific documents, ensure that native data types remain consistent across tools from different vendors.[65]
| Type | Size (x86-64) | Alignment (x86-64) |
|---|---|---|
| char | 1 byte | 1 byte |
| short | 2 bytes | 2 bytes |
| int | 4 bytes | 4 bytes |
| long | 8 bytes | 8 bytes |
| pointer | 8 bytes | 8 bytes |
| double | 8 bytes | 8 bytes |