GNU/Linux >> Tutoriales Linux >  >> Linux

Una guía para comprender las bibliotecas de software de Linux en C

Las bibliotecas de software son una forma duradera, fácil y sensata de reutilizar el código. Este artículo explica cómo crear bibliotecas desde cero y ponerlas a disposición de los clientes. Aunque las dos bibliotecas de muestra tienen como objetivo Linux, los pasos para crear, publicar y usar estas bibliotecas se aplican a otros sistemas similares a Unix.

Las bibliotecas de muestra están escritas en C, lo que se adapta bien a la tarea. El kernel de Linux está escrito principalmente en C y el resto en lenguaje ensamblador. (Lo mismo ocurre con los primos de Windows y Linux, como macOS). Las bibliotecas estándar del sistema para entrada/salida, redes, procesamiento de cadenas, matemáticas, seguridad, codificación de datos, etc., también están escritas principalmente en C. Para escribir una biblioteca en C es por lo tanto para escribir en el idioma nativo de Linux. Además, C establece la marca de rendimiento entre los lenguajes de alto nivel.

Más recursos de Linux

  • Hoja de trucos de los comandos de Linux
  • Hoja de trucos de comandos avanzados de Linux
  • Curso en línea gratuito:Descripción general técnica de RHEL
  • Hoja de trucos de red de Linux
  • Hoja de trucos de SELinux
  • Hoja de trucos de los comandos comunes de Linux
  • ¿Qué son los contenedores de Linux?
  • Nuestros últimos artículos sobre Linux

También hay dos clientes de muestra (uno en C, el otro en Python) para acceder a las bibliotecas. No sorprende que un cliente de C pueda acceder a una biblioteca escrita en C, pero el cliente de Python ilustra que una biblioteca escrita en C puede servir a clientes de otros lenguajes.

Bibliotecas estáticas versus dinámicas

Los sistemas Linux tienen dos tipos de bibliotecas:

  • Una biblioteca estática (también conocida como archivo de biblioteca) se integra en un cliente compilado estáticamente (por ejemplo, uno en C o Rust) durante la fase de enlace del proceso de compilación. En efecto, cada cliente obtiene su propia copia de la biblioteca. Una desventaja significativa de una biblioteca estática surge si es necesario revisarla (por ejemplo, para corregir un error), ya que cada cliente de la biblioteca debe volver a vincularse a la biblioteca estática. Una biblioteca dinámica, que se describe a continuación, evita esta deficiencia.
  • Una biblioteca dinámica (también conocida como compartida) se marca durante la fase de enlace de un programa cliente compilado estáticamente, pero el programa cliente y el código de la biblioteca permanecen desconectados hasta el tiempo de ejecución; el código de la biblioteca no se integra en el cliente. En tiempo de ejecución, el cargador dinámico del sistema conecta una biblioteca compartida con un cliente en ejecución, independientemente de si el cliente proviene de un lenguaje compilado estáticamente, como C, o de un lenguaje compilado dinámicamente, como Python. Como resultado, una biblioteca dinámica se puede actualizar sin incomodar a los clientes. Finalmente, varios clientes pueden compartir una sola copia de una biblioteca dinámica.

En general, se prefieren las bibliotecas dinámicas a las estáticas, aunque existe un costo en complejidad y rendimiento. Así es como se crea y publica cada tipo de biblioteca:

  1. El código fuente de la biblioteca se compila en uno o más módulos de objetos, que son archivos binarios que se pueden incluir en una biblioteca y vincular a clientes ejecutables.
  2. Los módulos de objeto se empaquetan en un solo archivo. Para una biblioteca estática, la extensión estándar es .a para "archivo". Para una biblioteca dinámica, la extensión es .so para "objeto compartido". Las dos bibliotecas de muestra, que tienen la misma funcionalidad, se publican como archivos libprimes.a (estático) y libshprimes.so (dinámica). El prefijo lib se utiliza para ambos tipos de biblioteca.
  3. El archivo de la biblioteca se copia en un directorio estándar para que los programas cliente, sin problemas, puedan acceder a la biblioteca. Una ubicación típica para la biblioteca, ya sea estática o dinámica, es /usr/lib o /usr/local/lib; otras ubicaciones son posibles.

