The direct answer: to find the length of a standard C++ array in Arduino, use the macro #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])). Unlike Python or JavaScript, standard C-arrays in Arduino do not have a built-in .length property. Relying on hardcoded sizes or misunderstanding how arrays decay into pointers when passed to functions is the number one cause of silent memory corruption and random reboots in embedded sketches.

This guide breaks down the exact memory mechanics of Arduino arrays, provides a complete rolling-average sensor project to test your bounds-checking, and details the exact debugging steps when your compiler throws an array bounds warning.

The Core Problem: AVR vs. ARM Memory Layouts

Before writing a single line of code, you must understand what an array actually is in Arduino C++. An array is just a contiguous block of memory. It does not carry metadata about its own size. When you declare int sensorBuffer[10];, the compiler allocates memory, but at runtime, the microcontroller only knows the starting address.

The Architecture Gotcha: On an AVR-based board like the Arduino Uno Rev3, an int is 2 bytes (16-bit). On ARM/ESP32 boards like the Arduino Nano 33 IoT or ESP32 DevKit, an int is 4 bytes (32-bit). If you hardcode your array byte-math assuming 2-byte integers, your sketch will compile on an Uno but cause a massive buffer overflow on an ESP32. Always use the sizeof() division trick to remain architecture-agnostic.

Worked Numeric Example

Let's look at the math on an Arduino Uno Rev3 (AVR architecture):

  • int readings[10]; allocates 10 elements.
  • sizeof(readings) returns the total byte size: 10 elements × 2 bytes = 20 bytes.
  • sizeof(readings[0]) returns the size of a single element: 2 bytes.
  • Dividing the total by the element: 20 / 2 = 10.

If you compile that exact same code on an ESP32, sizeof(readings) becomes 40, and sizeof(readings[0]) becomes 4. The result is still 40 / 4 = 10. The macro adapts automatically.

Project Build: Rolling Average Sensor Buffer

To demonstrate proper array length handling and bounds checking, we will build a temperature logger that stores the last 20 readings in an array, calculates a rolling average, and triggers an LED sequence if the temperature spikes. This requires strict bounds management to prevent overwriting adjacent memory.

Parts List

  • Microcontroller: Arduino Uno Rev3 (ATmega328P) or Uno R4 Minima
  • Sensor: TMP36 Analog Temperature Sensor
  • Indicators: 3x 5mm LEDs (Green, Yellow, Red)
  • Resistors: 3x 220Ω (for LEDs), 1x 10kΩ (optional pull-down for sensor stability)
  • Wiring: Jumper wires, half-size breadboard

Pin Mapping Table

ComponentArduino PinNotes
TMP36 VCC5VDo not use 3.3V on Uno Rev3
TMP36 GNDGNDCommon ground required
TMP36 SignalA0Analog input (10-bit ADC)
Green LED AnodeD8220Ω resistor in series
Yellow LED AnodeD9220Ω resistor in series
Red LED AnodeD10220Ω resistor in series

Wiring Steps

  1. Insert the TMP36 sensor into the breadboard. Connect the left pin (VCC) to 5V, the right pin to GND, and the middle pin to A0.
  2. Connect the anodes (long legs) of the Green, Yellow, and Red LEDs to pins D8, D9, and D10 respectively, through the 220Ω resistors.
  3. Connect all LED cathodes to the common GND rail.
  4. Verify all connections with a multimeter in continuity mode before applying power to prevent shorting the 5V rail to the analog input.

Complete Compilable Code

This sketch targets the Arduino Uno Rev3 (AVR core). It includes explicit bounds checking and the architecture-agnostic length macro.

/*
 * Rolling Average Temperature Logger
 * Target Board: Arduino Uno Rev3 (AVR)
 * Demonstrates safe Arduino array length calculation and bounds checking.
 */

// Architecture-agnostic macro for finding array length
#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))

// Pin Definitions
const int PIN_SENSOR = A0;
const int PIN_LED_GREEN = 8;
const int PIN_LED_YELLOW = 9;
const int PIN_LED_RED = 10;

// Buffer Configuration
const int MAX_READINGS = 20;
int tempBuffer[MAX_READINGS];
int currentIndex = 0;
bool bufferFull = false;

void setup() {
  Serial.begin(9600);
  pinMode(PIN_LED_GREEN, OUTPUT);
  pinMode(PIN_LED_YELLOW, OUTPUT);
  pinMode(PIN_LED_RED, OUTPUT);
  
  // Initialize array to zero to prevent garbage data averaging
  for (int i = 0; i < ARRAY_SIZE(tempBuffer); i++) {
    tempBuffer[i] = 0;
  }
  
  Serial.print("Buffer initialized. Max elements: ");
  Serial.println(ARRAY_SIZE(tempBuffer));
}

void loop() {
  // 1. Read Sensor
  int rawADC = analogRead(PIN_SENSOR);
  
  // 2. Bounds Checking (Crucial for preventing memory corruption)
  if (currentIndex >= ARRAY_SIZE(tempBuffer)) {
    currentIndex = 0; // Wrap around safely
    bufferFull = true;
  }
  
  // 3. Store Reading
  tempBuffer[currentIndex] = rawADC;
  currentIndex++;
  
  // 4. Calculate Average safely using actual array length
  long sum = 0;
  int elementsToAverage = bufferFull ? ARRAY_SIZE(tempBuffer) : currentIndex;
  
  for (int i = 0; i < elementsToAverage; i++) {
    sum += tempBuffer[i];
  }
  int averageADC = sum / elementsToAverage;
  
  // 5. Convert to Celsius (TMP36 formula for 5V reference)
  float voltage = (averageADC * 5.0) / 1024.0;
  float tempC = (voltage - 0.5) * 100.0;
  
  // 6. Trigger LEDs based on thresholds
  updateStatusLEDs(tempC);
  
  Serial.print("Avg Temp: ");
  Serial.print(tempC);
  Serial.println(" C");
  
  delay(500);
}

