Firmware embebido: mejores prácticas para inicializar arrays en C con datos de archivos externos y formas de onda
Aprenda a inicializar arrays en C usando archivos de texto externos
Esta guía explica cómo poblar arrays en un programa C con datos almacenados en archivos de texto separados. Los valores no se incrustan en el código fuente; en su lugar, se cargan en tiempo de compilación. Cubrimos arrays unidimensionales y multidimensionales, mostramos cómo controlar si el array reside en RAM o memoria no volátil, e ilustramos cómo elegir entre múltiples archivos de datos para la inicialización.
Los ejemplos apuntan a un microcontrolador ARM de 32 bits y se compilan con GCC. Todos los fragmentos usan C estándar y funcionan con este compilador.
Conceptos básicos de inicialización de un array
Cuando declara un array en C, puede proporcionar una lista de valores —conocidos como inicializadores— entre llaves. Por ejemplo:

Si omite el tamaño entre corchetes, el compilador lo infiere del número de inicializadores. Si el array se declara más grande que la lista de inicializadores, los elementos restantes se ponen a cero por defecto. Proporcionar más inicializadores que el tamaño declarado provoca un error de compilación.
Espacio en blanco
Los inicializadores se separan por comas, y puede agregar libremente espacios en blanco —espacios, tabulaciones, saltos de línea, retornos de carro, tabulaciones verticales o avances de página— entre ellos. En C, las declaraciones pueden abarcar múltiples líneas; esto es especialmente útil para arrays grandes que pueden llenar varias páginas de código fuente.

Inicializando un array desde un archivo
El preprocesador de C puede incluir archivos de texto arbitrarios usando la directiva #include. Esta característica no se limita a archivos de cabecera o fuente; cualquier archivo de texto plano es aceptable. Un archivo simple que contenga números, comas y espacios en blanco puede usarse para inicializar un array:

El archivo incluido debe ser texto plano —sin formato enriquecido, cabeceras u otros artefactos. Un ejemplo rápido creado con el Bloc de notas de Windows se ve así:

Al compilar, el array aparece en memoria como se muestra en la captura del depurador. En este caso el array reside en RAM, indicado por las direcciones altas en la columna "Location".

Almacenando un array en memoria no volátil y seleccionando un archivo de datos
En el ejemplo anterior, el array es una variable global sin ubicación explícita. El compilador asume que puede modificarse en tiempo de ejecución, por lo que coloca la variable en RAM. Los valores iniciales viven en memoria no volátil (típicamente Flash) y una rutina de arranque los copia a RAM. Si el array es verdaderamente constante, declararlo const le dice al compilador que lo mantenga solo en Flash, ahorrando RAM preciosa. La siguiente captura muestra el efecto: la "Location" del array apunta a direcciones bajas de Flash.

Puede usar directivas #define y #if para alternar entre almacenamiento en RAM y Flash, o para elegir diferentes archivos de datos para la inicialización. Por ejemplo, el siguiente código permite elegir la ubicación de almacenamiento en tiempo de compilación:

La construcción #if es un ejemplo clásico de inclusión condicional. Determina si se aplica el calificador const basado en una bandera de compilación.
De manera similar, puede seleccionar condicionalmente qué archivo suministra los datos iniciales:

Prueba con un array grande
Para validar la técnica, usé una forma de onda de ruido de 10,000 muestras almacenada en un archivo CSV. El gráfico siguiente muestra la forma de onda, mientras que la declaración demuestra cómo se incorporan los datos al programa:


El archivo CSV original carecía de comas después de cada valor. Usando una búsqueda/reemplazo con expresión regular (buscar \R y reemplazar con ,\R) se agregaron comas a las 10,000 entradas en una sola operación.
El array resultante es visible en el depurador, agrupado ordenadamente en filas de 100 elementos.

Arrays multidimensionales
Al trabajar con datos de dos o tres dimensiones, recuerde que C almacena los arrays en orden de fila mayor. Por ejemplo, un array uint16_t test[2][3] se dispone en memoria como:
[0,0] [0,1] [0,2] [1,0] [1,1] [1,2]
Acceder a elementos consecutivos a lo largo del subíndice más a la derecha es más rápido porque los datos son contiguos. Si necesita dos vectores de 1,000 muestras, declare test[2][1000] para maximizar la amigabilidad con la caché.
El inicializador para un array bidimensional normalmente se agrupa con llaves anidadas:

Sin embargo, puede omitir las llaves exteriores; el compilador llena el array de izquierda a derecha. Emite una advertencia: missing braces around initializer. Mientras el número total de valores coincida con el tamaño del array, el código compila.

Para distribuir los datos en múltiples archivos, simplemente incluya cada archivo en secuencia, envolviendo toda la lista con un par de llaves:

Inicializando arrays en uniones
Una union permite que diferentes tipos compartan la misma memoria. Por ejemplo, una unión que contiene un vector unidimensional y una matriz bidimensional ocupa el mismo espacio de direcciones:

Al inicializar, el compilador llena el primer miembro primero. Si se invierte el orden, puede ver una advertencia porque la lista de inicializadores no está completamente entre llaves. Doble llave alrededor del #include asegura el agrupamiento correcto:
El archivo de datos para la unión se ve así:

En memoria, tanto vector como matrix comienzan en la misma ubicación, como muestra el depurador:

¿Conoce otro método para inicializar un array multidimensional desde un único archivo numérico? Comparta sus ideas en los comentarios.
Consejo adicional: Cadenas
Inicializar una cadena desde un archivo externo requiere que las comillas estén dentro del propio archivo. Un #include directo dentro del literal de cadena rompe el compilador. En su lugar, coloque las comillas en el archivo e inclúyalo así:

La cadena resultante reside en RAM, como se ilustra en la captura.

Estos ejemplos son teóricos y deberían funcionar con cualquier compilador C estándar. Si encuentra peculiaridades específicas del compilador, háganoslo saber en los comentarios.
Fuente proporcionada.
Incrustado
- Comparativa: Nexus Integra frente a las principales plataformas IoT y Big Data
- STMicroelectronics lanza STM32Cube.AI: potenciando dispositivos edge con redes neuronales embebidas
- DATA MODUL lanza monitores open‑frame ultra‑brillantes 21,5" y 31,5" con EasyTouch para exterior e interior
- Microchip introduce ADCs Delta‑Sigma 24‑bit y 16‑bit con data rates 153,6 kSPS
- Contrinex 2020: Sensores listos para cloud y cortinas láser Bluetooth para seguridad
- La revolución cloud: cómo el cloud computing transforma TI, seguridad y carreras
- Dando Vida a IoT con IBM y Tech Data – Parte 2
- Convirtiendo IoT en Realidad: Insights de Tech Data e IBM – Parte 1
- Arrays en C++: Declaración, inicialización y técnicas con punteros
- Asignación dinámica de arrays en C++: Guía práctica con ejemplos de código
- Lectura de archivos en Java con BufferedReader – Guía práctica con ejemplos