In C programming, strong and weak functions refer to a priority mechanism used by the compiler when handling functions with the same name.
When there are multiple definitions of functions with the same name in a program, the “strong” function will override the “weak” function, ensuring that the linker selects the correct function implementation.
This mechanism is particularly useful in embedded system development, library function redefinition, and system-level programming, allowing developers to override default function implementations without modifying the original library code.
Basic Concepts
Strong Function
A strong function is a function definition with a clear implementation that has a higher priority during linking. When the linker encounters a strong function, it will prefer it over a weak function.
// Strong function example
void my_function(void)
{
printf("This is a strong function implementation\n");
}
Weak Function
A weak function is a function definition that can be overridden by a strong function. It typically exists as a default implementation or placeholder, and the weak function will only be used during the linking phase if no corresponding strong function is found.
In the GCC compiler, a weak function can be declared using<span>__attribute__((weak))</span>:
// Weak function example
void __attribute__((weak)) my_function(void) {
printf("This is a weak function implementation\n");
}
Declaration Methods
Weak Function Declaration in GCC
The common methods to declare weak functions in the GCC compiler are:
// Method 1: Using __attribute__((weak))
void __attribute__((weak)) weak_func(void)
{
// Default implementation
}
// Method 2: Declare first, then define
void weak_func(void) __attribute__((weak));
void weak_func(void)
{
// Default implementation
}
Declaration Methods in Other Compilers
Different compilers may have different syntax:
-
IAR Compiler:
#pragma weak weak_func void weak_func(void) { /* ... */ } -
ARMCC Compiler:
__weak void weak_func(void) { /* ... */ }
Linking Rules
The linker follows these rules when handling strong and weak functions:
- If there is only one function definition (regardless of strong or weak), that definition is used.
- If there are multiple strong function definitions, it will result in a linking error (duplicate definition).
- If there is one strong function and one or more weak functions, the strong function is chosen.
- If there are only multiple weak functions, the first encountered weak function is chosen (which may produce a warning).
Typical Applications
Redefinability of Library Functions
Library developers can provide default weak function implementations, allowing users to override them when needed:
// Weak function definition in library
void __attribute__((weak)) library_func(void)
{
// Default implementation
}
// User code can redefine
void library_func(void)
{
// User-defined implementation
}
Interrupt Handling in Embedded Systems
In embedded systems, weak functions are often used to define default interrupt handlers:
// Default interrupt handler (weak definition)
void __attribute__((weak)) TIMER1_IRQHandler(void)
{
while(1); // Default enters infinite loop
}
// User can redefine in application
void TIMER1_IRQHandler(void)
{
// Actual interrupt handling code
}
Default Implementation of Callback Functions
Providing optional default callback function implementations:
// Framework code
void __attribute__((weak)) on_data_received(int data)
{
// Default does nothing
}
void process_data(int data)
{
// Process data...
on_data_received(data); // Call callback
}
// User code can choose to implement
void on_data_received(int data)
{
printf("Received: %d\n", data);
}
Stub Functions for Testing
In unit testing, weak functions can be used to simulate real functions:
// Production code
int __attribute__((weak)) read_sensor(void)
{
return hardware_read_sensor();
}
// Test code
int read_sensor(void)
{
return mock_value; // Return mock value
}
Advanced Usage
Checking if a Weak Function has been Overridden
In some cases, we may need to check if a weak function has been overridden:
extern void __attribute__((weak)) weak_func(void);
if (weak_func)
{
// Function has been overridden
weak_func();
}
else
{
// Use default behavior
}
Weak and Strong Symbols Extension
Not only functions, but variables can also have strong and weak distinctions:
int __attribute__((weak)) weak_var = 10;
int strong_var = 20; // Strong symbol
Handling Multiple Weak Functions
When multiple weak functions exist, the linker typically chooses the first encountered:
// file1.c
void __attribute__((weak)) func() { printf("Weak 1\n"); }
// file2.c
void __attribute__((weak)) func() { printf("Weak 2\n"); }
// Linking order determines which implementation is used
Considerations
- Portability: Strong and weak functions are not part of the C standard and are compiler extensions; different compiler implementations may vary.
- Debugging Difficulty: Overriding functions may make it difficult to trace actual calls during debugging.
- Performance Impact: Calling weak functions through pointers may incur slight performance overhead.
- Naming Conflicts: Misuse may lead to hard-to-detect naming conflicts.
- Initialization Order: The constructors of global objects may depend on weak functions, requiring attention to initialization order.
Real Cases
UART Driver
// Driver library provides weak function
void __attribute__((weak)) UART_Init(void)
{
// Default initialization code
}
// User rewrites based on hardware
void UART_Init(void)
{
// Specific hardware initialization
USART1->BRR = 0x341;
// ...
}
Plugin System Design
// Core framework
void __attribute__((weak)) plugin_init(void) {}
void __attribute__((weak)) plugin_process(int data) {}
void framework_run(void)
{
plugin_init();
while(1)
{
int data = get_data();
plugin_process(data);
}
}
// Plugin implementation
void plugin_init(void)
{
printf("Plugin loaded\n");
}
void plugin_process(int data)
{
printf("Processing data: %d\n", data);
}
Strong and Weak Functions with Function Pointers
Weak functions can be combined with function pointers to provide more flexible designs:
void __attribute__((weak)) default_handler(int);
void (*handler)(int) = default_handler;
void process_event(int event)
{
if (handler) handler(event);
}
// User can override
void default_handler(int event)
{
printf("Default handling: %d\n", event);
}
Conclusion
Strong and weak functions are a powerful feature in C, especially useful in system-level programming and embedded development. They offer the following advantages:
- Allow library functions to be overridden by user-defined implementations.
- Provide default implementations while maintaining flexibility.
- Support modular design and plugin architectures.
- Facilitate unit testing and simulation.
However, care must be taken regarding their compiler dependency and potential maintenance complexity.
Proper use of strong and weak functions can greatly enhance the flexibility and reusability of code, especially in scenarios where default behavior is provided but customization is allowed.

END
Source: Learning Embedded Together
Copyright belongs to the original author. If there is any infringement, please contact for deletion..▍Recommended ReadingWhy is C++ rarely used in microcontroller development?Xiaomi is really stingy; a single MCU can handle all functions.Upload a PCB photo with only 2 lines of silk screen, and GPT-5 helped me solve everything!→ Follow for more updates ←