Ventajas y desventajas de los diferentes modos de transporte. Transporte logístico de transporte. Transporte internacional por carretera

Para utilizar correctamente gcc, el compilador de C estándar para Linux, es necesario conocer las opciones de la línea de comandos. Además, gcc extiende el lenguaje C. Incluso si tiene la intención de escribir su código fuente en el estándar ANSI de ese lenguaje, existen algunas extensiones de gcc que necesita conocer para comprender los archivos de encabezado de Linux.

La mayoría de las opciones de la línea de comandos son las mismas que utilizan los compiladores de C. No existen estándares para algunas opciones. En este capítulo, cubriremos las opciones más importantes que se utilizan en la programación diaria.

Intentar ajustarse al estándar ISO C es útil, pero como C es un lenguaje de bajo nivel, hay situaciones en las que las características estándar no son lo suficientemente expresivas. Hay dos áreas donde las extensiones gcc se usan ampliamente: interactuar con código ensamblador (estos temas se tratan en http://www.delorie.com/djgpp/doc/brennan/) y construir bibliotecas compartidas (consulte el Capítulo 8). Dado que los archivos de encabezado forman parte de bibliotecas compartidas, algunas extensiones también aparecen en los archivos de encabezado del sistema.

Por supuesto, hay muchas más extensiones que son útiles en cualquier otro tipo de programación y que realmente pueden ayudar con la codificación. Puede encontrar más información sobre estas extensiones en la documentación de gcc Texinfo.

5.1. opciones de gcc

gcc acepta muchas opciones de comando. Afortunadamente, el conjunto de opciones que realmente necesita tener en cuenta no es tan grande y en este capítulo lo veremos.

La mayoría de las opciones son iguales o similares a las de otros compiladores, gcc incluye una enorme documentación de sus opciones disponibles a través de info gcc (la página de manual de gcc también brinda esta información, sin embargo, las páginas de manual no se actualizan con tanta frecuencia como la documentación de Texinfo). ).

-o nombre de archivo Especifica el nombre del archivo de salida. Por lo general, esto no es necesario si está compilando en un archivo objeto, es decir, el valor predeterminado es sustituir nombredearchivo.c por nombredearchivo.o. Sin embargo, si crea un ejecutable, de forma predeterminada (por razones históricas) se crea con el nombre a.out. Esto también es útil cuando desea colocar el archivo de salida en un directorio diferente.
-Con Se compila sin vincular el archivo fuente especificado en la línea de comando. Como resultado, se crea un archivo objeto para cada archivo fuente. Cuando se usa make, generalmente se invoca el compilador gcc para cada archivo objeto; De esta manera, en caso de error, es más fácil encontrar qué archivo no se pudo compilar. Sin embargo, si escribe comandos a mano, no es raro que se especifiquen varios archivos en una sola llamada a gcc. Si puede surgir ambigüedad al especificar varios archivos en la línea de comando, es mejor especificar solo un archivo. Por ejemplo, en lugar de gcc -c -o a.o a.c b.c , tiene sentido usar gcc -c -o a.o b.c .
-Dfoo Define macros de preprocesador en la línea de comando. Es posible que deba descartar caracteres que el shell trata como caracteres especiales. Por ejemplo, al definir una cadena, debe evitar el uso de los caracteres que terminan en la cadena " . Las dos formas más comunes son "-Dfoo="bar"" y -Dfoo=\"bar\" . La primera forma funciona mucho mejor si hay espacios en la cadena, porque el shell trata los espacios en blanco de una manera especial.
-yo directorio Agrega un directorio a la lista de directorios para buscar archivos incluidos.
-L directorio Agrega un directorio a la lista de directorios para buscar bibliotecas; gcc preferirá las bibliotecas compartidas a las estáticas, a menos que se especifique lo contrario.
-l foo Enlaces a la biblioteca lib foo. A menos que se especifique lo contrario, gcc prefiere vincular bibliotecas compartidas (lib foo .so) a bibliotecas estáticas (lib foo .a). El vinculador busca funciones en todas las bibliotecas enumeradas en el orden en que aparecen. La búsqueda finaliza cuando se encuentran todas las funciones requeridas.
-estático Enlaces solo con bibliotecas estáticas. Véase el capítulo 8.
-g, -ggdb Incluye información de depuración. La opción -g hace que gcc incluya información de depuración estándar. La opción -ggdb especifica incluir cantidad inmensa información que sólo el depurador gdb puede entender.
Si el espacio en disco es limitado o desea sacrificar alguna funcionalidad por la velocidad del enlace, debe usar -g. En este caso, es posible que necesite utilizar un depurador distinto de gdb. Para una depuración más completa, debe especificar -ggdb. En este caso, gcc se preparará tanto como sea posible. información detallada para bgd. Cabe señalar que, a diferencia de la mayoría de los compiladores, gcc incluye información de depuración en el código optimizado. Sin embargo, el seguimiento en el depurador de código optimizado puede ser complicado porque el tiempo de ejecución puede saltar y omitir partes del código que espera ejecutar. Sin embargo, es posible conseguir Buen rendimiento sobre cómo la optimización de los compiladores cambia la forma en que se ejecuta el código.
-O, -Encendido Hace que gcc optimice el código. De forma predeterminada, gcc realiza una pequeña optimización; al especificar un número (n), la optimización se lleva a cabo en un cierto nivel. El nivel de optimización más común es 2; actualmente el nivel de optimización más alto en la versión estándar de gcc es 3. Recomendamos usar -O2 o -O3; -O3 puede aumentar el tamaño de la aplicación, así que si eso es importante, prueba ambas. Si la memoria y el espacio en disco son importantes para su aplicación, también puede usar la opción -Os, que minimiza el tamaño del código a costa de un mayor tiempo de ejecución. gcc solo habilita funciones integradas cuando se aplica al menos una optimización mínima (-O).
-ansi Soporte en programas C de todos los estándares ANSI (X3.159-1989) o su equivalente ISO (ISO/IEC 9899:1990) (comúnmente denominado C89 o menos comúnmente C90). Tenga en cuenta que esto no garantiza el cumplimiento total del estándar ANSI/ISO.
La opción -ansi deshabilita las extensiones gcc que normalmente entran en conflicto con los estándares ANSI/ISO. (Debido al hecho de que estas extensiones son compatibles con muchos otros compiladores de C, esto no es un problema en la práctica). También define la macro __STRICT_ANSI__ (como se describe más adelante en este libro) que los archivos de encabezado utilizan para admitir una configuración conforme a ANSI/ISO. ambiente.
-pedante Muestra todas las advertencias y errores requeridos por el estándar de lenguaje ANSI/ISO C. Esto no proporciona un cumplimiento total con el estándar ANSI/ISO.
-Muro Habilita la generación de todas las advertencias de gcc, lo cual suele ser útil. Pero esto no incluye opciones que puedan resultar útiles en casos concretos. Se establecerá un nivel similar de granularidad para el analizador de pelusa de su código fuente; gcc le permite activar y desactivar manualmente cada advertencia del compilador. El manual de gcc describe todas las advertencias en detalle.
5.2. archivos de encabezado
5.2.1. largo largo

El tipo long long indica que el bloque de memoria es al menos tan grande como long. En Intel i86 y otras plataformas de 32 bits, long es de 32 bits, mientras que long es de 64 bits. En plataformas de 64 bits, los punteros y los longs toman 64 bits, y los long pueden tomar 32 o 64 bits según la plataforma. El tipo long long es compatible con el estándar C99 (ISO/IEC 9899:1999) y es una extensión C de larga data proporcionada por gcc.

5.2.2. Funciones integradas

Algunas partes de los archivos de encabezado de Linux (particularmente aquellas que son específicas de un sistema en particular) utilizan funciones integradas de manera muy extensiva. Son tan rápidos como las macros (sin costo para las llamadas a funciones) y brindan todo tipo de validación que está disponible en una llamada a funciones normal. El código que llama a funciones integradas debe compilarse con al menos una optimización mínima (-O) habilitada.

5.2.3. Palabras clave extendidas alternativas

En gcc, cada palabra clave extendida (palabras clave no definidas por el estándar ANSI/ISO) tiene dos versiones: ella misma palabra clave y una palabra clave rodeada a ambos lados por dos guiones bajos. Cuando el compilador se utiliza en modo estándar (normalmente cuando la opción -ansi está habilitada), las palabras clave extendidas normales no se reconocen. Entonces, por ejemplo, la palabra clave de atributo en un archivo de encabezado debe escribirse como __atributo__.

5.2.4. Atributos

La palabra clave extendida del atributo se utiliza para pasar más información sobre una función, variable o tipo declarado a gcc de la que permite el código ANSI/ISO C. Por ejemplo, el atributo alineado le dice a gcc exactamente cómo alinear una variable o tipo; el atributo empaquetado indica que no se utilizará ningún relleno; noreturn especifica que la función nunca regresa, lo que permite a gcc optimizar mejor y evitar advertencias falsas.

Los atributos de la función se declaran agregándolos a la declaración de la función, por ejemplo:

void die_die_die(int, char*) __attribute__ ((__noreturn__));

Una declaración de atributo se coloca entre paréntesis y un punto y coma y contiene la palabra clave del atributo seguida de los atributos entre paréntesis dobles. Si hay muchos atributos, se debe utilizar una lista separada por comas.

printm(carácter*, ...)

Atributo__((const,

formato(printf, 1, 2)));

En este ejemplo, puede ver que printm no considera ningún valor distinto de los especificados y no tiene efectos secundarios relacionados con la generación de código (const), printm indica que gcc debe verificar los argumentos de la función de la misma manera que printf() argumentos. El primer argumento es la cadena de formato y el segundo argumento es el primer parámetro de reemplazo (formato).

Algunos atributos se discutirán a medida que avance el material (por ejemplo, durante la descripción de la construcción de bibliotecas compartidas en el Capítulo 8). Puede encontrar información completa sobre los atributos en la documentación de gcc en formato Texinfo.

