The Core Question: What Programming Language Does Arduino Use?

If you are asking what programming language does Arduino use, the direct answer is C++. However, it is not standard, bare-metal C++. The Arduino ecosystem utilizes a specialized dialect of C++ wrapped in the Arduino Core API, compiled via the AVR-GCC toolchain (for classic AVR boards) or ARM-GCC (for 32-bit boards like the Due or Portenta).

For makers and engineers, this distinction is the root cause of the most frustrating IDE errors. The Arduino IDE is designed to shield beginners from C++ complexities by automatically injecting headers and generating function prototypes. But when you step outside basic sketches into object-oriented programming, templates, or memory-constrained optimization, this "helper" layer actively fights you. Understanding the underlying C++ mechanics is the only way to accurately diagnose and resolve cryptic compiler errors.

The .ino to .cpp Translation Trap

When you click "Verify" or "Upload," the Arduino IDE does not compile your .ino file directly. First, it concatenates all .ino tabs into a single file, injects #include <Arduino.h> at the very top, and uses a tool called ctags to auto-generate function prototypes. Finally, it renames the file to .cpp and hands it to the GCC compiler.

This automated translation is responsible for a massive percentage of "phantom" errors in the Arduino IDE.

Error: 'MyType' was not declared in this scope

The Symptom: You define a custom struct or class at the top of your sketch, use it as a return type for a function, and the compiler throws a scope error, even though the definition is clearly above the function.

struct SensorData {
  float temp;
  int humidity;
};

SensorData readSensor() { // IDE auto-prototype fails here
  // ...
}

The Diagnosis: The IDE's ctags parser often fails to recognize custom types defined in the same .ino file when generating prototypes. It inserts the prototype above your struct definition, causing the compiler to read the function signature before it knows what SensorData is.

The Fix:

  • Quick Fix: Manually write your own function prototype above the function but below the struct definition.
  • Pro Fix: Move your custom types and functions into a separate .h and .cpp file within the same sketch folder. The IDE's auto-prototyping only targets .ino files, completely bypassing standard C++ compilation rules for separate modules.

Memory Mismanagement: String vs. char[]

Because Arduino uses C++, you have access to standard memory management tools. However, the hardware constraints of microcontrollers like the ATmega328P (which has only 2,048 bytes of SRAM) mean that standard C++ practices can lead to catastrophic runtime failures.

The Silent Killer: Heap Fragmentation

The Arduino String class (note the capital 'S') is a wrapper around a dynamically allocated C-style character array. Every time you concatenate, modify, or return a String object, the underlying C++ code calls malloc() and free() on the heap.

Expert Insight: On an ATmega328P, heavy use of the String class will fragment the 2KB SRAM within minutes. The compiler will not warn you about this. The sketch will compile perfectly, upload successfully, and then randomly reboot or freeze due to stack-heap collisions. This is a runtime language misuse error, not a compile-time error.

Comparison Matrix: String Handling in Arduino C++

Feature Arduino String Standard C++ std::string C-Style char[]
Memory Allocation Dynamic (Heap) Dynamic (Heap) Static (Stack/Global)
Fragmentation Risk High High None
AVR-GCC Support Native (Arduino Core) Requires <string> (Heavy overhead) Native (AVR-Libc)
Diagnostic Safety Hides bounds errors Throws exceptions (if enabled) Requires manual bounds checking

Source: AVR Libc User Manual details memory sections and heap management constraints on 8-bit AVR architectures.

The Fix: For error-free, long-running embedded C++, abandon the String class. Use fixed-size char arrays and standard C functions like snprintf() from <stdio.h>. If you must parse serial data, read it byte-by-byte into a pre-allocated static buffer.

Modern GCC Strictness: C++17 and the 'Register' Keyword

As of 2026, the Arduino IDE 2.x ecosystem utilizes modern AVR-GCC toolchains (version 12 and above). These newer compilers enforce stricter C++17 standards compared to the legacy GCC 7.x used in older IDE versions. This shift frequently breaks older, copy-pasted sketches from legacy forums.

Error: ISO C++17 does not allow 'register' storage class specifier

The Symptom: You compile an older library or sketch and are met with a hard compiler error pointing to a variable declared with the register keyword.

register int i = 0; // Triggers fatal error in modern AVR-GCC

The Diagnosis: In early C and C++, the register keyword hinted to the compiler to store a variable in a CPU register for faster access. Modern GCC compilers feature advanced optimization passes (like -O2 and -O3) that make manual register allocation obsolete. Consequently, the C++17 standard officially removed the keyword.

The Fix: Simply delete the register keyword. The GCC optimizer will automatically assign the variable to a hardware register if it improves execution speed. For deep-dive compiler flags, refer to the official GCC documentation.

Object-Oriented Pitfalls: The VTable Error

Because Arduino uses C++, you can write fully object-oriented code using classes, inheritance, and polymorphism. However, the Arduino environment lacks a standard C++ runtime library (libstdc++) initialization sequence, which can lead to bizarre linker errors.

Error: Undefined reference to 'vtable for MyClass'

The Symptom: You create a base class with virtual functions and a derived class. The code compiles, but the linker fails at the very end of the build process with a vtable error.

The Diagnosis: The compiler generates a virtual method table (vtable) when it sees the first non-inline virtual function defined. If you declare a virtual destructor in your header file but forget to implement it in your .cpp file (or forget to define it entirely), the linker cannot build the vtable. Furthermore, using the new keyword to instantiate objects that rely on complex C++ runtime initialization can fail silently on AVR boards due to missing __cxa_pure_virtual handlers.

The Fix:

  1. Ensure every virtual function declared in your .h file has a concrete implementation, even if it is just an empty body {}.
  2. Provide a dummy pure virtual handler in your main sketch to satisfy the linker and catch runtime C++ exceptions:
    extern "C" void __cxa_pure_virtual(void) {
      while (1); // Trap execution if a pure virtual function is called
    }

Diagnosing Macro Collisions in the Arduino Core

The Arduino API relies heavily on C-style #define macros for hardware abstraction (e.g., digitalWrite, PI, LED_BUILTIN). Because macros are processed by the preprocessor before the C++ compiler even sees the code, they can cause catastrophic naming collisions.

Error: Expected unqualified-id before numeric constant

The Symptom: You name a variable or class member PI, INPUT, or OUTPUT, and the compiler throws a syntax error that makes no logical sense.

class MotorController {
  int OUTPUT; // Error: 'OUTPUT' is a macro defined as 1 in Arduino.h
};

The Diagnosis: The preprocessor blindly replaces your variable name with the macro value. The compiler actually sees int 1;, which is invalid C++ syntax.

The Fix: Never use all-caps naming conventions for variables or class members in the Arduino ecosystem. Reserve all-caps exclusively for your own #define macros or const variables, and always prefix them with a unique namespace (e.g., MY_APP_OUTPUT). Alternatively, use constexpr instead of #define for constants, as constexpr respects C++ scoping rules and will not leak into the global preprocessor namespace.

Summary: Mastering the Arduino C++ Dialect

Understanding what programming language Arduino uses is the first step toward writing robust, production-ready firmware. By recognizing that you are writing C++17 constrained by an automated IDE wrapper and limited SRAM, you can stop fighting the compiler. Ditch the String class, manage your own prototypes, respect the preprocessor, and leverage the Arduino Language Reference not just as a syntax guide, but as a map of the underlying C++ abstractions.