Los pasos detallados para crear y publicar cada tipo de biblioteca estarán disponibles en breve. Primero, sin embargo, presentaré las funciones de C en las dos bibliotecas.

Las funciones de la biblioteca de muestra

Las dos bibliotecas de muestra se crean a partir de las mismas cinco funciones de C, cuatro de las cuales son accesibles para los programas cliente. La quinta función, que es una utilidad para una de las otras cuatro, muestra cómo C permite ocultar información. El código fuente de cada función es lo suficientemente corto como para que las funciones se puedan alojar en un solo archivo fuente, aunque varios archivos fuente (por ejemplo, uno para cada una de las cuatro funciones publicadas) son una opción.

Las funciones de la biblioteca son elementales y tratan, de varias maneras, con números primos. Todas las funciones esperan valores enteros sin signo (es decir, no negativos) como argumentos:

  • El is_prime la función comprueba si su único argumento es un número primo.
  • Los are_coprimes La función verifica si sus dos argumentos tienen un máximo común divisor (mcd) de 1, lo que define los coprimos.
  • Los prime_factors función enumera los factores primos de su argumento.
  • El goldbach la función espera un valor entero par de 4 o más, enumerando los dos números primos que suman este argumento; puede haber varios pares sumadores. La función lleva el nombre del matemático del siglo XVIII Christian Goldbach, cuya conjetura de que todo número par mayor que dos es la suma de dos números primos sigue siendo uno de los problemas sin resolver más antiguos de la teoría de números.

La función de utilidad gcd reside en los archivos de biblioteca implementados, pero no se puede acceder a esta función fuera de su archivo contenedor; por lo tanto, un cliente de biblioteca no puede invocar directamente el gcd función. Una mirada más cercana a las funciones de C aclara el punto.

Más sobre funciones C

Cada función en C tiene una clase de almacenamiento, que determina el alcance de la función. Para las funciones hay dos opciones:

  • La clase de almacenamiento predeterminada para funciones es extern , que le da un alcance global a la función. Un programa cliente puede llamar a cualquier extern función en las bibliotecas de ejemplo. Aquí está la definición de la función are_coprimes con un extern explícito :
    extern unsigned are_coprimes(unsigned n1, unsigned n2) {
      ...
    }
  • La clase de almacenamiento static limita el alcance de una función al archivo en el que se define la función. En las bibliotecas de muestra, la función de utilidad gcd es static :
    static unsigned gcd(unsigned n1, unsigned n2) {
      ...
    }

Solo funciona dentro de primes.c archivo puede invocar gcd , y solo la función are_coprimes lo hace Cuando las bibliotecas estáticas y dinámicas se construyen y publican, otros programas pueden llamar a un extern función como are_coprimes pero no el static función gcd . El static la clase de almacenamiento oculta el gcd función de los clientes de la biblioteca al limitar el alcance de la función a las otras funciones de la biblioteca.

Las funciones que no sean gcd en el primes.c el archivo no necesita especificar una clase de almacenamiento, que por defecto sería extern . Sin embargo, es común en las bibliotecas hacer que el extern explícito.

C distingue entre definiciones de funciones y declaraciones, lo cual es importante para las bibliotecas. Comencemos con las definiciones. C solo tiene funciones con nombre, y cada función se define con:

  • Un nombre único. Dos funciones en un programa no pueden tener el mismo nombre.
  • Una lista de argumentos, que puede estar vacía. Los argumentos se escriben.
  • Un tipo de valor devuelto (p. ej., int para un entero de 32 bits con signo) o void si no se devuelve ningún valor.
  • Un cuerpo encerrado entre llaves. En un ejemplo artificial, el cuerpo podría estar vacío.

