C - Static Assertions with _Static_assert in C
Introduction
A static assertion is a feature in C that allows a programmer to check a condition during compilation rather than while the program is running. It is useful when certain assumptions must always be true for a program to work correctly.
C11 introduced the _Static_assert keyword for performing compile-time checks. If the condition provided to _Static_assert is true, compilation continues normally. If the condition is false, the compiler produces a compilation error.
This makes static assertions particularly useful for detecting programming mistakes and invalid assumptions at an early stage.
Syntax
The basic syntax is:
_Static_assert(constant_expression, "error message");
The first argument must be an expression that can be evaluated at compile time. The expression should result in a non-zero value for the assertion to succeed.
For example:
#include <stdio.h>
int main() {
_Static_assert(sizeof(int) >= 2, "int is too small");
printf("Program compiled successfully.\n");
return 0;
}
Here, the compiler checks whether sizeof(int) >= 2 is true. If it is true, the program can be compiled. If it is false, compilation fails and the specified message is reported.
Why Static Assertions Are Useful
Normally, many checks are performed while a program is running.
For example:
int age = 15;
if (age < 18) {
printf("Age is below 18\n");
}
This condition is evaluated at runtime.
A static assertion works differently. It checks a condition before the executable program is created. This is valuable when the condition represents a requirement that should never change during execution.
For example, suppose a program is designed to work only when an integer has a particular size:
_Static_assert(sizeof(int) == 4, "This program requires a 4-byte int");
If the compiler's environment does not meet this requirement, the programmer receives an error immediately instead of potentially discovering the problem later.
Compile-Time Checking
The most important characteristic of _Static_assert is that it performs compile-time validation.
Consider:
_Static_assert(10 > 5, "Condition failed");
The expression 10 > 5 is true, so compilation continues.
Now consider:
_Static_assert(10 < 5, "10 is not less than 5");
The condition is false, so the compiler reports an error.
This allows developers to catch certain errors before the program is executed.
Checking Data Type Sizes
Static assertions are especially useful when programs depend on specific data sizes.
For example:
#include <stdio.h>
_Static_assert(sizeof(char) == 1, "Unexpected char size");
int main() {
printf("Character size is valid.\n");
return 0;
}
The C standard defines sizeof(char) as 1, so this assertion should succeed on a conforming implementation.
Another example is:
_Static_assert(sizeof(long) >= 4, "long must contain at least 4 bytes");
This can protect code that depends on long having a minimum size.
Using Static Assertions with Structures
Static assertions can also be useful when a program depends on the size of a structure.
For example:
struct Employee {
int id;
char name[20];
};
_Static_assert(sizeof(struct Employee) >= 24,
"Employee structure is unexpectedly small");
The compiler checks the size of the structure during compilation.
This can be useful in applications involving binary data formats, communication protocols, embedded systems, or memory layouts where the size of a structure matters.
However, structure size can include padding inserted by the compiler. Therefore, programmers should not assume that the structure size is simply the sum of the sizes of its individual members.
Static Assertions with Constants
The condition supplied to _Static_assert must be suitable for compile-time evaluation.
For example:
#define MAX_USERS 100
_Static_assert(MAX_USERS > 0, "MAX_USERS must be positive");
Since MAX_USERS is a preprocessor constant, the compiler can evaluate the condition.
Another example:
#define BUFFER_SIZE 256
_Static_assert(BUFFER_SIZE >= 128,
"Buffer size must be at least 128");
This helps ensure that configuration values satisfy the requirements of the program.
Using Static Assertions for Configuration Validation
Large programs often have configuration constants that determine how the application behaves.
For example:
#define MAX_CONNECTIONS 50
#define MIN_CONNECTIONS 1
_Static_assert(MAX_CONNECTIONS >= MIN_CONNECTIONS,
"Invalid connection configuration");
If someone later changes the configuration to:
#define MAX_CONNECTIONS 0
#define MIN_CONNECTIONS 1
the static assertion will fail during compilation.
This prevents an invalid configuration from reaching the execution stage.
Static Assertions and Runtime Assertions
Static assertions and runtime assertions serve different purposes.
A runtime assertion is commonly created using the assert() macro:
#include <assert.h>
int age = 10;
assert(age >= 18);
The condition depends on the value of age while the program is running.
A static assertion is used when the condition can be determined during compilation:
_Static_assert(sizeof(int) >= 4,
"int must be at least 4 bytes");
The main difference is therefore the time at which the check occurs.
| Feature | _Static_assert |
assert() |
|---|---|---|
| Checking time | Compile time | Runtime |
| Main purpose | Validate compile-time assumptions | Validate runtime conditions |
| Requires constant expression | Yes | No |
| Failure | Compilation error | Runtime assertion failure |
| C standard | Introduced in C11 | Available earlier |
Static Assertions in C11
_Static_assert was standardized in C11.
A simple C11 program can use:
#include <stdio.h>
_Static_assert(sizeof(int) >= 4,
"Integer size requirement not satisfied");
int main() {
printf("Requirements satisfied.\n");
return 0;
}
Some compilers and language modes may also support the spelling static_assert, particularly through the appropriate C standard headers or newer language specifications. _Static_assert is the traditional C11 keyword and is the clearest form when specifically discussing the C11 feature.
Multiple Static Assertions
A program can contain multiple static assertions.
#define BUFFER_SIZE 512
#define MAX_USERS 100
_Static_assert(BUFFER_SIZE >= 256,
"Buffer size is too small");
_Static_assert(MAX_USERS > 0,
"Maximum users must be positive");
_Static_assert(sizeof(int) >= 4,
"Integer size is insufficient");
Each condition is checked independently during compilation.
This is useful for validating several assumptions about a program's environment and configuration.
Advantages of Static Assertions
Static assertions provide several benefits.
First, they detect problems early. An invalid assumption is discovered during compilation rather than after deployment.
Second, they document important requirements. A statement such as:
_Static_assert(sizeof(int) == 4,
"Program requires 4-byte integers");
clearly communicates an assumption to other programmers.
Third, they reduce the possibility of platform-related errors. Programs that interact with hardware, binary formats, or external systems often depend on specific sizes and layouts.
Fourth, they help maintain large projects. If someone changes a configuration value, data type, or structure and unintentionally violates an important requirement, the compiler can identify the problem.
Limitations
Static assertions cannot replace normal runtime validation.
For example:
int value;
scanf("%d", &value);
The value entered by the user is not known during compilation. Therefore, _Static_assert cannot be used to determine whether that particular input is valid.
A runtime condition requires ordinary conditional statements or mechanisms such as assert().
Another limitation is that static assertions are intended for conditions that can be evaluated at compile time. They cannot generally be used to check arbitrary runtime data.
Practical Example
Consider a program that stores records in a fixed-size buffer:
#include <stdio.h>
#define RECORD_SIZE 64
#define BUFFER_SIZE 1024
_Static_assert(RECORD_SIZE > 0,
"Record size must be positive");
_Static_assert(BUFFER_SIZE >= RECORD_SIZE,
"Buffer must be large enough for one record");
int main() {
printf("Record size: %d bytes\n", RECORD_SIZE);
printf("Buffer size: %d bytes\n", BUFFER_SIZE);
return 0;
}
Here, two assumptions are checked before the program runs.
The first assertion ensures that RECORD_SIZE is valid. The second ensures that the buffer is at least large enough to contain one record.
If somebody later changes:
#define BUFFER_SIZE 32
the following assertion becomes false:
_Static_assert(BUFFER_SIZE >= RECORD_SIZE,
"Buffer must be large enough for one record");
The compiler will report the problem instead of allowing the invalid configuration to pass unnoticed.
Conclusion
_Static_assert is an important C feature for compile-time validation. It allows programmers to express assumptions that must be true for a program to compile correctly. It is particularly useful for checking data-type sizes, structure sizes, configuration values, buffer requirements, and platform-specific conditions.
The key idea is simple: use _Static_assert when a condition must be verified before the program runs, and use runtime checks when the required information is available only during execution.