De vez en cuando, es posible que se encuentre revisando los archivos de encabezado de Linux. Lo más probable es que encuentre varios diseños que no cumplan con ANSI/ISO. Vale la pena investigar algunos de ellos. Todas las construcciones cubiertas en este libro se tratan con más detalle en la documentación de gcc.

De vez en cuando, es posible que se encuentre revisando los archivos de encabezado de Linux. Lo más probable es que encuentre varios diseños que no cumplan con ANSI/ISO. Vale la pena investigar algunos de ellos. Todas las construcciones cubiertas en este libro se tratan con más detalle en la documentación de gcc.

Ahora que ha aprendido un poco sobre el estándar C, echemos un vistazo a las opciones que ofrece el compilador gcc para garantizar que cumple con el estándar C del lenguaje en el que está escribiendo. Hay tres formas de garantizar que su código C cumpla con los estándares y esté libre de fallas: opciones que controlan a qué versión del estándar desea ajustarse, definiciones que controlan los archivos de encabezado y opciones de advertencia que activan una verificación de código más estricta.

gcc tiene un enorme conjunto de opciones y aquí solo cubriremos aquellas que consideramos más importantes. Puede encontrar una lista completa de opciones en las páginas de manual en línea de gcc. También discutiremos brevemente algunas de las opciones de la directiva #define que se pueden usar; normalmente deberían especificarse en su código fuente antes de cualquier línea #include, o definirse en la línea de comando gcc. Es posible que le sorprenda la abundancia de opciones para elegir qué estándar usar en lugar de una simple bandera para forzar el uso del estándar actual. La razón es que muchos programas antiguos dependen del comportamiento histórico del compilador y requerirían un trabajo importante para actualizarlos a los últimos estándares. Rara vez, o nunca, querrás actualizar tu compilador para que rompa el código en ejecución. A medida que cambian los estándares, es importante poder trabajar con un estándar en particular, incluso si no es la versión más reciente del estándar.

Incluso si está escribiendo un programa pequeño para uso personal, donde el cumplimiento de los estándares puede no ser tan importante, a menudo tiene sentido incluir advertencias gcc adicionales para obligar al compilador a buscar errores en su código antes de que se ejecute el programa. Esto siempre es más eficiente que revisar el código en el depurador y preguntarse dónde podría estar el problema. El compilador tiene muchas opciones que van más allá de la simple verificación de estándares, como la capacidad de detectar código que se ajusta a un estándar pero que posiblemente tenga una semántica cuestionable. Por ejemplo, un programa puede tener un orden de ejecución que permita acceder a una variable antes de que se inicialice.

Si necesita escribir un programa para uso compartido, dado el grado de conformidad y los tipos de advertencias del compilador que cree que son suficientes, es muy importante esforzarse un poco más y lograr que su código se compile sin ninguna advertencia. Si acepta algunas advertencias y adquiere el hábito de ignorarlas, un día puede aparecer una advertencia más seria que corre el riesgo de pasar por alto. Si su código siempre se compila sin mensajes de advertencia, es probable que le llame la atención una nueva advertencia. Compilar código sin previo aviso es un buen hábito que se puede adoptar.

Opciones del compilador para estándares de seguimiento