Cada función en un programa debe definirse exactamente una vez.

Aquí está la definición completa de la función de biblioteca are_coprimes :

extern unsigned are_coprimes(unsigned n1, unsigned n2) { /* definition */
  return 1 == gcd(n1, n2); /* greatest common divisor of 1? */
}

La función devuelve un valor booleano (0 para falso o 1 para verdadero), dependiendo de si los dos argumentos enteros tienen un máximo común divisor de 1. La función de utilidad gcd calcula el máximo común divisor de argumentos enteros n1 y n2 .

Una declaración de función, a diferencia de una definición, no tiene cuerpo:

extern unsigned are_coprimes(unsigned n1, unsigned n2); /* declaration */

La declaración termina con un punto y coma después de la lista de argumentos; no hay llaves que encierren un cuerpo. Una función puede declararse varias veces dentro de un programa.

¿Por qué son necesarias las declaraciones? En C, una función llamada debe ser visible para quien la llama. Hay varias formas de proporcionar dicha visibilidad, dependiendo de cuán quisquilloso sea el compilador. Una forma segura es definir la función llamada por encima de su llamador cuando ambos residen en el mismo archivo:

void f() {...}     /* f is defined before being called */
void g() { f(); }  /* ok */

La definición de la función f podría moverse debajo de la llamada desde la función g si f fueron declarados por encima de la llamada:

void f();         /* declaration makes f visible to caller */
void g() { f(); } /* ok */
void f() {...}    /* easier to put this above the call from g */

Pero, ¿qué pasa si la función llamada reside en un archivo diferente al de la persona que llama? ¿Cómo se hacen visibles las funciones definidas en un archivo en otro archivo, dado que cada función debe definirse exactamente una vez en un programa?

Este problema afecta a las bibliotecas, ya sean estáticas o dinámicas. Por ejemplo, las funciones en las dos bibliotecas de números primos se definen en el archivo fuente primes.c , cuyas copias binarias se encuentran en cada biblioteca; pero estas funciones definidas deben ser visibles para un cliente de biblioteca en C, que es un programa separado con sus propios archivos fuente.

Proporcionar visibilidad a través de los archivos es lo que pueden hacer las declaraciones de función. Para los ejemplos de "primos", hay un archivo de encabezado llamado primes.h que declara que las cuatro funciones se harán visibles para los clientes de la biblioteca en C:

/** header file primes.h: function declarations **/
extern unsigned is_prime(unsigned);
extern void prime_factors(unsigned);
extern unsigned are_coprimes(unsigned, unsigned);
extern void goldbach(unsigned);

Estas declaraciones sirven como interfaz al especificar la sintaxis de invocación para cada función.

Para comodidad del cliente, el archivo de texto primes.h podría almacenarse en un directorio en la ruta de búsqueda del compilador de C. Las ubicaciones típicas son /usr/include y /usr/local/include . Un cliente C #include este archivo de encabezado cerca de la parte superior del código fuente del cliente. (Por lo tanto, un archivo de encabezado se importa al "cabezal" de otro archivo fuente). Los archivos de encabezado C también sirven como entrada para las utilidades (por ejemplo, bindgen de Rust ) que permiten a los clientes en otros idiomas acceder a una biblioteca C.

En resumen, una función de biblioteca se define exactamente una vez pero se declara donde sea necesario; cualquier cliente de biblioteca en C necesita la declaración. Un archivo de encabezado debe contener declaraciones de funciones, pero no definiciones de funciones. Si un archivo de encabezado contenía definiciones, el archivo podría incluirse varias veces en un programa C, rompiendo así la regla de que una función debe definirse exactamente una vez en un programa C.

El código fuente de la biblioteca

A continuación se muestra el código fuente de dos bibliotecas. Este código, el archivo de encabezado y los dos clientes de muestra están disponibles en mi sitio web.

Funciones de la biblioteca

#include <stdio.h>
#include <math.h>

