gcc - El
compilador C
El gcc es el
compilador GNU con soporte para los lenguajes C, C++, objective
C, Fortran y posiblemente
otros. GCC
asumirá cuál es el lenguaje usado por el código fuente según
la extensión del archivo. Las
siguientes extensiones son válidas:
.c - C
.h - header para C, C++ o Objective C
.C, .cc .cxx - C++
.m - Objective C
Para compilar un archivo en C basta usar el siguiente comando:
gcc programa.c -o programa
Donde programa.c es el archivo que contiene el código fuente
y programa es el archivo ejecutable a ser generado.
Se puede especificar más de un archivo fuente al mismo
tiempo. Ejemplo:
gcc archivo1.c archivo2.c archivo3.c -o programa o
Algunas opciones útiles de línea de comando: -o [nombre del
ejecutable] define el nombre del archivo ejecutable a ser
generado. Si esta opción no es usada el nombre usado by default
es a.out -g incluye los símbolos necesarios pra la depuración
del programa. Es recomendable usar esta opción siempre. -Wall
genera mensajes extras de "warnings" (advertencias),
útil para encontrar errores comunes. -c Genera un archivo
objecto en vez de uno ejecutable, un archivo objecto tendrá la
extensión .o
Exemplo:
gcc -c archivo1.c -Wall -g
gcc -c archivo2.c -Wall -g
Para generar un ejecutable a partir de un archivo1.o y
archivo2.o basta lo siguiente:
gcc arquivo1.o arquivo2.o -o programao
- -Ipath
- especifica un PATH para el directorio que contiene los
archivos de include (.h)
Ejemplo:
gcc -I/home/yo/include archivo.c -o programa
- -Dsímbolo
- hace que el archivo sea compilado con un símbolo #define
Ejemplo:
gcc -DDEBUG archivo.c -o programa
- -lbiblioteca
- especifica una biblioteca que será adicionada como
un linck al programa
- -Lpath
- especifica un PATH para un directorio que contiene
blibliotecas/libraries
Ejemplo
gcc archivo.c -o programa -L/home/yo/lib -lAlgo
Básicamente hay dos tipos de bibliotecas: estáticas,
donde el código contenido en ellas son incluídas en
el programa, y dinámicas, donde el código será linkeado
dinámicamente en el tiempo de ejecución del programa.
Las bibliotecas estáticas tiene nombres del tipo:
libAlgo.a, mientras que las dinámicas tienen nombres
del tipo: libAlgo.so
Apéndice
gdb depurador
Cuando un programa realiza una operación ilegal, como
dividir por cero, ejecución de una instrucción ilegal o
acceso a un área de memoria no permitida, este programa será
terminado con una senial del tipo SIGSEGV y se generará un
archivo core, es lo que sucede cuando uno ve:
Segmentation fault (core dumped)
Los archivos core contienen una imagen de la memoria del
proceso generada en el momento en que dicho programa ejecutó
la operación ilegal. Para ver lo que sucedió se puede
utilizar GDB (GNU debugger) para analizar cuál fue esa
operación ilegal y sus motivos. Es neceario que el programa
haya sido compilado con la opción -g para que gdb pueda
mostrar información.
gdb programa core
Con esta instrucción gdb cargará el programa con el
archivo core el en directorio actual.
Dado el siguiente programa:
#include <stdlib.h>
#include <stdio.h>
char *foo(char *str)
{
char *p = NULL;
strcpy(p, str);
return p;
}
int main(int argc, char **argv)
{
printf("%s\n",
foo("Hello, World"));
return 0;
}
Compilado de la siguiente forma:
gcc hello.c -g -Wall -o hello
Y ejecutado como:
% ./hello
Segmentation Fault (core dumped)
Ejecutamos gdb: (nota: donde se lee sparc-sun-etc, leáse
i686-gnu-linux-etc)
% gdb hello core
GNU gdb 4.18
Copyright 1998 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public
License, and you are
welcome to change it and/or distribute copies of it
under certain conditions.
Type "how copying" to see the conditions.
There is absolutely no warranty for GDB. Type "how
warranty" for details.
This GDB was configured as
"sparc-sun-solaris2.5.1"...
Core was generated by ./hello'.
Program terminated with signal 11, Segmentation Fault.
Reading symbols from /usr/lib/libc.so.1...done.
Reading symbols from /usr/lib/libdl.so.1...done.
#0 0xef723d34 in strcpy () from /usr/lib/libc.so.1
(gdb)
Para hacer un backtrace (ver el contenido de la pila de
ejecución) se usa el comando bt:
(gdb) bt
#0 0xef723d34 in strcpy () from /usr/lib/libc.so.1
#1 0x106c4 in foo (str=0x11288 "Hello, World")
at hello.c:9
#2 0x106f8 in main (argc=1, argv=0xeffffaac) at
hello.c:17
La primera línea del backtrace dice:
#0 0xef723d34 in strcpy () from /usr/lib/libc.so.1
^ ^
^
^
| |
|
|
| |
|
biblioteca donde está definida la función
| |
|
libc.so é la biblioteca estándar de C.
| |
|
| |
nombre de la función que estaba siendo ejecutada en el
momento
| |
del aborto.
| |
| dirección
de la operación que estaba siendo ejecutada. Generalmente
no es muy
| úútil, a
no ser que usted sea un "hardcore hacker". En ese
caso no veo por qué
| está
leyendo esto...
|
profundidad de la pila de ejecución
quiere decir que esta función que estaba siendo
ejecutada en el
momento en que el programa fue abortado.
Y lo de la segunda:
#1 0x106c4 in foo (str=0x11288 "Hello,
World") at hello.c:9
^ ^
^ ^
^ ^
| |
| |
| |
| |
| |
| número de la línea
| |
| |
| que estaba siendo
| |
| |
| ejecutada.
| |
| |
|
| |
| |
nombre del archivo donde fue
| |
| |
definida la función
| |
| |
| |
| los parámetros (y sus
valores) con que esta función
| |
| fue llamada. Observando el
resto del backtrace, se puede
| |
| ver que quién llamó a esta
función con tal valor, fue
| |
| main()
| |
|
| |
la función foo, que llamó strcpy() con parámetros
| |
absurdos.
| |
| etc etc
|
etc etc
Continuando el análisis, vimos que la función en donde
ocurrió la operación ilegal fue strcpy(), o sea, una función
del sistema. Suponiendo que el sistema esté libre de bugs
tan estúpidos que un programa tan simple como este pueda
encontrar, deducimos que la culpa es de quien llamó a
strcpy(), o sea, foo()
Para cambiar el contexto de gdb al contexto de la función
foo(), se usa el comando frame:
(gdb) frame 1 #1 0x106c4 in foo (str=0x11288 "Hello, World") at
hello.c:9
9 strcpy(p, str);
(gdb)
La función en donde se manifiesta el problema es
strcpy() que se supone libre de problemas en sí misma.
Entonces es probable que el problema se encuentre en lo parámetros
pasados a ella:
(gdb) print p $1 = 0x0
(gdb) print str $2 = 0x11288 "Hello, World"
Aparentemente, el valor de str no tiene ningún error
pero el de "p" es una dirección nula, que es la
causa del problema. ahora para ver las fuentes donde esa
función fue definida se usa el comando list
(gdb) list
4
5 char *foo(char *str)
6 {
7 char *p = NULL;
8
9 strcpy(p, str);
10
11 return p;
12 }
13
El comando list lista algunas líneas del programa en la
función que está causando problemas. Para obtener más
información sobre ella, use 'help list'. Ahora ya se está
en condiciones de reparar el programa.
Otros comandos útiles:
run
corre el programa que fue cargado
next
avanza una línea en el programa siendo ejecutado (en el
modo paso a paso)
step
avanza un comando en el programa siendo ejecutado. Es lo
mismo que next, pero si hay una llamada de función, éstaentrará
en la función
break <función>
coloca un breakpoint en una función (o programa detendrá
la ejecución al llegar a esa función. help breakpoint
mostrará otras opciones de sintaxis.
cont
continúa la ejecución normal del programa (salida del
modo paso a paso)
quit
salir del gdb
Make
Make facilita bastante el mantenimiento de un programa
con un tamanio razonable (más de 2 o 3 archivos fuente).
Después de definir algunas reglas de administración basta
tipear make para tener el programa compilado en lugar de
tipear toda una línea de comando gcc.
Archivo en donde se debe colocar las reglas de compilación
para un programa es Makefile
Ejemplo
# compilador a ser usado
CC = gcc
# flags a ser utilizados por el compilador a la hora de compilar las fuentes.
CFLAGS = -g -Wall
# bibliotecas a ser linkados al programa
LIBS = -lm
# lista de archivos fuente del programa
SRCS = archivo1.c archivo2.c archivo3.c
# lista de archivos objeto a ser generados a partir dos archivos fuente
OBJS = archivo1.o archivo2.o archivo3.o
# nombre del programa ejecutable a ser generado
PROG = programa
#
# Regla para compilación de dos archivos fuente (.c --> .o)
.c.o: $(SRCS)
$(CC) -c $<
#
# Regla para linkear dos archivos objeto para un ejecutable
$(PROG): $(OBJS)
$(CC) -o $@ $(OBJS) $(LIBS)
#
# make clean, para apagar arquivos .o e backup de fontes.
clean:
rm -f *.o
rm -f *~
Nota: deben utilizarse TABs en lugar de espacios en
blanco.
Makefile puede ser adaptado para otros programas, sólo
basta cambiar la lista de archivos fuente, objetos, nombre
del programa y alguna flag o biblioteca que se precise.
Algunos includes y bibliotecas útiles.
Header |
Biblioteca |
Descripción |
stdlib.h |
|
Contiene la mayoría de las rutinas estándar de
ANSI, como malloc(), free() etc |
stdio.h |
Estándar |
Contiene las rutinas relacionadas com I/O, como
printf(), scanf(), fgets(), fopen() etc. |
string.h |
Estándar |
Rutinas de manipulción de strings, como strcmp(),
memcpy(), memset() etc. |
math.h |
-lm |
Rutinas matemáticas, como sin(), sqr(), exp()
etc. |
unistd.h |
Estándar |
Rutinas específicas de Unix, como fork(),
execvp(), kill() etc |