Ansi es la opción más importante en cuanto a estándares y hace que el compilador actúe según el estándar de lenguaje ISO C90. Deshabilita algunas extensiones gcc que no cumplen con los estándares, deshabilita los comentarios de estilo C++ (//) en programas C y habilita el manejo de trígrafos ANSI (secuencias de tres caracteres). Además, contiene la macro __STRICT_ANSI__ , que deshabilita algunas extensiones en archivos de encabezado que no son compatibles con el estándar. En futuras versiones del compilador, el estándar aceptado puede cambiar.

Std=: esta opción proporciona un control más preciso sobre qué estándar utilizar al proporcionar un parámetro que especifica exactamente qué estándar se requiere. Las siguientes son las principales opciones disponibles:

C89: admite el estándar C89;

Iso9899:1999: admite la última versión del estándar ISO, C90;

Gnu89: admite el estándar C89 pero permite algunas extensiones GNU y algunas funciones C99. En la versión 4.2 de gcc, esta opción es la predeterminada.

Opciones para el seguimiento del estándar en directivas definidas

Hay constantes (#defines) que se pueden especificar como opciones en la línea de comando o como definiciones en el código fuente del programa. Generalmente pensamos que usan la línea de comando del compilador.

STRICT_ANSI__ Fuerza el uso del estándar ISO C. Se define cuando se proporciona la opción -ansi en la línea de comando del compilador.

POSIX_C_SOURCE=2: habilita la funcionalidad definida por IEEE Std 1003.1 y 1003.2. Volveremos a estos estándares más adelante en este capítulo.

BSD_SOURCE: habilita la funcionalidad de los sistemas BSD. Si entran en conflicto con las definiciones POSIX, las definiciones BSD tienen prioridad.

GNU_SOURCE: permite una amplia gama de propiedades y funciones, incluidas extensiones GNU. Si estas definiciones entran en conflicto con las definiciones POSIX, estas últimas tienen prioridad.

Opciones del compilador para generar advertencias

Estas opciones se pasan al compilador desde la línea de comando. Y nuevamente, enumeraremos solo los principales, Lista llena se puede encontrar en el manual de ayuda en línea de gcc.

Pedantic es la opción de limpieza más poderosa para el código C. Además de habilitar la opción de compararlo con el estándar C, deshabilita algunas construcciones tradicionales de C que están prohibidas por el estándar y hace que todas las extensiones GNU sean ilegales del estándar. Esta opción debe usarse para hacer que su código C sea lo más portátil posible. La desventaja es que el compilador está muy preocupado por la limpieza de su código y, a veces, tiene que devanarse los sesos para deshacerse de algunas advertencias restantes.

Wformat: comprueba la exactitud de los tipos de argumentos de funciones de la familia printf.

Wparéntesis: busca paréntesis, incluso cuando no son necesarios. Esta opción es muy útil para comprobar que las estructuras complejas se inicializan según lo previsto.

wswitch-default: comprueba la presencia de la variante predeterminada en las declaraciones de cambio, lo que generalmente se considera un buen estilo de programación.

Wunused: comprueba una variedad de casos, como funciones estáticas declaradas pero no declaradas, parámetros no utilizados y resultados descartados.

Muro: habilita la mayoría de los tipos de advertencias de gcc, incluidas todas las opciones -W anteriores (solo -pedantic no está cubierto). Con su ayuda, es fácil lograr la limpieza del código del programa.

Nota

Hay muchas más opciones de advertencia avanzadas; consulte las páginas web de gcc para obtener todos los detalles. En general, recomendamos usar -Wall; este es un buen compromiso entre verificar que el código del programa Alta calidad y la necesidad de que el compilador genere muchas advertencias triviales que resultan difíciles de anular.

GCC está incluido en cada distribución. Linux y normalmente está configurado de forma predeterminada. La interfaz GCC es una interfaz de compilación estándar en la plataforma UNIX, que se remonta a finales de los años 60 y principios de los 70 del siglo pasado: una interfaz de línea de comandos. No tengas miedo, en el pasado el mecanismo de interacción del usuario se ha perfeccionado a la perfección posible en este caso, y trabaja con GCC (con algunas utilidades adicionales y una buena editor de texto) es más fácil que con cualquier IDE visual moderno. Los autores del kit intentaron automatizar al máximo el proceso de compilación y montaje de aplicaciones. El usuario llama al programa de control. gcc, interpreta los argumentos de la línea de comando pasados ​​(opciones y nombres de archivos) y para cada archivo de entrada, de acuerdo con el lenguaje de programación utilizado, ejecuta su propio compilador y luego, si es necesario, gcc llama automáticamente al ensamblador y al enlazador (enlazador).

Curiosamente, los compiladores son una de las pocas aplicaciones UNIX que se preocupan por las extensiones de archivos. Por extensión, GCC determina qué tipo de archivo se encuentra frente a él y qué se debe (o se puede) hacer con él. Archivos fuente de idioma C debe tener la extensión .c , en el idioma C++, alternativamente, .cpp , archivos de encabezado en el idioma C.h, .o archivos de objetos, etc. Si usas la extensión incorrecta, gcc no funcionará correctamente (si acepta hacer algo).

Pasemos a la práctica. Escribamos, compilemos y ejecutemos algún programa simple. No seamos originales, ya que el archivo fuente de un programa de ejemplo en el lenguaje C Creemos un archivo con el siguiente contenido:

/* Hola C */

#incluir

Principal( vacío )
{

Printf("Hola mundo\n");

devolver 0 ;

Ahora en el directorio c hello.c emitimos el comando:

$ gcc hola.c

Después de unas fracciones de segundo, aparecerá el archivo a.out en el directorio:

$ls
a.out hola.c

Este es el archivo ejecutable terminado de nuestro programa. Por defecto gcc le da al ejecutable de salida el nombre a.out (érase una vez este nombre significaba salida del ensamblador).

$archivo a.out
a.out: ejecutable ELF LSB de 64 bits, x86-64, versión 1 (SYSV), vinculado dinámicamente (usa bibliotecas compartidas), para GNU/Linux 2.6.15, no eliminado

Ejecutemos el resultado. software:

$ ./a.salida
Hola Mundo


¿Por qué es necesario especificar explícitamente la ruta al archivo en el comando de ejecución para ejecutar un archivo desde el directorio actual? Si la ruta al archivo ejecutable no se especifica explícitamente, el shell, al interpretar los comandos, busca el archivo en los directorios, cuya lista está especificada por la variable del sistema PATH.

$ eco $RUTA
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/juegos

Los directorios de la lista están separados por dos puntos. Al buscar archivos, el shell busca en los directorios en el orden en que aparecen. De forma predeterminada, por razones de seguridad, el directorio actual es . no está incluido en la lista; en consecuencia, el shell no buscará archivos ejecutables en ella.

¿Por qué no se recomienda hacer? en RUTA? Se cree que en un sistema multiusuario real siempre habrá alguna persona mala que colocará un programa malicioso en el directorio público con un nombre de archivo ejecutable que coincide con el nombre de algún comando que a menudo llama un administrador local con superusuario. derechos... La trama tendrá éxito si. está al principio del listado del directorio.


Utilidad archivo muestra información sobre el tipo (desde el punto de vista del sistema) del archivo pasado en la línea de comando, para algunos tipos de archivos muestra todo tipo de información adicional respecto del contenido del expediente.

$archivo hola.c
hola.c: texto del programa ASCII C
$anotación de archivo.doc
anotación.doc: documento CDF V2, Little Endian, sistema operativo: Windows, versión 5.1, página de códigos: 1251, autor: MIH, plantilla: Normal.dot, último guardado por: MIH, número de revisión: 83, nombre de la aplicación de creación: Microsoft Office Word, tiempo total de edición: 09:37:00, última impresión: jueves 22 de enero 07:31:00 2009, hora y fecha de creación: lunes 12 de enero a las 07:36:00 de 2009, hora y fecha del último guardado: jueves 22 de enero 07:34:00 2009, Número de páginas: 1, Número de palabras: 3094, Número de caracteres: 17637, Seguridad: 0

En realidad, eso es todo lo que se requiere del usuario para que la solicitud sea exitosa. gcc :)

El nombre del archivo ejecutable de salida (así como cualquier otro archivo generado por gcc) se puede cambiar con opciones -o:

$ gcc -o hola hola.c
$ls
hola hola.c
$ ./hola
Hola Mundo


En nuestro ejemplo, la función main() devuelve el valor aparentemente innecesario 0 . En sistemas tipo UNIX, al final del programa, se acostumbra devolver un número entero al shell; en caso de finalización exitosa, cero, en caso contrario, cualquier otro. El intérprete de shell asignará automáticamente el valor resultante a una variable de entorno denominada? . Puedes ver su contenido usando el comando echo $? :

$ ./hola
Hola Mundo
$ eco $?
0

Se dijo arriba que gcc es un programa de control diseñado para automatizar el proceso de compilación. Veamos qué sucede realmente como resultado de ejecutar el comando gcc hello.c.

El proceso de compilación se puede dividir en 4 etapas principales: procesamiento del preprocesador, compilación real, ensamblaje y vinculación.

Opciones gcc le permitirá interrumpir el proceso en cualquiera de estas etapas.

El preprocesador prepara el archivo fuente para la compilación: elimina los comentarios, agrega el contenido de los archivos de encabezado (directiva del preprocesador #include), implementa la expansión de macros (constantes simbólicas, directiva del preprocesador #define).

Tomar ventaja -E opción otras acciones gcc puede interrumpir y ver el contenido del archivo procesado por el preprocesador.

$ gcc -E -o hola.i hola.c
$ls
hola.c hola.i
$ menos hola.i
. . .
# 1 "/usr/include/stdio.h" 1 3 4
# 28 "/usr/include/stdio.h" 3 4
# 1 "/usr/include/features.h" 1 3 4
. . .
typedef char sin firmar __u_char;
typedef unsigned short int __u_short;
typedef unsigned int __u_int;
. . .
extern int printf (__const char *__restrict __format, ...);
. . .
#4 "hola.c" 2
principal (vacío)
{
printf("Hola mundo\n");
devolver 0;
}

Después del procesamiento por parte del preprocesador, el texto fuente de nuestro programa se hinchó y adquirió un formato ilegible. El código que una vez escribimos con nuestras propias manos se redujo a unas pocas líneas al final del archivo. El motivo es la inclusión del archivo de encabezado de la biblioteca estándar. C. El archivo de encabezado stdio.h contiene muchas cosas diferentes y también requiere la inclusión de otros archivos de encabezado.

Observe la extensión del archivo hola.i. Por acuerdos gcc la extensión .i corresponde a archivos con código fuente en el idioma C no requiere procesamiento previo al procesador. Estos archivos se compilan sin pasar por el preprocesador:

$ gcc -o hola hola.i
$ls
hola hola.c hola.i
$ ./hola
Hola Mundo

Después del preprocesamiento llega el turno de la compilación. El compilador convierte el código fuente del programa en un lenguaje de alto nivel en código en lenguaje ensamblador.

El significado de la palabra compilación es vago. Los wikipedistas, por ejemplo, consideran que, refiriéndose a estándares internacionales esa compilación es "transformación por el programa compilador código fuente cualquier programa escrito en un lenguaje de programación de alto nivel, en un lenguaje cercano al código de máquina o en código objeto". En principio, esta definición nos conviene, el lenguaje ensamblador está realmente más cerca del lenguaje de máquina que C. Pero en la vida cotidiana, la compilación suele entenderse simplemente como cualquier operación que convierte el código fuente de un programa en cualquier lenguaje de programación en código ejecutable. Es decir, un proceso que incluye los cuatro pasos anteriores también puede denominarse compilación. Una ambigüedad similar está presente en el presente texto. Por otro lado, la operación de convertir el texto fuente de un programa a código en lenguaje ensamblador también puede denotarse con la palabra traducción: "transformación de un programa presentado en uno de los lenguajes de programación en un programa en otro lenguaje y, en cierto sentido, equivalente al primero."

Detener el proceso de creación de un archivo ejecutable al final de la compilación permite -S opción:

$ gcc -S hola.c
$ls
hola.c hola.s
$archivo hola.s
hola.s: texto del programa ensamblador ASCII
$ menos hola.s
.archivo "hola.c"
.sección .rodata
.LC0:
.string "Hola mundo"
.texto
.glob principal
.tipo principal, @función
principal:
empujar %ebp
movl %esp, %ebp
yl $-16, %esp
subl $16, %esp
movl $.LC0, (%esp)
llamada pone
movimiento $0, %eax
dejar
retirado
.tamaño principal, .-principal


En el directorio apareció el archivo hello.s, que contiene la implementación del programa en lenguaje ensamblador. Tenga en cuenta que especificar el nombre del archivo de salida con opciones -o en este caso no fue necesario gcc lo generó automáticamente reemplazando la extensión .c con .s en el nombre del archivo fuente. Para la mayoría de las operaciones básicas gcc el nombre del archivo de salida se forma mediante dicha sustitución. La extensión .s es estándar para archivos fuente en lenguaje ensamblador.

Por supuesto, también puedes obtener el código ejecutable del archivo hello.s:

$ gcc -o hola hola.s
$ls
hola hola.c hola.s
$ ./hola
Hola Mundo

La siguiente etapa de la operación ensambladora es la traducción del código del lenguaje ensamblador al código de máquina. El resultado de la operación es un archivo objeto. Un archivo objeto contiene bloques de código de máquina listos para su ejecución, bloques de datos y una lista de funciones y variables externas definidas en el archivo ( tabla de símbolos ), pero al mismo tiempo no especifica las direcciones absolutas de las referencias a funciones y datos. Un archivo objeto no se puede ejecutar directamente, pero posteriormente (en la etapa de vinculación) se puede combinar con otros archivos objeto (en este caso, de acuerdo con las tablas de símbolos, se calcularán y completarán las direcciones de las referencias cruzadas existentes entre archivos). ). Opción gcc-c , detiene el proceso al final del paso de ensamblaje:

$ gcc -c hola.c
$ls
hola.c hola.o
$archivo hola.o
hola.o: ELF LSB de 64 bits reubicable, x86-64, versión 1 (SYSV), no desmontado

Los archivos objeto utilizan la extensión estándar .o.

Si el archivo objeto recibido hello.o se pasa al vinculador, este último calculará las direcciones de los enlaces, agregará el código para iniciar y finalizar el programa, el código para llamar a las funciones de la biblioteca y, como resultado, tendremos un archivo listo. -Archivo ejecutable creado del programa.

$ gcc -o hola hola.o
$ls
hola hola.c hola.o
$ ./hola
Hola Mundo

Lo que hemos hecho ahora (o más bien gcc hizo por nosotros) y está el contenido de la última etapa: vincular (vincular, vincular).

Bueno, quizás sobre la compilación y todo eso. Ahora veamos algunas opciones, en mi opinión, importantes. gcc.

Opción -I ruta/al/directorio/con/encabezado/archivos - agrega el directorio especificado a la lista de rutas de búsqueda para archivos de encabezado. Directorio agregado por opción -I Se busca primero y luego la búsqueda continúa en los directorios estándar del sistema. si opciones -I múltiples, los directorios que especifican se escanean de izquierda a derecha a medida que aparecen las opciones.

-Opción de pared- muestra avisos provocados por posibles errores en el código que no impiden la compilación del programa, pero que, según el compilador, pueden provocar ciertos problemas durante su ejecución. Una opción importante y útil, desarrolladores. gcc Recomiendo usarlo siempre. Por ejemplo, se emitirán muchas advertencias al intentar compilar dicho archivo:

1 /* comentario.c */
2
3 estático En t k = 0
4 estático En t yo( En t a);
5
6 principal()
7 {
8
9 En t a;
10
11 En t antes de Cristo;
12
13b+1;
14
15b=c;
16
17 En t*pag;
18
19b = *p;
20
21 }


$ gcc -o comentario comentario.c
$ gcc -Wall -o comentario comentario.c
comentario.c:7: advertencia: el tipo de retorno por defecto es 'int'

comentario.c:13: advertencia: declaración sin efecto
comentario.c:9: advertencia: variable no utilizada 'a'
comentario.c:21: advertencia: el control llega al final de la función no anulada
comentario.c: En el nivel superior:
comentario.c:3: advertencia: 'k' definida pero no utilizada
comentario.c:4: advertencia: 'l' declarado 'estático' pero nunca definido
comentario.c: En la función 'principal':
comentario.c:15: advertencia: 'c' se usa sin inicializar en esta función
comentario.c:19: advertencia: 'p' se usa sin inicializar en esta función

Opción -Error- convierte todas las advertencias en errores. Aborta el proceso de compilación si se produce una advertencia. Utilizado junto con -Opción de pared.

$ gcc -Werror -o comentario comentario.c
$ gcc -Werror -Wall -o comentario comentario.c
cc1: las advertencias se tratan como errores
comentario.c:7: error: el tipo de retorno por defecto es 'int'
comentario.c: En la función 'principal':
comentario.c:13: error: declaración sin efecto
comentario.c:9: error: variable no utilizada 'a'

Opción -g- coloca la información necesaria para que el depurador funcione en un objeto o archivo ejecutable gdb. Al construir un proyecto con el fin de depurarlo posteriormente, opción -g debe incluirse tanto en el momento de la compilación como en el momento del enlace.

Opciones -O1, -O2, -O3- establecer el nivel de optimización del código generado por el compilador. A medida que aumenta el número, aumenta el grado de optimización. La acción de las opciones se puede ver en este ejemplo.

Archivo original:

/* círculo.c */

Principal( vacío )
{

En t i;

para(yo = 0 ; yo< 10 ; ++i)
;

devolver i;

Compilando con el nivel de optimización predeterminado:

$ gcc -S círculo.c
$ menos círculos
.archivo "círculo.c"
.texto
.glob principal
.tipo principal, @función
principal:
empujar %ebp
movl %esp, %ebp
subl $16, %esp
movimiento $0, -4(%ebp)
jmp .L2
.L3:
agregar $1, -4(%ebp)
.L2:
cmpl $9, -4(%ebp)
jle .L3
movl -4(%ebp), %eax
dejar
retirado
.tamaño principal, .-principal
.ident "GCC: (Ubuntu 4.4.3-4ubuntu5) 4.4.3"
.sección .nota.GNU-stack,"",@progbits

Compilación con máximo nivel de optimización:

$ gcc -S -O3 círculo.c
$ menos círculos
.archivo "círculo.c"
.texto
.p2alinear 4.15
.glob principal
.tipo principal, @función
principal:
empujar %ebp
movimiento $10, %eax
movl %esp, %ebp
población % ebp
retirado
.tamaño principal, .-principal
.ident "GCC: (Ubuntu 4.4.3-4ubuntu5) 4.4.3"
.sección .nota.GNU-stack,"",@progbits

En el segundo caso, no hay ni siquiera un indicio de ningún ciclo en el código resultante. De hecho, el valor de i se puede calcular en la etapa de compilación, lo cual se hizo.

Desgraciadamente, en proyectos reales, la diferencia de rendimiento en diferentes niveles de optimización es casi imperceptible...

Opción -O0- cancela cualquier optimización de código. La opción es obligatoria en la etapa de depuración de la aplicación. Como se muestra arriba, la optimización puede conducir a un cambio en la estructura del programa más allá del reconocimiento, la conexión entre el ejecutable y el código fuente no será explícita, respectivamente, la depuración paso a paso del programa no será posible. Cuando la opción está habilitada -gramo, se recomienda incluir y -O0.

-Os opción- especifica la optimización no por la eficiencia del código, sino por el tamaño del archivo resultante. El rendimiento del programa debe ser comparable al rendimiento del código obtenido al compilarlo con el nivel de optimización predeterminado.

Opción -marcha=arquitectura- especifica la arquitectura de destino del procesador. La lista de arquitecturas soportadas es extensa, por ejemplo, para procesadores de la familia Intel/AMD puedes configurar i386, pentium, prescott, opteron-sse3 etc. Los usuarios de distribuciones binarias deben tener en cuenta que para que los programas con la opción especificada funcionen correctamente, es deseable que todas las bibliotecas incluidas estén compiladas con la misma opción.

Las opciones pasadas al vinculador se analizarán a continuación.

Pequeña adición:

Se dijo arriba que gcc determina el tipo (lenguaje de programación) de los archivos transferidos por su extensión y, de acuerdo con el tipo (lenguaje) adivinado, realiza acciones sobre ellos. El usuario está obligado a controlar las extensiones de los archivos creados, eligiéndolas según lo requerido por los acuerdos. gcc. De hecho gcc puedes poner archivos con nombres arbitrarios. opción gcc -x le permite especificar explícitamente el lenguaje de programación de los archivos compilados. La acción de la opción se aplica a todos los archivos posteriores enumerados en el comando (hasta la aparición de la siguiente opción -X). Posibles argumentos de opción:

c c-encabezado c-cpp-salida

c++ c++-encabezado c++-cpp-salida

objetivo-c objetivo-c-encabezado objetivo-c-cpp-salida

objetivo-c++ objetivo-c++-encabezado objetivo-c++-cpp-salida

ensamblador ensamblador-con-cpp

ada

f77 f77-cpp-entrada

f95 f95-cpp-entrada

Java

El propósito de los argumentos debe quedar claro en su redacción (aquí cpp no tiene nada que ver con C++, este es un archivo de código fuente preprocesado por el preprocesador). Vamos a revisar:

$mv hola.c hola.txt
$ gcc -Wall -x c -o hola hola.txt
$ ./hola
Hola Mundo

Compilación separada

Punto fuerte de los idiomas. C/C++ es la capacidad de dividir el código fuente del programa en varios archivos. Incluso se puede decir más: la posibilidad de una compilación separada es la base del lenguaje, sin ella uso efectivo C no es concebible. Es una programación de múltiples archivos que le permite implementar en C grandes proyectos como Linux(aquí bajo la palabra Linux tanto el núcleo como el sistema en su conjunto están implicados). ¿Qué le da una compilación separada al programador?

1. Le permite hacer que el código del programa (proyecto) sea más legible. El archivo fuente de varias docenas de pantallas se vuelve casi abrumador. Si, de acuerdo con alguna lógica (preestablecida), lo dividimos en varios fragmentos pequeños (cada uno en un archivo separado), será mucho más fácil hacer frente a la complejidad del proyecto.

2. Reduce el tiempo de recompilación del proyecto. Si se realizan cambios en un archivo, no tiene sentido recompilar todo el proyecto, basta con recompilar solo este archivo modificado.

3. Le permite distribuir el trabajo del proyecto entre varios desarrolladores. Cada programador crea y depura su parte del proyecto, pero en cualquier momento será posible recopilar (reconstruir) todos los desarrollos resultantes en el producto final.

4. Sin una compilación separada, no habría bibliotecas. A través de bibliotecas, reutilización y distribución de código a C/C++, y el código es binario, lo que permite, por un lado, proporcionar a los desarrolladores un mecanismo simple para incluirlo en sus programas y, por otro lado, ocultarles detalles específicos de implementación. Cuando se trabaja en un proyecto, ¿siempre vale la pena pensar en ello y no necesitar algo de lo que ya se ha hecho en el futuro? ¿Quizás valga la pena resaltar y organizar parte del código como biblioteca de antemano? En mi opinión, este enfoque simplifica enormemente la vida y ahorra mucho tiempo.

CCG, por supuesto, admite la compilación por separado y no requiere instrucciones especiales por parte del usuario. En general, todo es muy sencillo.

He aquí un ejemplo práctico (aunque muy, muy condicional).

Conjunto de archivos de código fuente:

/* C Principal */

#incluir

#incluir "primero.h"
#incluir "segundo.h"

En t principal( vacío )
{

Primero();
segundo();

Printf("Función principal...\n");

devolver 0 ;


/* primero.h */

vacío primero( vacío );


/* primero.c */

#incluir

#incluir "primero.h"

vacío primero( vacío )
{

Printf("Primera función...\n");


/* segundo.h */

vacío segundo( vacío );


/* segundo.c */

#incluir

#incluir "segundo.h"

vacío segundo( vacío )
{

Printf("Segunda función...\n");

En general tenemos esto:

$ls
primero.c primero.h principal.c segundo.c segundo.h

Toda esta economía se puede resumir en un solo comando:

$ gcc -Wall -o main main.c primero.c segundo.c
$ ./principal
Primera función...
Segunda función...
función principal...

Sólo que esto no nos dará prácticamente ninguna ventaja, bueno, a excepción de un código más estructurado y legible, repartido en varios archivos. Todas las ventajas enumeradas anteriormente aparecerán en el caso de este enfoque de compilación:

$ gcc -Pared -c principal.c
$ gcc -Pared -c primero.c
$ gcc -Pared -c segundo.c
$ls
primero.c primero.h primero.o principal.c principal.o segundo.c segundo.h segundo.o
$ gcc -o principal principal.o primero.o segundo.o
$ ./principal
Primera función...
Segunda función...
función principal...

¿Qué hemos hecho? De cada archivo fuente (compilando con la opción -C) recibió un archivo de objeto. Luego, los archivos objeto se vincularon al ejecutable final. Por supuesto comandos gcc hay más, pero nadie ensambla proyectos manualmente, existen utilidades de ensamblador para esto (la más popular hacer). Al utilizar las utilidades del ensamblador, se manifestarán todas las ventajas de la compilación separada enumeradas anteriormente.

Surge la pregunta: ¿cómo logra el enlazador juntar archivos objeto calculando correctamente el direccionamiento de la llamada? ¿Cómo sabe siquiera que el archivo second.o contiene el código de función second() y que el código del archivo main.o contiene su llamada? Resulta que todo es simple: en el archivo objeto hay un llamado tabla de símbolos , que incluye los nombres de algunas posiciones de código (funciones y variables externas). El vinculador examina la tabla de símbolos de cada archivo objeto, busca posiciones comunes (con nombres coincidentes), a partir de las cuales saca conclusiones sobre la ubicación real del código de las funciones utilizadas (o bloques de datos) y, en consecuencia, Vuelve a calcular las direcciones de llamada en el archivo ejecutable.

Puede ver la tabla de símbolos usando la utilidad Nuevo Méjico.

$nm principal.o
tu primero
00000000 T principal
tu pones
U segundo
$nm primero.o
00000000T primero
tu pones
$nm segundo.o
tu pones
00000000 T segundo

La llamada puts se debe al uso de la función de biblioteca estándar printf() , que se convirtió en puts() en el momento de la compilación.

La tabla de símbolos se escribe no sólo en el archivo objeto, sino también en el archivo ejecutable:

$ nm principal
08049f20d_DINÁMICO
08049ff4d _GLOBAL_OFFSET_TABLE_
080484fc R_IO_stdin_used
w _Jv_RegisterClasses
08049f10 d __CTOR_END__
08049f0cd __CTOR_LIST__
08049f18 D __DTOR_END__
08049f14 d __DTOR_LIST__
08048538r __FRAME_END__
08049f1cd __JCR_END__
08049f1c d __JCR_LIST__
0804a014 A __bss_inicio
0804a00c D __data_start
080484b0 t __do_global_ctors_aux
08048360 t __do_global_dtors_aux
0804a010 D __dso_handle
w __gmon_start__
080484aa T __i686.get_pc_thunk.bx
08049f0cd __init_array_end
08049f0cd __init_array_start
08048440T__libc_csu_fini
08048450 T __libc_csu_init
U __libc_start_main@@GLIBC_2.0
0804a014 A _edata
0804a01c A_end
080484dc T_fini
080484f8 R_fp_hw
080482b8 T_inicio
08048330 T_inicio
0804a014b completado.7021
0804a00c W inicio_datos
0804a018 bdtor_idx.7023
0804840c T primero
080483c0 t frame_dummy
080483e4 T principal
U pone@@GLIBC_2.0
08048420 T segundo

La inclusión de una tabla de símbolos en el ejecutable es particularmente necesaria para facilitar la depuración. En principio, no es realmente necesario para ejecutar la aplicación. Para ejecutables de programas reales, con muchas definiciones de funciones y variables externas, que involucran un montón de bibliotecas diferentes, la tabla de símbolos se vuelve bastante extensa. Para reducir el tamaño del archivo de salida, se puede eliminar usando opción gcc -s.

$ gcc -s -o principal principal.o primero.o segundo.o
$ ./principal
Primera función...
Segunda función...
función principal...
$ nm principal
nm: principal: sin símbolos

Cabe señalar que durante la vinculación, el vinculador no verifica el contexto de la llamada a la función, no monitorea el tipo de valor devuelto, ni el tipo y número de parámetros recibidos (y no tiene dónde obtenerlos). información de). Toda la validación de llamadas debe realizarse en tiempo de compilación. En el caso de la programación multiarchivo, es necesario utilizar para ello el mecanismo de los archivos de cabecera del lenguaje. C.

Bibliotecas

Biblioteca - en idioma C, un archivo que contiene código objeto que se puede adjuntar a un programa usando la biblioteca en la etapa de vinculación. De hecho, una biblioteca es una colección de archivos objeto especialmente vinculados.

El propósito de las bibliotecas es proporcionar al programador un mecanismo estándar para la reutilización de código, y el mecanismo es simple y confiable.

Desde el punto de vista del sistema operativo y del software de aplicación, las bibliotecas son estático Y compartido (dinámica ).

El código de la biblioteca estática se incluye en el archivo ejecutable durante la vinculación de este último. La biblioteca está "cableada" en el archivo, el código de la biblioteca está "fusionado" con el resto del código del archivo. Un programa que utiliza bibliotecas estáticas se vuelve autónomo y puede ejecutarse prácticamente en cualquier computadora con la arquitectura y el sistema operativo adecuados.

El código de la biblioteca compartida es cargado y vinculado al código del programa por el sistema operativo, a petición del programa durante su ejecución. El código de la biblioteca dinámica no está incluido en el archivo ejecutable del programa; solo se incluye un enlace a la biblioteca en el archivo ejecutable. Como resultado, un programa que utiliza bibliotecas compartidas ya no es independiente y sólo puede ejecutarse exitosamente en un sistema donde las bibliotecas involucradas estén instaladas.

El paradigma de biblioteca compartida proporciona tres beneficios importantes:

1. El tamaño del archivo ejecutable se reduce considerablemente. En un sistema que incluye muchos binarios que usan el mismo código, no es necesario conservar una copia de ese código para cada archivo ejecutable.

2. El código de biblioteca compartido utilizado por varias aplicaciones se almacena en la RAM en una sola instancia (en realidad, no es tan simple...), lo que resulta en una reducción en la necesidad del sistema de RAM disponible.

3. No es necesario reconstruir cada ejecutable si se realizan cambios en el código de la biblioteca compartida. Los cambios y correcciones al código de la biblioteca dinámica se reflejarán automáticamente en cada uno de los programas que la utilicen.

Sin el paradigma de biblioteca compartida, no habría distribuciones precompiladas (binarias) Linux(sí, pase lo que pase). Imagine el tamaño de una distribución que tendría un código de biblioteca estándar colocado en cada binario. C(y todas las demás bibliotecas incluidas). Imagínese lo que tendría que hacer para actualizar el sistema, después de corregir una vulnerabilidad crítica en una de las bibliotecas más utilizadas...

Ahora un poco de práctica.

Para ilustrar, usemos el conjunto de archivos fuente del ejemplo anterior. Coloquemos el código (implementación) de las funciones primero() y segundo() en nuestra biblioteca casera.

Linux tiene el siguiente esquema de nomenclatura para los archivos de la biblioteca (aunque no siempre se observa): el nombre del archivo de la biblioteca comienza con el prefijo lib, seguido del nombre real de la biblioteca, al final con la extensión .a ( archivo ) - para biblioteca estática, .so ( objeto compartido ) - para compartida (dinámica), después de la expansión, los dígitos del número de versión se enumeran mediante un punto (solo para una biblioteca dinámica). El nombre del archivo de encabezado correspondiente a la biblioteca (nuevamente, como regla general) consta del nombre de la biblioteca (sin prefijo ni versión) y la extensión .h. Por ejemplo: libogg.a, libogg.so.0.7.0, ogg.h.

Primero, creemos y usemos una biblioteca estática.

Las funciones first() y second() conformarán el contenido de nuestra biblioteca libhello. El nombre del archivo de la biblioteca, respectivamente, será libhello.a. Comparemos el archivo de encabezado hello.h con la biblioteca.

/* Hola h */

vacío primero( vacío );
vacío segundo( vacío );

Por supuesto, las líneas:

#incluir "primero.h"


#incluir "segundo.h"

en los archivos main.c , first.c y second.c se deben reemplazar con:

#incluir "hola.h"

Bueno, ahora ingresa la siguiente secuencia de comandos:

$ gcc -Pared -c primero.c
$ gcc -Pared -c segundo.c
$ ar crs libhello.a primero.o segundo.o
$filelibhello.a
libhello.a: archivo ar actual

Como ya se mencionó, una biblioteca es una colección de archivos objeto. Con los dos primeros comandos, creamos estos archivos objeto.

A continuación, debe vincular los archivos objeto en un conjunto. Para ello, se utiliza un archivador. Arkansas- la utilidad "pega" varios archivos en uno, en el archivo resultante incluye la información necesaria para restaurar (extraer) cada archivo individual (incluidos sus atributos de propiedad, acceso y tiempo). No se realiza ninguna "compresión" del contenido del archivo ni ninguna otra transformación de los datos almacenados.

opción de nombre c- crear un archivo, si el archivo con el nombre arname no existe, se creará; de lo contrario, los archivos se agregarán al archivo existente.

opción r- establece el modo de actualización del archivo; si ya existe un archivo con el nombre especificado en el archivo, se eliminará y el nuevo archivo se agregará al final del archivo.

Opciones- agrega (actualiza) el índice del archivo. En este caso, el índice de archivo es una tabla en la que para cada nombre simbólico (nombre de función o bloque de datos) definido en los archivos archivados, se asocia el nombre del archivo de objeto correspondiente. El índice de archivo es necesario para acelerar el trabajo con la biblioteca; para encontrar la definición deseada, no es necesario buscar en las tablas de símbolos de todos los archivos, puede ir inmediatamente al archivo que contiene el nombre que está buscando. . Puede ver el índice del archivo utilizando la utilidad ya familiar Nuevo Méjico usándolo opción -s(También se mostrarán las tablas de símbolos de todos los archivos objeto del archivo):

$ nm -s libhello.a
índice de archivo:
primero en primero.o
segundo en segundo.o

primero o:
00000000T primero
tu pones

segundo.o:
tu pones
00000000 T segundo

Para crear un índice de archivo, existe una utilidad especial. ranlib. La biblioteca libhello.a podría haberse creado así:

$ ar cr libhello.a primero.o segundo.o
$ranlib libhello.a

Sin embargo, la biblioteca funcionará bien sin un índice de archivo.

Ahora usemos nuestra biblioteca:

$ gcc -Pared -c principal.c
$
$ ./principal
Primera función...
Segunda función...
función principal...

Obras...

Bueno ahora comenta... Hay dos nuevas opciones gcc:

-l opción de nombre- pasado al vinculador, indica la necesidad de vincular la biblioteca libname al archivo ejecutable. Conectar significa indicar que tales o cuales funciones (variables externas) están definidas en tal o cual biblioteca. En nuestro ejemplo, la biblioteca es estática, todos los nombres simbólicos se referirán al código ubicado directamente en el archivo ejecutable. Presta atención a las opciones -l el nombre de la biblioteca se proporciona como nombre sin el prefijo lib.

Opción -L /ruta/al/directorio/con/bibliotecas - pasado al vinculador, especifica la ruta al directorio que contiene las bibliotecas vinculadas. En nuestro caso, el punto . , el vinculador buscará primero bibliotecas en el directorio actual y luego en los directorios definidos en el sistema.

Aquí es necesario hacer una pequeña observación. El hecho es que para una serie de opciones. gcc el orden en el que aparecen en la línea de comando es importante. Así es como el vinculador busca código que coincida con los nombres especificados en la tabla de símbolos del archivo en las bibliotecas enumeradas en la línea de comando. después el nombre de este archivo. El vinculador ignora el contenido de las bibliotecas enumeradas antes del nombre del archivo:

$ gcc -Pared -c principal.c
$ gcc -o principal -L. -hola principal.o
main.o: En función `principal":
main.c:(.text+0xa): referencia indefinida a `primero"
main.c:(.text+0xf): referencia indefinida al `segundo"

$ gcc -o principal principal.o -L. -hola
$ ./principal
Primera función...
Segunda función...
función principal...

Este tipo de comportamiento gcc debido al deseo de los desarrolladores de brindar al usuario la posibilidad de combinar archivos con bibliotecas de diferentes maneras, usar nombres que se cruzan ... En mi opinión, si es posible, es mejor no molestarse con esto. En general, las bibliotecas de enlaces deben aparecer después del nombre del archivo que hace referencia a ellas.

existe forma alternativa especificando la ubicación de las bibliotecas en el sistema. Dependiendo de la distribución, la variable de entorno LD_LIBRARY_PATH o LIBRARY_PATH puede contener una lista de directorios separados por dos puntos en los que el vinculador debe buscar bibliotecas. Como regla general, por defecto esta variable no está definida en absoluto, pero nada impide que se cree:

$ eco $LD_LIBRARY_PATH

/usr/lib/gcc/i686-pc-linux-gnu/4.4.3/../../../../i686-pc-linux-gnu/bin/ld: no se puede encontrar -hello
Collect2: ld salió con el código de retorno 1
$ exportar LIBRARY_PATH=.
$ gcc -o principal principal.o -hola
$ ./principal
Primera función...
Segunda función...
función principal...

Las manipulaciones con variables de entorno son útiles al crear y depurar sus propias bibliotecas, así como si es necesario conectar alguna biblioteca compartida no estándar (obsoleta, actualizada, modificada, generalmente diferente de la incluida en el kit de distribución) a la aplicación.

Ahora creemos y usemos la biblioteca dinámica.

El conjunto de archivos fuente permanece sin cambios. Ingresamos comandos, vemos qué pasó, leemos los comentarios:

$ gcc -Wall -fPIC -c primero.c
$ gcc -Wall -fPIC -c segundo.c
$ gcc -shared -o libhello.so.2.4.0.5 -Wl,-soname,libhello.so.2 primero.o segundo.o

¿Qué obtuviste como resultado?

$ archivo libhello.so.2.4.0.5
libhello.so.2.4.0.5: objeto compartido ELF LSB de 64 bits, x86-64, versión 1 (SYSV), vinculado dinámicamente, no eliminado

El archivo libhello.so.2.4.0.5 es nuestra biblioteca compartida. Hablemos sobre cómo usarlo a continuación.

Ahora los comentarios:

Opción -fPIC- requiere que el compilador, al crear archivos objeto, genere código independiente de posición (PIC - Código independiente de posición ), su principal diferencia está en la forma en que se presentan las direcciones. En lugar de especificar posiciones fijas (estáticas), todas las direcciones se calculan en función de los desplazamientos especificados en tabla de compensación global (tabla de compensación global - GOT ). El formato de código independiente de la posición le permite conectar módulos ejecutables al código del programa principal en el momento de su carga. En consecuencia, el objetivo principal del código independiente de la posición es la creación de bibliotecas dinámicas (compartidas).

-opción compartida- indica gcc, que como resultado, no se debe crear un archivo ejecutable, sino un objeto compartido: una biblioteca dinámica.

Opción -Wl,-soname,libhello.so.2- conjuntos soname bibliotecas. Hablaremos de soname en detalle en el siguiente párrafo. Ahora analicemos el formato de la opción. Esta construcción extraña, a primera vista, con comas está destinada a la interacción directa entre el usuario y el vinculador. Durante la compilación gcc llama al vinculador automáticamente, automáticamente, a su propia discreción, gcc le pasa las opciones necesarias para completar con éxito la tarea. Si el usuario necesita intervenir él mismo en el proceso de vinculación, puede utilizar la opción especial gcc -Wl, -opción, valor1, valor2.... ¿Qué significa pasar al enlazador ( -Wl) opción -opción con argumentos valor1, valor2 etcétera. En nuestro caso, al vinculador se le dio la opción -soname con un argumento libhello.so.2.

Ahora sobre soname. Al crear y distribuir bibliotecas, existe un problema de compatibilidad y control de versiones. Para que el sistema, específicamente el cargador de biblioteca dinámico, tenga una idea de qué versión de la biblioteca se utilizó al compilar la aplicación y, en consecuencia, es necesaria para su funcionamiento exitoso, se proporcionó un identificador especial: soname , colocado tanto en el archivo de la biblioteca como en el archivo ejecutable de la aplicación. El identificador soname es una cadena que contiene el nombre de la biblioteca con el prefijo lib , un punto, la extensión so, un punto nuevamente y uno o dos dígitos (separados por puntos) de la versión de la biblioteca, nombre lib .so. X . y . Es decir, soname hace coincidir el nombre del archivo de la biblioteca hasta el primer o segundo dígito del número de versión. Deje que el nombre del ejecutable de nuestra biblioteca sea libhello.so.2.4.0.5, entonces el nombre de la biblioteca podría ser libhello.so.2. ¡Al cambiar la interfaz de la biblioteca, se debe cambiar su nombre! Cualquier modificación de código que cause incompatibilidad con versiones anteriores debe ir acompañada de un nuevo nombre.

¿Cómo funciona todo? Dejemos que se requiera una biblioteca con el nombre hola para la ejecución exitosa de alguna aplicación, que haya una en el sistema y que el nombre del archivo de la biblioteca sea libhello.so.2.4.0.5 y el nombre de la biblioteca escrita en él sea libhello. .entonces.2 . En la etapa de compilación de la solicitud, el vinculador, según la opción -hola, buscará en el sistema un archivo llamado libhello.so . En un sistema real, libhello.so es un enlace simbólico al archivo libhello.so.2.4.0.5. Al acceder al archivo de la biblioteca, el vinculador lee el valor de soname registrado en él y, entre otras cosas, lo coloca en el archivo ejecutable de la aplicación. Cuando se inicia la aplicación, el cargador dinámico de bibliotecas recibirá una solicitud para incluir una biblioteca con el nombre de sonido leído del archivo ejecutable e intentará encontrar una biblioteca en el sistema cuyo nombre de archivo coincida con el nombre de sonido. Es decir, el cargador intentará encontrar el archivo libhello.so.2. Si el sistema está configurado correctamente, debe contener un enlace simbólico libhello.so.2 al archivo libhello.so.2.4.0.5, el cargador obtendrá acceso a la biblioteca requerida y luego sin dudarlo (y sin verificar nada más) conéctelo a la aplicación. Ahora imagine que hemos portado la aplicación compilada de esta manera a otro sistema donde solo está implementada la versión anterior de la biblioteca con soname libhello.so.1. Intentar ejecutar el programa generará un error, ya que no hay ningún archivo llamado libhello.so.2 en este sistema.

Entonces, en tiempo de compilación, el vinculador debe proporcionar un archivo de biblioteca (o un enlace simbólico a un archivo de biblioteca) llamado nombre lib .so, en tiempo de ejecución el cargador necesita un archivo (o un enlace simbólico) llamado nombre lib .so. X . y . ¿Qué tiene que ver el nombre lib name .so con eso? X . y debe coincidir con la cadena soname de la biblioteca utilizada.

En distribuciones binarias, como regla general, el archivo de biblioteca libhello.so.2.4.0.5 y su enlace libhello.so.2 se colocarán en el paquete libhello, y el enlace libhello.so, que es necesario solo para la compilación, junto con el archivo de encabezado de la biblioteca hello.h se empaquetará en el paquete libhello-devel (el paquete devel también contendrá el archivo de la versión estática de la biblioteca libhello.a, la biblioteca estática se puede usar, también solo en la compilación escenario). Al descomprimir el paquete, todos los archivos y enlaces enumerados (excepto hello.h) estarán en el mismo directorio.

Asegurémonos de que la cadena soname proporcionada esté realmente registrada en nuestro archivo de biblioteca. Usemos la mega utilidad. volcado de objetos con opción -pag :

$ objdump -p libhello.so.2.4.0.5 | grep SONAME
libhello.so.2


Utilidad volcado de objetos- una poderosa herramienta que le permite obtener información completa sobre el contenido interno (y la estructura) de un objeto o archivo ejecutable. La página de manual de la utilidad dice que volcado de objetos En primer lugar, será útil para los programadores que crean herramientas de depuración y compilación, y no solo para escribir algunos programas de aplicación :) En particular, con la opción -d es un desensamblador. Hemos utilizado la opción -pag- mostrar diversa metainformación sobre el archivo objeto.

En el ejemplo anterior de creación de una biblioteca, seguimos implacablemente los principios de compilación separada. Por supuesto, sería posible compilar la biblioteca de esta manera, con una sola llamada. gcc:

$ gcc -shared -Wall -fPIC -o libhello.so.2.4.0.5 -Wl,-soname,libhello.so.2 primero.c segundo.c

Ahora intentemos usar la biblioteca resultante:

$ gcc -Pared -c principal.c
$
/usr/bin/ld: no se puede encontrar -hello
Collect2: ld devolvió 1 estado de salida

El enlazador jura. Recuerde lo dicho anteriormente sobre los enlaces simbólicos. Crea libhello.so y vuelve a intentarlo:

$ ln -s libhello.so.2.4.0.5 libhello.so
$ gcc -o principal principal.o -L. -hola -Wl,-rpath,.

Ahora todos están felices. Ejecute el binario generado:

Error... El cargador se queja, no puede encontrar la biblioteca libhello.so.2. Asegurémonos de que el enlace a libhello.so.2 esté realmente registrado en el archivo ejecutable:

$ objdump -p principal | grep NECESARIO
libhello.so.2
libc.so.6

$ ln -s libhello.so.2.4.0.5 libhello.so.2
$ ./principal
Primera función...
Segunda función...
función principal...

Funcionó... Ahora comenta sobre nuevas opciones. gcc.

Opción -Wl,-rpath,.- construcción ya familiar, pasar una opción al vinculador -rpath con un argumento . . Mediante el uso -rpath en el archivo ejecutable del programa, puede agregar rutas adicionales donde el cargador de biblioteca compartida buscará archivos de biblioteca. En nuestro caso, el camino . - La búsqueda de archivos de la biblioteca comenzará desde el directorio actual.

$ objdump -p principal | grep RPATH
RUTA.

Gracias a esta opción, al iniciar el programa no es necesario cambiar las variables de entorno. Está claro que si mueve el programa a otro directorio e intenta ejecutarlo, no se encontrará el archivo de la biblioteca y el cargador mostrará un mensaje de error:

$mv principal..
$ ../principal
Primera función...
Segunda función...
función principal...

También puede averiguar qué bibliotecas compartidas necesita una aplicación utilizando la utilidad ldd:

$ldd principal
linux-vdso.so.1 => (0x00007fffaddff000)
libhello.so.2 => ./libhello.so.2 (0x00007f9689001000)
libc.so.6 => /lib/libc.so.6 (0x00007f9688c62000)
/lib64/ld-linux-x86-64.so.2 (0x00007f9689205000)

En conclusión ldd para cada biblioteca requerida, se especifican su nombre y la ruta completa al archivo de la biblioteca, determinados de acuerdo con la configuración del sistema.

Ahora es el momento de hablar sobre dónde se supone que deben colocarse los archivos de la biblioteca en el sistema, dónde el cargador intenta encontrarlos y cómo gestionar este proceso.

Según acuerdos FHS (Estándar de jerarquía del sistema de archivos) el sistema debe tener dos (al menos) directorios para almacenar archivos de la biblioteca:

/lib - aquí se recopilan las principales bibliotecas del kit de distribución, necesarias para el funcionamiento de los programas de /bin y /sbin ;

/usr/lib - las bibliotecas que necesitan las aplicaciones de /usr/bin y /usr/sbin se almacenan aquí;

Los archivos de encabezado correspondientes a las bibliotecas deben estar en el directorio /usr/include.

El cargador predeterminado buscará archivos de biblioteca en estos directorios.

Además de los enumerados anteriormente, el directorio /usr/local/lib debe estar presente en el sistema; debe haber bibliotecas implementadas por el usuario por su cuenta, sin pasar por el sistema de administración de paquetes (no incluidas en el kit de distribución). Por ejemplo, las bibliotecas compiladas a partir de fuentes estarán en este directorio de forma predeterminada (los programas instalados desde fuentes se ubicarán en /usr/local/bin y /usr/local/sbin, por supuesto, estamos hablando de distribuciones binarias). Los archivos de encabezado de las bibliotecas en este caso se colocarán en /usr/local/include.

En algunas distribuciones (en ubuntu) el cargador no está configurado para ver el directorio /usr/local/lib, por lo que si el usuario instala la biblioteca desde el código fuente, el sistema no la verá. Esta distribución fue realizada por los autores de la distribución específicamente para enseñar al usuario a instalar software sólo a través del sistema de gestión de paquetes. A continuación se describirá cómo proceder en este caso.

De hecho, para simplificar y acelerar el proceso de búsqueda de archivos de biblioteca, el cargador no mira los directorios anteriores cada vez que accede, sino que utiliza la base de datos almacenada en el archivo /etc/ld.so.cache (biblioteca cache). Contiene información sobre en qué parte del sistema se encuentra el archivo de biblioteca correspondiente al soname dado. El cargador, después de recibir una lista de bibliotecas requeridas por una aplicación en particular (una lista de bibliotecas soname especificadas en el archivo ejecutable del programa), determina la ruta al archivo de cada biblioteca requerida usando /etc/ld.so.cache y la carga en la memoria. Además, el cargador puede explorar los directorios enumerados en las variables del sistema LD_LIBRARY_PATH, LIBRARY_PATH y en el campo RPATH del ejecutable (ver arriba).

La utilidad se utiliza para administrar y mantener actualizado el caché de la biblioteca. ldconfig. si se ejecuta ldconfig sin ninguna opción, el programa buscará los directorios especificados en la línea de comando, los directorios confiables /lib y /usr/lib, los directorios listados en el archivo /etc/ld.so.conf. Para cada archivo de biblioteca que se encuentre en los directorios especificados, se leerá un soname, se creará un enlace simbólico basado en el soname y se actualizará la información en /etc/ld.so.cache.

Asegurémonos de que:

$ls
hola.h libhello.so libhello.so.2.4.0.5 principal.c
$
$ sudo ldconfig /full/ruta/a/dir/c/ejemplo
$ls
hola.h libhello.so libhello.so.2 libhello.so.2.4.0.5 principal principal.c
$ ./principal
Primera función...
Segunda función...
función principal...

Primera llamada ldconfig Almacenamos en caché nuestra biblioteca y la excluimos con la segunda llamada. Tenga en cuenta que la opción principal se omitió al compilar -Wl,-rpath,. Como resultado, el cargador buscó las bibliotecas necesarias solo en el caché.

Ahora debería quedar claro qué hacer si, después de instalar la biblioteca desde el código fuente, el sistema no la ve. En primer lugar, debe agregar la ruta completa al directorio con los archivos de la biblioteca (por defecto /usr/local/lib) al archivo /etc/ld.so.conf. Formato /etc/ld.so.conf: el archivo contiene una lista de directorios separados por dos puntos, espacios, tabulaciones o nuevas líneas en los que buscar bibliotecas. Luego llame ldconfig sin opciones, pero con derechos de superusuario. Todo debería funcionar.

Bueno, al final, hablemos de cómo se llevan las versiones estáticas y dinámicas de las bibliotecas. ¿Cuál es la pregunta real? Arriba, al discutir los nombres aceptados y la ubicación de los archivos de la biblioteca, se dijo que los archivos de las versiones estática y dinámica de la biblioteca se almacenan en el mismo directorio. Cómo gcc¿Averiguar qué tipo de biblioteca queremos usar? De forma predeterminada, se prefiere la biblioteca dinámica. Si el vinculador encuentra un archivo de biblioteca dinámica, no duda en vincularlo al archivo ejecutable del programa:

$ls
hola.h libhello.a libhello.so libhello.so.2 libhello.so.2.4.0.5 principal.c
$ gcc -Pared -c principal.c
$ gcc -o principal principal.o -L. -hola -Wl,-rpath,.
$ldd principal
linux-vdso.so.1 => (0x00007fffe1bb0000)
libhello.so.2 => ./libhello.so.2 (0x00007fd50370b000)
libc.so.6 => /lib/libc.so.6 (0x00007fd50336c000)
/lib64/ld-linux-x86-64.so.2 (0x00007fd50390f000)
$ du -h principal
12K principal

Preste atención al tamaño del archivo ejecutable del programa. Es lo mínimo posible. Todas las bibliotecas utilizadas están vinculadas dinámicamente.

existe gcc -opción estática- una instrucción al vinculador para que utilice solo versiones estáticas de todas las bibliotecas necesarias para la aplicación:

$ gcc -static -o principal principal.o -L. -hola
$ archivo principal
principal: ejecutable ELF LSB de 64 bits, x86-64, versión 1 (GNU/Linux), vinculado estáticamente, para GNU/Linux 2.6.15, no despojado
$ldd principal
no es un ejecutable dinámico
$ du -h principal
728K principal

El tamaño del archivo ejecutable es 60 veces mayor que en el ejemplo anterior: las bibliotecas de idiomas estándar están incluidas en el archivo. C. Ahora nuestra aplicación se puede transferir de forma segura de un directorio a otro e incluso a otras máquinas, el código de la biblioteca de saludos está dentro del archivo y el programa es completamente autónomo.

¿Qué pasa si es necesario vincular estáticamente sólo una parte de las bibliotecas utilizadas? Posible variante la solución es hacer que el nombre de la versión estática de la biblioteca sea diferente del nombre de la compartida, y al compilar la aplicación especificar qué versión queremos usar esta vez:

$ mv libhello.a libhello_s.a
$ gcc -o principal principal.o -L. -hola_s
$ldd principal
linux-vdso.so.1 => (0x00007fff021f5000)
libc.so.6 => /lib/libc.so.6 (0x00007fd0d0803000)
/lib64/ld-linux-x86-64.so.2 (0x00007fd0d0ba4000)
$ du -h principal
12K principal

Dado que el tamaño del código de la biblioteca libhello es insignificante,

$ du -h libhello_s.a
4.0K libhello.a

el tamaño del archivo ejecutable resultante es prácticamente el mismo que el tamaño del archivo creado mediante enlace dinámico.

Bueno, quizás eso sea todo. Muchas gracias a todos los que terminaron de leer en este punto.

Se cree ampliamente que GCC va por detrás de otros compiladores en términos de rendimiento. En este artículo, intentaremos descubrir qué optimizaciones básicas del compilador GCC se deben aplicar para lograr un rendimiento aceptable.

¿Cuáles son las opciones predeterminadas en GCC?

(1) De forma predeterminada, GCC utiliza el nivel de optimización "-O0". Claramente no es óptimo en términos de rendimiento y no se recomienda para compilar el producto final.
GCC no reconoce la arquitectura en la que se ejecuta la compilación hasta que se pasa la opción "-march=native". De forma predeterminada, GCC utiliza la opción establecida durante su configuración. Para conocer la configuración de GCC, simplemente ejecute:

Esto significa que GCC agregará "-march=corei7" a sus opciones (a menos que se especifique otra arquitectura).
La mayoría de los compiladores GCC para x86 (básico para Linux de 64 bits) agregan: “-mtune=generic -march=x86-64” a las opciones dadas, ya que no se proporcionaron opciones específicas de la arquitectura en la configuración. Siempre puedes conocer todas las opciones pasadas al iniciar GCC, así como sus opciones internas, con el comando:

Como resultado, comúnmente se usa:

Especificar la arquitectura a utilizar es importante para el rendimiento. La única excepción pueden considerarse aquellos programas en los que la llamada a las funciones de la biblioteca ocupa casi todo el tiempo de inicio. GLIBC puede elegir la función óptima para una arquitectura determinada en tiempo de ejecución. Es importante tener en cuenta que cuando se vinculan estáticamente, algunas funciones GLIBC no están versionadas para diferentes arquitecturas. Es decir, el ensamblaje dinámico es mejor si la velocidad de las funciones GLIBC es importante..
(2) De forma predeterminada, la mayoría de los compiladores GCC para x86 en modo de 32 bits utilizan el modelo de punto flotante x87, ya que fueron configurados sin "-mfpmath=sse". Sólo si la configuración de GCC contiene "--with-mfpmath=sse":

el compilador utilizará el modelo SSE de forma predeterminada. En todos los demás casos, es mejor agregar la opción "-mfpmath=sse" a la compilación en modo de 32 bits.
Entonces, comúnmente usado:

¡Agregar la opción "-mfpmath=sse" es importante en el modo de 32 bits! La excepción es un compilador que tiene "--with-mfpmath=sse" en su configuración.

¿Modo de 32 bits o 64 bits?

El modo de 32 bits se usa generalmente para reducir la cantidad de memoria utilizada y, como resultado, acelerar el trabajo con ella (se colocan más datos en el caché).
En el modo de 64 bits (en comparación con el de 32 bits), el número de registros públicos disponibles aumenta de 6 a 14, los registros XMM de 8 a 16. Además, todas las arquitecturas de 64 bits admiten la extensión SSE2, por lo que en el modo de 64 bits no es necesario agregue la opción “-mfpmath” =sse".
Se recomienda utilizar el modo de 64 bits para tareas informáticas y el modo de 32 bits para aplicaciones móviles.

¿Cómo conseguir el máximo rendimiento?

No existe un conjunto de opciones para maximizar el rendimiento, pero hay muchas opciones en GCC que vale la pena probar. A continuación se muestra una tabla con opciones recomendadas y pronósticos de crecimiento para los procesadores Intel Atom e Intel Core i7 de segunda generación en relación con la opción “-O2”. Las predicciones se basan en la media geométrica de los resultados de un conjunto específico de tareas compiladas por GCC versión 4.7. También supone que la configuración del compilador se realizó para x86-64 genérico.
Previsión de aumento de rendimiento para aplicaciones móviles relativo a “-O2” (solo en modo 32 bits, ya que es el principal para el segmento móvil):

Previsión de aumento del rendimiento en tareas computacionales en relación con “-O2” (en modo de 64 bits):
-m64 -Orápido -flto ~17%
-m64 -Ofast -flto -march=nativo ~21%
-m64 -Ofast -flto -march=native -funroll-loops ~22%

La ventaja del modo de 64 bits sobre el de 32 bits para tareas informáticas con las opciones “-O2 -mfpmath=sse” es aproximadamente ~5%
Todos los datos del artículo son un pronóstico basado en los resultados de un determinado conjunto de puntos de referencia.
A continuación se muestra una descripción de las opciones utilizadas en el artículo. Descripción completa (en inglés): http://gcc.gnu.org/onlinedocs/gcc-4.7.1/gcc/Optimize-Options.html"
  • "-Ofast" como "-O3 -ffast-math" permite un mayor nivel de optimizaciones y optimizaciones más agresivas para cálculos aritméticos (como la reasociación real)
  • Optimizaciones entre módulos "-flto"
  • "-m32" modo de 32 bits
  • "-mfpmath=sse" permite utilizar registros XMM en aritmética real (en lugar de pila real en modo x87)
  • "-funroll-loops" permite desenrollar el bucle

Logística de transporte(análisis de diferentes tipos de transporte: ventajas, desventajas)

El transporte es una rama de la producción material que transporta personas y mercancías. en estructura producción social El transporte pertenece al ámbito de la producción de servicios materiales.

Cabe señalar que una parte importante de las operaciones logísticas en el camino del flujo de materiales desde la fuente primaria de materias primas hasta el consumo final se lleva a cabo utilizando diversos Vehículo. El costo de estas operaciones es hasta el 50% del costo total de la logística.

Según su finalidad, se distinguen dos grupos principales de transporte: Transporte público - industria economía nacional, que satisface las necesidades de todos los sectores de la economía y de la población en el transporte de mercancías y pasajeros. El transporte público sirve al ámbito de la circulación y de la población. A menudo se le llama línea principal (la línea principal es la línea principal en algún sistema, en este caso, en el sistema de comunicación). El concepto de transporte público abarca transporte ferroviario, transporte acuático (marítimo y fluvial), transporte por carretera, aéreo y por tuberías).

Transporte no público: transporte intraproducción, así como vehículos de todo tipo pertenecientes a organizaciones ajenas al transporte.

La organización del movimiento de mercancías mediante transporte no público es objeto de estudio de la logística industrial. El problema de la elección de canales de distribución se resuelve en el ámbito de la logística de distribución.

Así, existen los siguientes tipos principales de transporte:

ferrocarril

río de aguas continentales

automotor

aire

tubería

Cada uno de los modos de transporte tiene características específicas en cuanto a la gestión logística, ventajas y desventajas que determinan la posibilidad de su uso en el sistema logístico. Diferentes tipos de transporte componen el complejo de transporte. El complejo de transporte de Rusia está formado por personas jurídicas y personas físicas registradas en su territorio: empresarios que realizan actividades de transporte y expedición de todo tipo de transporte, diseño, construcción, reparación y mantenimiento de vías férreas, carreteras y estructuras, tuberías, obras. relacionados con el mantenimiento de estructuras hidráulicas navegables, agua y vías respiratorias mensajes, sosteniendo investigación científica y capacitación del personal, empresas que forman parte del sistema de transporte y fabrican vehículos, así como organizaciones que realizan otros trabajos relacionados con el proceso de transporte. El Código Postal de Rusia tiene más de 160 mil kilómetros de vías ferroviarias y de acceso principales, 750 mil kilómetros de carreteras pavimentadas, 1,0 millones de kilómetros de líneas marítimas y 101 mil kilómetros de vías terrestres. vías navegables, 800 mil km de líneas aéreas. A través de estas comunicaciones se transportan diariamente alrededor de 4,7 millones de toneladas de carga sólo en transporte público (según datos de 2000), más de 4 millones de personas trabajan en el TC y la participación del transporte en el producto interno bruto del país es de aproximadamente el 9%. Por tanto, el transporte es una parte esencial de la infraestructura de la economía y de todo el potencial social y productivo de nuestro país.

En mesa. 1 (4, 295) Se dan las características logísticas comparativas de varios modos de transporte.

Cuadro 1 Características de los modos de transporte

tipo de transporte

Ventajas

Defectos

ferrocarril

alta capacidad de carga y rendimiento. Independencia de las condiciones climáticas, época del año y día.

Alta regularidad del transporte. Tarifas relativamente bajas; importantes descuentos para envíos en tránsito. Alta velocidad Entrega de mercancías a largas distancias.

Número limitado de transportistas. Grandes inversiones de capital en la base productiva y técnica. Alto consumo de materiales e intensidad energética del transporte. Baja disponibilidad hasta los puntos finales de venta (consumo).

Seguridad de carga insuficientemente alta.

Posibilidad de transporte intercontinental. Bajo costo de transporte en largas distancias. Alta capacidad de carga y carga. Baja intensidad de capital del transporte.

Transporte limitado.

Baja velocidad de entrega (largo tiempo de tránsito).

Dependencia de las condiciones geográficas, de navegación y meteorológicas.

La necesidad de crear una infraestructura portuaria compleja.

Agua Interna (río)

Alta capacidad de carga en ríos y embalses de aguas profundas.

Bajo costo de transporte. Baja intensidad de capital.

Transporte limitado. Baja velocidad de entrega.

Dependencia de profundidades desiguales de ríos y embalses, condiciones de navegación. Estacionalidad. Fiabilidad insuficiente del transporte y seguridad de la carga.

automotor

Alta disponibilidad.

Posibilidad de entrega de carga puerta a puerta.

Alta maniobrabilidad, flexibilidad, dinamismo. Alta velocidad de entrega. Posibilidad de utilizar varias rutas y esquemas de entrega.

Alta seguridad de la carga. Posibilidad de envío de carga en pequeños lotes.

Bajo rendimiento. Dependencia del clima y las condiciones de la carretera. Costo relativamente alto del transporte en largas distancias.

Limpieza ambiental insuficiente.

Aire

La mayor velocidad de entrega de carga. Alta fiabilidad.

La mayor seguridad de la carga.

Las rutas de transporte más cortas.

Alto costo de transporte, las tarifas más altas entre otros modos de transporte. Alta intensidad de capital, intensidad material y energética del transporte. Dependiente del clima. Accesibilidad geográfica insuficiente.

tubería

Bajo costo. Alto rendimiento (ancho de banda). Alta seguridad de la carga. Baja intensidad de capital.

Tipos limitados de carga (gas, productos petrolíferos, emulsiones). materias primas). Disponibilidad insuficiente de pequeños volúmenes de mercancías transportadas.

Así, en primer lugar, el responsable de logística debe decidir si crea su propia flota de vehículos o utiliza transporte contratado (público o privado). A la hora de elegir una alternativa, suelen partir de un determinado sistema de criterios, entre los que se incluyen: El coste de crear y operar su propia flota de vehículos. El costo de pagar los servicios de transporte, empresas de transporte y otros intermediarios logísticos en el transporte. Velocidad del transporte.

Calidad del transporte (fiabilidad en la entrega, seguridad de la carga, etc.)

En la mayoría de los casos, las empresas manufactureras recurren a los servicios de empresas de transporte especializadas.