extern unsigned is_prime(unsigned n) {
  if (n <= 3) return n > 1;                   /* 2 and 3 are prime */
  if (0 == (n % 2) || 0 == (n % 3)) return 0; /* multiples of 2 or 3 aren't */

  /* check that n is not a multiple of other values < n */
  unsigned i;
  for (i = 5; (i * i) <= n; i += 6)
    if (0 == (n % i) || 0 == (n % (i + 2))) return 0; /* not prime */

  return 1; /* a prime other than 2 or 3 */
}

extern void prime_factors(unsigned n) {
  /* list 2s in n's prime factorization */
  while (0 == (n % 2)) {  
    printf("%i ", 2);
    n /= 2;
  }

  /* 2s are done, the divisor is now odd */
  unsigned i;
  for (i = 3; i <= sqrt(n); i += 2) {
    while (0 == (n % i)) {
      printf("%i ", i);
      n /= i;
    }
  }

  /* one more prime factor? */
  if (n > 2) printf("%i", n);
}

/* utility function: greatest common divisor */
static unsigned gcd(unsigned n1, unsigned n2) {
  while (n1 != 0) {
    unsigned n3 = n1;
    n1 = n2 % n1;
    n2 = n3;
  }
  return n2;
}

extern unsigned are_coprimes(unsigned n1, unsigned n2) {
  return 1 == gcd(n1, n2);
}

extern void goldbach(unsigned n) {
  /* input errors */
  if ((n <= 2) || ((n & 0x01) > 0)) {
    printf("Number must be > 2 and even: %i is not.\n", n);
    return;
  }

  /* two simple cases: 4 and 6 */
  if ((4 == n) || (6 == n)) {
    printf("%i = %i + %i\n", n, n / 2, n / 2);
    return;
  }
 
  /* for n >= 8: multiple possibilities for many */
  unsigned i;
  for (i = 3; i < (n / 2); i++) {
    if (is_prime(i) && is_prime(n - i)) {
      printf("%i = %i + %i\n", n, i, n - i);
      /* if one pair is enough, replace this with break */
    }
  }
}

Estas funciones sirven como grano para el molino de la biblioteca. Las dos bibliotecas se derivan exactamente del mismo código fuente y el archivo de encabezado primes.h es la interfaz C para ambas bibliotecas.

Construyendo las bibliotecas

Los pasos para crear y publicar bibliotecas estáticas y dinámicas difieren en algunos detalles. Solo se requieren tres pasos para la biblioteca estática y solo dos más para la biblioteca dinámica. Los pasos adicionales en la construcción de la biblioteca dinámica reflejan la flexibilidad añadida del enfoque dinámico. Comencemos con la biblioteca estática.

El archivo fuente de la biblioteca primes.c se compila en un módulo de objeto. Aquí está el comando, con el signo de porcentaje como indicación del sistema (los signos dobles afilados introducen mis comentarios):

% gcc -c primes.c ## step 1 static

Esto produce el archivo binario primes.o , el módulo de objeto. La bandera -c significa solo compilar.

El siguiente paso es archivar los módulos de objeto utilizando el ar de Linux utilidad:

% ar -cvq libprimes.a primes.o ## step 2 static

Las tres banderas -cvq son abreviaturas de "crear", "detallado" y "añadir rápidamente" (en caso de que se deban agregar nuevos archivos a un archivo). Recuerde que el prefijo lib es estándar, pero el nombre de la biblioteca es arbitrario. Por supuesto, el nombre de archivo de una biblioteca debe ser único para evitar conflictos.

El archivo está listo para ser publicado:

% sudo cp libprimes.a /usr/local/lib ## step 3 static

La biblioteca estática ahora es accesible para los clientes, cuyos ejemplos se publicarán próximamente. (El sudo se incluye para garantizar los derechos de acceso correctos para copiar un archivo en /usr/local/lib .)

La biblioteca dinámica también requiere uno o más módulos de objetos para empaquetar:

