Type casting in C allows developers to convert a variable from one data type to another. This is especially crucial in embedded systems where memory management and hardware interactions demand precise control over data representations.
Types of Type Casting
There are two types of casting in C based on their explicitness:
1. Implicit Type Casting (Type Promotion)
As the name suggests the implicit cast occurs automatically in some scenarios, i.e. the cast is implied. The compiler promotes smaller types to larger types to prevent data loss.
Promotion Hierarchy:char → int → float → double
Example
int i = 10;float f = i; // 'i' is promoted to float automatically
Warning
In embedded systems, be cautious as implicit promotions can lead to unexpected behaviors, especially when dealing with hardware registers or memory-mapped I/O.
2. Explicit Type Casting (Manual Conversion)
Explicit type cast is used to forcefully convert a variable into a different type. This is common when precision control is necessary or when interfacing with hardware.
Syntax:(type) expression;
Example
float f = 3.14;int i = (int)f; // 'i' becomes 3, fractional part truncated
Risks with Variable Casts
Data Loss: Casting from a larger to a smaller type can truncate data.
int i = 300;char c = (char)i; // 'c' may not hold the intended value
Pointer Type Casting
Just like regular variable type casting, pointers can also be type casted from one type to another. But these casts are always explicit and there is no type promotion in pointers.
Pointer typecasting is primarily a tool for dynamic memory management and is often used with functions like malloc(). The malloc() function always returns a pointer of type void*. Therefore to be able to use this pointer with other pointer types an explicit cast is carried out.
Example
The below array defines an array of int of size 5.
Assuming a 16-bit hardware register at address 0x4000:
#define REG16 (*(volatile uint16_t *)0x4000)uint16_t value = REG16;
Here, we cast the address to a pointer to a volatile 16-bit unsigned integer to read the register’s value.
b. Interpreting Byte Streams
When receiving data over communication interfaces:
uint8_t buffer[2] = {0x01, 0x02}; // The size of whole buffer is 16 bits or 2 bytesuint16_t *data = (uint16_t *)buffer; // Consider the data as whole 2 bytes block
This casts the byte buffer to a 16-bit pointer. Ensure that the system’s endianness and alignment requirements are considered.