El error que llegó al final
Una clínica tiene un programa que envía avisos de citas por SMS. Tras un cambio, ya no se compila. El mensaje dice undefined reference to send_sms.
Una compañera revisa el código C línea a línea durante una hora. El código está bien. El error lo dio el último paso, que ni siquiera lee el código. Saber qué paso habla ahorra esa hora.
Al final de esta lección
Nota
Puedo nombrar las etapas por las que un compilador lleva un programa.
Palabras clave
| Palabra | Qué significa | Ejemplo |
|---|---|---|
| Driver | el programa que lanza las demás herramientas en orden | gcc |
| Preprocesador | la etapa que pega archivos y sustituye macros | |
| Ensamblador | el programa que pasa el ensamblador a código máquina | as |
| Archivo objeto | código máquina que aún no es un programa entero | hello.o |
| Símbolo | un nombre que el enlazador tiene que encontrar | |
| Enlazador | el programa que une archivos objeto y bibliotecas | ld |
Las celdas que rellenan los estudiantes quedan en blanco en esta vista previa.
La primera etapa sustituye macros
El archivo hello.c tiene una macro, GREETING, y una línea #include.
El preprocesador trabaja antes que nada. Pega el contenido de stdio.h y cambia cada macro por su texto. La opción -E detiene gcc justo ahí y escribe el resultado en pantalla. El primer Run entra en la carpeta. El segundo muestra solo el final.
Entra en la carpeta del compilador
cd ~/compilerSe muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.
El final del código preprocesado
gcc -E hello.c | tail -n 6Se muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.
gcc lanza otros programas por ti
Cuando escribes gcc hello.c, gcc hace muy poco por sí mismo. Es un driver: lanza otros programas, uno tras otro, y pasa a cada uno el archivo del anterior.
La opción -### hace que gcc escriba esas órdenes sin ejecutar ninguna. El Run se queda solo con el programa de cada orden.
Los programas que gcc lanzaría
gcc -### -o hello hello.c 2>&1 | grep "^ " | cut -d " " -f 2Se muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.
Ejemplo: tres programas, cuatro etapas
| Programa | Etapas que hace | Lo que escribe |
|---|---|---|
| cc1 | Preprocesar y compilar | Ensamblador, un archivo .s |
| as | Ensamblar | Un archivo objeto, .o |
| collect2 | Enlazar, llamando a ld | El programa |
Las celdas que rellenan los estudiantes quedan en blanco en esta vista previa.
Guarda el archivo de cada etapa
Normalmente los archivos intermedios van a una carpeta temporal y se borran. La opción -save-temps los deja junto al código.
Una sola compilación deja entonces cuatro archivos: hello.i, hello.s, hello.o y hello. La orden file lee el principio de cada uno y dice de qué tipo es.
Compila una vez y guárdalo todo
gcc -save-temps -o hello hello.cSe muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.
¿Qué es cada archivo?
file hello.i hello.s hello.o helloSe muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.
Ejemplo: lo que dijo file
| Archivo | Lo hizo | Cómo lo llama file |
|---|---|---|
| hello.i | El preprocesador | C source, ASCII text |
| hello.s | El compilador | assembler source, ASCII text |
| hello.o | El ensamblador | ELF 64-bit LSB relocatable |
| hello | El enlazador | ELF 64-bit LSB pie executable |
Las celdas que rellenan los estudiantes quedan en blanco en esta vista previa.
Un archivo objeto tiene huecos
hello.o contiene el código de square y de main. Pero main llama a printf, y printf no está en este archivo. Vive en la biblioteca de C.
nm lista los símbolos de un archivo objeto. T significa que el código está aquí. U significa sin definir: un hueco que el enlazador debe llenar.
Después lanza tú el enlazador ld, sin biblioteca de C. Lee lo que dice.
Los símbolos de hello.o
nm hello.oSe muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.
Enlaza sin nada más
ld hello.o -o brokenSe muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.
Un error frecuente
Advertencia
Mucha gente cree que todo error de compilación es un fallo en su código. Un undefined reference viene del enlazador, cuando el código ya compiló bien. Significa que el cuerpo de un símbolo no llegó al último paso. Revisa qué se enlaza, no la línea de C.
Cuando el que habla es el compilador
Ahora el caso contrario. El primer Run hace una copia, cube.c, que llama a cube(7), una función que no existe en ningún sitio. hello.c no cambia.
El segundo Run compila la copia con -c, así que gcc no llega a enlazar. El tercero busca el archivo objeto.
Haz una copia con otra función
sed 's/square(7)/cube(7)/' hello.c > cube.cSe muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.
Compila la copia sin enlazar
gcc -c cube.cSe muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.
¿Se escribió cube.o?
ls cube.oSe muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.
Para ir más lejos
Escríbelo tú. Añade a cube.c la línea int cube(int n); encima de main, con un editor de texto. Compila con gcc -c cube.c y lee nm cube.o. Después compila con gcc cube.c.
Explica por qué ahora el error sale de otra etapa, y qué tendrías que añadir para que el programa se cree.