% gcc primes.c -c -fpic ## step 1 dynamic

La bandera añadida -fpic indica al compilador que genere código independiente de la posición, que es un módulo binario que no necesita cargarse en una ubicación de memoria fija. Tal flexibilidad es crítica en un sistema de múltiples bibliotecas dinámicas. El módulo de objeto resultante es un poco más grande que el generado para la biblioteca estática.

Este es el comando para crear el archivo de biblioteca único a partir de los módulos de objeto:

% gcc -shared -Wl,-soname,libshprimes.so -o libshprimes.so.1 primes.o ## step 2 dynamic

La bandera -shared indica que la biblioteca es compartida (dinámica) en lugar de estática. El -Wl flag introduce una lista de opciones del compilador, la primera de las cuales establece el soname de la biblioteca dinámica , que se requiere. El soname primero especifica el nombre lógico de la biblioteca (libshprimes.so ) y luego, siguiendo el -o marca, el nombre del archivo físico de la biblioteca (libshprimes.so.1 ). El objetivo es mantener el nombre lógico constante y permitir que el nombre del archivo físico cambie con las nuevas versiones. En este ejemplo, el 1 al final del nombre del archivo físico libshprimes.so.1 representa la primera versión de la biblioteca. Los nombres de archivo lógicos y físicos pueden ser los mismos, pero la mejor práctica es tener nombres separados. Un cliente accede a la biblioteca a través de su nombre lógico (en este caso, libshprimes.so ), como aclararé en breve.

El siguiente paso es hacer que los clientes puedan acceder fácilmente a la biblioteca compartida copiándola en el directorio apropiado; por ejemplo, /usr/local/lib again:

% sudo cp libshprimes.so.1 /usr/local/lib ## step 3 dynamic

Ahora se establece un enlace simbólico entre el nombre lógico de la biblioteca compartida (libshprimes.so ) y su nombre de archivo físico completo (/usr/local/lib/libshprimes.so.1 ). Es más fácil dar el comando con /usr/local/lib como directorio de trabajo:

% sudo ln --symbolic libshprimes.so.1 libshprimes.so ## step 4 dynamic

El nombre lógico libshprimes.so no debe cambiar, pero el destino del enlace simbólico (libshrimes.so.1 ) se puede actualizar según sea necesario para nuevas implementaciones de bibliotecas que corrigen errores, mejoran el rendimiento, etc.

El último paso (precautorio) es invocar el ldconfig utilidad, que configura el cargador dinámico del sistema. Esta configuración garantiza que el cargador encontrará la biblioteca recién publicada:

% sudo ldconfig ## step 5 dynamic

La biblioteca dinámica ahora está lista para los clientes, incluidos los dos ejemplos que siguen.

Cliente de biblioteca A C

El cliente C de muestra es el probador del programa, cuyo código fuente comienza con dos #include directivas:

#include <stdio.h>  /* standard input/output functions */
#include <primes.h> /* my library functions */

Los corchetes angulares alrededor de los nombres de archivo indican que estos archivos de encabezado se encuentran en la ruta de búsqueda del compilador (en el caso de primes.h , el directorio /usr/local/include ). Sin este #include , el compilador se quejaría de declaraciones faltantes para funciones como is_prime y prime_factors , que se encuentran publicados en ambas bibliotecas. Por cierto, el código fuente del programa de prueba no necesita cambiar en absoluto para probar cada una de las dos bibliotecas.

Por el contrario, el archivo fuente de la biblioteca (primes.c ) se abre con estos #include directivas:

#include <stdio.h>
#include <math.h>

El archivo de cabecera math.h es necesario porque la función de biblioteca prime_factors llama a la función matemática sqrt en la biblioteca estándar libm.so .

Como referencia, aquí está el código fuente del programa de prueba:

El programa de prueba

#include <stdio.h>
#include <primes.h>

