Unlocking static, volatile, sizeof & I2C: Essential C Tips for Embedded Engineers
This article explains the roles of the static and volatile keywords, compares sizeof and strlen, shows how to reliably compare floating‑point numbers, discusses the impact of floating‑point operations on STM32 interrupt performance, and outlines the I2C protocol and address configuration for embedded systems.
1. What does the static keyword do and why is a static variable initialized only once?
When applied to a local variable, static makes it a static variable stored in the static area, whose lifetime matches the program’s execution; it is initialized before main starts and destroyed when the program exits.
When applied to a global variable, static does not change its storage (global variables are already in the static area) but restricts its linkage, so the variable can be accessed only within the translation unit where it is defined.
When applied to a function, the function can be called only from the file that defines it; its declaration and definition must reside in the same file.
When applied to a class member variable, static makes it a class‑wide variable shared by all objects, including derived classes. It must be defined outside the class, e.g., int Base::var = 10;, and cannot be initialized inside a constructor (unless it is a const static member, which may be initialized inside the class).
When applied to a member function, static creates a single function shared by all objects, which does not receive a this pointer and therefore can only access static members of the class. Note: a member function cannot be declared both const and static.
Static members can be accessed without creating any object instance, which is useful for functions that are not tied to a particular object, such as mathematical functions like sin and cos.
2. What does the volatile keyword mean and when should it be used?
volatileis a type qualifier designed for variables that may be accessed or modified by different threads or by hardware. Without it, the compiler might optimize away reads/writes, leading to stale or inconsistent values.
Typical uses include:
Hardware registers (e.g., status registers) where each read/write may have side effects.
Variables modified by interrupt service routines that other code polls.
Shared flags in a multitasking environment.
A variable can be both const and volatile when it is read‑only from software but may change unexpectedly, such as a read‑only status register.
3. Difference between sizeof and strlen and their usage scenarios
sizeofis an operator that yields the size in bytes of a type or variable, while strlen is a function that returns the number of characters in a null‑terminated string. sizeof works for any data type; strlen works only for strings ending with '\0'.
When applied to a string literal, sizeof includes the terminating null character, whereas strlen does not count it.
4. How to compare two floating‑point numbers correctly
Float values have about 6 decimal digits of precision, double about 16. Direct equality comparison is unreliable; instead, compare the absolute difference against a small epsilon.
float x = a - b;
const float EPSILON = 0.00001f;
if (x >= -EPSILON && x <= EPSILON) {
// A and B are considered equal
cout << "A 与 B 相等" << endl;
} else {
cout << "不相等" << endl;
}For higher precision, use double and a smaller epsilon such as 1e-8.
5. Impact of using floating‑point operations in STM32 interrupts
Performing floating‑point calculations inside an interrupt or a thread can corrupt the FPU registers used by other tasks (e.g., a TCP communication thread), leading to data errors.
On 32‑bit microcontrollers, floating‑point operations increase memory usage; using hardware FPU acceleration can mitigate CPU load.
Always suffix floating‑point literals with f for single precision (e.g., 10.1f) to avoid unnecessary double‑precision computation, which is roughly ten times slower.
6. Overview of the I²C protocol and its speed on STM32
I²C uses two lines: Serial Data (SDA) and Serial Clock (SCL). Each device has a unique address and can act as a transmitter or receiver.
When multiple masters compete for the bus, arbitration decides which master continues.
Data on SDA must be stable during the high period of SCL; changes are only allowed when SCL is low.
Start condition: SDA transitions low while SCL is high. Stop condition: SDA transitions high while SCL is high.
Each transmitted byte consists of 8 data bits followed by an ACK bit, making a 9‑bit frame.
Hardware I²C is controlled by the peripheral itself, requiring minimal CPU intervention; software (bit‑banged) I²C consumes CPU cycles but offers flexible timing.
7. Configuring I²C slave and master addresses
The least‑significant bit of the address byte is the read/write flag. For a device like 24C02, bits 7‑4 are fixed, while bits 3‑1 are set by hardware pins (high = 1, low = 0).
After the start condition, the master sends the 7‑bit slave address followed by the R/W bit (0 = write, 1 = read).
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Open Source Linux
Focused on sharing Linux/Unix content, covering fundamentals, system development, network programming, automation/operations, cloud computing, and related professional knowledge.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
