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.
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
| Component | Arduino Pin | Notes |
|---|---|---|
| TMP36 VCC | 5V | Do not use 3.3V on Uno Rev3 |
| TMP36 GND | GND | Common ground required |
| TMP36 Signal | A0 | Analog input (10-bit ADC) |
| Green LED Anode | D8 | 220Ω resistor in series |
| Yellow LED Anode | D9 | 220Ω resistor in series |
| Red LED Anode | D10 | 220Ω resistor in series |
Wiring Steps
- 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.
- Connect the anodes (long legs) of the Green, Yellow, and Red LEDs to pins D8, D9, and D10 respectively, through the 220Ω resistors.
- Connect all LED cathodes to the common GND rail.
- 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:
- 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. Callingsizeof(myArr)inside that function will return2(on AVR) or4(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). - Off-by-One Loop Errors: Check your
forloops. If your array has 20 elements (indices 0 to 19), a loop written asfor(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 (<). - 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 toint buffer[30];and forget to updatelen, your logic will break. Fix: Never hardcode the length variable. Always derive it using theARRAY_SIZEmacro or use C++11std::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.