int main() {
  /* is_prime */
  printf("\nis_prime\n");
  unsigned i, count = 0, n = 1000;
  for (i = 1; i <= n; i++) {
    if (is_prime(i)) {
      count++;
      if (1 == (i % 100)) printf("Sample prime ending in 1: %i\n", i);
    }
  }
  printf("%i primes in range of 1 to a thousand.\n", count);

  /* prime_factors */
  printf("\nprime_factors\n");
  printf("prime factors of 12: ");
  prime_factors(12);
  printf("\n");
 
  printf("prime factors of 13: ");
  prime_factors(13);
  printf("\n");
 
  printf("prime factors of 876,512,779: ");
  prime_factors(876512779);
  printf("\n");

  /* are_coprimes */
  printf("\nare_coprime\n");
  printf("Are %i and %i coprime? %s\n",
         21, 22, are_coprimes(21, 22) ? "yes" : "no");
  printf("Are %i and %i coprime? %s\n",
         21, 24, are_coprimes(21, 24) ? "yes" : "no");

  /* goldbach */
  printf("\ngoldbach\n");
  goldbach(11);    /* error */
  goldbach(4);     /* small one */
  goldbach(6);     /* another */
  for (i = 100; i <= 150; i += 2) goldbach(i);

  return 0;
}

Al compilar tester.c en un ejecutable, la parte complicada es el orden de las banderas de enlace. Recuerde que las dos bibliotecas de muestra comienzan con el prefijo lib , y cada uno tiene la extensión habitual:.a para la biblioteca estática libprimes.a y .so para la biblioteca dinámica libshprimes.so . En una especificación de enlaces, el prefijo lib y la extensión se cae. Un indicador de enlace comienza con -l (L minúscula), y un comando de compilación puede contener muchas banderas de enlace. Aquí está el comando de compilación completo para el programa de prueba, usando la biblioteca dinámica como ejemplo:

% gcc -o tester tester.c -lshprimes -lm

El primer indicador de enlace identifica la biblioteca libshprimes.so y el segundo indicador de enlace identifica la biblioteca matemática estándar libm.so .

El enlazador es perezoso, lo que significa que el orden de las banderas del enlace es importante. Por ejemplo, invertir el orden de las especificaciones del enlace genera un error en tiempo de compilación:

% gcc -o tester tester.c -lm -lshprimes ## danger!

La bandera que enlaza con libm.so viene primero, pero ninguna función de esta biblioteca se invoca explícitamente en el programa de prueba; por lo tanto, el enlazador no enlaza con math.so biblioteca. La llamada al sqrt la función de biblioteca ocurre solo en prime_factors función que ahora está contenida en libshprimes.so biblioteca. El error resultante al compilar el programa de prueba es:

primes.c: undefined reference to 'sqrt'

En consecuencia, el orden de las banderas de enlace debe notificar al enlazador que el sqrt se necesita la función:

% gcc -o tester tester.c -lshprimes -lm ## -lshprimes 1st

El enlazador recoge la llamada a la función de biblioteca sqrt en el libshprimes.so biblioteca y, por lo tanto, hace el enlace apropiado a la biblioteca de matemáticas libm.so . Existe una opción más complicada para vincular que admite el orden de bandera de vínculo; en este caso, sin embargo, la forma más sencilla es organizar las banderas de enlace de forma adecuada.

Aquí hay algunos resultados de una ejecución del cliente de prueba:

is_prime
Sample prime ending in 1: 101
Sample prime ending in 1: 401
...
168 primes in range of 1 to a thousand.

prime_factors
prime factors of 12: 2 2 3
prime factors of 13: 13
prime factors of 876,512,779: 211 4154089

are_coprime
Are 21 and 22 coprime? yes
Are 21 and 24 coprime? no

goldbach
Number must be > 2 and even: 11 is not.
4 = 2 + 2
6 = 3 + 3
...
32 =  3 + 29
32 = 13 + 19
...
100 =  3 + 97
100 = 11 + 89
...

