-
Notifications
You must be signed in to change notification settings - Fork 1
/
gnuc.tex
322 lines (257 loc) · 12.6 KB
/
gnuc.tex
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
%Autor: aplatanad
%aplatanad: 8
\chapter{GNU C/C++}\label{gnuc}
\label{gnuc.tex}
En este capítulo tomaremos en consideración la programación en lenguaje
C/C++ bajo el sistema operativo GNU/Linux. Todos los lenguajes de
programación compilados pasan en menor o mayor medida por las mismas
fases, por lo que mucho de lo que estudiaremos se puede aplicar a otros
lenguajes y problemas.
\section{Compilado y enlazado}
Todos los leguajes de programación compilados, y el C no es una
excepción, requieren de dos fases para generar el binario (o ejecutable)
de un programa:
\begin{description}
\item[Compilar] Traduce cada archivo fuente en C {\tt (*.c)} del
programa a lenguaje máquina, almacenando la traducción en los archivos
de código objeto {\tt (*.o)}. Para ello usamos el {\sf GCC (GNU C
Compiler)} a través del comando {\tt gcc}\index{gcc}.
\item[Enlazar] Une todos los archivos de código objeto generados en la
etapa anterior para fundirlos en el ejecutable de la aplicación. Para
esta tarea se utiliza el comando {\tt ld}\index{ld}.
\end{description}
Por fortuna para nosotros {\tt gcc} llama por defecto a {\tt ld},
ahorrándonos tener que realizar los dos pasos a mano. Como ejemplo
tomemos el siguiente programa:
\begin{ejemplo}{holamundo.c}{Ejemplo de programa en C} Ejemplo de un
sencillo programa en C para compilar y enlazar. El programa muestra un
mensaje por la salida estándar y termina.
\end{ejemplo}
Y ejecutemos el comando:
\begin{verbatim}
$ gcc holamundo.c
\end{verbatim}
Si listásemos el contenido del directorio actual con el comando {\tt ls}
veríamos un nuevo archivo con el nombre de {\tt a.out}\index{a.out}. Ese
archivo contiene nuestro programa y puede ser fácilmente ejecutado.
\begin{verbatim}
$ ./a.out
El que a buen árbol se arrima, buena sombra le cobija.
\end{verbatim}
Es probable que pocos programadores consideren que {\tt a.out} sea
un nombre apropiado para el ejecutable de su aplicación. Con el
fin de cambiar dicho nombre se puede utilizar la opción {\tt -o
nombre\_ejecutable}.
\begin{verbatim}
$ gcc -o holamundo holamundo.c
$ ./holamundo
El que a buen árbol se arrima, buena sombra le cobija.
\end{verbatim}
Como podemos apreciar, el ejecutable de nuestra aplicación ahora se
denomina {\tt holamundo}.
Una práctica muy habitual en programación es dividir nuestro código
en varios archivos, cada uno especializado en un tarea determinada.
Supongamos que disponemos del siguiente programa:
\begin{ejemplo}{holamain.c}{Ejemplo de programa en varios archivos}
Definición de la función {\tt main()}; punto de entrada al ejemplo de un
sencillo programa en C en varios archivos. La función llama a {\tt
holafunc()}, definida en {\tt holafunc.c}, para que muestre un mensaje
por la salida estándar.
\end{ejemplo}
\begin{ejemplo}{holafunc.h}{Ejemplo de programa en varios archivos}
Declaración de la función {\tt holafunc()} encargada de mostrar un
mensaje por la salida estándar.
\end{ejemplo}
\begin{ejemplo}{holafunc.c}{Ejemplo de programa en varios archivos}
Definición de la función {\tt holafunc()} encargada de mostrar un
mensaje por la salida estándar.
\end{ejemplo}
Para generar nuestro programa sólo debemos listar los archivos que lo
forman en la línea de comandos del {\tt gcc}.
\begin{verbatim}
$ gcc -o holamundo holamain.c holafunc.c
$ ./holamundo
El que a buen árbol se arrima, buena sombra le cobija.
\end{verbatim}
\section{Enlazado con bibliotecas externas}
Como hemos comentado anteriormente, {\tt gcc} compila cada unos de los
archivos de código fuente pasados en la línea de comandos. El resultado
es un archivo de código objeto por cada archivo de código fuente. Dichos
archivos de código objeto son enlazados por {\tt ld} para generar el
ejecutable de nuestra aplicación. Cuando {\tt gcc} llama a {\tt ld} no
solo se están enlazando a nuestro programa el código objeto de {\tt
holamain.c} y el de {\tt holafunc.c}. El compilador sabe que para que
nuestro programa funcione es necesario que esté enlazado a una serie de
bibliotecas estándar del sistema, así que las incluye automáticamente.
Una de esas bibliotecas es la {\sf Biblioteca GNU Estándar de C (GLIBC,
GNU C Library)}, comunmente conocida como {\tt libc}\index{libc}.
Podemos acceder a la documentación de dicha biblioteca a través del
comando:
\begin{verbatim}
$ info libc
\end{verbatim}
Funciones como {\tt fopen()}, {\tt malloc()}, {\tt printf()} y en
general todas las del C estándar y muchas más se definen en la {\tt
libc}. Evidentemente en nuestro sistema existen bibliotecas para toda
clase de tareas cuya documentación se puede obtener recurriendo a la
ayuda del sistema. Sin embargo, la mayor parte de esas bibliotecas no se
enlazan automáticamente, así que nos queda la duda de cómo resolver este
problema. Por ejemplo vamos a modificar {\tt holafunc.c}:
\begin{ejemplo}{holafuncm.c}{Ejemplo de progrma que requiere la
biblioteca de funciones matemáticas} Definición de la función {\tt
holafunc()} encargada de mostrar un mensaje por la salida estándar y de
calcular el coseno de 0.5 radianes. \end{ejemplo}
Si compilamos {\tt holamundo} veremos el siguiente mensaje de error:
\begin{verbatim}
$ gcc -o holamundo holamain.c holafuncm.c
/tmp/ccKSzM6q.o: In function `holafunc':
/tmp/ccKSzM6q.o(.text+0x30): undefined reference to `cos'
collect2: ld returned 1 exit status
\end{verbatim}
Ese mensaje nos indica que la función {\tt cos()} llamada desde {\tt
holafunc()} no está definida puesto que se encuentra en una biblioteca
que no está siendo enlazada al programa. Para resolver el problema se
emplea la opción {\tt -lnombre\_biblioteca}, con la que se indica la
biblioteca adicional que debe ser enlazada al programa.
\label{holafuncm}
\begin{verbatim}
$ gcc -lm -o holamundo holamain.c holafuncm.c
$ ./holamundo
El que a buen árbol se arrima, buena sombra lo cobija,
y el coseno de 0.500000 es 0.877583.
\end{verbatim}
Al especificar {\tt -lm} se enlaza la biblioteca {\tt libm} (como
vemos no hace falta poner el prefijo {\tt lib} cuando se indica el
nombre de la biblioteca) que contiene la definición de {\tt cos()}. Es
importante destacar que el enlazador sólo busca bibliotecas en una serie
de directorios estándar de nuestro sistema. Por ejemplo, {\tt libm} se
encuentra en {\tt /usr/lib/} que es un directorio estándar. Si deseamos
enlazar bibliotecas situadas en otros directorios, como por ejemplo el
directorio actual, debemos usar la opción {\tt -Lruta\_biblioteca}. Por
ejemplo, el siguiente comando enlaza a nuestro programa una biblioteca
de nombre {\tt libpropia} que se encuentra en el directorio actual:
\begin{verbatim}
$ gcc -L. -lm -lpropia -o holamundo holamain.c holafuncm.c
\end{verbatim}
\section{Separar compilación y enlazado}
En caso de que prefiramos separar la etapa de compilación y enlazado, se
utiliza la opción {\tt -c} con el compilador {\tt gcc}. De esa manera le
informamos de que sólo queremos que genere el código objeto. Probemos a
generar el código objeto para cada archivo:
\begin{verbatim}
$ gcc -c holamain.c holafuncm.c
\end{verbatim}
Ahora tenemos dos nuevos archivos denominados {\tt holamain.o} y
{\tt holafuncm.o} que se corresponden con el código objeto de {\tt
holamain.c} y {\tt holafuncm.c} respectivamente.
Para enlazar, sólo debemos especificar los archivos de código objeto en
la línea de comandos del {\tt gcc}.
\begin{verbatim}
$ gcc -lm -o holamundo holamain.o holafuncm.o
\end{verbatim}
La parte positiva de esto es que ahora podemos enlazar en nuestra
aplicación código objeto generado por otros lenguajes, como {\tt
Fortran} (véase \ref{fortran+c}) o {\tt Pascal}. Además, nos permite
compilar sólo aquellos archivos de código fuente que han cambiado desde
la última compilación. Esto no parece importante cuando trabajamos en
proyectos pequeños con apenas un par de archivos. Pero es fundamental a
la hora de desarrollar programas de mayor envergadura.
También es posible utilizar directamente {\tt ld}, en lugar de
ejecutar {\tt gcc} y que éste llame al primero. El problema es que
debemos indicarle a mano al {\tt ld} que enlace a nuestro programa
las bibliotecas estándar del sistema, puesto que él no lo hace
automáticamente. Ya que {\tt gcc} hace dicho trabajo por nosotros,
evitaremos entrar en más detalles.
\section{Bibliotecas de enlace dinámico}
Un uso interesante del {\tt ld} es para generar nuestras propias
bibliotecas de enlace dinámico. Ahora que disponemos de un archivo {\tt
holafuncm.o} ejecutemos lo siguiente:
\begin{verbatim}
$ ld -shared -lm -o libholafunc.so holafuncm.o
\end{verbatim}
Aunque también es posible, y recomendable, invocar al {\tt gcc} para que
realice el mismo trabajo.
\begin{verbatim}
$ gcc -shared -lm -o libholafunc.so holafuncm.o
\end{verbatim}
En ambos casos, si listamos el contenido del directorio observaremos un
nuevo archivo denominado {\tt libholafunc.so} que es nuestra biblioteca
de enlace dinámico. Dicha biblioteca puede ser utilizada por cualquier
aplicación del sistema, si la enlazamos como hemos aprendido.
\begin{verbatim}
$ gcc -L. -lholafunc -o holamundo holamain.o
\end{verbatim}
A diferencia de ejemplos anteriores, nuestro programa no funcionará si
no disponemos de {\tt libholafunc.so}. Sin embargo, cualquier programa
futuro podrá utilizar las funciones de nuestra biblioteca.
El inconveniente de crear bibliotecas de enlace dinámico es que hay
que instalarlas en algún directorio donde el enlazador dinámico
({\tt ld.so}) sepa que hay bibliotecas (p.ej. {\tt /usr/lib}), pero
la labor de instalar o desinstalar bibliotecas en el sistema, así
como la de configurar {\tt ld.so} para que las busque es del {\tt
root}. Por ello, si ejecutáramos ahora nuestro programa éste no
funcionaría, puesto que sería incapaz de encontrar {\tt libholafunc.so}.
Para que esto no sea así debemos definir la variable de entorno
\$LD\_LIBRARY\_PATH\index{\$LD\_LIBRARY\_PATH} con la ruta de nuestra
biblioteca.
\begin{verbatim}
$ export LD_LIBRARY_PATH=./
$ ./holamundo
El que a buen árbol se arrima, buena sombra le cobija,
y el coseno de 0.500000 es 0.877583.
\end{verbatim}
La forma en la que hemos generado nuestra biblioteca de enlace dinámico
no suele dar problemas en sistemas GNU/Linux bajo plataforma Intel x86.
Sin embargo, no es el procedimiento recomendado. Lo ideal, con el fin
de garantizar la compatibilidad entre plataformas, es que compilemos
el código fuente de nuestra biblioteca de enlace dinámico con la
opción {\tt -fPIC}. Dicha opción fuerza al compilador a generar código
independiente de la posición. De esa manera la biblioteca puede ser
cargada en cualquier punto del espacio de direcciones de la memoria de
las aplicaciones que la utilizan. Por tanto, el procedimiento estándar
para generar nuestro programa queda de la siguiente manera:
\begin{verbatim}
$ gcc -c holamain.c
$ gcc -fPIC -c holafuncm.c
$ gcc -shared -lm -o libholafunc.so holafuncm.o
$ gcc -L. -lholafunc -o holamundo holamain.o
\end{verbatim}
O, en caso de no querer conservar los archivos de código objeto, podemos
hacerlo de la siguiente manera:
\begin{verbatim}
$ gcc -fPIC -shared -lm -o libholafunc.so holafuncm.c
$ gcc -L. -lholafunc -o holamundo holamain.c
\end{verbatim}
\section{¿Y que pasa con el C++?}
A la hora de compilar código en C++ (habitualmente con extensiones
{\tt *.C, *.cc, *.cpp, *.c++, *.cp, *.cxx}) se utiliza el comando
{\tt g++}\index{g++}. Básicamente se encarga de ejecutar el {\tt
gcc} habilitando el C++ como lenguaje por defecto, y añadiendo las
bibliotecas estándar del C++ en la fase de enlazado. Por tanto, todo lo
explicado para {\tt gcc} es aplicable para el {\tt g++}. Y si no, veamos
el siguiente código de ejemplo:
\begin{ejemplo}{holamain.cc}{Ejemplo de programa en C++}
Definición de la función {\tt main()}; punto de entrada al ejemplo de un
sencillo programa en C++. La función crea un objeto de clase {\tt
saludo}, definida en {\tt saludo.cc}, para que muestre un mensaje por la
salida estándar.
\end{ejemplo}
\begin{ejemplo}{saludo.h}{Ejemplo de programa en C++}
Declaración de la clase {\tt saludo} encargada de mostrar un mensaje por
la salida estándar.
\end{ejemplo}
\begin{ejemplo}{saludo.cc}{Ejemplo de programa en C++}
Definición de la clase {\tt saludo} encargada de mostrar un mensaje por
la salida estándar.
\end{ejemplo}
El cual se genera de forma semejante al caso en el que trabajamos en
lenguaje C.
\begin{verbatim}
$ g++ -o holamundo holamain.cc saludo.cc
$ ./holamundo
El que a buen árbol se arrima, buena sombra le cobija.
\end{verbatim}
Con todo esto se puede decir que ya estamos hechos unos {\em C/C++ Linux
Programmers}. Por lo que sólo queda navegar un poco, escoger el {\em
proyecto de software libre} que más nos guste, o disguste, y ponernos a
colaborar.