The Core Truth: C, C++, and the Arduino Wrapper

When makers, students, and software engineers first transition into embedded systems, the most common foundational question they ask is: in what language is Arduino programmed? The direct, technical answer is C and C++. However, the Arduino ecosystem does not use raw, unadulterated C++. Instead, it utilizes a custom build process and an abstraction layer known as the Arduino Core API, which wraps standard C++ libraries into simplified .ino sketch files.

This abstraction is designed to lower the barrier to entry, but it frequently causes severe compilation, syntax, and linker errors for developers who attempt to write advanced C++ code or port standard desktop C++ libraries into the Arduino IDE. If you are asking what language Arduino is programmed in because your code is failing to compile, you are likely running into conflicts between standard C++ syntax and the Arduino IDE's pre-processor.

In this troubleshooting guide, we will dissect the underlying C/C++ architecture of the Arduino toolchain, identify the most common language-related compilation failures, and provide actionable, expert-level fixes to get your sketches building cleanly.

Under the Hood: The GCC Toolchain and Hidden Main

To troubleshoot language errors, you must understand how the Arduino IDE processes your code. When you click "Verify" or "Upload," the IDE performs the following background operations:

  1. Concatenation: All .ino files in your sketch folder are merged into a single temporary file.
  2. Header Injection: The IDE automatically injects #include <Arduino.h> at the very top of the file. This header pulls in the core C/C++ APIs (like digitalWrite, Serial, and standard math libraries).
  3. Prototype Generation: A regex-based parser scans your code and automatically generates C++ function prototypes for any custom functions you have written, placing them at the top of the file.
  4. Compilation: The merged file is compiled using avr-g++ (for 8-bit AVR boards like the Uno) or arm-none-eabi-g++ (for 32-bit ARM boards like the Nano 33 BLE or Portenta H7).

According to the Arduino Core API GitHub Repository, the modern Arduino Core is built heavily on C++11 and C++14 standards, with newer 2026 ARM cores leveraging C++17 features. Understanding this pipeline is critical for diagnosing the errors below.

Troubleshooting Matrix: Language & Syntax Errors

Below is a diagnostic matrix mapping common Arduino IDE error outputs to their root C/C++ causes and immediate fixes.

