Articulo de referencia

C (lenguaje de programación)

Comprobado \n{{ Cite book\n | date = December 16, 2005\n | isbn = 978-0-596-55071-4\n | first1 = Peter\n | last1 = Prinz\n | first2 = Tony\n | last2 = Crawford\n | page = 3\n | ...

Comprobado
Página protegida con cambios pendientes

C [ c ] es un lenguaje de programación de propósito general creado en la década de 1970 por Dennis Ritchie . Por diseño, C proporciona a los programadores un acceso relativamente directo a las características de las arquitecturas típicas de CPU , personalizadas para el conjunto de instrucciones de destino . Se ha utilizado y se sigue utilizando para implementar sistemas operativos (especialmente núcleos [ 10 ] ), controladores de dispositivos y pilas de protocolos , aunque su uso en software de aplicación está disminuyendo. [ 11 ] C se utiliza en ordenadores que van desde superordenadores hasta microcontroladores y sistemas embebidos .

C fue desarrollado inicialmente como sucesor del lenguaje de programación B en Bell Labs por Ritchie entre 1972 y 1973. Su propósito inicial era construir utilidades que se ejecutaran en Unix , y posteriormente se aplicó a la reimplementación del núcleo del sistema operativo Unix. [ 12 ] Durante la década de 1980, C ganó popularidad gradualmente, convirtiéndose en uno de los lenguajes de programación más utilizados , [ 13 ] [ 14 ] con compiladores de C disponibles para prácticamente todas las arquitecturas de computadoras y sistemas operativos modernos. El libro The C Programming Language , coescrito por el diseñador original del lenguaje, sirvió durante muchos años como el estándar de facto para el lenguaje. [ 15 ] [ 1 ] C ha sido estandarizado desde 1989 por el Instituto Nacional Estadounidense de Estándares (ANSI) y, posteriormente, conjuntamente por la Organización Internacional de Normalización (ISO) y la Comisión Electrotécnica Internacional (IEC).

C es un lenguaje procedimental imperativo que admite programación estructurada , ámbito léxico de variables y recursión , con un sistema de tipos estático . Fue diseñado para compilarse y proporcionar acceso de bajo nivel a la memoria y construcciones de lenguaje que se asignan eficientemente a instrucciones de máquina , todo ello con un soporte mínimo en tiempo de ejecución . A pesar de sus capacidades de bajo nivel, el lenguaje fue diseñado para fomentar la programación multiplataforma. Un programa en C que cumpla con los estándares y que esté escrito pensando en la portabilidad puede compilarse para una amplia variedad de plataformas informáticas y sistemas operativos con pocos cambios en su código fuente.

Aunque ni C ni su biblioteca estándar ofrecen algunas características populares presentes en otros lenguajes, es lo suficientemente flexible como para admitirlas. Por ejemplo, la orientación a objetos y la recolección de basura se proporcionan mediante las bibliotecas externas GLib Object System y el recolector de basura Boehm , respectivamente.

Desde el año 2000, C se ha clasificado típicamente como el lenguaje más o el segundo más popular en el índice TIOBE . [ 16 ]

Características

Dennis Ritchie (derecha), el inventor del lenguaje de programación C, con Ken Thompson.

C es un lenguaje imperativo y procedimental con una estructura de código fuente libre . Las sentencias terminan con punto y coma y se agrupan en bloques mediante llaves . Todo el código ejecutable se encuentra dentro de funciones, en lugar de sentencias de nivel superior. El lenguaje tiene un número relativamente pequeño de palabras clave, y los identificadores definidos por el usuario no se distinguen de las palabras clave mediante ningún símbolo .

Los parámetros se pasan por valor; el paso por referencia se logra con punteros . El valor de retorno de una función puede ignorarse, y aunque una función normalmente no puede anidarse dentro de otra función, algunos traductores lo admiten. C admite recursión , y el polimorfismo en tiempo de ejecución puede lograrse usando punteros a funciones. El flujo de control se proporciona a través de construcciones como if, for, do, while, y switch. El lenguaje ofrece operadores aritméticos ; bit a bit ; y lógicos que incluyen +, +=, ++, &, ||, , y se pueden realizar múltiples asignaciones en una sola instrucción. Una variable declarada dentro de un bloque es accesible solo dentro de ese bloque y solo debajo de su declaración.

La tipificación de datos es débilmente impuesta y estática , con conversión implícita entre tipos primitivos. Los tipos definidos por el usuario permiten alias de especificadores de tipo. Los arreglos se definen y acceden con notación de corchetes, por ejemplo month[11]. Los tipos de estructura definidos por el usuario permiten que los elementos de datos relacionados se pasen y copien como una unidad, aunque dos estructuras no se pueden comparar sin código personalizado para comparar cada campo. Las uniones admiten miembros superpuestos, lo que permite que varios tipos de datos compartan la misma ubicación de memoria , y los tipos de enumeración admiten alias de valores enteros. C carece de un tipo de cadena dedicado, pero proporciona sintaxis para cadenas terminadas en nulo , con manejo asociado en su biblioteca estándar.

El lenguaje admite acceso de bajo nivel a la memoria de la computadora mediante punteros, construcciones tipo procedimiento en forma de funciones que devuelven valores voidy asignación dinámica de memoria a través de funciones de la biblioteca estándar. Incluye el preprocesador de C para la definición de macros , la inclusión de archivos fuente y la compilación condicional . La modularidad se admite procesando los archivos por separado, con la visibilidad controlada a través de los atributos staticy extern. La funcionalidad se minimiza en el núcleo del lenguaje, y las características relativamente complejas, como E/S , manipulación de cadenas y funciones matemáticas, se proporcionan a través de bibliotecas estándar. El código compilado resultante tiene requisitos relativamente sencillos de la plataforma subyacente, lo que hace que C sea deseable para sistemas operativos y embebidos .

Ejemplo de "Hola, mundo"

Programa "Hola, mundo" de Brian Kernighan (1978)

El ejemplo de programa "Hello, World!" que apareció en la primera edición de K&R se ha convertido en el modelo para un programa introductorio en la mayoría de los libros de texto de programación. El programa imprime "hello, world" en la salida estándar .

La versión original era: [ 17 ]

main () { printf ( "hola, mundo \n " ); }

Una versión más moderna es: [ d ]

#include <stdio.h>int main ( void ) { printf ( "hola, mundo \n " ); }

La primera línea es una directiva del preprocesador , indicada por , que hace que el preprocesador reemplace esa línea de código con el texto del archivo de cabecera, que contiene declaraciones para funciones de entrada y salida, incluyendo . Los corchetes angulares alrededor de indican que el archivo de cabecera se puede localizar mediante una estrategia de búsqueda que selecciona los archivos de cabecera proporcionados con el compilador en lugar de archivos con el mismo nombre que puedan encontrarse en directorios específicos del proyecto.#includestdio.hprintfstdio.h

La siguiente línea de código declara la función de punto de entradamain . El entorno de ejecución llama a esta función para iniciar la ejecución del programa. El especificador de tipo intindica que la función devuelve un valor entero. La voidlista de parámetros indica que la función no consume argumentos. El entorno de ejecución pasa dos argumentos (de tipo inty char *[]), pero esta implementación los ignora. El estándar ISO C (sección 5.1.2.2.1) requiere una sintaxis que sea void o estos dos argumentos , un tratamiento especial que no se aplica a otras funciones. 

La llave de apertura indica el comienzo del código que define la función.

La siguiente línea de código llama (redirige la ejecución a) la función de la biblioteca estándar de C printfcon la dirección del primer carácter de una cadena terminada en nulo especificada como un literal de cadena . El texto \nes una secuencia de escape que indica el carácter de nueva línea , el cual, al mostrarse en una terminal, mueve el cursor al principio de la siguiente línea. Aunque printfdevuelve un intvalor, este se descarta silenciosamente. El punto y coma ;finaliza la instrucción de llamada.

La llave de cierre indica el final de la mainfunción. Antes de C99, return 0;se requería una instrucción explícita al final de la mainfunción, pero desde C99, la mainfunción (al ser la llamada inicial a la función) regresa implícitamente 0al llegar a su llave de cierre final. [ e ]

Historia

Primeros desarrollos

El origen de C está estrechamente ligado al desarrollo del sistema operativo Unix , implementado originalmente en lenguaje ensamblador en un PDP-7 por Dennis Ritchie y Ken Thompson , incorporando varias ideas de sus colegas. Finalmente, decidieron portar el sistema operativo a un PDP-11 . La versión original de Unix para PDP-11 también se desarrolló en lenguaje ensamblador. [ 12 ]

B

Thompson quería un lenguaje de programación para desarrollar utilidades para la nueva plataforma. Primero intentó escribir un compilador de Fortran , pero pronto abandonó la idea y en su lugar creó una versión reducida del lenguaje de programación de sistemas recientemente desarrollado llamado BCPL . La descripción oficial de BCPL no estaba disponible en ese momento, [ 19 ] y Thompson modificó la sintaxis para que fuera menos "prolija" y similar a un ALGOL simplificado conocido como SMALGOL. [ 20 ] Llamó al resultado B , [ 12 ] describiéndolo como "semántica de BCPL con mucha sintaxis de SMALGOL". [ 20 ] Al igual que BCPL, B tenía un compilador de arranque para facilitar la portabilidad a nuevas máquinas. [ 20 ] Finalmente, se escribieron pocas utilidades en B porque era demasiado lento y no podía aprovechar las características del PDP-11, como el direccionamiento por bytes .

A diferencia de BCPL, // commentque marcaba los comentarios hasta el final de la línea, B adoptó /* comment */como delimitador de comentarios un estilo más parecido al de PL/1, permitiendo que los comentarios aparecieran en medio de las líneas. (El estilo de comentarios de BCPL se reintroduciría en C++). [ 12 ]

Nuevo lanzamiento B y primer lanzamiento C

En 1971, Ritchie comenzó a mejorar B para aprovechar las características del más potente PDP-11. Una adición significativa fue un tipo de datos de caracteres. Lo llamó New B (NB). [ 20 ] Thompson comenzó a usar NB para escribir el núcleo de Unix , y sus requisitos moldearon la dirección del desarrollo del lenguaje. [ 20 ] [ 21 ]

Hasta 1972, se añadieron tipos más complejos al lenguaje NB. NB tenía arreglos de inty char, y a estos tipos se añadieron punteros, la capacidad de generar punteros a otros tipos, arreglos de todos los tipos y tipos que podían devolver las funciones. Los arreglos dentro de las expresiones se trataban efectivamente como punteros. Se escribió un nuevo compilador y el lenguaje pasó a llamarse C. [ 12 ]

El compilador C y algunas utilidades creadas con él se incluyeron en la versión 2 de Unix , también conocida como Research Unix . [ 22 ]

Reescritura de estructuras y núcleo Unix

En la versión 4 de Unix , publicada en noviembre de 1973, el núcleo de Unix fue ampliamente reimplementado en C. [ 12 ] Para entonces, el lenguaje C había adquirido algunas características poderosas como los tipos.struct

El preprocesador se introdujo alrededor de 1973 a instancias de Alan Snyder y también en reconocimiento de la utilidad de los mecanismos de inclusión de archivos disponibles en BCPL y PL/I . Su versión original solo proporcionaba archivos incluidos y reemplazos simples de cadenas, #includeasí como #definede macros sin parámetros. Poco después, se amplió, principalmente por Mike Lesk y luego por John Reiser, para incorporar macros con argumentos y compilación condicional . [ 12 ]

Unix fue uno de los primeros núcleos de sistema operativo implementados en un lenguaje distinto al ensamblador . Ejemplos anteriores incluyen el sistema Multics (escrito en PL/I ) y el Programa de Control Maestro (MCP) para el Burroughs B5000 (escrito en ALGOL ) en 1961. Alrededor de 1977, Ritchie y Stephen C. Johnson realizaron cambios adicionales en el lenguaje para facilitar la portabilidad del sistema operativo Unix. El Compilador C Portátil de Johnson sirvió de base para varias implementaciones de C en nuevas plataformas. [ 21 ]

K&R C

La portada del libro El lenguaje de programación C , primera edición, de Brian Kernighan y Dennis Ritchie.

En 1978, Brian Kernighan y Dennis Ritchie publicaron la primera edición de The C Programming Language . [ 23 ] Conocido como K&R por las iniciales de sus autores, el libro sirvió durante muchos años como una especificación informal del lenguaje. La versión de C que describe se conoce comúnmente como " K&R C ". Dado que se publicó en 1978, ahora también se le conoce como C78 . [ 24 ] La segunda edición del libro [ 25 ] cubre el estándar ANSI C posterior , que se describe a continuación.

K&R introdujo varias características lingüísticas:

  • Biblioteca de E/S estándar
  • long inttipo de datos
  • unsigned inttipo de datos
  • Los operadores de asignación compuesta de la forma (como ) se cambiaron a la forma (es decir, ) para eliminar la ambigüedad semántica creada por construcciones como , que se habían interpretado como (decremento de 10) en lugar del posiblemente previsto (sea −10).=op=-op=-=i=-10i =- 10ii = -10i

Incluso después de la publicación del estándar ANSI de 1989, durante muchos años K&R C siguió siendo considerado el " mínimo común denominador " al que se limitaban los programadores de C cuando se deseaba la máxima portabilidad, ya que muchos compiladores antiguos todavía estaban en uso y porque el código K&R C escrito cuidadosamente también puede ser código C estándar válido.

Aunque las versiones posteriores de C requieren que las funciones tengan una declaración de tipo explícita, K&R C solo requiere que las funciones que devuelven un tipo distinto de intse declaren antes de su uso. Se presumía que las funciones utilizadas sin declaración previa devolvían int.

Por ejemplo:

función larga ();función_llamada (){largo longvar ;registrar intvar ;longvar = long_function ();si ( longvar > 1 )intvar = 0 ;demásintvar = int_function ();devolver intvar ;}

La declaración de long_function()(en la línea 1) es necesaria ya que devuelve long; no int. La función int_functionpuede ser llamada (línea 11) aunque no esté declarada ya que devuelve int. Además, la variable intvarno necesita ser declarada como tipo intya que ese es el tipo predeterminado para registerla palabra clave.

Dado que las declaraciones de funciones no incluían información sobre los argumentos, no se realizaban comprobaciones de tipo , aunque algunos compiladores emitían una advertencia si diferentes llamadas a una función utilizaban distintos números o tipos de argumentos. Se desarrollaron herramientas como la utilidad lint de Unix que (entre otras cosas) verificaban la coherencia en el uso de funciones en varios archivos fuente.

En los años posteriores a la publicación de K&R C, se añadieron varias características al lenguaje, con el apoyo de compiladores de AT&T (en particular PCC [ 26 ] ) y otros proveedores. Estas incluían:

La popularidad del lenguaje, la falta de acuerdo sobre las interfaces de la biblioteca estándar y la falta de cumplimiento de la especificación K&R llevaron a esfuerzos de estandarización. [ 27 ]

ANSI C e ISO C

A finales de la década de 1970 y durante la década de 1980, se implementaron versiones de C para una amplia variedad de ordenadores centrales , miniordenadores y microordenadores , incluido el IBM PC , a medida que su popularidad aumentaba significativamente.

En 1983, el Instituto Nacional Estadounidense de Estándares (ANSI) formó un comité, X3J11, para establecer una especificación estándar de C. X3J11 basó el estándar C en la implementación de Unix; sin embargo, la parte no portable de la biblioteca C de Unix se transfirió al grupo de trabajo 1003 del IEEE para que sirviera de base para el estándar POSIX de 1988. En 1989, el estándar C fue ratificado como ANSI X3.159-1989 "Lenguaje de Programación C". Esta versión del lenguaje se conoce comúnmente como ANSI C , Estándar C o, a veces, C89 .

En 1990, la Organización Internacional de Normalización (ISO) adoptó el estándar ANSI C (con cambios de formato) como ISO/IEC 9899:1990, que a veces se denomina C90 . Por lo tanto, los términos "C89" y "C90" se refieren al mismo lenguaje de programación.

ANSI, al igual que otros organismos nacionales de normalización, ya no desarrolla la norma C de forma independiente, sino que se remite a la norma internacional C, mantenida por el grupo de trabajo ISO/IEC JTC1/SC22 /WG14. La adopción nacional de una actualización de la norma internacional suele producirse en el plazo de un año desde su publicación por ISO.

Uno de los objetivos del proceso de estandarización de C era crear un superconjunto de K&R C, incorporando muchas de las características no oficiales introducidas posteriormente. El comité de estándares también incluyó varias características adicionales, como prototipos de funciones (tomados de C++), voidpunteros, compatibilidad con conjuntos de caracteres y configuraciones regionales internacionales , y mejoras en el preprocesador. Si bien la sintaxis para las declaraciones de parámetros se amplió para incluir el estilo utilizado en C++, se siguió permitiendo la interfaz de K&R para garantizar la compatibilidad con el código fuente existente.

C89 es compatible con los compiladores de C actuales, y la mayor parte del código C moderno se basa en él. Cualquier programa escrito únicamente en C estándar y sin suposiciones dependientes del hardware se ejecutará correctamente en cualquier plataforma con una implementación de C compatible, dentro de sus límites de recursos. Sin tales precauciones, los programas pueden compilarse solo en una plataforma determinada o con un compilador específico, debido, por ejemplo, al uso de bibliotecas no estándar, como las bibliotecas de interfaz gráfica de usuario (GUI) , o a la dependencia de atributos específicos del compilador o de la plataforma, como el tamaño exacto de los tipos de datos y el orden de bytes .

En los casos en que el código deba ser compilable por compiladores basados ​​en C que cumplan con el estándar o por compiladores basados ​​en C de K&R, la __STDC__macro se puede utilizar para dividir el código en secciones estándar y de K&R para evitar el uso, en un compilador basado en C de K&R, de características disponibles solo en C estándar.

Tras el proceso de estandarización ANSI/ISO, la especificación del lenguaje C se mantuvo relativamente estática durante varios años. En 1995, se publicó la Enmienda Normativa 1 al estándar C de 1990 (ISO/IEC 9899/AMD1:1995, conocida informalmente como C95) para corregir algunos detalles y añadir un soporte más amplio para conjuntos de caracteres internacionales. [ 28 ]

C99

La norma C fue revisada nuevamente a finales de la década de 1990, lo que llevó a la publicación de ISO/IEC 9899:1999 en 1999, comúnmente conocida como " C99 ". Desde entonces, ha sido modificada tres veces mediante correcciones técnicas. [ 29 ]

C99 introdujo varias características nuevas, incluidas funciones en línea , varios tipos de datos nuevos (incluido long long intun complextipo para representar números complejos ), matrices de longitud variable y miembros de matriz flexibles , soporte mejorado para punto flotante IEEE 754 , soporte para macros variádicas (macros de aridad variable ) y soporte para comentarios de una línea que comienzan con //, como en BCPL o C++. Muchas de estas ya se habían implementado como extensiones en varios compiladores de C.

C99 es en su mayor parte compatible con versiones anteriores de C90, pero es más estricto en algunos aspectos; en particular, una declaración que carece de un especificador de tipo ya no se asume implícitamente. Se define intuna macro estándar con un valor para indicar que se dispone de compatibilidad con C99. GCC , Solaris Studio y otros compiladores de C ahora admiten muchas o todas las nuevas características de C99. Sin embargo, el compilador de C en Microsoft Visual C++ implementa el estándar C89 y aquellas partes de C99 que son necesarias para la compatibilidad con C++11 . [ 30 ]__STDC_VERSION__199901L

Además, el estándar C99 requiere compatibilidad con identificadores que utilizan Unicode en forma de caracteres de escape (por ejemplo \u0040, o \U0001f431) y sugiere compatibilidad con nombres Unicode sin formato.

C11

En 2007 se inició el trabajo en otra revisión del estándar C, denominada informalmente "C1X" hasta su publicación oficial como ISO/IEC 9899:2011 el 8 de diciembre de 2011. El comité de estándares C adoptó directrices para limitar la adopción de nuevas características que no hubieran sido probadas por implementaciones existentes.

El estándar C11 añade numerosas características nuevas a C y a la biblioteca, incluyendo macros genéricas de tipos, estructuras anónimas, soporte Unicode mejorado, operaciones atómicas, multihilo y funciones con comprobación de límites. También hace que algunas partes de la biblioteca C99 existente sean opcionales y mejora la compatibilidad con C++. La macro estándar __STDC_VERSION__se define para 201112Lindicar que el soporte para C11 está disponible.

C17

C17 es el nombre informal de ISO/IEC 9899:2018, un estándar para el lenguaje de programación C publicado en junio de 2018. No introduce nuevas características del lenguaje, solo correcciones técnicas y aclaraciones a defectos de C11. La macro estándar __STDC_VERSION__se define para 201710Lindicar que se dispone de soporte para C17.

C23

C23 es un nombre informal para la revisión principal actual del estándar del lenguaje C. Se la conocía como "C2X" durante la mayor parte de su desarrollo. Se basa en versiones anteriores e introduce características como nuevas palabras clave, un significado adicional para autoproporcionar inferencia de tipos al declarar variables, nuevos tipos, incluidos nullptr_ty _BitInt(N), y expansiones de la biblioteca estándar. [ 31 ]

C23 se publicó en octubre de 2024 como ISO/IEC 9899:2024. [ 32 ] La macro estándar __STDC_VERSION__se define para 202311Lindicar que hay soporte para C23.

C2Y

C2Y es un nombre informal para la próxima revisión importante del estándar del lenguaje C, después de C23 (C2X), que se espera que se publique a finales de la década de 2020, de ahí el '2' en "C2Y". Un borrador inicial de C2Y fue publicado en febrero de 2024 como N3220 por el grupo de trabajo ISO/IEC JTC1/SC22 /WG14. [ 33 ]

C incrustado

Históricamente, la programación en C para sistemas embebidos requiere extensiones no estándar del lenguaje C para admitir características poco comunes como aritmética de punto fijo , múltiples bancos de memoria distintos y operaciones básicas de entrada/salida.

En 2008, el Comité de Estándares C publicó un informe técnico que extendía el lenguaje C [ 34 ] para abordar estos problemas, proporcionando un estándar común al que todas las implementaciones debían adherirse. Incluye varias características no disponibles en C estándar, como aritmética de punto fijo, espacios de direcciones con nombre y direccionamiento básico de hardware de E/S.

Definición

C tiene una gramática formal especificada por el estándar C. [ 35 ] Los finales de línea generalmente no son significativos en C; sin embargo, los límites de línea sí tienen importancia durante la fase de preprocesamiento. Los comentarios pueden aparecer entre los delimitadores /*y */, o (desde C99) después //hasta el final de la línea. Los comentarios delimitados por /*y */no se anidan, y estas secuencias de caracteres no se interpretan como delimitadores de comentarios si aparecen dentro de literales de cadena o de caracteres. [ 36 ]

Los archivos fuente de C contienen declaraciones y definiciones de funciones. Las definiciones de funciones, a su vez, contienen declaraciones e instrucciones . Las declaraciones definen nuevos tipos usando palabras clave como `type` struct, union`type` y enum`type`, o asignan tipos a las variables y, posiblemente, reservan espacio para nuevas variables, generalmente escribiendo el tipo seguido del nombre de la variable. Palabras clave como `type` chary int`type` especifican tipos integrados. Las secciones de código se encierran entre llaves (`type` {y }`type`, a veces llamadas "corchetes") para limitar el alcance de las declaraciones y para que actúen como una sola instrucción para las estructuras de control.

Como lenguaje imperativo, C utiliza sentencias para especificar acciones. La sentencia más común es una sentencia de expresión , que consiste en una expresión a evaluar, seguida de un punto y coma; como efecto secundario de la evaluación, se pueden llamar funciones y asignar nuevos valores a las variables. Para modificar la ejecución secuencial normal de las sentencias, C proporciona varias sentencias de control de flujo identificadas por palabras clave reservadas. La programación estructurada es compatible con la ejecución condicional if... [ ] y con ... , , y la ejecución iterativa (bucles). La sentencia tiene expresiones separadas de inicialización, prueba y reinicialización, cualquiera o todas las cuales se pueden omitir. y se pueden usar dentro del bucle. Break se usa para salir de la sentencia de bucle más interna y continue se usa para saltar a su reinicialización. También hay una sentencia no estructurada, que se ramifica directamente a la etiqueta designada dentro de la función. selecciona una para ser ejecutada en función del valor de una expresión entera. A diferencia de muchos otros lenguajes, el flujo de control continuará con la siguiente a menos que sea terminada por una .elsedowhilewhileforforbreakcontinuegotoswitchcasecasebreak

Las expresiones pueden usar una variedad de operadores integrados y pueden contener llamadas a funciones. El orden en que se evalúan los argumentos de las funciones y los operandos de la mayoría de los operadores no está especificado. Las evaluaciones incluso pueden estar intercaladas. Sin embargo, todos los efectos secundarios (incluido el almacenamiento en variables) ocurrirán antes del siguiente " punto de secuencia "; los puntos de secuencia incluyen el final de cada instrucción de expresión y la entrada y salida de cada llamada a función. Los puntos de secuencia también ocurren durante la evaluación de expresiones que contienen ciertos operadores ( &&, ||, ?:y el operador coma ). Esto permite un alto grado de optimización del código objeto por parte del compilador, pero requiere que los programadores de C tengan más cuidado para obtener resultados confiables que en otros lenguajes de programación.

Kernighan y Ritchie afirman en la Introducción de El lenguaje de programación C : «C, como cualquier otro lenguaje, tiene sus defectos. Algunos operadores tienen la precedencia incorrecta; algunas partes de la sintaxis podrían mejorarse». [ 37 ] El estándar C no intentó corregir muchos de estos defectos, debido al impacto que tales cambios tendrían en el software ya existente.

Conjunto de caracteres

El conjunto básico de caracteres fuente de C incluye los siguientes caracteres: [ 38 ]

El carácter de nueva línea indica el final de una línea de texto; no tiene por qué corresponder a un único carácter real, aunque por comodidad C lo trata como tal.

El estándar POSIX exige un conjunto de caracteres portátil que añade algunos caracteres (en particular, "@") al conjunto de caracteres básicos del código fuente de C. Ambos estándares no prescriben ninguna codificación de valores en particular: tanto ASCII como EBCDIC cumplen con estos estándares, ya que incluyen al menos esos caracteres básicos, aunque utilicen valores codificados diferentes para ellos.

Se pueden usar caracteres codificados multibyte adicionales en literales de cadena , pero no son completamente portátiles . Dado que los caracteres Unicode multinacionales C99\uXXXX se pueden incrustar de forma portátil dentro del texto fuente C usando o \UXXXXXXXXcodificación (donde Xdenota un carácter hexadecimal).

El conjunto básico de caracteres de ejecución de C contiene los mismos caracteres, junto con representaciones para el carácter nulo , alerta , retroceso y retorno de carro . [ 38 ]

La compatibilidad en tiempo de ejecución con conjuntos de caracteres extendidos ha aumentado con cada revisión del estándar C.

Palabras reservadas

Todas las versiones de C tienen palabras reservadas que distinguen entre mayúsculas y minúsculas . Al ser palabras reservadas, no se pueden usar para nombres de variables.

C89 tiene 32 palabras reservadas:

C99 añadió cinco palabras reservadas más: (‡ indica un alias ortográfico alternativo para una palabra clave C23)

C11 añadió siete palabras reservadas más: [ 39 ] (‡ indica un alias ortográfico alternativo para una palabra clave C23)

  • _Alignas
  • _Alignof
  • _Atomic
  • _Generic
  • _Noreturn
  • _Static_assert
  • _Thread_local

C23 se reservó quince palabras más:

  • alignas
  • alignof
  • bool
  • constexpr
  • false
  • nullptr
  • static_assert
  • thread_local
  • true
  • typeof
  • typeof_unqual
  • _BitInt
  • _Decimal32
  • _Decimal64
  • _Decimal128

La mayoría de las palabras reservadas recientemente comienzan con un guion bajo seguido de una letra mayúscula, ya que los identificadores de ese tipo estaban previamente reservados por el estándar C para su uso exclusivo por parte de las implementaciones. Dado que el código fuente de los programas existentes no debería haber estado utilizando estos identificadores, no se vería afectado cuando las implementaciones de C comenzaran a admitir estas extensiones del lenguaje de programación. Algunos encabezados estándar definen sinónimos más convenientes para los identificadores con guion bajo. Algunas de esas palabras se agregaron como palabras clave con su ortografía convencional en C23 y se eliminaron las macros correspondientes.

Antes de C89, entryestaba reservada como palabra clave. En la segunda edición de su libro The C Programming Language , que describe lo que se conoció como C89, Kernighan y Ritchie escribieron: «La ... [palabra clave] entry, anteriormente reservada pero nunca usada, ya no está reservada» y «La entrypalabra clave muerta se retira». [ 40 ]

Operadores

C admite un amplio conjunto de operadores , que son símbolos utilizados dentro de una expresión para especificar las manipulaciones que se realizarán al evaluar dicha expresión. C dispone de operadores para:

C utiliza el operador =(utilizado en matemáticas para expresar igualdad) para indicar asignación, siguiendo el precedente de Fortran y PL/I , pero a diferencia de ALGOL y sus derivados, C utiliza el operador ==para comprobar la igualdad. La similitud entre los operadores de asignación e igualdad puede dar lugar al uso accidental de uno en lugar del otro, y en muchos casos el error no produce un mensaje de error (aunque algunos compiladores producen advertencias). Por ejemplo, la expresión condicional if (a == b + 1)podría escribirse erróneamente como if (a = b + 1), que se evaluará como truea menos que el valor de aesté 0después de la asignación. [ 41 ]

La precedencia de operadores en C no siempre es intuitiva. Por ejemplo, el operador ==se vincula más estrechamente que (se ejecuta antes que) los operadores &(AND bit a bit) y |(OR bit a bit) en expresiones como x & 1 == 0, que deben escribirse como (x & 1) == 0si esa es la intención del programador. [ 42 ]

Tipos de datos

El sistema de tipos en C es estático y débilmente tipado , lo que lo hace similar al sistema de tipos de descendientes de ALGOL como Pascal . [ 43 ] Hay tipos integrados para enteros de varios tamaños, tanto con signo como sin signo, números de punto flotante y tipos enumerados ( enum). El tipo entero charse usa a menudo para caracteres de un solo byte. C99 agregó un tipo de datos booleano . También hay tipos derivados que incluyen arreglos , punteros , registros ( struct) y uniones ( union).

C se usa frecuentemente en la programación de sistemas de bajo nivel, donde puede ser necesario eludir el sistema de tipos. El compilador intenta garantizar la corrección de tipos de la mayoría de las expresiones, pero el programador puede anular estas comprobaciones de diversas maneras, ya sea mediante una conversión explícita de tipos para transformar un valor de un tipo a otro, o mediante el uso de punteros o uniones para reinterpretar los bits subyacentes de un objeto de datos de alguna otra forma.

Algunos consideran que la sintaxis de declaración de C es poco intuitiva, especialmente para los punteros a funciones . (La idea de Ritchie era declarar identificadores en contextos que se asemejaran a su uso: " la declaración refleja el uso "). [ 44 ]

Las conversiones aritméticas habituales de C permiten generar código eficiente, pero a veces pueden producir resultados inesperados. Por ejemplo, la comparación de enteros con signo y sin signo de igual longitud requiere convertir el valor con signo a sin signo. Esto puede generar resultados inesperados si el valor con signo es negativo.

Consejos

C admite el uso de punteros , un tipo de referencia que registra la dirección o ubicación de un objeto o función en memoria. Los punteros se pueden desreferenciar para acceder a los datos almacenados en la dirección a la que apuntan o para invocar una función a la que apuntan. Los punteros se pueden manipular mediante asignación o aritmética de punteros . La representación en tiempo de ejecución de un valor de puntero suele ser una dirección de memoria sin procesar (posiblemente aumentada con un campo de desplazamiento dentro de la palabra), pero dado que el tipo de un puntero incluye el tipo del elemento al que apunta, las expresiones que incluyen punteros se pueden verificar en tiempo de compilación. La aritmética de punteros se escala automáticamente según el tamaño del tipo de datos al que apunta.

En C, los punteros se utilizan para muchos propósitos. Las cadenas de texto se manipulan comúnmente mediante punteros a matrices de caracteres. La asignación dinámica de memoria se realiza mediante punteros; el resultado de una operación se conviertemalloc generalmente al tipo de datos que se almacenarán. Muchos tipos de datos, como los árboles , se implementan comúnmente como objetos asignados dinámicamente y enlazados mediante punteros. Los punteros a otros punteros se utilizan a menudo en matrices multidimensionales y matrices de objetos. Los punteros a funciones ( punteros a funciones ) son útiles para pasar funciones como argumentos a funciones de orden superior (como qsort o bsearch ), en tablas de despacho o como funciones de devolución de llamada a manejadores de eventos . [ 18 ]structstruct

Un valor de puntero nulo apunta explícitamente a ninguna ubicación válida. La desreferenciación de un valor de puntero nulo no está definida, lo que a menudo resulta en un fallo de segmentación . Los valores de puntero nulo son útiles para indicar casos especiales, como la ausencia de un puntero "siguiente" en el nodo final de una lista enlazada , o como indicación de error de funciones que devuelven punteros. En contextos apropiados en el código fuente, como para asignar a una variable de puntero, una constante de puntero nulo se puede escribir como 0, con o sin conversión explícita a un tipo de puntero, como la NULLmacro definida por varios encabezados estándar o, desde C23 con la constante nullptr. En contextos condicionales, los valores de puntero nulo se evalúan como false, mientras que todos los demás valores de puntero se evalúan como true.

Los punteros void ( void *) apuntan a objetos de tipo no especificado y, por lo tanto, pueden usarse como punteros de datos "genéricos". Dado que se desconoce el tamaño y el tipo del objeto al que apuntan, no se pueden desreferenciar los punteros void ni se permite la aritmética de punteros sobre ellos, aunque se pueden convertir fácilmente (y en muchos contextos se convierten implícitamente) a y desde cualquier otro tipo de puntero de objeto. [ 18 ]

Careless use of pointers is potentially dangerous. Because they are typically unchecked, a pointer variable can be made to point to any arbitrary location, which can cause undesirable effects. Although properly used pointers point to safe places, they can be made to point to unsafe places by using invalid pointer arithmetic; the objects they point to may continue to be used after deallocation (dangling pointers); they may be used without having been initialized (wild pointers); or they may be directly assigned an unsafe value using a cast, union, or through another corrupt pointer. In general, C is permissive in allowing manipulation of and conversion between pointer types, although compilers typically provide options for various levels of checking. Some other programming languages address these problems by using more restrictive reference types.

Arrays

Array types in C are traditionally of a fixed, static size specified at compile time. The more recent C99 standard also allows a form of variable-length arrays. However, it is also possible to allocate a block of memory (of arbitrary size) at run time, using the standard library's malloc function, and treat it as an array.

Since arrays are always accessed (in effect) via pointers, array accesses are typically not checked against the underlying array size, although some compilers may provide bounds checking as an option.[45][46] Array bounds violations are therefore possible and can lead to various repercussions, including illegal memory accesses, corruption of data, buffer overflows, and run-time exceptions.

C does not have a special provision for declaring multi-dimensional arrays, but rather relies on recursion within the type system to declare arrays of arrays, which effectively accomplishes the same thing. The index values of the resulting "multi-dimensional array" can be thought of as increasing in row-major order. Multi-dimensional arrays are commonly used in numerical algorithms (mainly from applied linear algebra) to store matrices. The structure of the C array is well suited to this particular task. However, in early versions of C the bounds of the array must be known fixed values or else explicitly passed to any subroutine that requires them, and dynamically sized arrays of arrays cannot be accessed using double indexing. (A workaround for this was to allocate the array with an additional "row vector" of pointers to the columns.) C99 introduced "variable-length arrays" which address this issue.

El siguiente ejemplo, utilizando C moderno (C99 o posterior), muestra la asignación de una matriz bidimensional en el montón y el uso de la indexación de matrices multidimensionales para los accesos (que puede utilizar la comprobación de límites en muchos compiladores de C):

int func ( int n , int m ) {float ( * p )[ n ][ m ] = malloc ( sizeof * p );si ( p == NULL ) {devolver -1 ;}para ( int i = 0 ; i < n ; i ++ ) {para ( int j = 0 ; j < m ; j ++ ) {( * p )[ i ][ j ] = i + j ;}}imprimir_array ( n , m , p );libre ( p );devolver 1 ;}

Y aquí hay una implementación similar usando la función Auto VLA de C99 : [ f ]

int func ( int n , int m ) {// Precaución: se deben realizar comprobaciones para garantizar que n * m * sizeof(float) NO exceda las limitaciones para los VLA automáticos y esté dentro del tamaño disponible de la pila.float p [ n ][ m ]; // auto VLA se mantiene en la pila y se dimensiona cuando se invoca la funciónpara ( int i = 0 ; i < n ; i ++ ) {para ( int j = 0 ; j < m ; j ++ ) {p [ i ][ j ] = i + j ;}}imprimir_array ( n , m , p );// No es necesario liberar la memoria (free(p)) ya que desaparecerá cuando la función finalice, junto con el resto del marco de pila.devolver 1 ;}

Intercambiabilidad de punteros a matrices

La notación de subíndice x[i](donde xdesigna un puntero) es azúcar sintáctico para *(x+i). [ 47 ] Aprovechando el conocimiento del compilador sobre el tipo de puntero, la dirección a la que x + iapunta no es la dirección base (a la que apunta x) incrementada en ibytes, sino que se define como la dirección base incrementada en imultiplicada por el tamaño de un elemento al que xapunta. Por lo tanto, x[i]designa el i+1-ésimo elemento del array.

Además, en la mayoría de los contextos de expresiones (una excepción notable es como operando de sizeof), una expresión de tipo array se convierte automáticamente en un puntero al primer elemento del array. Esto implica que un array nunca se copia en su totalidad cuando se nombra como argumento de una función, sino que solo se pasa la dirección de su primer elemento. Por lo tanto, aunque las llamadas a funciones en C utilizan la semántica de paso por valor , los arrays se pasan en efecto por referencia .

El tamaño total de un array xse puede determinar aplicando sizeofa una expresión de tipo array. El tamaño de un elemento se puede determinar aplicando el operador sizeofa cualquier elemento desreferenciado de un array A, como en n = sizeof A[0]. Por lo tanto, el número de elementos en un array declarado Ase puede determinar como sizeof A / sizeof A[0]. Tenga en cuenta que si solo se dispone de un puntero al primer elemento, como suele ocurrir en el código C debido a la conversión automática descrita anteriormente, se pierde la información sobre el tipo completo del array y su longitud.

Gestión de la memoria

Una de las funciones más importantes de un lenguaje de programación es proporcionar herramientas para gestionar la memoria y los objetos que se almacenan en ella. C proporciona tres formas principales de asignar memoria para objetos: [ 18 ]

  • Asignación de memoria estática : el espacio para el objeto se proporciona en el binario en tiempo de compilación; estos objetos tienen una extensión (o vida útil) mientras el binario que los contiene esté cargado en la memoria.
  • Asignación automática de memoria : los objetos temporales se pueden almacenar en la pila , y este espacio se libera automáticamente y se puede reutilizar una vez que se sale del bloque en el que se declaran.
  • Asignación dinámica de memoria : se pueden solicitar bloques de memoria de tamaño arbitrario en tiempo de ejecución utilizando funciones de biblioteca como mallocdesde una región de memoria llamada montón ; estos bloques persisten hasta que posteriormente se liberan para su reutilización llamando a la función de biblioteca realloco free.

Estos tres enfoques son apropiados en diferentes situaciones y presentan diversas ventajas y desventajas. Por ejemplo, la asignación de memoria estática tiene una sobrecarga mínima, la asignación automática puede implicar una sobrecarga ligeramente mayor, y la asignación de memoria dinámica puede tener una sobrecarga considerable tanto para la asignación como para la liberación. La persistencia de los objetos estáticos resulta útil para mantener la información de estado entre llamadas a funciones; la asignación automática es fácil de usar, pero el espacio de la pila suele ser mucho más limitado y transitorio que la memoria estática o el espacio del montón; y la asignación de memoria dinámica permite una asignación conveniente de objetos cuyo tamaño solo se conoce en tiempo de ejecución. La mayoría de los programas en C utilizan ampliamente los tres enfoques.

Siempre que sea posible, la asignación automática o estática suele ser la más sencilla, ya que el compilador gestiona el almacenamiento, liberando al programador de la tarea, potencialmente propensa a errores, de asignar y liberar manualmente el almacenamiento. Sin embargo, muchas estructuras de datos pueden cambiar de tamaño en tiempo de ejecución, y dado que las asignaciones estáticas (y las automáticas antes de C99) deben tener un tamaño fijo en tiempo de compilación, existen muchas situaciones en las que la asignación dinámica es necesaria. [ 18 ] Antes del estándar C99, los arreglos de tamaño variable eran un ejemplo común de esto. (Véase el artículo sobre asignación dinámica de memoria en C para un ejemplo de arreglos asignados dinámicamente). A diferencia de la asignación automática, que puede fallar en tiempo de ejecución con consecuencias incontrolables, las funciones de asignación dinámica devuelven una indicación (en forma de un valor de puntero nulo) cuando no se puede asignar el almacenamiento requerido. (La asignación estática demasiado grande suele ser detectada por el enlazador o el cargador , incluso antes de que el programa pueda comenzar su ejecución).

Salvo que se especifique lo contrario, los objetos estáticos contienen punteros nulos o con valor cero al iniciar el programa. Los objetos asignados de forma automática y dinámica se inicializan solo si se especifica explícitamente un valor inicial; de lo contrario, inicialmente tienen valores indeterminados (normalmente, cualquier patrón de bits presente en el almacenamiento , que podría no representar un valor válido para ese tipo). Si el programa intenta acceder a un valor no inicializado, el resultado es indefinido. Muchos compiladores modernos intentan detectar este problema y advertir sobre él, pero pueden producirse tanto falsos positivos como falsos negativos .

La asignación de memoria en el montón debe sincronizarse con su uso real en cualquier programa para que se reutilice al máximo. Por ejemplo, si el único puntero a una asignación de memoria en el montón queda fuera de ámbito o su valor se sobrescribe antes de que se desasigne explícitamente, esa memoria no se puede recuperar para su posterior reutilización y se pierde para el programa, un fenómeno conocido como fuga de memoria . Por el contrario, es posible que la memoria se libere pero se referencie posteriormente, lo que conlleva resultados impredecibles. Normalmente, los síntomas del fallo aparecen en una parte del programa que no está relacionada con el código que causa el error, lo que dificulta su diagnóstico. Estos problemas se mitigan en lenguajes con recolección automática de basura .

Bibliotecas

El lenguaje de programación C utiliza bibliotecas como su principal método de extensión. En C, una biblioteca es un conjunto de funciones contenidas en un único archivo de "archivo". Cada biblioteca suele tener un archivo de cabecera que contiene los prototipos de las funciones que contiene y que un programa puede utilizar, así como las declaraciones de tipos de datos especiales y símbolos de macro utilizados con estas funciones. Para que un programa utilice una biblioteca, debe incluir su archivo de cabecera y enlazarla con el programa, lo que en muchos casos requiere indicadores del compilador (por ejemplo, -lm`link the math library`, abreviatura de "link the math library"). [ 18 ]

La biblioteca C más común es la biblioteca estándar C , especificada por las normas ISO y ANSI C , que viene incluida en todas las implementaciones de C (las implementaciones destinadas a entornos limitados, como los sistemas embebidos, pueden proporcionar solo un subconjunto de la biblioteca estándar). Esta biblioteca admite entrada y salida de flujo, asignación de memoria, operaciones matemáticas, cadenas de caracteres y valores de tiempo. Varios archivos de cabecera estándar independientes (por ejemplo, stdio.h) especifican las interfaces para estas y otras funcionalidades de la biblioteca estándar.

Otro conjunto común de funciones de la biblioteca C son aquellas utilizadas por aplicaciones específicamente diseñadas para sistemas Unix y similares a Unix , especialmente las funciones que proporcionan una interfaz con el núcleo . Estas funciones se detallan en varios estándares, como POSIX y la Especificación Única de UNIX .

Dado que muchos programas se han escrito en C, existe una amplia variedad de otras bibliotecas disponibles. Las bibliotecas suelen escribirse en C porque los compiladores de C generan código objeto eficiente ; luego, los programadores crean interfaces para la biblioteca de modo que las rutinas puedan utilizarse desde lenguajes de alto nivel como Java , Perl y Python . [ 18 ]

Manejo de archivos y flujos

File input and output (I/O) is not part of the C language itself but instead is handled by libraries (such as the C standard library) and their associated header files (e.g. stdio.h). File handling is generally implemented through high-level I/O which works through streams. A stream is from this perspective a data flow that is independent of devices, while a file is a concrete device. The high-level I/O is done through the association of a stream to a file. In the C standard library, a buffer (a memory area or queue) is temporarily used to store data before it is sent to the final destination. This reduces the time spent waiting for slower devices, for example a hard drive or solid-state drive. Low-level I/O functions are not part of the standard C library but are generally part of "bare metal" programming (programming that is independent of any operating system such as most embedded programming). With few exceptions, implementations include low-level I/O.

Language tools

A number of tools have been developed to help C programmers find and fix statements with undefined behavior or possibly erroneous expressions, with greater rigor than that provided by the compiler.

Automated source code checking and auditing tools exist, such as Lint. A common practice is to use Lint to detect questionable code when a program is first written. Once a program passes Lint, it is then compiled using the C compiler. Also, many compilers can optionally warn about syntactically valid constructs that are likely to actually be errors. MISRA C is a proprietary set of guidelines to avoid such questionable code, developed for embedded systems.[48]

There are also compilers, libraries, and operating system level mechanisms for performing actions that are not a standard part of C, such as bounds checking for arrays, detection of buffer overflow, serialization, dynamic memory tracking, and automatic garbage collection.

Memory management checking tools like Purify or Valgrind and linking with libraries containing special versions of the memory allocation functions can help uncover run-time errors in memory usage.[49][50]

Uses

C has been widely used to implement end-user and system-level applications.[51]

Rationale for use in systems programming

Some software written in C

C se utiliza ampliamente para la programación de sistemas en la implementación de sistemas operativos y aplicaciones de sistemas embebidos . [ 52 ] Esto se debe a varias razones:

  • El lenguaje C permite acceder al hardware y la memoria de la plataforma mediante punteros y tipado aleatorio , de modo que las características específicas del sistema (por ejemplo, registros de control/estado , registros de E/S ) se pueden configurar y utilizar con código escrito en C; permite un control total de la plataforma en la que se ejecuta.
  • El código generado por la compilación no requiere muchas características del sistema y se puede invocar desde algún código de arranque de forma sencilla; es fácil de ejecutar.
  • Las sentencias y expresiones del lenguaje C suelen corresponderse bien con las secuencias de instrucciones del procesador de destino y, en consecuencia, requieren pocos recursos del sistema durante la ejecución ; su ejecución es rápida.
  • Gracias a su amplio conjunto de operadores, el lenguaje C puede aprovechar muchas de las características de las CPU de destino. Si una CPU específica dispone de instrucciones más especializadas, se puede crear una variante del lenguaje con funciones intrínsecas para explotar dichas instrucciones, lo que permite utilizar prácticamente todas las características de la CPU de destino.
  • Este lenguaje facilita la superposición de estructuras sobre bloques de datos binarios, lo que permite comprender, navegar y modificar los datos; incluso puede escribir estructuras de datos y sistemas de archivos.
  • El lenguaje admite un amplio conjunto de operadores, incluida la manipulación de bits, para aritmética y lógica de enteros, y posiblemente diferentes tamaños de números de coma flotante; puede procesar datos estructurados adecuadamente de manera efectiva.
  • C es un lenguaje bastante pequeño, con solo un puñado de instrucciones y sin demasiadas características que generen código de destino extenso; es comprensible.
  • C tiene control directo sobre la asignación y liberación de memoria, lo que proporciona una eficiencia razonable y una sincronización predecible a las operaciones de manejo de memoria, sin ninguna preocupación por eventos esporádicos de recolección de basura que detienen el sistema ; tiene un rendimiento predecible.
  • C permite el uso e implementación de diferentes esquemas de asignación de memoria , incluyendo uno típico mallocy free; un mecanismo más sofisticado con arenas ; o una versión para un núcleo de sistema operativo que puede adaptarse a DMA , uso dentro de manejadores de interrupciones o integrado con el sistema de memoria virtual .
  • Dependiendo del enlazador y del entorno, el código C también puede llamar a bibliotecas escritas en lenguaje ensamblador , y puede ser llamado desde lenguaje ensamblador; además, interactúa bien con otro código de nivel inferior.
  • C, sus convenciones de llamada y sus estructuras de enlace se utilizan habitualmente junto con otros lenguajes de alto nivel, y se admiten llamadas tanto a C como desde C; por lo tanto, interactúa bien con otro código de alto nivel.
  • C cuenta con un ecosistema maduro y extenso, que incluye bibliotecas, frameworks, compiladores de código abierto, depuradores y utilidades, y es el estándar de facto. Es probable que los controladores ya existan en C, o que exista una arquitectura de CPU similar como back-end de un compilador de C, por lo que hay menos incentivos para elegir otro lenguaje.

Juegos

Los videojuegos suelen desarrollarse a partir de una combinación de lenguajes. C ha tenido un papel importante, especialmente en aquellos juegos que buscan obtener el máximo rendimiento de las plataformas informáticas. Un ejemplo es Doom , de 1993. [ 53 ]

World Wide Web

Históricamente, C se usó a veces para el desarrollo web usando la Interfaz de Puerta de Enlace Común (CGI) como una "puerta de enlace" para la información entre la aplicación web, el servidor y el navegador. [ 54 ] C pudo haber sido elegido sobre los lenguajes interpretados debido a su velocidad, estabilidad y disponibilidad casi universal. [ 55 ] Ya no es una práctica común que el desarrollo web se haga en C, [ 56 ] y muchos otros lenguajes de desarrollo web son populares. Las aplicaciones donde el desarrollo web basado en C continúa incluyen las páginas de configuración HTTP en enrutadores , dispositivos IoT y similares, aunque incluso aquí algunos proyectos tienen partes en lenguajes de nivel superior, por ejemplo, el uso de Lua dentro de OpenWRT .

Dos servidores web populares , Apache HTTP Server y Nginx , están escritos en C. [ 57 ] [ 58 ] El enfoque cercano al metal de C permite la construcción de estos sistemas de software de alto rendimiento. [ 59 ]

C como lenguaje intermedio

C se utiliza a veces como lenguaje intermedio en implementaciones de otros lenguajes. Este enfoque puede emplearse por motivos de portabilidad o conveniencia; al usar C como lenguaje intermedio, no se necesitan generadores de código adicionales específicos de la máquina. C posee algunas características, como las directivas de preprocesador de número de línea y las comas superfluas opcionales al final de las listas de inicialización, que facilitan la compilación del código generado. Sin embargo, algunas de las limitaciones de C han impulsado el desarrollo de otros lenguajes basados ​​en C diseñados específicamente para su uso como lenguajes intermedios, como C-- . Además, los principales compiladores contemporáneos , GCC y LLVM, ofrecen una representación intermedia que no es C, y admiten interfaces para muchos lenguajes, incluido C.

Bibliotecas computacionalmente intensivas

C permite a los programadores crear implementaciones eficientes de algoritmos y estructuras de datos, ya que la capa de abstracción del hardware es delgada y su sobrecarga es baja, un criterio importante para programas que requieren mucha computación. Por ejemplo, la Biblioteca Aritmética de Precisión Múltiple de GNU , la Biblioteca Científica de GNU , Mathematica y MATLAB están escritas total o parcialmente en C. Muchos lenguajes admiten la llamada a funciones de biblioteca en C; por ejemplo, el framework NumPy, basado en Python , utiliza C para los aspectos de alto rendimiento e interacción con el hardware.

Otros lenguajes están escritos en C.

Una consecuencia de la amplia disponibilidad y eficiencia de C es que los compiladores , bibliotecas e intérpretes de otros lenguajes de programación a menudo se implementan en C. [ 60 ] Por ejemplo, las implementaciones de referencia de Python , [ 61 ] Perl , [ 62 ] Ruby , [ 63 ] y PHP [ 64 ] están escritas en C.

Limitaciones

El propio Ritchie bromeó sobre las limitaciones del lenguaje que creó: [ 65 ]

el poder del lenguaje ensamblador y la conveniencia del... lenguaje ensamblador

Dennis Ritchie

Si bien C es popular, influyente y enormemente exitosa, tiene inconvenientes, entre ellos:

  • El manejo estándar de memoria dinámicamalloc con y freees propenso a errores. Un uso inadecuado puede provocar fugas de memoria y punteros colgantes . [ 66 ]
  • El uso de punteros y la manipulación directa de la memoria implican que es posible la corrupción de la misma.
  • Existe comprobación de tipos , pero no se aplica a ciertas áreas como las funciones variádicas , y dicha comprobación puede eludirse de forma trivial o inadvertida. Es de tipado débil , a pesar de ser de tipado estático.
  • Dado que el código generado por el compilador contiene pocas comprobaciones en tiempo de ejecución, el programador tiene la responsabilidad de considerar todos los resultados posibles, protegerse contra desbordamientos de búfer, comprobar los límites de los arreglos, desbordamientos de pila , agotamiento de memoria y tener en cuenta las condiciones de carrera , el aislamiento de hilos, etc.
  • El uso de punteros y su manipulación en tiempo de ejecución permite dos formas de acceder a los mismos datos (alias), lo cual no siempre es determinable en tiempo de compilación. Esto significa que algunas optimizaciones disponibles para otros lenguajes, como Fortran, no son posibles en C. Por esta razón, a veces se considera que Fortran es más rápido. [ 67 ]
  • Algunas de las funciones de la biblioteca estándar, por ejemplo scanfo strncat, pueden provocar desbordamientos de búfer .
  • Existe una estandarización limitada en el soporte para variantes de bajo nivel en el código generado, como diferentes convenciones de llamada a funciones e ABI ; diferentes convenciones de empaquetado de estructuras ; y diferente orden de bytes dentro de enteros grandes (incluido el endianness). En muchas implementaciones de lenguaje, algunas de estas opciones pueden manejarse con la directiva del preprocesador #pragma, [ 68 ] [ 69 ] y otras con palabras clave adicionales, por ejemplo, use __cdeclcalling convention. La directiva y las opciones no son compatibles de forma consistente. [ 70 ]
  • El manejo de cadenas de caracteres mediante la biblioteca estándar requiere mucho código y una gestión explícita de la memoria.
  • El lenguaje no admite directamente la orientación a objetos, la introspección , la evaluación de expresiones en tiempo de ejecución (como evalen JavaScript), la recolección de basura, etc.
  • Hay pocas medidas de protección contra el mal uso de las características del lenguaje, lo que puede dar lugar a código difícil de mantener . En particular, el preprocesador de C puede ocultar efectos problemáticos como la doble evaluación y otros peores. [ 71 ] Esta capacidad para el código ofuscado se ha celebrado con concursos como el Concurso Internacional de Código C Ofuscado y el Concurso Underhanded C.
  • C carece de soporte estándar para el manejo de excepciones y solo ofrece códigos de retorno para la comprobación de errores. Las setjmpfuncioneslongjmp de la biblioteca estándar se han utilizado [ 72 ] para implementar un mecanismo try-catch mediante macros. Además, gotolas sentencias se utilizan comúnmente para el manejo de errores.

Medidas de mitigación para los problemas de C

Para ciertos fines, se han adoptado estilos restringidos de C, por ejemplo, MISRA C o CERT C , en un intento de reducir la posibilidad de comportamientos no deseados. [ 73 ] Bases de datos como CWE intentan contabilizar las formas en que los sistemas en general, especialmente aquellos codificados en C, presentan vulnerabilidades potenciales, junto con recomendaciones para su mitigación.

Existen herramientas que pueden mitigar algunos de los inconvenientes. Los compiladores de C modernos incluyen comprobaciones que pueden generar advertencias para ayudar a identificar muchos errores potenciales.

El uso de punteros en C puede hacerse menos riesgoso mediante el uso de extensiones de la arquitectura del conjunto de instrucciones, como CHERI o Permission Overlay Extensions . Estas técnicas modifican la naturaleza fundamental de los punteros a nivel de hardware para incluir comprobaciones de límites y propósitos, lo que puede ayudar a prevenir desbordamientos de búfer y accesos inapropiados al montón.

Desde principios de la década de 2020, el kernel de Linux tiene secciones escritas en Rust , un lenguaje que cuenta con medidas específicas para mejorar la seguridad. [ 74 ]

Índice TIOBE

Muchos lenguajes desarrollados después de C fueron influenciados por C y tomaron prestados aspectos de C, incluidos C++ , C# , C shell , D , Go , Java , JavaScript , Julia , Limbo , LPC , Objective-C , Perl , PHP , Python , Ruby , Rust , Swift , Verilog y SystemVerilog . [ 8 ] [ 75 ] Algunos afirman que la influencia más generalizada ha sido sintáctica : que estos lenguajes combinan la sintaxis de sentencias y expresiones de C con sistemas de tipos, modelos de datos y estructuras de programas a gran escala que difieren de las de C, a veces radicalmente.

Existen varios intérpretes de C o lenguajes similares a C, como Ch y CINT , que también se pueden utilizar para la creación de scripts.

Cuando los lenguajes de programación orientados a objetos se popularizaron, C++ y Objective-C fueron dos extensiones diferentes de C que proporcionaron capacidades orientadas a objetos. Ambos lenguajes se implementaron originalmente como compiladores de código fuente a código fuente ; el código fuente se traducía a C y luego se compilaba con un compilador de C. [ 76 ]

El lenguaje de programación C++ (originalmente llamado "C con Clases ") fue ideado por Bjarne Stroustrup como un enfoque para proporcionar funcionalidad orientada a objetos con una sintaxis similar a la de C. [ 77 ] C++ agrega mayor fuerza de tipado, ámbito y otras herramientas útiles en la programación orientada a objetos, y permite la programación genérica a través de plantillas. Casi un superconjunto de C, C++ ahora admite la mayor parte de C, con algunas excepciones .

Objective-C era originalmente una capa delgada sobre C, y sigue siendo un superconjunto estricto de C que permite la programación orientada a objetos mediante un paradigma de tipado híbrido dinámico/estático. Objective-C deriva su sintaxis tanto de C como de Smalltalk : la sintaxis que involucra el preprocesamiento, las expresiones, las declaraciones de funciones y las llamadas a funciones se hereda de C, mientras que la sintaxis para las características de la programación orientada a objetos se tomó originalmente de Smalltalk.

Además de C++ y Objective-C , Ch , Cilk y Unified Parallel C son prácticamente superconjuntos de C.

Véase también

Notas

  1. "Thompson había hecho un breve intento de producir un sistema codificado en una versión temprana de C —antes de las estructuras— en 1972, pero abandonó el esfuerzo." [ 2 ] [ 3 ] [ 4 ]
  2. "El esquema de composición tipográfica adoptado por C tiene una deuda considerable con Algol 68, aunque quizás no surgió en una forma que los seguidores de Algol aprobarían." [ 6 ] [ 7 ] [ 4 ]
  3. Se pronuncia / ˈ s / , como la letra ' c ' . [ 9 ]
  4. El código de ejemplo original se compilará en la mayoría de los compiladores modernos que no estén en modo de cumplimiento estricto de estándares, pero no cumple completamente con los requisitos de C89 ni de C99. De hecho, C99 exige que se genere un mensaje de diagnóstico.
  5. El valor de retorno0se usa normalmente en este contexto para indicar éxito. [ 18 ]
  6. El código deprint_array(no se muestra) también difiere ligeramente debido al tipo de p , que es un puntero a la matriz 2D en la versión malloc, y solo una matriz 2D en la versión auto VNA.

Referencias

  1. 1 2 Prinz, Peter; Crawford, Tony (16 de diciembre de 2005). C en pocas palabras . O'Reilly Media, Inc. pág. 3. ISBN  978-0-596-55071-4.
  2. Ritchie (1993a) , pág. 9.
  3. Ritchie (1993b) , pág. 9.
  4. 1 2 Ritchie (2003) .
  5. "N3221 – Informe del editor, posterior a la reunión de Estrasburgo, Francia, de enero de 2024" . ISO/IEC JTC1/SC22/WG14 . Normas abiertas. 21 de febrero de 2024. Consultado el 24 de mayo de 2024 .
  6. Ritchie (1993a) , pág. 8.
  7. Ritchie (1993b) , pág. 8.
  8. 1 2 "Verilog HDL (y C)" (PDF) . Escuela de Investigación en Ciencias de la Computación de la Universidad Nacional Australiana. 3 de junio de 2010. Archivado del original (PDF) el 6 de noviembre de 2013. Consultado el 19 de agosto de 2013. Década de 1980: Verilog se introduce por primera vez; Verilog se inspira en el lenguaje de programación C.
  9. "El nombre se basa en la letra C del alfabeto inglés y se pronuncia como ella" . El sonido del lenguaje de programación C. Diccionario inglés-chino. Archivado del original el 17 de noviembre de 2022. Consultado el 17 de noviembre de 2022 .
  10. Muñoz, Daniel. "Después de todos estos años, el mundo sigue funcionando con programación en C | Toptal" . Blog de ingeniería de Toptal . Consultado el 15 de junio de 2024 .
  11. "El lenguaje C cae a su nivel de popularidad más bajo" . Developer.com . 9 de agosto de 2016. Archivado del original el 22 de agosto de 2022. Consultado el 1 de agosto de 2022 .
  12. 1 2 3 4 5 6 7 Ritchie (1993a) .
  13. "Popularidad de los lenguajes de programación" . 2009. Archivado del original el 16 de enero de 2009. Consultado el 16 de enero de 2009 .
  14. "Índice de la comunidad de programación TIOBE" . 2009. Archivado del original el 4 de mayo de 2009. Consultado el 6 de mayo de 2009 .
  15. Ward, Terry A. (agosto de 1983). "C anotado / Una bibliografía del lenguaje C" . Byte . pág. 268. Recuperado el 31 de enero de 2015 . 
  16. "Índice TIOBE de septiembre de 2024" . Archivado del original el 18 de septiembre de 2024. Consultado el 16 de diciembre de 2025 .
  17. Kernighan y Ritchie (1978) , pág. 6.
  18. ^ Klemens , Ben ( 2013 ) .Siglo XXI C. Medios O'Reilly . ISBN 978-1-4493-2714-9.
  19. Ritchie, Dennis. "BCPL a B a C" . lysator.liu.se . Archivado del original el 12 de diciembre de 2019. Recuperado el 10 de septiembre de 2019 .
  20. ^ Jensen, Richard (9 de diciembre de 2020 ) . ""Una estupidez mayúscula"—los orígenes de C" . Ars Technica . Archivado del original el 28 de marzo de 2022. Recuperado el 28 de marzo de 2022 .
  21. 1 2 Johnson, SC ; Ritchie, DM (1978). "Portabilidad de programas C y el sistema UNIX". Bell System Tech. J. 57 ( 6): 2021–2048 . CiteSeerX 10.1.1.138.35 . doi : 10.1002/j.1538-7305.1978.tb02141.x . ISSN 0005-8580 . S2CID 17510065 .   (Nota: El PDF es un escaneo OCR del original y contiene una representación de "IBM 370" como "IBM 310".)
  22. McIlroy, MD (1987). Un lector de Research Unix: extractos anotados del Manual del programador, 1971–1986 (PDF) (Informe técnico). CSTR. Bell Labs. pág. 10. 139. Archivado del original (PDF) el 11 de noviembre de 2017. Recuperado el 1 de febrero de 2015 . 
  23. ^ Kernighan y Ritchie (1978) .
  24. "Páginas del manual C". Manual de información miscelánea de FreeBSD ( edición FreeBSD 13.0). 30 de mayo de 2011. Archivado del original el 21 de enero de 2021. Consultado el 15 de enero de 2021 . Archivado el 21 de enero de 2021 en Wayback Machine .
  25. ^ Kernighan y Ritchie (1988) .
  26. Stroustrup, Bjarne (2002). Rivalidad entre hermanos: C y C++ (PDF) (Informe). AT&T Labs. Archivado del original (PDF) el 24 de agosto de 2014. Recuperado el 14 de abril de 2014 .
  27. "Fundamentos del estándar nacional estadounidense para sistemas de información: lenguaje de programación C" . Archivado del original el 17 de julio de 2024. Consultado el 17 de julio de 2024 .
  28. C Integridad . Organización Internacional de Normalización. 30 de marzo de 1995. Archivado del original el 25 de julio de 2018. Recuperado el 24 de julio de 2018 .
  29. "JTC1/SC22/WG14 – C" . Página principal . ISO/IEC. Archivado del original el 12 de febrero de 2018. Consultado el 2 de junio de 2011 . 
  30. Andrew Binstock (12 de octubre de 2011). "Entrevista con Herb Sutter" . Dr. Dobbs . Archivado del original el 2 de agosto de 2013. Recuperado el 7 de septiembre de 2013 .
  31. "ISO/IEC 9899:2024 (en) — Borrador de trabajo N3220" (PDF) . Consultado el 11 de julio de 2025 .
  32. "WG14-N3132 : Calendario revisado del C23" (PDF) . open-std.org . 4 de junio de 2023. Archivado del original (PDF) el 9 de junio de 2023. 
  33. "WG14-N3220 : Borrador de trabajo, C2y" (PDF) . open-std.org . 21 de febrero de 2024. Archivado del original (PDF) el 26 de febrero de 2024. 
  34. "TR 18037: Embedded C" (PDF) . open-std.org . 4 de abril de 2006. ISO/IEC JTC1 SC22 WG14 N1169. Archivado del original (PDF) el 25 de febrero de 2021. Consultado el 26 de julio de 2011 .
  35. Harbison, Samuel P.; Steele, Guy L. (2002). C: Manual de referencia (5.ª ed.). Englewood Cliffs, NJ : Prentice Hall . ISBN  978-0-13-089592-9.Contiene una gramática BNF para C.
  36. Kernighan y Ritchie (1988) , pág. 192.
  37. Kernighan y Ritchie (1978) , pág. 3.
  38. 1 2 "Borrador del Comité ISO/IEC 9899:TC3: 5.2.1 Conjuntos de caracteres" . 2007.
  39. "Borrador del Comité ISO/IEC 9899:201x (ISO C11)" (PDF) . open-std.org . 2 de diciembre de 2010. Archivado del original (PDF) el 22 de diciembre de 2017. Consultado el 16 de septiembre de 2011 .
  40. ^ Kernighan y Ritchie (1988) , págs.192 , 259.
  41. "10 errores comunes de programación en C++" . Cs.ucr.edu . Archivado del original el 21 de octubre de 2008. Consultado el 26 de junio de 2009 .
  42. Schultz, Thomas (2004). C y el 8051 (3.ª ed.). Otsego, MI: PageFree Publishing Inc. pág. 20. ISBN   978-1-58961-237-2. Consultado el 10 de febrero de 2012 .
  43. Feuer, Alan R.; Gehani, Narain H. (marzo de 1982). "Comparación de los lenguajes de programación C y Pascal". ACM Computing Surveys . 14 (1): 73– 92. doi : 10.1145/356869.356872 . S2CID 3136859 . 
  44. Kernighan y Ritchie (1988) , pág. 122.
  45. Por ejemplo, gcc proporciona _FORTIFY_SOURCE. "Funciones de seguridad: comprobaciones de búfer en tiempo de compilación (FORTIFY_SOURCE)" . fedoraproject.org. Archivado del original el 7 de enero de 2007. Consultado el 5 de agosto de 2012 .
  46. ^เอี่ยมสิริวงศ์, โอภาศ (2016). Programación con C. Bangkok, Tailandia: SE-EDUCATION PUBLIC COMPANY LIMITED. págs. 225-230 . ISBN  978-616-08-2740-4.
  47. Raymond, Eric S. (11 de octubre de 1996). The New Hacker's Dictionary (3.ª ed.). MIT Press. pág. 432. ISBN   978-0-262-68092-9. Consultado el 5 de agosto de 2012 .
  48. "Página de manual para lint (Sección 1 de freebsd)" . unix.com . 24 de mayo de 2001. Consultado el 15 de julio de 2014 .
  49. Hardison, Nate. "CS107 Valgrind Memcheck" . web.stanford.edu . Consultado el 23 de junio de 2023 .
  50. Hastings, Reed; Joyce, Bob. "Purify: Detección rápida de fugas de memoria y errores de acceso" (PDF) . Pure Software Inc .: 9.
  51. Muñoz, Daniel. "Después de todos estos años, el mundo sigue funcionando con programación en C" . Blog de ingeniería de Toptal . Consultado el 17 de noviembre de 2023 .
  52. Dale, Nell B.; Weems, Chip (2014). Programación y resolución de problemas con C++ (6.ª ed.). Burlington, Massachusetts: Jones & Bartlett Learning. ISBN  978-1-4496-9428-9OCLC 894992484 
  53. "Desarrollo de Doom" . DoomWiki.org . 2 de marzo de 2025. Consultado el 2 de marzo de 2025 .
  54. ^ Libro de consulta del Dr. Dobb . Estados Unidos: Miller Freeman, Inc. Noviembre-diciembre de 1995.
  55. "Uso de C para programación CGI" . linuxjournal.com. 1 de marzo de 2005. Archivado del original el 13 de febrero de 2010. Consultado el 4 de enero de 2010 .
  56. Perkins, Luc (17 de septiembre de 2013). "Desarrollo web en C: ¿una locura? ¿O una locura astuta?" . Medium . Archivado del original el 4 de octubre de 2014. Recuperado el 8 de abril de 2022 .
  57. "¿Qué lenguaje de programación utiliza NGINX?" .
  58. "¿Qué es Apache y qué aporta al desarrollo de sitios web?" 15 de febrero de 2022.
  59. "Sistema de tiempo compartido UNIX: El lenguaje de programación C" . Nokia Bell Labs . Consultado el 15 de junio de 2026 .
  60. "C – la madre de todos los idiomas" . Academia de TIC del IITK . 13 de noviembre de 2018. Archivado del original el 31 de mayo de 2021. Consultado el 11 de octubre de 2022 .
  61. "1. Ampliando Python con C o C++" . Documentación de Python 3.10.7 . Archivado del original el 5 de noviembre de 2012. Consultado el 11 de octubre de 2022 .
  62. Conrad, Michael (22 de enero de 2018). "Una descripción general del motor Perl 5" . Opensource.com . Archivado del original el 26 de mayo de 2022. Recuperado el 11 de octubre de 2022 .
  63. "De C y C++ a Ruby" . Lenguaje de programación Ruby . Archivado del original el 12 de agosto de 2013. Consultado el 11 de octubre de 2022 .
  64. Para, Michael (3 de agosto de 2022). "¿Qué es PHP? Cómo escribir tu primer programa en PHP" . freeCodeCamp . Archivado del original el 4 de agosto de 2022. Consultado el 11 de octubre de 2022 .
  65. Metz, Cade (13 de octubre de 2011). "Dennis Ritchie: Los hombros sobre los que se apoyó Steve Jobs" . Wired . Archivado del original el 12 de abril de 2022. Recuperado el 19 de abril de 2022 .
  66. Grupo de Investigación sobre Seguridad en Internet. "¿Qué es la seguridad de la memoria y por qué es importante?" . Prossimo . Consultado el 3 de marzo de 2025 .
  67. Howell, Egor (16 de mayo de 2023). "Por qué deberías considerar usar Fortran como científico de datos" . Towards Data Science . Recuperado el 9 de julio de 2026 .
  68. corob-msft (31 de marzo de 2022). "Directivas pragma y las palabras clave __pragma y _Pragma" . Microsoft Learn . Archivado del original el 24 de septiembre de 2022. Recuperado el 24 de septiembre de 2022 .
  69. "Pragmas (El preprocesador de C)" . GCC, la colección de compiladores GNU . Archivado del original el 17 de junio de 2002. Recuperado el 24 de septiembre de 2022 .
  70. "Pragmas" . Guía y referencia para desarrolladores del compilador Intel C++ Classic . Intel. Archivado del original el 10 de abril de 2022. Recuperado el 10 de abril de 2022 .
  71. "En alabanza del preprocesador de C" . apenwarr . 13 de agosto de 2007. Consultado el 9 de julio de 2023 .
  72. Roberts, Eric S. (21 de marzo de 1989). "Implementación de excepciones en C" (PDF) . DEC Systems Research Center . SRC-RR-40. Archivado del original (PDF) el 15 de enero de 2017. Recuperado el 4 de enero de 2022 .
  73. "Descripción general de la codificación segura" (PDF) . Instituto de Ingeniería de Software, Universidad Carnegie Mellon . Consultado el 15 de diciembre de 2025 .
  74. "Nuevo parche de Linux confirma: el experimento con Rust ha terminado, Rust llegó para quedarse" . www.phoronix.com . Consultado el 15 de diciembre de 2025 .
  75. O'Regan, Gerard (24 de septiembre de 2015). Pilares de la informática : un compendio de empresas tecnológicas clave . Springer. ISBN  978-3-319-21464-1OCLC 922324121 
  76. Rauchwerger, Lawrence (2004). Lenguajes y compiladores para computación paralela : 16.º taller internacional, LCPC 2003, College Station, TX, EE. UU., 2-4 de octubre de 2003 : artículos revisados . Springer. ISBN   978-3-540-24644-2OCLC 57965544 
  77. Stroustrup, Bjarne (1993). "Una historia de C++: 1979–1991" (PDF) . Archivado del original (PDF) el 2 de febrero de 2019. Recuperado el 9 de junio de 2011 .

Fuentes

Lecturas adicionales

  • Plauger, PJ (1992). The Standard C Library (1.ª  ed.). Prentice Hall. ISBN 978-0-13-131509-9.(fuente)
  • Banahan, M.; Brady, D.; Doran, M. (1991). The C Book: Featuring the ANSI C Standard (2.ª  ed.). Addison-Wesley. ISBN 978-0-201-54433-6.(gratis)
  • Feuer, Alan R. (1985). El libro de rompecabezas C (1.ª  ed.). Prentice Hall. ISBN 0-13-109934-5.
  • Harbison, Samuel; Steele, Guy Jr. (2002). C: Manual de referencia (5.ª  ed.). Pearson. ISBN 978-0-13-089592-9.(archivo)
  • King, KN (2008). Programación en C: Un enfoque moderno (2.ª  ed.). WW Norton. ISBN 978-0-393-97950-3.(archivo)
  • Griffiths, David; Griffiths, Dawn (2012). Head First C (1.ª  ed.). O'Reilly. ISBN 978-1-4493-9991-7.
  • Perry, Greg; Miller, Dean (2013). Programación en C: Guía para principiantes absolutos (3.ª  ed.). Que. ISBN 978-0-7897-5198-0.
  • Deitel, Paul; Deitel, Harvey (2015). C: Cómo programar (8  ed.). Pearson. ISBN 978-0-13-397689-2.
  • Gustedt, Jens (2025). Modern C (3.ª  ed.). Manning. ISBN 9781633437777.(gratis)
  • Sitio web oficial del Grupo de Trabajo ISO C
    • ISO/IEC 9899 , ​​documentos oficiales C disponibles públicamente, incluido el fundamento C99.
    • "C99 con correcciones técnicas TC1, TC2 y TC3 incluidas" (PDF) . Archivado del original (PDF) el 25 de octubre de 2007. (3,61  MB)
  • Preguntas frecuentes sobre comp.lang.c
  • Una historia de C , por Dennis Ritchie
  • Referencia y ejemplos de la biblioteca C