void updateStatusLEDs(float temp) {
  // Reset all LEDs first
  digitalWrite(PIN_LED_GREEN, LOW);
  digitalWrite(PIN_LED_YELLOW, LOW);
  digitalWrite(PIN_LED_RED, LOW);
  
  if (temp < 22.0) {
    digitalWrite(PIN_LED_GREEN, HIGH);
  } else if (temp < 28.0) {
    digitalWrite(PIN_LED_YELLOW, HIGH);
  } else {
    digitalWrite(PIN_LED_RED, HIGH);
  }
}

Debugging: First Three Things to Check When It Fails

If your sketch behaves erratically, resets randomly, or fails to compile, array mismanagement is the prime suspect. Here is the exact error string you will see in the Arduino IDE output console when the compiler catches a static violation:

warning: array subscript is above array bounds [-Warray-bounds]

If you are compiling for an ESP32 and the error happens at runtime due to dynamic indexing, you will see this exact panic string in the Serial Monitor:

Guru Meditation Error: Core 1 panic'ed (StoreProhibited). Exception was unhandled.

When you encounter these errors or unexplained reboots, check these three things immediately:

  1. Pointer Decay in Functions: Did you pass the array to a function like void processArray(int myArr[])? When you do this, the array decays into a pointer. Calling sizeof(myArr) inside that function will return 2 (on AVR) or 4 (on ARM) because it is measuring the pointer, not the array. Fix: Always pass the length as a second argument: void processArray(int myArr[], int len).
  2. Off-by-One Loop Errors: Check your for loops. If your array has 20 elements (indices 0 to 19), a loop written as for(int i=0; i<=ARRAY_SIZE(arr); i++) will attempt to write to index 20. This overwrites the next variable in SRAM, often corrupting the stack or hardware registers. Fix: Always use strictly less than (<).
  3. Hardcoded Size Variables Falling Out of Sync: If you define int len = 20; at the top of your sketch, but later change the array declaration to int buffer[30]; and forget to update len, your logic will break. Fix: Never hardcode the length variable. Always derive it using the ARRAY_SIZE macro or use C++11 std::array.

Modern Alternatives: How to Extend and Simplify

If you are using a modern Arduino core (AVR GCC 7.3+ or any ARM/ESP32 core), you have access to C++11 features that eliminate the sizeof macro entirely. According to the C++ standard reference for std::array, using standard library containers is safer and more expressive.

Using std::array (Fixed Size)

Replace raw C-arrays with std::array. It knows its own length and prevents pointer decay when passed by reference.

#include <array>

std::array<int, 20> tempBuffer; // Type, then size

void setup() {
  Serial.begin(9600);
  // .size() is a built-in method that returns 20
  Serial.println(tempBuffer.size()); 
}

Using std::vector (Dynamic Size)

If you need an array that grows dynamically (e.g., logging data until an SD card initializes), use std::vector. Warning: Dynamic allocation on microcontrollers with limited SRAM (like the Uno's 2KB) can cause heap fragmentation. Use vectors sparingly on AVR boards.

#include <vector>

std::vector<int> dynamicLog;

void loop() {
  dynamicLog.push_back(analogRead(A0));
  Serial.println(dynamicLog.size()); // Automatically tracks length
}

Frequently Asked Questions

Why does sizeof() return 4 when I pass my Arduino array to a function?

This is a fundamental C++ behavior called pointer decay. When you pass a raw array to a function, it implicitly converts to a pointer to its first element. On 32-bit architectures (ESP32, Arduino Zero), pointers are 4 bytes long. On 8-bit AVR (Uno), they are 2 bytes. The sizeof() operator inside the function is measuring the pointer itself, not the original array data. To fix this, you must pass the array length as a separate integer parameter alongside the array pointer, or pass the array by reference using templates.

Can I use the .length() method on an Arduino char array?

No. The .length() method only exists on the Arduino String object (capital 'S'), which is a C++ class wrapper. A standard char array (lowercase 'c' string) is just a raw block of memory ending with a null terminator (\0). To find the number of characters in a standard char array, use the C-standard strlen(myCharArray) function from <string.h>. Note that strlen() counts characters up to the null terminator, while sizeof() returns the total allocated memory including the null terminator.

How do I find the length of a multidimensional array in Arduino?

The sizeof() trick still works, but you must be specific about which dimension you are measuring. For a 2D array like int matrix[5][10];, sizeof(matrix) / sizeof(matrix[0]) returns the number of rows (5). To get the number of columns, you evaluate the first row: sizeof(matrix[0]) / sizeof(matrix[0][0]), which returns 10. However, multidimensional arrays consume SRAM rapidly (5x10 ints on an Uno is 100 bytes, which is 5% of your total available memory). Consider flattening 2D arrays into 1D arrays using index math (index = row * COLS + col) to save memory and improve cache locality.

Is there a built-in Arduino array length function in the standard library?

Historically, no; the Arduino language (Wiring) relied on C-style macros. However, because modern Arduino IDE versions use C++14 and C++17 compilers, you can use the standard library template std::extent or simply rely on the std::array container which features a native .size() method. If you are strictly using raw C-arrays and want a standard library function instead of a custom macro, you can use std::size(myArray) by including <iterator>. This is the safest, most modern approach for raw arrays as it will intentionally throw a compiler error if you accidentally try to use it on a decayed pointer, preventing runtime crashes.