IDE Error Message Root C++ Cause Actionable Fix
expected constructor, destructor, or type conversion Missing semicolon at the end of a class definition, or an unrecognized type in the global scope before setup(). Check all custom class declarations for trailing semicolons. Ensure all #include statements are placed at the very top of the sketch.
undefined reference to `setup' The linker cannot find the mandatory setup() and loop() functions, usually because they were accidentally nested inside another function or commented out. Verify that setup() and loop() exist in the global namespace and are not trapped inside a class or conditional block.
stray '\302' in program Hidden UTF-8 formatting characters (like non-breaking spaces) introduced when copying code from web browsers or PDFs. Delete the offending line entirely and re-type it manually using standard ASCII spaces in the IDE.
cannot convert 'String' to 'const char*' Attempting to pass an Arduino String object to a standard C function (like strcpy or mqtt.publish) that expects a C-style character array. Append .c_str() to your String object (e.g., myString.c_str()) to extract the underlying const char* pointer.
variable or field 'myFunc' declared void The IDE's auto-prototype generator failed to recognize a complex C++ type (like std::vector) used as a function parameter. Move the function into a separate .cpp file and create a manual .h header file to bypass the IDE's regex parser.

Deep-Dive Fixes for C++ Compilation & Linker Errors

1. The Auto-Prototype Generator Trap

One of the most frustrating hurdles when writing advanced C++ in the Arduino IDE is the auto-prototype generator. Because the IDE attempts to automatically create forward declarations for your functions, it relies on a regular expression that fundamentally misunderstands modern C++ templates and namespaces.

The Failure Scenario: You write a function that accepts a Standard Template Library (STL) vector:

void processData(std::vector<int> &data) {
  // processing logic
}

The IDE's regex parser sees std::vector<int>, fails to parse the angle brackets, and generates a malformed prototype like void processData(std::vector<int> &data); in the wrong scope, resulting in a cascade of syntax errors.

The Expert Fix: Stop using .ino files for complex C++ logic. Create a new tab in the Arduino IDE, name it DataProcessor.cpp, and write your standard C++ code there. Create a corresponding DataProcessor.h header file. The IDE does not run its regex prototype generator on .cpp files, allowing the avr-g++ compiler to process standard C++ syntax natively.

2. SRAM Exhaustion and String Class Fragmentation

While standard C++ relies on the std::string class, Arduino utilizes a custom String class. On 8-bit AVR microcontrollers like the ATmega328P (found in the Arduino Uno), you only have 2KB of SRAM. The Arduino String class dynamically allocates memory on the heap. In long-running 2026 IoT applications, continuous concatenation and destruction of String objects leads to severe heap fragmentation, ultimately causing the microcontroller to silently reboot or freeze.

The Expert Fix: Avoid the Arduino String class for high-reliability applications. Instead, use fixed-size C-style character arrays (char buffers) and standard C string manipulation functions from the AVR Libc User Manual, such as snprintf().

// BAD: Causes heap fragmentation
String payload = "{"temp": " + String(dht.readTemperature()) + "}";

// GOOD: Static memory allocation, zero fragmentation
char payload[64];
snprintf(payload, sizeof(payload), "{\"temp\": %.2f}", dht.readTemperature());

If you absolutely must use the Arduino String class, always use the reserve() method in your setup() function to pre-allocate contiguous memory blocks and prevent runtime fragmentation.

3. Namespace Collisions and 'extern "C"' Linking

Because Arduino is programmed in C++, the compiler uses name mangling to support function overloading. However, if you import a legacy C library (such as a specific sensor driver written in pure C), the C++ linker will fail to find the functions, throwing an undefined reference error.

The Expert Fix: You must instruct the C++ compiler to treat the C library headers with C-style linkage. Wrap your C library includes in an extern "C" block:

#ifdef __cplusplus
extern "C" {
#endif

#include "legacy_c_sensor_driver.h"

#ifdef __cplusplus
}
#endif

This is a mandatory pattern when integrating older C-based hardware abstraction layers (HALs) into modern C++ Arduino sketches.

Beyond C++: Alternative MCU Languages in 2026

While the native Arduino IDE and the broader Arduino Language Reference remain strictly bound to C and C++, the broader microcontroller landscape in 2026 has expanded significantly. If you are struggling with C++ memory management, pointer arithmetic, or linker errors, you may consider alternative languages that compile to the same bare-metal hardware:

  • Rust (via avr-hal and embassy): Rust has become the premier alternative for embedded systems in 2026. It enforces strict memory safety at compile-time, entirely eliminating the heap fragmentation and null-pointer crashes that plague C++ Arduino sketches. Frameworks like embassy allow for async/await patterns on ARM Cortex-M MCUs.
  • MicroPython / CircuitPython: For rapid prototyping where execution speed is secondary to development speed, Python-based firmware allows you to bypass the C++ compilation step entirely, utilizing a REPL (Read-Eval-Print Loop) over the serial port.
  • JavaScript (Moddable / JerryScript): Specific ESP32-based boards now support JS engines optimized for constrained SRAM environments, allowing web developers to write MCU logic without touching a C++ compiler.

Summary Checklist for Clean C++ Builds

To ensure your Arduino sketches compile without language-related errors, run through this pre-flight checklist before hitting "Upload":

  1. Verify that all complex C++ templates and STL containers are isolated in .cpp and .h files, not .ino files.
  2. Ensure all C-style libraries are wrapped in extern "C" blocks to prevent name-mangling linker errors.
  3. Replace dynamic Arduino String concatenations with snprintf() and static char arrays to protect the 2KB SRAM limit on AVR boards.
  4. Check for hidden UTF-8 characters if you encounter "stray" character errors after copying code from the web.
  5. Confirm that your board definition in the Boards Manager matches the GCC toolchain version expected by your third-party libraries (e.g., ensuring ARM Cortex-M boards are using the correct CMSIS-DSP headers).

By understanding that Arduino is fundamentally a C++ environment wrapped in a simplified API, you can bypass the IDE's quirks, leverage the full power of the GCC compiler, and write robust, production-grade firmware.