Static Analysis for MISRA C#
MISRA C (Motor Industry Software Reliability Association) is a set of guidelines for writing safe, reliable, and maintainable C code, particularly for embedded systems. It is widely used in safety-critical applications such as automotive, aerospace, and medical devices. Below are some important examples of MISRA C rules, organized by categories, along with explanations and examples.
Use of Standard Types#
Rule: Avoid implicit conversions
Rule 10.1: The value of an expression shall not be implicitly converted to a type with a different essential type category.
Example (Non-compliant):
int32_t x = 10;
float y = x; // Implicit conversion from int32_t to float
Compliant:
int32_t x = 10;
float y = (float)x; // Explicit conversion
Avoiding Dangerous Constructs#
Rule: Do not use non-constant variables in constant expressions
Rule 8.7: Functions and objects should not be defined with both extern and static.
Example (Non-compliant):
static int x;
extern static int x; // Invalid declaration
Compliant:
static int x; // Either static…
// OR
extern int x; // … or extern, but not both
Control Flow#
Rule: Ensure predictable control flow
Rule 14.1: There shall be no unreachable code.
Example (Non-compliant):
void example(void) {
return;
int x = 10; // Unreachable code
}
Compliant:
void example(void) {
return;
}
Array and Pointer Usage#
Rule: Avoid pointer arithmetic
Rule 18.4: Pointer arithmetic shall not be used.
Example (Non-compliant):
int arr[10];
int *ptr = arr + 5; // Pointer arithmetic
Compliant:
int arr[10];
int *ptr = &arr[5]; // Use array indexing
Static Analysis-Friendly Code#
Rule: Initialize variables before use
Rule 9.1: All automatic variables shall be initialized before use.
Example (Non-compliant):
int x;
if (x == 0) { // Use of uninitialized variable
// Do something
}
Compliant:
int x = 0;
if (x == 0) {
// Do something
}
Avoiding Undefined Behavior#
Rule: Avoid division by zero
Rule 13.1: A loop counter shall not have a floating-point type.
Example (Non-compliant):
float i = 0.0;
while (i < 10.0) {
i += 0.1; // Floating-point loop counter
}
Compliant:
int i = 0;
while (i < 10) {
i++;
}
Error Handling#
Rule: Always check the return values of functions
Rule 22.2: A function that is declared void shall not return a value.
Example (Non-compliant):
void example(void) {
return 10; // Void function returning a value
}
Compliant:
void example(void) {
// No return value
}
No Overuse of Preprocessor Directives#
Rule: Avoid complex macros
Rule 19.4: Macros shall only expand to a single statement, expression, or do-while loop.
Example (Non-compliant):
#define COMPLEX_MACRO(x) if (x) { doSomething(); } else { doSomethingElse(); }
Compliant:
#define SIMPLE_MACRO(x) (x) ? doSomething() : doSomethingElse()
Avoiding Recursion#
Rule 17.2: Functions shall not call themselves, either directly or indirectly.
Example (Non-compliant):
int factorial(int n) {
if (n <= 1) {
return 1;
}
return n * factorial(n - 1); // Recursive call
}
Compliant:
int factorial(int n) {
int result = 1;
for (int i = 2; i <= n; i++) {
result *= i;
}
return result;
}
Memory Management#
Rule: Avoid dynamic memory allocation
Rule 20.4: Dynamic memory allocation and deallocation functions should not be used.
Example (Non-compliant):
int *ptr = (int *)malloc(sizeof(int)); // Dynamic memory allocation
free(ptr);
Compliant:
int arr[10]; // Use static memory instead of dynamic allocation
Summary of Importance#
Safety: Reduces undefined behavior and system crashes.
Reliability: Ensures deterministic behavior in embedded systems.
Portability: Makes code less dependent on compiler or hardware specifics.
Compliance: Helps meet standards like ISO 26262 and IEC 61508.
Following these examples helps improve code quality and ensures compliance with industry standards, which is crucial in safety-critical embedded applications.