Introduction

Arrays are non primitive data types made from a collection of single primitive data type, arranged in a sequential manner. Arrays are contiguous i.e. each entity of array follows its previous one immediately in memory.

In embedded systems, arrays are useful for string storage, buffers, registers, buses, etc.


Declaration and Initialization

a) Declaration

int numbers[5]; // declares an array of 5 integers

b) Initialization

int numbers[5] = {10, 20, 30, 40, 50};

Partial Initialization
Specifying only a few elements of the array, automatically initializes all other elements to 0.

int numbers[5] = {10, 20}; 
// is effectively the same as
int numbers[5] = {10, 20, 0, 0, 0}; 

Implied Declaration
The compiler fills in the size of the array automatically by counting the elements in the initialization part.

int numbers[] = {1, 2, 3};
// numbers is now of size 3.

Methods of Reference

Prerequisites

  • Base Pointer: Points to the first element of the array [0]. The array name has the base pointer of the array.
  • Offset: References the indexed variable inside the array. This happens by adding the offset with the base pointer to get the effective memory address of the indexed variable. Hence, as the base pointer points to the first element of the array, the index of the first element has to be 0.

Example

int myArray[10];
myArray[5] = 64;
// ^    ^
//Base|Offset

We can use 3 methods for array referencing in C, only 2 of them are valid and the 3rd one is kind of a side-effect.

a) *(base + index)

Using the basic working principles of pointers and addresses, the base address can be used with the index/offset to get the effective address. This effective address can in turn manipulate the variable at the specified index.
This is what actually happens when the compiler compiles the code into intermediate form. The notation base[index] gets converted to *(base + index).
Example

int myArray[10];
// Lets update the 5th element [4]
*(myArray + 4) = 32;

b) base[index]

The standardized and most widely used form of reference is the base[index] notation.
This effectively works the same as the previous one, but is more readable and easy to work with.

Example

int myArray[10];
myArray[4] = 32;

c) index[base] ?

Although unintuitive and seeming like a typo or a syntax error, this notation is as effective as the base[index] notation. In fact, they both are the same in the terms of working. We saw, that the base[index] form gets converted into *(base + index) by the compiler. It treats base and index as 2 regular variables of pointer type and adds them together to get the effective address. Here the index gets typecasted from regular integer to the pointer by multiplying it with the size of pointer type.
e.g. if the offset is 2 and the array is of type int (4 bytes), the offset will be typecasted by multiplying by 4.
So, even if we change their locations, it still gives the effective address which is the key factor of array referencing.

4[myArray] = 32; // --> Compiles to *(4 + myArray) = 32;
myArray[4] = 32; // --> Compiles to *(myArray + 4) = 32;

Types of Arrays

a) One-Dimensional Array

A linear list of elements.

Example:

char vowels[] = {'a', 'e', 'i', 'o', 'u'};

b) Two-Dimensional Array (Matrix)

Used to store tabular data.

Example:

int matrix[2][3] = {
    {1, 2, 3},
    {4, 5, 6}
};

Accessing:

printf("%d", matrix[1][2]); // prints 6

c) Multi-Dimensional Array

Arrays with more than two dimensions. Rarely used but useful in scientific computing.


Character Arrays (Strings)

A string in C is an array of characters ending with a null character '\0'.

Example:

char name[] = "Alice";

This is equivalent to:

char name[] = {'A', 'l', 'i', 'c', 'e', '\0'};

Arrays in Embedded C

In Embedded C, arrays are used to store and manage data buffers, sensor readings, GPIO states, register maps, and more. They provide a structured way to access contiguous memory for efficient hardware-level operations.

1. Arrays and Memory Mapping

Embedded systems often read from or write to memory-mapped registers.

Example: Register Array

volatile uint32_t* GPIO_PORT = (uint32_t*) 0x50000000;
GPIO_PORT[0] = 0x01;  // Set output pin

2. Arrays with const for Lookup Tables (LUTs)

To save flash memory and speed up execution.

Example: Sine wave LUT

const uint8_t sine_wave[8] = {0, 70, 100, 70, 0, -70, -100, -70};

3. Arrays in ISRs and DMA Buffers

Used in interrupts and DMA to collect or transfer data without CPU intervention.

Example: UART DMA Receive Buffer

volatile uint8_t rx_buffer[64];  // Used with DMA

Key Points for Embedded Arrays:

  • Use const arrays for LUTs to store them in Flash.

  • Always declare volatile if the array is updated by peripherals/ISRs.

  • Be cautious of memory size limits on MCUs (e.g., SRAM constraints).

  • Use DMA-friendly aligned arrays if required:

    uint8_t buffer[256] __attribute__((aligned(4)));

    Refer to Data Types for more info on _attribute_