sizeof
The sizeof operator is a compile-time unary operator in the C and C++ programming languages that returns the size, in bytes, of a given expression, variable, or type.[1] It evaluates to an integer of type size_t, which represents the maximum size of any object, and is essential for determining memory requirements without executing runtime code.[2] The operator can take either an expression (e.g., sizeof variable) or a parenthesized type name (e.g., sizeof(int)), and its result depends on the implementation's architecture, such as the size of fundamental types like char (always 1 byte) or int (typically 4 bytes on 32-bit systems).[3]
Introduced during the development of C in the early 1970s by Dennis Ritchie at Bell Labs, sizeof emerged as part of C's evolving type system, which was designed to support byte-addressable hardware like the PDP-11 computer used for Unix.[4] Prior languages like B lacked strong typing, making explicit size queries unnecessary or impossible; C's addition of types such as char, int, and pointers necessitated a mechanism to query storage sizes for portability and efficient memory use. By 1978, the first edition of The C Programming Language by Kernighan and Ritchie formalized sizeof as a key feature for computing object sizes at compile time, enabling practices like array traversal (e.g., sizeof(array)/sizeof(array[0]) to find element count) and safe dynamic allocation via functions like malloc.
In practice, sizeof promotes code portability by abstracting architecture-specific details, though results can vary (e.g., sizeof(long) may be 4 or 8 bytes).[3] It cannot be applied to incomplete types, functions, or the void type, and for variable-length arrays (introduced in C99), evaluation occurs at runtime.[2] This operator remains foundational in systems programming, embedded development, and performance-critical applications, influencing standards like ISO C (first published 1990) and C++ (first released in 1985).
Overview
Purpose
The [sizeof](/page/sizeof) operator is a unary compile-time operator in the C and C++ programming languages that yields the size, in bytes, of the object representation of a given type or the type of an expression, without evaluating the expression itself.[5][1] This evaluation occurs at compile time, returning a value of type size_t, an unsigned integer type defined in <stddef.h>, which represents the storage size required for the operand.[5][1]
The operator originated in early implementations of C during the 1970s at Bell Labs[6] and was formalized in the ANSI C standard (C89, also known as ISO/IEC 9899:1990), addressing inconsistencies in type sizes across diverse hardware platforms in pre-standard C compilers.[5][7][1]
Prior to standardization, variations in data type sizes between systems, such as different word lengths on mainframes versus minicomputers, complicated code portability, and sizeof provided a mechanism to query these sizes explicitly within the language.[7][1]
A primary purpose of sizeof is to enable portable code by allowing developers to query and adapt to platform-specific type sizes, thereby avoiding hard-coded assumptions that could fail across architectures.[1][5] It supports safe memory management, such as in dynamic allocation with functions like malloc, where specifying the correct byte count—e.g., malloc(n * sizeof([int](/page/INT)))—ensures appropriate storage without excess or deficiency.[1][7] By providing accurate sizes, it helps prevent common errors like buffer overflows in operations involving arrays or structures, where misjudging bounds could lead to memory corruption.[1] For instance, sizeof([int](/page/INT)) is typically 4 bytes on both 32-bit and 64-bit systems, though the exact size is implementation-defined, highlighting the need for runtime-agnostic size determination to maintain code reliability.[5][1]
Syntax
The sizeof operator is a unary operator in C and C++ that yields the size in bytes of the object representation of its operand's type. It supports two primary syntactic forms: sizeof (type-name) to query the size of a specified type, and sizeof unary-expression to query the size of the type of the given expression.[5][8]
Parentheses are mandatory around a type name but optional around a unary expression, allowing forms such as sizeof x or sizeof(x) for expressions. The result is always a constant expression evaluated at compile time, with type size_t—an implementation-defined unsigned integer type declared in <stddef.h> for C and <cstddef> for C++. The operator does not evaluate its operand (and thus ignores any side effects), except for variable-length arrays in C, where the size may be determined at runtime.[5][8]
In C++, the core sizeof operator remains unary and cannot be overloaded, though an extended form sizeof... (identifier) exists to yield the number of elements in a template parameter pack as a size_t value. Applying sizeof to an expression of incomplete type (including void), function type, or bit-field (in C++11 and later) results in undefined behavior; operands must designate complete types where applicable.[8]
The following code snippets illustrate basic usage:
c
sizeof(int); // Yields the size of the `int` type in bytes
sizeof(int); // Yields the size of the `int` type in bytes
c
sizeof *ptr; // Yields the size of the type pointed to by `ptr`
sizeof *ptr; // Yields the size of the type pointed to by `ptr`
Usage
Arithmetic Types
In C and C++, the sizeof operator applied to arithmetic types yields the size in bytes of the storage required for those types, which is implementation-defined beyond certain minimum guarantees specified in the language standards. The fundamental arithmetic types include integer types (such as char, short, int, and long) and floating-point types (such as float and double). For integer types, the C99 standard mandates that sizeof(char) equals 1 byte (8 bits), while sizeof(short) is at least 2 bytes, sizeof(int) is at least 2 bytes, and sizeof(long) is at least 4 bytes; typical implementations on 32-bit or 64-bit systems use 2 bytes for short, 4 bytes for int, and 4 or 8 bytes for long, respectively.[9] Similarly, floating-point types typically require 4 bytes for float and 8 bytes for double, though these sizes align with implementation choices often following IEEE 754 conventions for precision and range.[9]
Signed and unsigned variants of the same integer type occupy identical storage sizes, as the standards treat them as having the same representation width, differing only in value interpretation (e.g., two's complement for signed types).[9] For example, sizeof(signed [int](/page/INT)) equals sizeof(unsigned [int](/page/INT)), both typically 4 bytes on common platforms. Enumeration types (enum) in C have an implementation-defined size that is compatible with an integer type, defaulting to the size of [int](/page/INT) in most compilers, ensuring they can hold the declared enumerator values without overflow.[9]
Due to these platform dependencies, code relying on specific sizes risks portability issues; for instance, sizeof(long) may be 4 bytes on 32-bit x86 systems but 8 bytes on 64-bit systems. To mitigate this, the C99 standard introduces fixed-width integer types via the <stdint.h> header, such as int32_t (exactly 32 bits if supported) and uint64_t, which provide consistent sizes across implementations where available.[9] An example demonstrating typical sizes is:
c
#include <stdio.h>
int main() {
printf("sizeof(char): %zu\n", sizeof(char)); // 1
printf("sizeof(short): %zu\n", sizeof(short)); // 2
printf("sizeof([int](/page/INT)): %zu\n", sizeof([int](/page/INT))); // 4
printf("sizeof(long): %zu\n", sizeof(long)); // 4 or 8
printf("sizeof(float): %zu\n", sizeof(float)); // 4
printf("sizeof(double): %zu\n", sizeof(double)); // 8
return 0;
}
#include <stdio.h>
int main() {
printf("sizeof(char): %zu\n", sizeof(char)); // 1
printf("sizeof(short): %zu\n", sizeof(short)); // 2
printf("sizeof([int](/page/INT)): %zu\n", sizeof([int](/page/INT))); // 4
printf("sizeof(long): %zu\n", sizeof(long)); // 4 or 8
printf("sizeof(float): %zu\n", sizeof(float)); // 4
printf("sizeof(double): %zu\n", sizeof(double)); // 8
return 0;
}
These values should be verified on the target architecture, as they reflect common but non-guaranteed configurations.[9]
Pointers and Arrays
The sizeof operator applied to a pointer yields the size in bytes required to store a memory address, which is independent of the pointed-to type and typically equals 4 bytes on 32-bit systems or 8 bytes on 64-bit systems. This size is implementation-defined by the C standard, as pointers to different types may vary but are usually uniform within a given architecture.[10] (Section 6.2.5)
For array objects of complete type, sizeof returns the total size in bytes, computed as the product of the number of elements and the size of each element.[10] (Section 6.5.3.4) For instance, in the declaration int arr[10];, assuming sizeof(int) is 4 bytes, sizeof(arr) evaluates to 40 bytes at compile time. Taking the address of the first element, as in sizeof(&arr[0]), however, returns the size of a pointer to int, not the full array.
c
#include <stdio.h>
int main() {
int arr[10];
[printf](/page/Printf)("sizeof(arr): %zu\n", sizeof(arr)); // 40 (on typical [32](/page/32)/64-bit system)
[printf](/page/Printf)("sizeof(&arr[0]): %zu\n", sizeof(&arr[0])); // 4 or 8 (pointer size)
[return 0](/page/Return_0);
}
#include <stdio.h>
int main() {
int arr[10];
[printf](/page/Printf)("sizeof(arr): %zu\n", sizeof(arr)); // 40 (on typical [32](/page/32)/64-bit system)
[printf](/page/Printf)("sizeof(&arr[0]): %zu\n", sizeof(&arr[0])); // 4 or 8 (pointer size)
[return 0](/page/Return_0);
}
A key distinction arises with array-to-pointer decay: except in specific contexts like the operand of sizeof or unary &, an lvalue of array type converts to a pointer to its initial element.[10] (Section 6.3.2.1) Thus, when an array is passed to a function, the parameter type is adjusted to a pointer, causing sizeof inside the function to yield the pointer size rather than the array's total size. This pitfall often leads to incorrect assumptions about array length in functions. To compute the number of elements in a non-decayed array, the expression sizeof(arr) / sizeof(arr[0]) provides the count, as it divides the total bytes by the bytes per element.
Multidimensional arrays are handled as arrays of arrays, so sizeof computes the total contiguous block size by multiplying all dimensions by the element size. For example, int matrix[3][4]; has size 3 * 4 * sizeof(int) or 48 bytes if int is 4 bytes, treating the entire structure as a single allocation.
c
#include <stdio.h>
int main() {
int matrix[3][4];
printf("sizeof(matrix): %zu\n", sizeof(matrix)); // 48 (assuming int=4 bytes)
printf("Number of rows: %zu\n", sizeof(matrix) / sizeof(matrix[0])); // 3
printf("Elements per row: %zu\n", sizeof(matrix[0]) / sizeof(matrix[0][0])); // 4
return 0;
}
#include <stdio.h>
int main() {
int matrix[3][4];
printf("sizeof(matrix): %zu\n", sizeof(matrix)); // 48 (assuming int=4 bytes)
printf("Number of rows: %zu\n", sizeof(matrix) / sizeof(matrix[0])); // 3
printf("Elements per row: %zu\n", sizeof(matrix[0]) / sizeof(matrix[0][0])); // 4
return 0;
}
Introduced in C99, variable-length arrays (VLAs) permit runtime-determined sizes via expressions.[9] (Section 6.7.5.2) Unlike fixed-length arrays, sizeof on a VLA is evaluated at runtime if its size depends on a non-constant expression, yielding the actual allocated bytes based on the evaluated dimensions.[10] (Section 6.5.3.4) VLAs decay to pointers in function parameters similarly to fixed arrays, limiting sizeof utility inside functions.
c
#include <stdio.h>
int main() {
int n = 5;
int vla[n]; // VLA with runtime size
[printf](/page/Printf)("sizeof(vla): %zu\n", sizeof(vla)); // 20 (5 * sizeof(int)=4)
return 0;
}
#include <stdio.h>
int main() {
int n = 5;
int vla[n]; // VLA with runtime size
[printf](/page/Printf)("sizeof(vla): %zu\n", sizeof(vla)); // 20 (5 * sizeof(int)=4)
return 0;
}
Structures and Unions
The size of a structure type in C is determined by the sum of the sizes of its members, plus any unnamed padding bytes added within the structure or at the end to satisfy alignment requirements. According to subclause 6.7.2.1 of the C11 standard, padding may be inserted between members or after the last member so that each member begins at an offset that is a multiple of its alignment requirement, and the overall structure size is a multiple of the structure's alignment.[11] This ensures efficient memory access and compatibility with arrays of the structure type. The alignment of the structure itself is the least common multiple of the alignments of its members, typically equal to the largest alignment among them.
For example, consider the following structure on a system where char has 1-byte alignment and int has 4-byte alignment:
c
struct example {
[char](/page/Char) a; // 1 byte
[int](/page/INT) b; // 4 bytes
};
struct example {
[char](/page/Char) a; // 1 byte
[int](/page/INT) b; // 4 bytes
};
Here, sizeof(struct example) evaluates to 8 bytes. The [char](/page/Char) member occupies the first byte at offset 0, followed by 3 bytes of padding to align the [int](/page/INT) member to a 4-byte boundary at offset 4, with the total size padded to 8 bytes for structure alignment.[12] Padding mechanics, such as these insertions, are added to enforce natural alignment rules, with further details covered in the implementation section on padding and alignment.
When a structure contains a nested structure as a member, the size calculation is performed recursively using the complete type of the inner structure, incorporating its own member sizes and any required padding. The offset of the nested structure within the outer one must respect the alignment of the inner structure's type, potentially introducing additional padding before it. This recursive approach ensures that the overall structure layout remains valid for memory access.
The size of a union type is sufficient to contain any of its members, equal to the size of the largest member (by byte count), plus possible padding at the end to achieve the union's alignment requirement. Per C11 standard subclause 6.7.2.1, the alignment of the union is the maximum alignment of its members, and all members share the same starting address within the union object. Thus, smaller members overlap with the storage of larger ones, optimizing space for variant data.
Introduced in the C99 standard (subclause 6.7.2.1), a flexible array member allows the last element of a structure to be an incomplete array type (e.g., int data[];), enabling variable-length objects. The sizeof operator applied to such a structure yields the offset and size up to but excluding the flexible array member, as if it were omitted, though trailing padding may be added based on the structure's alignment.[13] For instance:
c
struct flex {
int count;
char data[]; // [Flexible array member](/page/Flexible_array_member)
};
struct flex {
int count;
char data[]; // [Flexible array member](/page/Flexible_array_member)
};
sizeof(struct flex) equals sizeof(int) (typically 4 bytes), ignoring the data array. To position the flexible array, use offsetof(struct flex, data), which gives the byte offset after fixed members. Memory allocation for an instance with n elements requires sizeof(struct flex) + n * sizeof(char).
Alignment rules for structures and unions dictate that each member's starting address must be a multiple of its alignment value, which for fundamental types is usually equal to their size (e.g., 4 bytes for int on 32-bit systems). The sizeof operator accounts for these rules in computing the total size, ensuring the aggregate type can be stored and accessed efficiently without violating hardware constraints.
Incomplete Types
Incomplete types in C and C++ are object types that lack sufficient information to determine the size required for objects of that type, such as the void type, arrays of unknown size, or structures and unions declared via forward declaration without a subsequent definition.[14][15]
The sizeof operator yields undefined behavior or a compilation error when applied to an incomplete type, as the standard prohibits its use on such types to avoid indeterminate results.[14] In C, this constitutes a constraint violation requiring a diagnostic message from the compiler (section 6.5.3.4).[14] In C++, the program is ill-formed, leading to a compile-time error ([expr.sizeof]).[15] The same restriction applies to function types, where sizeof cannot be used directly, though sizeof on a function pointer yields the size of the pointer itself.[14][15] For void, an incomplete type by definition, sizeof(void) is likewise prohibited in both languages.[14][15]
This behavior is illustrated in code attempting to apply sizeof to a forward-declared structure:
c
struct foo; // Forward declaration: incomplete type
size_t s = sizeof(struct foo); // Compilation error: invalid application of 'sizeof' to incomplete type
struct foo; // Forward declaration: incomplete type
size_t s = sizeof(struct foo); // Compilation error: invalid application of 'sizeof' to incomplete type
Compilers such as GCC report an error like "invalid application of ‘sizeof’ to incomplete type ‘struct foo’".[14]
Incomplete types are commonly employed in header files to implement opaque pointers, where the full definition is hidden in the implementation file to enforce abstraction and prevent direct access to internal details. A prominent example is the FILE type in <stdio.h>, defined as an incomplete structure to allow library implementers to control its layout without exposing it to user code; thus, sizeof(FILE) is not permitted in application code.[16]
Regarding standards evolution, the C89 standard (ISO/IEC 9899:1989) imposed the same strict prohibition on sizeof for incomplete types, function types, and void as subsequent revisions.[14] C99 (ISO/IEC 9899:1999) retained this rule but introduced variable-length arrays (VLAs), which are complete types whose size may be determined at runtime. For a VLA, sizeof evaluates to a non-constant expression based on the runtime value of the size expression.[17] (Section 6.5.3.4) This VLA exception does not extend to other incomplete types, and C11 (ISO/IEC 9899:2011) made VLA support optional while preserving the core constraints.[14] In C++, the rules have remained consistent across standards, with no VLA support and ill-formed status for all such cases.[15]
Object Members
When applying the sizeof operator to a specific member of an object, such as sizeof(structobj.member) or sizeof(obj->member), it evaluates to the size in bytes of the type of that member, without evaluating the member expression itself beyond determining its type.[2] This holds for non-static data members in structs, classes, or unions, providing the size of the member's declared type regardless of the enclosing object's layout.[18]
For bit-fields, which are members declared with a bit width (e.g., int flag : 1;), applying the sizeof operator directly to a bit-field lvalue is invalid in both C and C++, as bit-fields do not have a size independent of the containing struct, union, or class. Bit-field allocation, packing, and alignment are implementation-defined.[19][20]
The offsetof macro from <cstddef> works in tandem with sizeof to determine member positions and extents, particularly in low-level operations like serialization or memory mapping.[21] For a standard-layout struct, offsetof(type, member) returns the byte offset from the struct's start to the member (always 0 for the first non-static member), while sizeof provides the member's size; their combination enables calculating spans, such as for packing data into buffers without padding assumptions.[21] For instance, in struct Example { char a; [double](/page/Double) b; };, offsetof(Example, b) is typically 8 (due to alignment padding after a), and sizeof(Example::b) is 8.[21]
When a struct member is an array, sizeof on that member returns the total byte size of the array, computed as the element type size multiplied by the array length.[2] This is useful for fixed-size arrays embedded in objects, such as struct Buffer { [int](/page/INT) data[10]; }; where sizeof(buf.data) equals 40 on platforms with 4-byte [int](/page/INT).
In unions, sizeof applied to a specific member returns the size of that member's type, which can be smaller than the union's overall size (padded to fit the largest member). For example, in union Variant { [int](/page/INT) i; [double](/page/Double) d; };, sizeof(unionobj.i) is typically 4, while the union size is 8 to accommodate d.
C++ extends this with class-specific features; for instance, sizeof on a non-static data member that is a pointer (common in implementations for virtual bases or vtable access) yields the platform's pointer size, usually sizeof(void*) at 8 bytes on 64-bit systems.[2] Virtual base classes introduce indirect pointer members for shared subobjects, but sizeof on such a derived member's type follows the pointer size rule without exposing layout details.[22]
Variadic Template Packs
In C++11 and later, variadic templates allow functions and classes to accept a variable number of template arguments through parameter packs, denoted by ellipsis syntax such as typename... Args. The sizeof... operator specifically queries the number of types in such a pack, returning a compile-time constant of type std::size_t, rather than the byte size of the types themselves.[23][24] This operator is essential for template metaprogramming tasks that require knowing the pack's cardinality without expanding it.
To compute the total byte size of types in a variadic pack, sizeof can be applied to each pack element during expansion, often using recursive templates or, in C++17 and later, fold expressions for conciseness. For instance, a recursive approach might define a base case for an empty pack and accumulate sizes by unpacking the pack head and tail.[25] In C++17, fold expressions simplify this with unary right-fold syntax, as in the following example:
cpp
#include <cstddef>
template<typename... Ts>
constexpr std::size_t total_size() {
return (sizeof(Ts) + ... + 0);
}
#include <cstddef>
template<typename... Ts>
constexpr std::size_t total_size() {
return (sizeof(Ts) + ... + 0);
}
This computes the sum of sizeof(Ts) for all types in the pack Ts at compile time.[26]
Integration with type traits from <type_traits> extends sizeof usage for array packs, where std::extent<T, N> retrieves the size of the Nth dimension of an array type T, and std::rank<T> gives the array's dimensionality. These can be combined in variadic templates to calculate total elements or storage needs for packs of arrays, such as determining the product of extents across multiple array types.[27][28]
A key limitation is that sizeof...(T) is ill-formed if T is not a parameter pack, requiring explicit pack identification and expansion in templates.[23] Prior to C++11, variadic templates were unavailable, so developers relied on alternatives like recursive template instantiations with fixed maximum arity or std::tuple for type lists to simulate variable arguments in metaprogramming.[24][25]
Implementation
Size Determination
The sizeof operator in C and C++ determines the size of a complete object or type at compile time by computing the total number of bytes required, based on the target's application binary interface (ABI). For primitive types such as int, char, or float, the compiler retrieves predefined sizes from ABI specifications or internal macros, ensuring consistency with the platform's memory model; for instance, in the LP64 ABI commonly used on 64-bit Unix-like systems, int is 4 bytes, while long and pointers are 8 bytes.[29][30] These primitive sizes serve as the foundation for more complex types.
For aggregate types like structures, unions, and arrays, the computation proceeds recursively by summing the sizes of constituent members or elements, incorporating alignment adjustments as dictated by the ABI to ensure proper memory access. In C++, the Itanium ABI exemplifies this for classes, where the non-virtual size (nvsize) is calculated first from bases and members in declaration order, followed by inclusion of virtual bases to derive the full sizeof; primitives and bases are handled via their inherent ABI sizes, with the process updating a running total rounded to the type's alignment boundary.[31] Similarly, in C, the GNU Compiler Collection (GCC) employs target-specific macros like INT_TYPE_SIZE for primitives and recursively applies them to aggregates during type layout.[30] This recursive approach guarantees that the result reflects the complete object's representation without evaluating the operand itself.[14]
The result of sizeof is always of type size_t, an unsigned integer type defined in <stddef.h> that is implementation-defined but guaranteed to hold the maximum size of any possible object on the target platform, typically matching the width of a pointer or larger to accommodate extensive allocations.[14] In compiler implementations, sizeof maps directly to internal type information tables or descriptors, such as GCC's tree nodes representing type sizes in bits, converted to bytes via BITS_PER_UNIT.[30]
During cross-compilation, the compiler enforces target ABI compliance by selecting appropriate macros and configurations, such as LP64 for 64-bit targets, to ensure sizeof yields values matching the execution environment rather than the host; mismatches can arise if host and target ABIs differ, but tools like GCC's multilib support mitigate this by building separate binaries.[30][29] Compilers also issue diagnostics for unexpected sizes stemming from non-standard extensions, such as GNU-specific attributes altering type layouts, via flags like -pedantic or -Wextensions to alert developers to potential portability issues.
Padding and Alignment
In computer programming, particularly in C and C++, data alignment refers to the requirement that objects of certain types must be placed at memory addresses that are multiples of a specific value, known as the alignment requirement. This value is typically a power of two, such as 1, 2, 4, or 8 bytes, and is often equal to the size of the type itself or dictated by the application binary interface (ABI) rules of the platform. For instance, an integer type like int commonly requires alignment to a 4-byte boundary on 32-bit systems to enable efficient access by the processor.[32][33]
The sizeof operator accounts for alignment by incorporating padding bytes into the calculated size of aggregate types like structures and unions. Padding is inserted between structure members to ensure each subsequent member starts at an address that satisfies its alignment requirement; for example, following a 1-byte char member, up to 7 bytes of padding may be added before an 8-byte double to align the latter properly. Additionally, trailing padding is often added at the end of a structure to make its total size a multiple of the structure's alignment requirement, which facilitates efficient layout when the structure is used in arrays. This ensures that all elements of an array of such structures remain properly aligned without additional adjustments. In unions, padding is minimal and typically limited to any necessary bytes beyond the size of the largest member to meet the union's alignment, which is the maximum alignment among its members; since members overlap at the same starting address, no internal padding between members is required.[8][32]
Compilers provide mechanisms to control padding and alignment, such as the implementation-defined #pragma pack directive in C and C++, which allows programmers to specify a maximum alignment boundary for structure members, effectively reducing or eliminating padding. For example, #pragma pack(1) packs members tightly with no padding, useful for interfacing with hardware or serialized data formats where exact byte layouts are critical. However, disabling padding can lead to unaligned access, which may cause portability issues or runtime errors on architectures that do not support it.[34][35]
While padding increases the result of sizeof and thus memory consumption, it enhances performance by enabling faster memory access patterns that align with hardware capabilities, avoiding penalties such as multiple bus cycles or exceptions for unaligned reads and writes. On modern processors, unaligned access can degrade performance significantly, sometimes by factors of 2 to 10, depending on the architecture.[36][32]
Consider the following structure example on a typical 64-bit system where double aligns to 8 bytes:
c
struct Example {
char c; // 1 byte
double d; // 8 bytes, requires 7 bytes of padding after c
}; // Plus 0 bytes trailing padding (total size multiple of 8)
struct Example {
char c; // 1 byte
double d; // 8 bytes, requires 7 bytes of padding after c
}; // Plus 0 bytes trailing padding (total size multiple of 8)
Here, sizeof(struct Example) evaluates to 16 bytes, including 7 bytes of padding after the char to align the double.[8]
The C11 and C++11 standards formalize alignment querying through the _Alignof (C) and alignof (C++) operators, respectively, which return the alignment requirement of a type as a complement to sizeof. These operators apply to complete types and help predict padding needs; for aggregates, the alignment is the strictest (largest) among their members.[33]