Para el goldbach función, incluso un valor par relativamente pequeño (por ejemplo, 18) puede tener varios pares de números primos que suman (en este caso, 5+13 y 7+11). Tales pares de primos múltiples se encuentran entre los factores que complican un intento de prueba de la conjetura de Goldbach.

Concluyendo con un cliente Python

Python, a diferencia de C, no es un lenguaje compilado estáticamente, lo que significa que el cliente Python de muestra debe acceder a la versión dinámica en lugar de a la estática de la biblioteca de números primos. Para hacerlo, Python tiene varios módulos (estándar y de terceros) que admiten una interfaz de función externa (FFI), que permite que un programa escrito en un idioma invoque funciones escritas en otro. Python ctypes es un FFI estándar y relativamente simple que permite que el código Python llame a funciones C.

Cualquier FFI tiene desafíos porque es poco probable que los lenguajes de interfaz tengan exactamente los mismos tipos de datos. Por ejemplo, la biblioteca de primos usa el tipo C unsigned int , que Python no tiene; los ctypes FFI mapea un C unsigned int a un Python int . De los cuatro extern Funciones de C publicadas en la biblioteca primos, dos se comportan mejor en Python con ctypes explícitos configuración.

Las funciones C prime_factors y goldbach tener void en lugar de un tipo de retorno, pero ctypes por defecto reemplaza el C void con Python int . Cuando se llama desde el código de Python, las dos funciones de C devuelven un valor entero aleatorio (por lo tanto, sin sentido) de la pila. Sin embargo, ctypes se puede configurar para que las funciones devuelvan None (tipo nulo de Python) en su lugar. Aquí está la configuración para los prime_factors función:

primes.prime_factors.restype = None

Una declaración similar maneja el goldbach función.

La siguiente sesión interactiva (en Python 3) muestra que la interfaz entre un cliente de Python y la biblioteca de números primos es sencilla:

>>> from ctypes import cdll

>>> primes = cdll.LoadLibrary("libshprimes.so") ## logical name

>>> primes.is_prime(13)
1
>>> primes.is_prime(12)
0

>>> primes.are_coprimes(8, 24)
0
>>> primes.are_coprimes(8, 25)
1

>>> primes.prime_factors.restype = None
>>> primes.goldbach.restype = None

>>> primes.prime_factors(72)
2 2 2 3 3

>>> primes.goldbach(32)
32 = 3 + 29
32 = 13 + 19

Las funciones en la biblioteca de números primos usan solo un tipo de datos simple, unsigned int . Si esta biblioteca C usaba tipos complicados como estructuras, y si los punteros a las estructuras se pasaban y devolvían funciones de biblioteca, entonces una FFI más poderosa que ctypes podría ser mejor para una interfaz fluida entre Python y C. No obstante, los ctypes El ejemplo muestra que un cliente de Python puede usar una biblioteca escrita en C. De hecho, la popular biblioteca NumPy para computación científica está escrita en C y luego expuesta en una API de Python de alto nivel.

La biblioteca de números primos simple y la biblioteca NumPy avanzada subrayan que C sigue siendo la lingua franca entre los lenguajes de programación. Casi todos los lenguajes pueden hablar con C y, a través de C, con cualquier otro lenguaje que hable con C. Python habla fácilmente con C y, como otro ejemplo, Java puede hacer lo mismo cuando el Proyecto Panamá se convierta en una alternativa a Java Native Interface (JNI). ).


Linux
  1. una guía práctica para aprender awk

  2. Comprender systemd al inicio en Linux

  3. Cómo instalar la biblioteca Ncurses en Linux

  4. Use una biblioteca C en Swift en Linux

  5. Aprendiendo a compilar cosas desde la fuente (en Unix/Linux/OSX)

Comprender los permisos de archivos de Linux

Comandos de Linux - Guía completa

Guía completa para instalar Linux en Chromebook

Una guía para principiantes sobre la resolución de problemas de red en Linux

Comprender los procesos en Linux

Los 10 mejores software de gestión de bibliotecas para sistemas Linux