Basic Data Types in C

1. Primitive Data Types

Data TypeSize (Typical)Range (Signed)
char1 byte-128 to 127
int4 bytes-2,147,483,648 to 2,147,483,647
float4 bytes~±3.4E–38 to ±3.4E+38
double8 bytes~±1.7E–308 to ±1.7E+308

2. Integer Modifiers

TypeSize (Typical)Range (Signed)
short int2 bytes-32,768 to 32,767
unsigned short2 bytes0 to 65,535
unsigned int4 bytes0 to 4,294,967,295
long int4 or 8 bytesPlatform dependent
long long int8 bytes±9.22 × 10^18
unsigned long4 or 8 bytes0 to 18.4 × 10^18

3. Floating Point Modifiers

TypeSize (Typical)Precision
float4 bytes6-7 digits
double8 bytes15 digits
long double10/12/16 bytes≥19 digits (platform-dependent)

ARM Cortex M Series Specific

1. Fixed-Width Integer Types (Preferred)

For portability across compilers and architectures, embedded C uses stdint.h types (C99 standard):

Data TypeSizeUse Case
int8_t8-bitSigned byte
uint8_t8-bitUnsigned byte
int16_t16-bitSigned short
uint16_t16-bitUnsigned short
int32_t32-bitSigned int
uint32_t32-bitUnsigned int
int64_t64-bitSigned long long (if supported)
uint64_t64-bitUnsigned long long

Example

#include <stdint.h>
 
uint8_t ledState = 0x01;     // 8-bit LED control register
int16_t temperature = -273;  // 16-bit signed sensor value

2. Floating Point Types

ARM Cortex-M cores have different levels of FPU (Floating Point Unit) support:

CoreFPU Support
Cortex-M0/M0+❌ None (use fixed-point)
Cortex-M3❌ None
Cortex-M4✅ Optional (single precision)
Cortex-M7✅ Single and optional double precision

Note

Use float (4 bytes) if FPU is present; otherwise, consider fixed-point math (e.g., Q15, Q31).

3. Volatile and Const Qualifiers

Crucial for embedded programming:

volatile

  • Tells the compiler not to optimize the variable.
  • Used when variable is changed outside the current flow (e.g., interrupt, hardware register).
volatile uint32_t* timerReg = (uint32_t*)0x40000000;

const

  • Used for ROM-stored data, e.g., lookup tables, config.
const uint8_t lookupTable[256] = { ... };

4. Bitfields and Packed Structs

Ever wondered how frame formats like TCP or IP had such precise field sizes? They utilized bitfields and packed structs. Each field inside a struct can be specified a memory size in bits and a label. Hence, later those specified bits in the struct/frame, representing a field can be referenced with it’s name instead of manual bit masking each bit in range. It is also useful when working with hardware registers:

typedef struct {
    uint8_t EN  : 1; // 1 bit field
    uint8_t MODE: 3; // 3 bit field
    uint8_t     : 4;  // 4 reserved bits
} __attribute__((packed)) ControlReg;
 
ControlReg* ctrl = (ControlReg*) 0x40001000;
ctrl->EN = 1;

The primitive datatype depends on the size of the whole packet/frame. If the total length of frame is 8 bits, the datatype is uint8_t and so on.

Note

__attribute__((packed))

  • Tells the compiler not to add padding between structure members.
  • This ensures the struct maps exactly to the memory layout of a hardware register.

Without packed, the compiler might insert padding bytes to align data, breaking the memory map.


5. Alignment & Memory Mapping

__attribute__((aligned(n)))

  • Forces the variable or struct to be aligned on a n-byte boundary.
  • This ensures that the starting address of struct or variable is divisible by n.
  • Some peripherals (like DMA) require aligned data; if not aligned, you may get hard faults or unexpected behavior.
uint8_t buffer[256] __attribute__((aligned(4)));
  • Ensures buffer starts at an address divisible by 4 (0xXXXXXX00, etc.).

__attribute__((section(".my_mem")))

  • Places a variable in a custom memory section, useful when:

    • You want code/data in a specific region of Flash or RAM.

    • You’re placing config or boot data in a fixed location.

uint8_t boot_flags __attribute__((section(".boot_data")));

This requires a matching section in your linker script:

.boot_data :
{
  . = ALIGN(4);
  KEEP(*(.boot_data))
} >RAM

Summary: Embedded Best Practices

Use ThisInstead ofWhy
uint8_tunsigned charPortable, explicit size
int16_tshortPredictable size
volatileNoneFor registers, ISRs
constHardcoded varsStore in Flash
BitfieldsMaskingEasier access to control bits
Fixed-pointFloat (no FPU)Efficient on MCUs without FPU