Herramientas de usuario

Diferencias

Muestra las diferencias entre dos versiones de la página.

Enlace a la vista de comparación

Ambos lados, revisión anteriorRevisión previa
Próxima revisión
Revisión previa
el_lenguaje_de_programacion_c [2024/09/27 22:30] peronel_lenguaje_de_programacion_c [2026/04/21 16:06] (actual) – editor externo 127.0.0.1
Línea 2: Línea 2:
  
 Este es un ejemplar amorosamente wikificado por ~peron de //El Lenguaje de Programación C//, 2da Edición, de B. Kerningham y D. Ritchie, adaptado para [[hacker|hackers]] del Tercer Mundo. Este es un ejemplar amorosamente wikificado por ~peron de //El Lenguaje de Programación C//, 2da Edición, de B. Kerningham y D. Ritchie, adaptado para [[hacker|hackers]] del Tercer Mundo.
 +
 +==== Prefacios ====
  
 [[El lenguaje de programación C - Prefacios|Prefacios]] [[El lenguaje de programación C - Prefacios|Prefacios]]
 +==== Capítulos ====
  
-=====Introducción===== +[[El lenguaje de programación C - Introducción|Introducción]]
- +
-C es un lenguaje de programación de propósito general que ha sido estrechamente asociado con el sistema UNIX en donde fue desarrollado puesto que tanto el sistema como los programas que corren en él están escritos en lenguaje C. Sin embargo, este lenguaje no está ligado a ningún sistema operativo ni a ninguna máquina, y aunque se le llama “lenguaje de programación de sistemas” debido a su utilidad para escribir compiladores y sistemas operativos, se utiliza con igual eficacia para escribir importantes programas en diversas disciplinas. +
- +
-Muchas de las ideas importantes de C provienen del lenguaje BCPL, desarrollado por Martin Richards. La influencia de BCPL sobre C se continuó indirectamente a través del lenguaje B, el cual fue escrito por Ken Thompson en 1970 para el primer sistema UNIX de la DEC PDP-7. +
- +
-BCPL y B son lenguajes “carentes de tipos”. En contraste, C proporciona una variedad de tipos de datos. Los tipos fundamentales son caracteres, enteros y números de punto flotante de varios tamaños. Además, existe una jerarquía de tipos de datos derivados, creados con apuntadores, arreglos, estructuras y uniones. Las expresiones se forman a partir de operadores y operandos; cualquier expresión, incluyendo una asignación o una llamada a función, puede ser una proposición. Los apuntadores proporcionan una aritmética de direcciones independiente de la máquina. +
- +
-C proporciona las construcciones fundamentales de control de flujo que se requieren en programas bien estructurados: agrupación de proposiciones, toma de decisiones (if-else), selección de un caso entre un conjunto de ellos (switch), iteración con la condición de paro en la parte superior (while, for) o en la parte inferior (do), y terminación prematura de ciclos (break). +
- +
-Las funciones pueden regresar valores de tipos básicos, estructuras, uniones o apuntadores. Cualquier función puede ser llamada recursivamente. Las variables locales son normalmente “automáticas”, o creadas de nuevo con cada invocación. La definición de una función no puede estar anidada, pero las variables pueden estar declaradas en una modalidad estructurada por bloques. Las funciones de un program a en C pueden existir en archivos fuente separados, que se compilan de m anera separada. Las variables pueden ser internas a una función, externas pero conocidas sólo dentro de un archivo fuente, o visibles al programa completo. +
- +
-Un paso de preprocesamiento realiza substitución de macros en el texto del programa, inclusión de otros archivos fuente y compilación condicional. +
- +
-C es un lenguaje de relativo “bajo nivel”. Esta caracterización no es peyorativa, simplemente significa que C trata con el mismo tipo de objetos que la mayoría de las computadoras, llámense caracteres, números y direcciones. Estos pueden ser combinados y cambiados de sitio con los operadores aritméticos y lógicos implantados por máquinas reales. +
- +
-C no proporciona operaciones para tratar directamente con objetos compuestos, tales como cadenas de caracteres, conjuntos, listas o arreglos. No existen ope­raciones que manipulen un arreglo o una cadena completa, aunque las estructuras pueden copiarse como una unidad. El lenguaje no define ninguna facilidad para asignación de almacenamiento que no sea la de definición estática y la disciplina de pilas provista por las variables locales de funciones; no emplea heap ni recolector de basura. Finalmente, C en sí mismo no proporciona capacidades de entrada/salida; no hay proposiciones READ o WRITE, ni métodos propios de acceso a archivos. Todos esos mecanismos de alto nivel deben ser proporcionados por funciones llamadas explícitamente. +
- +
-De manera semejante, C solamente ofrece un control de flujo franco, y lineal: condiciones, ciclos, agrupamientos y subprogramas, pero no multiprogramación, operaciones paralelas, sincronización ni co-rutinas. +
- +
-Aunque la ausencia de alguna de esas capacidades puede parecer como una grave deficiencia (“¿significa que se tiene que llamar a una función para comparar dos cadenas de caracteres?”), el mantener al lenguaje de un tamaño modesto tiene beneficios reales. Puesto que C es relativamente pequeño, se puede describir en un pequeño espacio y aprenderse con rapidez. Un programador puede razonablemente esperar conocer, entender y utilizar en verdad la totalidad del lenguaje. +
- +
-Por muchos años, la definición de C fue el manual de referencia de la primera edición de El lenguaje de programación C. En 1983, el American National Standards Institute (ANSI) estableció un comité para proporcionar una moderna y comprensible definición de C. La definición resultante, el estándar ANSI o “ANSI C” , se esperaba fuera aprobada a fines de 1988. La mayoría de las características del estándar ya se encuentran soportadas por compiladores modernos. +
- +
-El estándar está basado en el manual de referencia original. El lenguaje ha cambiado relativamente poco; uno de los propósitos del estándar fue asegurar que la mayoría de los programas existentes pudiesen permanecer válidos o, al menos, que los compiladores pudieran producir mensajes de advertencia acerca del nuevo comportamiento. +
- +
-Para la mayoría de los programadores, el cambio más importante es una nueva sintaxis para declarar y definir funciones. Una declaración de función ahora puede incluir una descripción de los argumentos de la función; la sintaxis de la definición cambia para coincidir. Esta información extra permite que los compiladores detecten más fácilmente los errores causados por argumentos que no coinciden; de acuerdo con nuestra experiencia, es una adición muy útil al lenguaje. +
- +
-Existen otros cambios de menor escala en el lenguaje. La asignación de estructuras y enumeraciones, que ha estado ampliamente disponible, es ahora parte oficial del lenguaje. Los cálculos de punto flotante pueden ahora realizarse con precisión sencilla. Las propiedades de la aritmética, especialmente para tipos sin signo, están esclarecidas. El preprocesador es más elaborado. La mayor parte de esos cambios sólo tendrán efectos secundarios para la mayoría de los programadores. +
- +
-Una segunda contribución significativa dei estándar es la definición de una bi­blioteca que acompañe a C. Esta especifica funciones para tener acceso al sistema operativo (por ejemplo, leer de archivos y escribir en ellos), entrada y salida con formato, asignación de memoria, manipulación de cadenas y otras actividades semejantes. Una colección de encabezadores (headers) estándar proporcionan un acceso uniforme a las declaraciones de funciones y tipos de datos. Los programas que utilizan esta biblioteca para interactuar con un sistema anfitrión están asegurados de un com portam iento com patible. La m ayor parte de la biblioteca está estrecham ente modelada con base en la “biblioteca E/S estándar" del sistema UNIX. Esta biblioteca se describió en la primera edición y ha sido también ampliamente utilizada em otros sistemas. De nuevo, la m ayoría de los programadores no notarán mucho el cambio. +
- +
-Debido a que los tipos de datos y estructuras de control provistas por C son manejadas directamente por la mayoría de las computadoras, la biblioteca de ejecución (run-time) requerida para implantar programas autocontenidos es pequeña. Las funciones de la biblioteca estándar únicamente se llam an en form a explícita, de manera que se pueden evitar cuando no se necesitan. La mayor parte puede escribirse en C, y excepto por detalles ocultos del sistema operativo, ellas mismas son portátiles. +
- +
-Aunque C coincide con las capacidades de muchas com putadoras, es independiente de cualquier arquitectura. Con un poco de cuidado es fácil escribir programas portátiles, esto es, programas que puedan correr sin cambios en una variedad de máquinas. El estándar explica los problemas de la transportabilidad, y prescribe un conjunto de constantes que caracterizan a la máquina en la que se ejecuta el programa. +
- +
-C no es un lenguaje fuertemente tipificado, sino que, al evolucionar, su verificación de tipos ha sido reforzada. La definición original de C desaprobó - pero permitió - el intercambio de apuntadores y enteros; esto se ha eliminado y el estándar ahora requiere la adecuada declaración y la conversión explícita que ya ha sido obligada por los buenos compiladores. La nueva declaración de funciones es otro paso en esta dirección. Los compiladores advertirán de la mayoría de los errores de tipo, y no hay conversión automática de tipos de datos incompatibles. +
- +
-Sin embargo, C mantiene la filosofía básica de que los programadores saben lo Que están haciendo; sólo requiere que establezcan sus intenciones en forma explícita. +
- +
-Como cualquier otro lenguaje, C tiene sus defectos. Algunos de los operadores tienen la precedencia equivocada; algunos elementos de la sintaxis pueden ser mejores. A pesar de todo, C ha probado ser un lenguaje extremadamente efectivo y expresivo p ara una am plia variedad de program as de aplicación. +
- +
-El libro está organizado com o sigue. El capítulo 1 es una introducción orientada a la parte central de C. El propósito es hacer que el lector se inicie tan pronto como le sea posible, puesto que creemos firmemente que la forma de aprender un nuevo lenguaje es escribir program as en él. La introducción supone un conocimiento práctico de los elementos básicos de la programación; no hay una explicación de computadoras, de compilación, ni del significado de una expresión como n = n + 1. Aunque hemos tratado de mostrar técnicas útiles de programación en donde fue posible, la intención del libro no es la de ser un texto de consulta sobre estructuras de datos y algoritmos; cuando nos vimos forzados a hacer una elección, nos hemos concentrado en el lenguaje. +
- +
-En los capítulos del 2 al 6 se discuten varios aspectos de C en mayor detalle y más formalmente de lo que se hace en el capítulo 1, aunque el énfasis está aún en los ejemplos de programas completos, más que en fragmentos aislados. El capítulo 2 trata de los tipos básicos de datos, operaciones y expresiones. El capítulo 3 trata sobre control de flujo: if-else, switch, while, for, etc. En el capítulo 4 se cubren funciones y la estructura de un programa —variables externas, reglas de alcance, archivos fuente múltiples y otros aspectos— y también abarca al preprocesador. El capítulo 5 discute sobre apuntadores y aritmética de direcciones. +
- +
-El capítulo 6 cubre estructuras y uniones. +
- +
-El capítulo 7 describe la biblioteca estándar, la cual proporciona una interfaz común con el sistema operativo. Esta biblioteca está definida por el estándar ANSI y se intenta que se tenga en todas las máquinas que manejan C; así, los programas que la usen para entrada, salida y otros accesos al sistema operativo se puedan transportar de un sistema a otro sin cambios. +
- +
-El capítulo 8 describe una interfaz entre los programas en C y el sistema ope­rativo UNIX, concentrándose en entrada/salida, el sistema de archivos y la asignación de memoria. Aunque algo de este capítulo es específico de sistemas UNIX, los programadores que usen otros sistemas de todas maneras encontrarán aquí material de utilidad, incluyendo alguna comprensión acerca de cómo está implantada una versión de la biblioteca estándar, así como sugerencias para obtener un código portátil. +
- +
-El apéndice A contiene un manual de consulta del lenguaje. El informe oficial de la sintaxis y la semántica de C es en sí el estándar ANSI. Ese documento, sin embargo, está principalmente pensado para quienes escriben compiladores. El manual de consulta de este libro transmite la definición del lenguaje en una forma más concisa y sin el mismo estilo legalista. El apéndice B es un resumen de la biblioteca estándar, de nuevo más para usuarios que para implantadores. El apéndice C es un breve resumen de los cambios del lenguaje original. A unque, en caso de duda, el estándar y el compilador en uso quedan como las autoridades finales sobre el lenguaje. +
- +
-[[El lenguaje de Programación C - Capitulo 1]] +
- +
-[[El lenguaje de Programación C - Capitulo 2]] +
- +
- +
-===== Capítulo 3: Control de Flujo ===== +
- +
-Las proposiciones de control de flujo de un lenguaje especifican el orden en que se realiza el procesamiento. Ya hemos visto la mayoría de las construcciones de control de flujo en ejemplos anteriores; aquí completaremos el conjunto, y sere­mos más precisos acerca de las discutidas con anterioridad. +
- +
-==== 3.1 Proposiciones y bloques ==== +
- +
-Una expresión como ''x = 0'' ó ''i++'' o ''printf(...)'' se convierte en una //proposi­ción// cuando va seguida de un punto y coma '';'', como en +
- +
-<code c> +
-x = 0; +
-i++; +
-printf(...); +
-</code> +
- +
-En C, el punto y coma '';'' es un terminador de proposición, en lugar de un separa­dor, como lo es en un lenguaje tipo Pascal. +
- +
-Las llaves ''{'' y ''}'' se emplean para agrupar declaraciones y proposiciones dentro de una //proposición compuesta// o //bloque//, de modo que son sintácticamente equi­valentes a una proposición sencilla. Las llaves que encierran las proposiciones de una función son un ejemplo obvio; otros ejemplos son las llaves alrededor de proposiciones múltiples después de un ''if'', ''else'', ''while'' o ''for''. (Pueden declararse variables dentro de cualquier bloque; esto se expondrá en el [[#capitulo 4|capítulo 4]]). No hay punto y coma después de la llave derecha que termina un bloque. +
- +
-==== 3.2 If-Else ==== +
- +
-La proposición ''if-else'' se utiliza para expresar decisiones. Formalmente, la sintaxis es +
- +
-<code c> +
-if (expresión) +
-    proposición1 +
-else +
-   proposición2 +
-</code> +
- +
-donde la parte del ''else'' es optativa. La expresión se evalúa; si es verdadera (esto es, si la expresión tiene un valor diferente de cero), la //proposición1// se ejecuta. Si es falsa (//expresión// es cero) y si existe una parte de ''else'', se ejecuta en su lugar la //proposición2//+
- +
-Puesto que un ''if'' simplemente prueba el valor numérico de una expresión, son posibles ciertas abreviaciones de código. Lo más obvio es escribir +
- +
-<code c> +
-if (expresión) +
-</code> +
- +
-en lugar de +
- +
-<code c> +
-if (expresión != 0) +
-</code> +
- +
-Algunas veces esto es claro y natural; otras puede ser misterioso. +
- +
-Debido a que la parte ''else'' de un ''if-else'' es optativa, existe una ambigüedad cuando un ''else'' se omite de una secuencia ''if'' anidada. Esto se resuelve al asociar el ''else'' con el ''if'' sin ''else'' anterior más cercano. Por ejemplo, en +
- +
-<code c> +
-if (n > 0) +
-    if (a > b) +
-        z = a; +
-else +
-    z = b; +
-</code> +
- +
-el ''else'' va con el ''if'' más interno, como se muestra con el sangrado. Si eso no es lo que se desea, se deben utilizar llaves para forzar la asociación correcta: +
- +
-<code c> +
-if (n > 0) { +
-    if (a > b) +
-        z = a; +
-+
-else +
-    z = b; +
-</code> +
- +
-La ambigüedad es especialmente perniciosa en situaciones como esta: +
- +
-<code c> +
-if (n > 0) +
-    for (i = 0; i < n; i++) +
-        if (s[i] > 0) { +
-            printf("..."); +
-            return i; +
-+
-else    /* EQUIVOCADO */ +
-    printf("error -- n es negativo\n"); +
-</code> +
- +
-El sangrado muestra en forma inequívoca lo que se desea, pero el compilador no entiende el mensaje y asocia el ''else'' con el ''if'' más interno. Puede ser difícil encon­trar esta clase de errores; es una buena idea utilizar llaves cuando hay varios ''if'' anidados. +
- +
-A propósito, nótese que hay un punto y coma '';'' después de ''z = a'' en +
- +
-<code c> +
-if (a > b) +
-    z = a; +
-else +
-    z = b; +
-</code> +
- +
-Esto se debe a que gramaticalmente al ''if'' le sigue una //proposición//, y una expresión como "''z = a;''" siempre se termina con punto y coma '';''+
-==== 3.3 Else-if ==== +
- +
- +
-La construcción +
- +
-<code c> +
-if (expresión) +
-    proposición +
-else if (expresión) +
-    proposición +
-else if (expresión) +
-    proposición +
-else if (expresión) +
-    proposición +
-else +
-    proposición +
-</code> +
- +
-ocurre de modo tan frecuente que bien vale una pequeña discusión aparte. Esta secuencia de proposiciones ''if'' es la forma más general de escribir una decisión múltiple. Las //expresiones// se evalúan en orden; si cualquier //expresión// es verdade­ra, se ejecuta la //proposición// asociada con ella, y esto termina toda la cadena. Co­mo siempre, el código para cada //proposición// es una proposición simple o un grupo dentro de llaves. +
- +
-La parte del último ''else'' maneja el caso “ninguno de los anteriores” o //caso por omisión// cuando ninguna de las otras condiciones se satisface. En algunos ca­sos no hay una acción explícita para la omisión; en ese caso el último +
- +
-<code c> +
-else +
-    proposición +
-</code> +
- +
-puede omitirse, o puede utilizarse para detección de errores al atrapar una condición “imposible” . +
- +
-Para ilustrar una decisión de tres vías, se muestra a continuación una función de búsqueda bi­naria que decide si un valor particular de ''x'' se encuentra en el arreglo ordenado ''v'' - Los elementos de ''v'' deben estar en orden ascendente. La función regresa la po­sición (un número entre ''0'' y ''n-1'') si ''x'' ocurre en ''v'', y ''-1'' si no es así. +
- +
-La búsqueda binaria primero compara el valor de entrada ''x'' con el elemento medio del arreglo ''v''. Si ''x'' es menor que el valor del medio, la búsqueda se enfoca sobre la mitad inferior de la tabla; de otra manera lo hace en la mitad superior, cualquier caso, el siguiente paso es comparar a ''x'' con el elemento medio de la mitad seleccionada. Este proceso de dividir en dos continúa hasta que se en­cuentra el valor o ya no hay elementos. +
- +
-<code c> +
-/* binsearch: encuentra x en v[0] <= v[1] <= ... <= v[n-1] */ +
-+
-    int low, high, mid; +
-     +
-    low = 0; +
-    high = n - 1; +
-    while (low <= high) { +
-        mid = (low+high)/2; +
-        if (x < v[mid]) +
-            high = mid + 1; +
-        else if (x > v[mid]) +
-            low = mid + 1; +
-        else    /* el elemento fue encontrado */ +
-            return mid; +
-    } +
-    return -1;    /* no fue encontrado */ +
-+
-</code> +
- +
-La decisión fundamental es si ''x'' es menor que, mayor que o igual al elemento me­dio ''v[mid]'' en cada paso; esto es un ''else-if'' natural. +
- +
-  * **Ejercicio 3-1**. Nuestra búsqueda binaria realiza dos pruebas dentro del ciclo, cuando una podría ser suficiente (al precio de más pruebas en el exterior). Escriba una versión con sólo una prueba dentro del ciclo y mida la diferencia en tiempo de ejecución. □ +
-==== 3.4 Switch ==== +
- +
-La proposición ''switch'' es una decisión múltiple que prueba si una expresión coincide con uno de un número de valores //constantes// enteros, y traslada el con­trol adecuadamente. +
- +
-<code c> +
-switch (expresión) { +
-    case exp-const: proposiciones +
-    case exp-const: proposiciones +
-    default: proposiciones +
-+
-</code> +
- +
-Cada ''case'' se etiqueta con uno o más valores constantes enteros o expresiones constantes enteras. Si un ''case'' coincide con el valor de la expresión, la ejecución comienza allí. Todas las expresiones ''case'' deben ser diferentes. El que está etiquetado co­mo ''default'' se ejecuta sí ninguno de los otros se satisface. El ''default'' es optativo; si no está y ninguno de los casos coincide, no se toma acción algu­na. Las cláusulas ''case'' y ''default'' pueden ocurrir en cualquier orden. +
- +
-En el [[#capitulo 1|capítulo 1]] se escribió un programa para contar las ocurrencias de cada dígito, espacio en blanco y todos los demás caracteres, usando una secuencia de ''if ... else if ... else''. Aquí está el mismo programa con un ''switch'': +
- +
-<code c> +
-#include <stdio.h> +
-main()    /* cuenta dígitos, espacios blancos, y otros */ +
-+
-        int c, i, nwhite, nother, ndigit[10]; +
-     +
-        nwhite = nother = 0; +
-        for (i = 0; i < 10; i++) +
-            ndigit[i] = 0; +
-        while ((c = getchar()) != EOF) { +
-            switch (c) { +
-            case '0': case '1': case '2': case '3': case '4': +
-            case '5': case '6': case '7': case '8': case '9': +
-                ndigit[c-'0']++; +
-                break; +
-            case ' ': +
-            case '\n': +
-            case '\t': +
-                nwhite++; +
-                break; +
-            default: +
-                nother++; +
-                break; +
-            } +
-    } +
-    printf("digits ="); +
-    for (i = 0; i < 10; i++) +
-        printf(" %d", ndigit[i]); +
-    printf(", espacio en blanco = %d, otros = %d\n", +
-        nwhite, nother); +
-    return 0; +
-+
-</code> +
- +
-La proposición ''break'' provoca una salida inmediata del ''switch''. Puesto que los ''case'' sirven sólo como etiquetas, después de que se ejecuta el código para Uno, la ejecución //pasa al siguiente//, a menos que se tome una acción específica para terminar el ''switch''. Las formas más comunes de dejar un ''switch'' son ''break'' y ''return''. Una proposición ''break'' también se puede emplear para forzar una salida inmediata de los ciclos ''while'', ''for'' y ''do'', como se verá más adelante en este capítulo. +
- +
-//Pasar a través// de los ''case'' es en parte bueno y en parte no. Por el lado positivo, esto permite conectar varios ''case'' a una acción simple, como con los dígitos de este ejemplo. Pero eso también implica que cada case normalmente debe termi­nar con un ''break'' para prevenir pasar al siguiente. Pasar de un ''case'' a otro no es una práctica muy sólida y es susceptible a la desintegración cuando se modifica el programa. Con la excepción de etiquetas múltiples para un cálculo simple, lo anterior se debe utilizar con cautela y emplear comentarios. +
- +
-Como formalidad, coloque un ''break'' después del último ''case'' (en este caso el ''default'') aun si desde ek punto de vista lógico resulta innecesario. Algún día - cuando se agregue otro ''case'' al final - esta práctica de programación defensiva lo salvará. +
- +
-  * **Ejercicio 3-2**. Escriba una función ''escape(s,t)'' que convierte caracteres como //nueva línea// y //tabulación// en secuencias de escape visibles como ''<nowiki>\n</nowiki>'' y ''<nowiki>\t</nowiki>'' mientras copia la cadena ''t'' a ''s''. Utilice un ''switch''. Escriba también una función para la di­rección inversa, convirtiendo secuencias de escape en caracteres reales. □ +
-==== 3.5 Ciclos - While y For ==== +
- +
-Ya hemos encontrado los ciclos ''while'' y ''for''. En +
- +
-<code c> +
-while (expresión) +
-    proposición +
-</code> +
- +
-la expresión se evalúa. Si es diferente de cero, se ejecuta la //proposición// y se reevalúa la //expresión//. Este ciclo continúa hasta que la //expresión// se hace cero, punto en el cual se suspende la ejecución para continuar después de la //proposición//+
- +
-La proposición ''for'' +
- +
-<code c> +
-for (expr1; expr2; expr3) +
-    proposición +
-</code> +
- +
-es equivalente a +
- +
-<code c> +
-expr1; +
-while (expr2) { +
-    proposición +
-    expr3; +
-+
-</code> +
- +
-excepto por el comportamiento de ''continue'' que se describe en la [[#3.7|sección 3.7]]. +
- +
-Gramaticalmente, las tres componentes de un ciclo ''for'' son expresiones. Por lo común, //expr1// y //expr3// son asignaciones o llamadas a función y //expr2// es una ex­presión de relación. Cualquiera de las tres partes se puede omitir, aunque deben permanecer los punto y coma '';''. Si se omite //expr1// o //expr3//, sólo se desecha de la expansión. Si la prueba //expr2// no está presente, se toma como permanentemente verdadera, así que +
- +
-<code c> +
-for (;;) { +
-    ... +
-+
-</code> +
- +
-es una iteración “infinita”, que presumiblemente será interrumpida por otros medios, como un ''break'' o un ''return''.  +
- +
-El usar ''while'' o ''for'' es principalmente cuestión de preferencia personal. Por ejemplo, en +
- +
-<code c> +
-while ((c = getchar()) == ' ' || c == '\n' || c = '\t'+
-    ; /* ignora caracteres espaciadores */ +
-</code> +
- +
-no hay inicialización o reinicialización, por lo que el ''while'' es más natural. +
- +
-El ''for'' se prefiere cuando existe una inicialización simple e incrementos, puesto que mantiene las proposiciones de control del ciclo juntas y visibles al principio del mismo. Esto es más obvio en +
- +
-<code c> +
-for (i = 0; i < n; i++) +
-    ... +
-</code> +
- +
-que es la forma característica de procesar los primeros //n// elementos de un arreglo en C, lo análogo al ciclo ''DO'' de Fortran o al ''for'' de Pascal. Sin embargo, la analo­gía no es perfecta puesto que tanto el índice como el límite de un ciclo ''for'' en C pueden ser alterados desde dentro del ciclo, y la variable del Índice retiene su valor cuando por cualquier razón terminan las iteraciones. Puesto que las componen­tes del ''for'' son expresiones arbitrarias, sus ciclos no están restringidos a progresio­nes aritméticas. Por otra parte, considere que es un mal estilo incluir en las secciones de inicialización e incremento operaciones no relacionadas con esas ac­tividades, que más bien se reservan para acciones de control del ciclo. +
- +
-Como un ejemplo más amplio, aquí está otra versión de ''atoi'' para convertir una cadena a su equivalente numérico. Esta es ligeramente más general que la del [[#capitulo 2|capítulo 2]]; trata también los espacios en blanco previos al número, y los signos ''+'' o ''-''. (El [[#capitulo 4|capitulo 4]] muestra ''atof'', que realiza la misma conversión para números de punto flotante). +
- +
-La estructura del programa refleja la forma de la entrada: +
- +
-  * ignora espacios en blanco, si los hay +
-  * toma el signo, si lo hay +
-  * toma la parte entera y conviértela +
- +
-Cada paso realiza su parte, y deja las cosas en forma clara para el siguiente. La totalidad del proceso termina con el primer carácter que no pueda ser parte de un número. +
- +
-<code c> +
-#include <ctype.h> +
- +
-/* atoi: convierte s a entero; versión 2 */ +
-int atoi(char s[]) +
-+
-    int i, n, sign; +
-     +
-    for (i = 0; isspace(s[i]); i++)    /* ignora espacio en blanco * / +
-        ; +
-    sign = (s[i] == '-') ? -1 : 1; +
-    if (s[i] == '+' || s[i] == '-'   /* ignora el signo */ +
-        i++; +
-    for (n = 0; isdigit(s[i]); i++) +
-        n = 10 * n + (s[i] - '0'); +
-    return sign * n; +
-+
-</code> +
- +
-La biblioteca estándar proporciona una función más elaborada, ''strtol'', para la conversión de cadenas a enteros largos; véase la [[#apendice b#seccion 5|sección 5 del apéndice B]]. +
- +
-Las ventajas de mantener centralizado el control del ciclo son aún más obvias cuando existen ciclos anidados. La siguiente función es una clasificación Shell para ordenar un arreglo de enteros. La idea básica de este algoritmo de ordena­miento - inventado en 1959 por D.L. Shell - es que en las primeras etapas sean comparados elementos lejanos (en lugar de los adyacentes, como en los or­denamientos de intercambio más simples). Esto tiende a eliminar rápidamente gran cantidad de desorden, así que los estados posteriores tienen menos trabajo por hacer. El intervalo entre los elementos comparados disminuye en forma gra­dual hasta uno, punto en el que el ordenamiento viene a ser efectivamente un mé­todo adyacente de intercambio. +
- +
-<code c> +
-/* shellsort: ordena v[0]...v[n-1] en orden ascendente */ +
-void shellsort(int v[], int n) +
-+
-    int gap, i, j, temp; +
-     +
-    for (gap = n/2; gap > 0; gap /= 2) +
-        for (i = gap; i < n; i++) +
-        for (j=i-gap; j>=0 && v[j]>v[j+gap]; j-=gap) { +
-            temp = v[j]; +
-            v[j] = v[j+gap]; +
-            v[j+gap] = temp; +
-        } +
-+
-</code> +
- +
-Existen tres ciclos anidados. El más externo controla el espacio entre los elementos comparados, reduciéndolo desde ''n/2'' por un factor de dos en cada paso hasta que llega a cero. El ciclo intermedio recorre los elementos. El ciclo más interno compara cada pareja de elementos que está separada por el espacio ''gap'' e invierte a las que estén desordenadas. Puesto que ''gap'' finalmente se reduce a uno, todos los elementos se ordenan correctamente. Nótese cómo la generalidad del ''for'' hace que el ciclo más externo coincida con la forma de los otros, aun cuando no es una progresión aritmética. +
- +
-Un último operador de C es la coma '','' que frecuentemente encuentra uso en la proposición ''for''. Una pareja de expresiones separadas por una coma se eva­lúa de izquierda a derecha, y el tipo y valor del resultado son el tipo y valor del operando derecho. Así, en una proposición ''for'' es posible colocar expresiones múltiples en las diferentes partes, por ejemplo, para procesar dos índices en para­lelo. Esto se ilustra en la función ''reverse(s)'', que invierte a la cadena ''s'' en el mis­mo lugar. +
- +
-<code c> +
-#include <string.h> +
- +
-/* reverse: invierte la cadena s en el mismo lugar */ +
-void reverse(char s[]) +
-+
-    int c, i, j; +
-     +
-    for (i = 0, j = strlen(s)-1; i < j; i++, j--) { +
-        c = s[i]; +
-        s[i] = s[j]; +
-        s[j] = c; +
-    } +
-+
-</code> +
- +
-Las comas que separan a los argumentos de una función, las variables en declara­ciones, etc., __no son__ operadores coma, y no garantizan evaluación de izquierda a derecha. +
- +
-Los operadores coma deberán utilizarse poco. Los usos más adecuados son en construcciones fuertemente relacionadas una con la otra, como en el ciclo ''for'' de ''reverse'', y en macros en donde un cálculo de paso múltiple debe ser una expre­sión simple. Una expresión coma podría también ser apropiada para el intercambio de elementos en ''reverse'', donde el intercambio puede ser a través de una operación simple: +
- +
-<code c> +
-for (i = 0, j = strlen(s)-1; i < j; i++, j--) +
-    c = s[i], s[i] = s[j], s[j] = c; +
-</code> +
- +
-  * **Ejercicio 3-3**. Escriba la función ''expand(sl,s2)'' que expande notación abreviada como ''a-z'', que viene en la cadena ''s1'', en la lista equivalente completa ''abc...xyz'' en ''s2''. Permita letras mayúsculas y minúsculas, así como dígitos, y esté preparado para manejar casos como ''a-b-c'' y ''a-z0-9'' y ''—a—z''. Haga que los guiones ''-'' al inicio o al final se tomen literalmente. □ +
- +
-==== 3.6 Ciclos - do-while ==== +
- +
-Como ya se expuso en el [[#capitulo 1|capítulo 1]], los ciclos ''while'' y ''for'' verifican al principio la condición de término. En contraste, el tercer ciclo en C, el ''do-while'', prueba al final __después__ de realizar cada paso a través del cuerpo del ciclo, el cual se ejecu­ta siempre por lo menos una vez. +
- +
-La sintaxis del do es +
- +
-<code c> +
-do +
-    proposición +
-while (expresión); +
-</code> +
- +
-La //proposición// se ejecuta y después se evalúa la //expresión//. Si es verdadera, la //proposición// se evalúa de nuevo, y así sucesivamente. Cuando la expresión se hace falsa, el ciclo termina. Excepto por el sentido de la prueba, el ''do-while'' es equiva­lente a la proposición ''repeat-until'' de Pascal. +
- +
-La experiencia demuestra que el ''do-while'' es mucho menos utilizado que el ''while'' y el ''for''. Aunque de cuando en cuando es valioso, como en la siguiente función ''itoa'', que convierte un número a una cadena de caracteres (lo inverso de ''atoi''). El trabajo es ligeramente más complicado de lo que podría pensarse en un +
-principio, debido a que los métodos fáciles para generar dígitos los generan en el orden incorrecto. Hemos elegido generar la cadena al revés y después invertirla. +
- +
-<code c> +
-/* itoa: convierte n a caracteres en s */ +
-void itoa(int n, char s[]) +
-+
-    int i, sign; +
-     +
-    if ((sign = n) < 0) /* registra el signo */ +
-        n = -n;         /* hace a n positivo */ +
-    i = 0; +
-    do {      /* genera digitos en orden inverso */ +
-        s[i++] = n % 10 + '0'; /* obtiene siguiente dígito */ +
-    } while ((n /= 10) > 0);   /* lo borra */ +
-    if (sign < 0) +
-        s[i++] = '-'; +
-    s[i] = '\0'; +
-reverse(s); +
-+
-</code> +
- +
-El ''do-while'' es necesario, o al menos conveniente, puesto que por lo menos se de­be instalar un carácter en el arreglo ''s'', aun si ''n'' es cero. También empleamos llaves alrededor de la proposición simple que hace el cuerpo del do-while, aunque son innecesarias, y así el lector apresurado no confundirá la sección del ''while'' con el //comienzo// de un ciclo ''while''+
- +
-  * **Ejercicio 3-4**. En una representación de números en complemento a dos, nuestra versión de ''itoa'' no maneja el número negativo más grande, esto es, el valor de ''n'' igual a ''-( 2^tamañopalabra-1)''. Explique por qué. Modifíquelo para imprimir el valor correctamente, sin importar la máquina en que ejecute. □ +
-  * **Ejercicio 3-5**. Escriba la función ''itob(n,s,b)'' que convierte al entero ''n'' en una re­presentación de caracteres con base ''b'' dentro de la cadena ''s''. En particular, ''itob(n,S,16)'' da formato a ''s'' como un entero hexadecimal en ''s''. □ +
-  * **Ejercicio 3-6**. Escriba una versión de ''itoa'' que acepte tres argumentos en lugar de dos. El tercer argumento es un ancho mínimo de campo; si es necesario, al número converti­do debe agregársele caracteres en blanco a la izquierda para hacerlo lo suficiente­mente ancho. □ +
- +
-==== 3.7 Break y Continue ==== +
- +
-Algunas veces es conveniente tener la posibilidad de abandonar un ciclo de otra manera que no sea probando al inicio o al final. La proposición ''break'' pro­porciona una salida anticipada de un ''for'', ''while'' y ''do'', tal como lo hace el ''switch''+
- +
-Un ''break'' provoca que el ciclo o ''switch'' más interno que lo encierra termine inme­diatamente. +
- +
-La siguiente función, ''trim'', elimina espacios blancos, tabuladores y nuevas lí­neas al final de una cadena, utilizando un ''break'' para salir de un ciclo cuando se encuentra el no-blanco, no-tabulador o no-nueva línea de más a la derecha. +
- +
-<code c> +
-/* trim: elimina blancos, tabuladores y nueva linea al final */ +
-int trim(char s[]) +
-+
-    int n; +
-     +
-    for (n = strlen(s)-1; n >= 0; n--) +
-        if (s[n] != ' ' && s[n] != '\t' && s[n] != '\n'+
-            break; +
-    s[n+1] = '\0'; +
-    return n; +
-+
-</code> +
- +
-''strlen'' regresa la longitud de la cadena. El ciclo ''for'' inicia al final y rastrea hacia atrás, buscando el primer carácter que no sea blanco o tabulador o nueva linea. +
- +
-El ciclo se interrumpe cuando se encuentra alguno o cuando //n// se hace negativa (esto es, cuando se ha rastreado toda la cadena. Se deberá verificar que este comportamiento es correcto, aun cuando la cadena esté vacía o sólo contiene espacios en blanco. +
- +
-La proposición ''continue'' está relacionada con el ''break'', pero se utiliza menos; provoca que inicie la siguiente iteración del ciclo ''for'', ''while'' o ''do'' que la contiene. Dentro de ''while'' y ''do'', esto significa que la parte de la prueba se ejecuta inmediatamente; en el ''for'', el control se traslada al paso de incremento. La propo­sición continué se aplica solamente a ciclos, no a ''switch''. Un ''continue'' dentro de un ''switch'' que está a su vez en un ciclo, provoca la siguiente iteración del ciclo. +
- +
-Como un ejemplo, el siguiente fragmento procesa sólo los elementos no negativos que están en el arreglo a; los valores negativos son ignorados. +
- +
-<code c> +
-for (i = 0; i < n; i++) +
-    if (a[i] < 0)    /* ignora elementos negativos */ +
-        continue; +
-... /* trabaja elementos positivos */ +
-</code> +
- +
-La proposición ''continue'' se emplea a menudo cuando la parte del ciclo que sigue es complicada, de modo que invertir la prueba y sangrar otro nivel podría anidar profundamente el programa. +
-==== 3.8 Goto y Etiquetas ==== +
- +
- +
-C proporciona la infinitamente abusable proposición ''goto'', y etiquetas para saltar hacia ellas. Formalmente, el ''goto'' nunca es necesario, y en la práctica es casi siempre más fácil escribir código sin él. En este libro no se ha usado ''goto'' alguno. +
- +
-Sin embargo, hay algunas situaciones donde los ''goto'' pueden encontrar un lu­gar. La más común es abandonar el procesamiento en alguna estructura profun­damente anidada, tal como salir de dos o más ciclos a la vez. La proposición ''break'' no se puede utilizar directamente, puesto que sólo sale del ciclo más inter­no. Así: +
- +
-<code c> +
-    for ( ... ) +
-        for ( ... ) { +
-            ... +
-            if (desastre) +
-                goto error; +
-        } +
-    ... +
-error: +
-    /* arregla el desorden /* +
-</code> +
- +
-Esta organización es útil si el código de manejo de error no es trivial y si los erro­res pueden ocurrir en varios lugares. +
- +
-Una etiqueta tiene la misma forma que un nombre de variable y es seguida por dos puntos '':''. Puede ser adherida a cualquier proposición de la misma función en la que está el ''goto''. El alcance de una etiqueta es toda la función. +
- +
-Como otro ejemplo, considérese el problema de determinar si dos arreglos, ''a'' y ''b'', tienen un elemento en común. Una posibilidad es +
- +
-<code c> +
-    for (i = 0; i < n; i++) +
-        for (j = 0; j < m; j++) +
-            if (a[i] == b[j]) +
-                goto encontrado; +
-    /* no se encontró ningún elemento común */ +
-    ... +
-encontrado: +
-    /* se encontró uno: a[i] == b[j] */ +
-    ... +
-</code> +
- +
-El código que involucra un ''goto'' siempre puede escribirse sin él, aunque tal vez al precio de algunas pruebas repetidas o variables extra. Por ejem plo, la bús­queda en los arreglos quedará +
- +
-<code c> +
-encontrado = 0; +
-for (i = 0; i < n && !encontrado; i++) +
-    for (j = 0; j < m && !encontrado; j++) +
-        if (a[i] == b[j]) +
-            encontrado = 1; +
-if (encontrado) +
-    /* Se encontró uno: a[i-1] == b[j-1] */ +
-    ... +
-else +
-    /* no se encontró ningún elemento común */ +
-    ... +
-</code> +
- +
-Con pocas excepciones, como las citadas aquí, el código que se basa en proposiciones ''goto'' es generalmente más difícil de entender y de mantener que el códi­go sin ellas. Aunque no somos dogmáticos acerca del asunto, se ve que las proposiciones ''goto'' deben ser utilizadas raramente, si acaso. +
-===== Capitulo 4: Funciones y la estructura del programa ===== +
- +
-Las funciones dividen tareas grandes de computación en varias más pequeñas, y permiten la posibilidad de construir sobre lo que otros ya han hecho, en lugar de comenzar desde cero. Las funciones apropiadas ocultan los detalles de operación de las partes del programa que no necesitan saber acerca de ellos, así que dan claridad a la totalidad y facilitan la penosa tarea de hacer cambios. +
- +
-El lenguaje C se diseñó para hacer que las funciones fueran eficientes y fáciles de usar; los programas escritos en C se componen de muchas funciones pequeñas en lugar de sólo algunas grandes. Un programa puede residir en uno o más archi­vos fuente, los cuales pueden compilarse por separado y cargarse junto con fun­ciones de biblioteca previamente compiladas. No trataremos aquí tales procesos, puesto que los detalles varían de un sistema a otro. +
- +
-La declaración y definición de funciones es el área donde el estándar ANSI ha hecho los cambios más visibles a C. Tal como mencionam os en el [[#capitulo 1|capítulo 1]], ahora es posible declarar los tipos de los argumentos cuando se declara una fun­ción. La sintaxis de la definición de funciones también cambia, de modo que las +
-declaraciones y las definiciones coincidan. Esto hace posible que el compilador pueda detectar muchos más errores de lo que podía anteriormente. Además, cuando los argumentos se declaran con propiedad, se realizan automáticamente las conversiones convenientes. +
- +
-El estándar clarifica las reglas sobre el alcance de los nombres; en particular, requiere que sólo haya una definición de cada objeto externo. La inicialización es más general: los arreglos y las estructuras automáticas ahora se pueden inicializar. +
- +
-El preprocesador de C también se ha mejorado. Las nuevas facilidades del Procesador incluyen un conjunto más completo de directivas para la compilación condicional, una forma de crear cadenas entrecomilladas a partir de argumentos de macros y un mejor control sobre el proceso de expansión de macros. +
- +
-==== 4.1 Conceptos básicos de funciones ==== +
- +
-Para comenzar, diseñemos y escribamos un programa que imprim a cada línea de su entrada que contenga un “patró n” o cadena de caracteres en particular. +
- +
-(Este es un caso especial del programa grep de UNIX.) Por ejem plo, al buscar el patrón de letras "ould." en el conjunto de líneas +
- +
-<code> +
-Ah Love! could you and I with Fate conspire +
-To grasp this sorry Scheme of Things entire, +
-Would not we shatter it to bits -- and then +
-Re-mould it nearer to the Heart's Desire! +
-</code> +
- +
-producirá la salida +
- +
-<code> +
-Ah Love! could you and I with Fate conspire +
-Would not we shatter it to bits -- and then +
-Re-mould it nearer to the Heart's Desire! +
-</code> +
- +
-El trabajo se ajusta ordenadamente en tres partes: +
- +
-<code c> +
-while {hay otra línea) +
-   if {la línea contiene el patrón) +
-      imprímela +
-</code> +
- +
-Aunque ciertamente es posible poner el código de todo esto en ''main'', una me­jor forma es aprovechar la estructura haciendo de cada parte una función separa­da. Es más fácil trabajar con tres piezas pequeñas que con una grande, debido a que los detalles irrelevantes se pueden ocultar dentro de las funciones, y minimi­zar así el riesgo de interacciones no deseadas. Los fragmentos incluso se pueden emplear en otros programas. +
- +
-“Mientras hay otra línea” es ''getline'', función que ya escribimos en el [[#capitulo 1|capítulo 1]], e “imprímela” es ''printf'', que alguien ya nos proporcionó. Esto significa que sólo necesitamos escribir una rutina para decidir si la línea contiene una ocurren­cia del patrón. +
- +
-Podemos resolver ese problema escribiendo una función ''strindex(s,t)'', que re­gresa la posición o índice en la cadena ''s'' en donde comienza la cadena ''t'', o ''-1'' si ''s'' no contiene ''t''. Debido a que los arreglos en C principian en la posición cero, los índices serán cero o positivos, y así un valor negativo como ''-1'' es conveniente para señalar una falla. Cuando posteriormente se necesite una coincidencia de pa­trones más elaborada, sólo se debe reemplazar ''strindex''; el resto del código pue­de permanecer igual. (La biblioteca estándar provee una función ''strstr'' que es semejante a ''strindex'', excepto en que regresa un apuntador en lugar de un índice.) +
- +
-Una vez definido todo este diseño, llenar los detalles del programa es simple. Aquí está en su totalidad, de modo que se puede ver cómo las piezas quedan juntas. Por ahora, el patrón que se buscará es una cadena literal, lo cual no es el mecanismo más general. Regresaremos en breve a una discusión sobre cómo inicializar arreglos de caracteres, y en el [[#capitulo 5|capítulo 5]] mostraremos cómo hacer que el patrón de caracteres sea un parámetro fijado cuando se ejecuta el programa. +
- +
-También hay una versión ligeramente diferente de ''getline'', que se podrá compa­rar con la del [[#capitulo 1|capítulo 1]]. +
- +
-<code c> +
-# include <stdio.h> +
-#define MAXILINE 1000    /* longitud máxima por línea de entrada */ +
- +
-int getline(char line[], int max) +
-int strindex(char source[], char searchfor[]); +
- +
-char pattern[] = "ould";     /* patrón a buscar */ +
- +
-/* encontrar todas las líneas que coincidan con el patrón */ +
-main() +
-+
-    char line[MAXLINE]; +
-    int found = 0; +
-     +
-    while (getline(line, MAXLINE) > 0) +
-        if (strindex(line, pattern) >= 0) { +
-            printf("%s", line); +
-            found++; +
-        } +
-    return found; +
-+
-</code> +
- +
-<code c> +
-/* getline: pone linea en s, retorna longitud */ +
-int getline(char s[], int lim) +
-+
-    int c, i; +
-     +
-    i = 0; +
-    while (--lim > 0 && (c=getchar()) != EOF && c != '\n'+
-        s[i++] = c; +
-    if (c == '\n'+
-        s[i++] = c; +
-    s[i] = '\0'; +
-    return i; +
-+
-</code> +
- +
-<code c> +
-/* strindex: retorna el índice de t en s, -1 si no hay */ +
-int strindex(char s[], char t[]) +
-+
-    int i, j, k; +
-     +
-    for (i = 0; s[i] != '\0'; i++) { +
-        for (j=i, k=0; t[k]!='\0' && s[j]==t[k]; j++, k++) +
-            ; +
-        if (k > 0 && t[k] == '\0'+
-            return i; +
-    } +
-    return -1; +
-+
-</code> +
- +
-Cada definición de función tiene la forma  +
- +
-<code c> +
-tipo-regresado nombre-de-función(declaraciones de argumentos) +
-+
-    declaraciones y proposiciones +
-+
-</code> +
- +
- +
-Varias partes pueden omitirse; una función mínima "dummy" es +
- +
-<code c> +
-nada() {} +
-</code> +
- +
-que no hace ni regresa nada. Una función ''hacer-nada'', como ésta, es algunas ve­ces útil para reservar lugar al desarrollar un programa. Si el tipo que regresa se omite, se supone ''int''+
- +
-Un programa es sólo un conjunto de definiciones de variables y funciones. La comunicación entre funciones es por argumentos y valores regresados por las fun­ciones, y a través de variables externas. Las funciones pueden presentarse en cual­quier orden dentro del archivo fuente, y el programa fuente se puede dividir en +
-varios archivos, mientras las funciones no se dividan. +
- +
-La proposición ''return'' es el mecanismo para que la función que se llama regre­se un valor a su invocador. Al ''return'' le puede seguir cualquier expresión: +
- +
-<code c> +
-return expresión +
-</code> +
- +
-La //expresión// se convertirá al tipo de retorno de la función si es necesario. Con frecuencia se utilizan paréntesis para encerrar la ''(''//expresión//'')'', pero son optativos. +
- +
-La función que llama tiene la libertad de ignorar el valor regresado. Incluso, no hay necesidad de un a expresión después de ''return''; en tal caso, ningún valor regresa al invocador. También el control regresa, sin valor, cuando la ejecución “cae al final” de la función al alcanzar la llave cerrada derecha ''}''. No es ilegal, +
-aunque probablemente un signo de problemas, el que una función regrese un va­lor desde un lugar y ninguno desde otro. En cualquier caso, si una función no regresa explícitamente un valor, su “valor” es ciertamente basura. +
- +
-El programa de búsqueda del patrón regresa un estado desde ''main'', el número de coincidencias encontradas. Este valor está disponible para ser empleado por el medio ambiente que llamó al programa. +
- +
-El mecanismo de cómo compilar y cargar un programa en C que reside en va­rios archivos fuente varía de un sistema a otro. En el sistema UNIX, por ejemplo, la orden //cc// mencionada en el [[#capitulo|capítulo 1]] hace el trabajo. Suponiendo que las tres funciones se almacenan en tres archivos llamados ''main.c'', ''getline.c'', y ''strin­dex.c''. Entonces la orden  +
- +
-<code bash> +
-cc main.c getline.c strindex.c +
-</code> +
- +
-compila los tres archivos, sitúa el código objeto resultante en los archivos ''main.o'', ''getline.o'', y ''strindex.o'', y después los carga todos dentro de un archivo ejecutable llamado ''a.out''. Si existe un error - digamos en ''main.c'' - dicho archivo pue­de volverse a compilar por sí mismo y el resultado cargado con los archivos objeto previos, con la orden. +
- +
-<code bash> +
-cc main.c getline.o strindex.o +
-</code> +
- +
-//cc// emplea la convención "''.c''" contra "''.o''" para distinguir los archivos fuente de los archivos objeto. +
- +
-  * **Ejercicio 4-1**. Escriba la función ''strrindex(s,t)'', que regresa la posición de la ocu­rrencia de más a la derecha de ''t'' en ''s'', o ''-1'' si no hay alguna. +
- +
-==== 4.2 Funciones que regresan valores no enteros ==== +
- +
-Basta ahora los ejemplos de funciones han regresado o ningún valor (void) o un ''int''. ¿Qué pasa si una función debe regresar algo de otro tipo? Muchas funciones numéricas como ''sqrt'', ''sin'' y ''cos'' regresan ''double''; otras funciones especiali­zadas regresan tipos diferentes. Para ilustrar cómo tratar con esto, escribamos y usemos la función ''atof(s)'', que convierte la cadena ''s'' a su valor equivalente de punto flotante de doble precisión. La función ''atoi'' es una extensión de ''atoi'', de la que mostramos versiones en los capítulos 2 y 3. Maneja signo y punto decimal optativos, y presencia o ausencia de parte entera o fraccionaria. Nuestra versión +
-no es una rutina de conversión de alta calidad; tomaría más espacio del que pode­mos dedicarle. La biblioteca estándar incluye un ''atof''; el //header// ''<math.h>'' la declara. +
- +
-Primero, ''atof'' por sí misma debe declarar el tipo del valor que regresa, puesto que no es ''int''. El nombre del tipo precede al nombre de la función: +
- +
-<code c> +
-#include <ctype.h> +
- +
-/* atof: convierte la cadena s a double */ +
-double atof(char s[]) +
-+
-    double val, power; +
-    int i, sign; +
-     +
-    for (i = 0; isspace(s[i]); i++) /* saltea espacio en blanco */ +
-        ; +
-    sign = (s[i] == '-') ? -1 : 1; +
-    if (s[i] == '+' || s[i] == '-'+
-        i++; +
-    for (val = 0.0; isdigit(s[i]); i++) +
-        val = 10.0 * val + (s[i] - '0'); +
-    if (s[i] == '.'+
-        i++; +
-    for (power = 1.0; isdigit(s[i]); i++) { +
-        val = 10.0 * val + (s[i] - '0'); +
-        power *= 10; +
-    } +
-    return sign * val / power; +
-+
-</code> +
- +
-Segundo, e igualmente importante, la rutina que llama debe indicar que ''atof'' regresa un valor que no es ''int''. Una forma de asegurar esto es declarar ''atof'' explíci­tamente en la rutina que la llama. La declaración se muestra en esta primitiva cal­culadora (apenas adecuada para un balance de chequera), que lee un número por +
-línea, precedido en forma optativa por un signo, y lo acumula, imprimiendo la suma actual después de cada entrada: +
- +
-<code c> +
-#include <stdio.h> +
-#define MAXLINE 100    /* calculadora rudimentaria * / +
-main() +
-+
-    double sum, atof(char []); +
-    char line[MAXLINE]; +
-    int getline(char line[], int max); +
-     +
-    sum = 0; +
-    while (getline(line, MAXLINE) > 0) +
-        printf("\t%g\n", sum += atof(line)); +
-    return 0; +
-+
-</code> +
- +
-La declaración +
- +
-<code c> +
-double sum, atof(char []); +
-</code> +
- +
-señala que ''sum'' es una variable ''double'', y que ''atof'' es una función que toma un argumento ''char[]'' y regresa un ''double''.  +
- +
-La función ''atof'' se debe declarar y definir consistentemente. Si ''atof'' en sí mis­ma y la llamada a ella en ''main'' tienen tipos inconsistentes dentro del mismo archi­vo fuente, el error será detectado por el compilador. Pero si (como es probable) ''atof'' fuera compilada separadamente, la falta de consistencia no se detectaría, ''atof'' regresaría un valor ''double'' que ''main'' trataría como ''int'', y se producirían resultados  incongruentes. +
- +
-A la luz de lo que hemos mencionado acerca de cómo deben coincidir las de­claraciones con las definiciones, esto podría ser sorprendente. La razón de que ocurra una falta de coincidencia es que, si no existe el prototipo de una función, ésta es declarada implícitam ente la primera vez que aparece en una expresión, co­mo +
- +
-<code c> +
-sum += atof(line) +
-</code> +
- +
-Si en una expresión se encuentra un nombre que no ha sido declarado previamen­te y está seguido por paréntesis izquierdo, se declara por contexto, de modo que se supone que es el nombre de una función que regresa un ''int'', y nada se supone acerca de sus argumentos. Aún más, si la declaración de una función no incluye argumentos como en +
- +
-<code c>double atof();</code>  +
- +
-también es tomada de modo que no se supone nada acerca de los argumentos de ''atof''; se desactiva toda revisión de parámetros. Este significado especial de la lista de argumentos vacía se hace para permitir que los programas en C viejos se compilen con los nuevos compiladores. Pero es una mala táctica usar esto con programas nuevos. Si la función toma argumentos, declárelos; si no los toma, use ''void''+
- +
-Dado ''atof'', propiamente declarado, podemos escribir ''atoi'' (convierte una cade­ +
-na a ''int'') en términos de él: +
- +
-<code c> +
-/* atoi: convierte la cadena s a entero usando atof */ +
-int atoi(char s[]) +
-+
-double atof(char s[]); +
-return (int) atof(s); +
-+
-</code> +
- +
-Nótese la estructura de las declaraciones y la proposición ''return''. El valor de la expresión en +
- +
-<code c>return expresión;</code> +
- +
-se convierte al tipo de la función antes de que se tome el ''return''. Por lo tanto, el valor de ''atof'', un ''double'', se convierte automáticamente a ''int'' cuando aparece en este ''return'', puesto que la función ''atoi'' regresa un ''int''. Sin embargo, esta opera­ción potencialmente descarta información, de manera que algunos compiladores lo previenen. El //cast// establece explícitamente lo que la operación intenta y suprime las advertencias. +
- +
-  * **Ejercicio 4-2**. Extienda ''atof'' para que maneje notación científica de la forma ''123.45e-6'' donde un número de punto flotante puede ir seguido por ''e'' o ''E'' y opcionalmente un exponente con signo. □ +
- +
-==== 4.3 Variables Externas ==== +
- +
-==== 4.4 Reglas y Alcance ==== +
- +
- +
-==== 4.5 Archivo de encabezamiento header ==== +
- +
-==== 4.6 Variables estáticas ==== +
- +
-==== 4.7 Variables tipo registro ==== +
- +
-==== 4.8 Estructura de bloques ==== +
- +
-==== 4.9 Inicialización ==== +
- +
-==== 4.10 Recursividad ==== +
- +
-==== 4.11 El preprocesador de C ==== +
- +
-=== 4.1.1 Inclusión de archivos === +
- +
-=== 4.1.2 Substitución de macros === +
- +
-=== 4.1.3 Inclusión condicional === +
- +
-===== Capitulo 5: Apuntadores y arreglos ===== +
- +
-==== 5.1 Apuntadores y Direcciones ==== +
- +
-==== 5.2 Apuntadores y argumentos de funciones ==== +
- +
-==== 5.3 Apuntadores y arreglos ==== +
- +
-==== 5.4 Aritmética de direcciones ==== +
- +
-==== 5.5 Apuntadores a caracteres, y funciones ==== +
- +
-==== 5.6 Arreglos de apuntadores; apuntadores a apuntadores ==== +
- +
-==== 5.7 Arreglos muitidimensionales ==== +
- +
-==== 5.8 Inicialización de arreglos de apuntadores ==== +
- +
-==== 5.9 Apuntadores vs. arreglos muitidimensionales ==== +
- +
-==== 5.10 Argumentos en la línea de órdenes ==== +
- +
-==== 5.11 Apuntadores a funciones ==== +
- +
-==== 5.12 Declaraciones complicadas ==== +
- +
-===== Capítulo 6: Estructuras ===== +
- +
-Una estructura es una colección de una o más variables, de tipos posiblemente diferentes, agrupadas bajo un solo nombre para manejo conveniente. (Las es­tructuras se conocen como “ records” en algunos otros lenguajes, principalm ente Pascal.) Las estructuras ayudan a organizar datos complicados, en particular dentro de programas grandes, debido a que permiten que a un grupo de variables relacionadas se les trate como una unidad en lugar de como entidades separadas. +
- +
-Un ejemplo tradicional de estructura es el registro de una nómina: un emplea­do está descrito por un conjunto de atributos, como nombre, domicilio, número del seguro social, salario, etc. Algunos de estos atributos pueden, a su vez, ser estructuras: un nombre tiene varios componentes, como los tiene un domicilio y aún un salario. Otro ejemplo, más típico para C, procede de las gráficas: un pun­to es un par de coordenadas, un rectángulo es un par de puntos, y otros casos semejantes. +
- +
-El principal cambio realizado por el estándar ANSI es la definición de la asig­nación de estructuras: las estructuras se pueden copiar y asignar, pasar a funcio­nes y ser regresadas por funciones. Esto ha sido manejado por muchos compiladores durante varios años, pero las propiedades están ahora definidas en forma precisa. Las estructuras y los arreglos automáticos ahora también se pueden inicializar. +
- +
-==== 6.1 Conceptos básicos sobre estructuras ==== +
- +
-==== 6.2 Estructuras y funciones ==== +
- +
-==== 6.3 Arreglos de estructuras ==== +
- +
-==== 6.4 Apuntadores a estructuras ==== +
- +
-==== 6.5 Estructuras autorreferenciadas ==== +
- +
-==== 6.6 Búsqueda en tablas ==== +
- +
-==== 6.7 Typedef ==== +
- +
-==== 6.8 Uniones ==== +
- +
-==== 6.9 Campos de bits ==== +
- +
-===== Capítulo 7: ===== +
- +
-Las operaciones de entrada y salida no son en si parle del lenguaje C, por lo que hasta ahora no las hemos destacado. Sin embargo, los programas interactúan con su medio ambiente en formas mucho más complicadas de las que hemos mos­trado antes. En este capítulo describiremos la biblioteca estándar, un conjunto de funciones que proporcionan entrada y salida, manipulación de cadenas, mane­jo de memoria, rutinas matemáticas y una variedad de otros servicios para programas en C, aunque haremos hincapié en la entrada y salida. +
- +
-El estándar ANSI define de manera precisa estas funciones de biblioteca, de modo que pueden existir en forma compatible en cualquier sistema en donde exista C. Los programas que restringen su interacción con el sistema a las facili­dades provistas por la biblioteca estándar pueden ser llevados de un sistema a otro sin cambios. +
- +
-Las propiedades de las funciones de biblioteca están especificadas en más de una docena de //headers//; ya hemos visto algunos, incluyendo ''<stdio.h>'', ''<string.h>'' y ''<ctype.h>''. No presentaremos aquí la totalidad de la biblioteca, puesto que estamos más interesados en escribir programas en C que los usan. La biblioteca se describe en detalle en el [[#apéndice B]]. +
- +
-==== 7.1 Entrada y salida estándar ==== +
- +
-==== 7.2 Salida con formato - printf ==== +
- +
-== Tabla 7-1. Conversiones básicas de Printf == +
- +
-==== 7.3 Listas de argumentos de longitud variable ==== +
- +
-==== 7.4 Entrada con formato — scanf ==== +
- +
-== Tabla 7-2 - Conversiones básicas de scanf == +
- +
-^Caracter ^Dato de entrada:tipo de argumento ^ +
-|'''' | | +
-|'''' | | +
-|'''' | | +
-|'''' | | +
-|'''' | | +
-|'''' | | +
-|'''' | | +
-|'''' | | +
-|'''' | | +
- +
- +
-==== 7.5 Acceso a archivos ==== +
- +
-==== 7.6 Manejo de errores—stderr y exit ==== +
- +
-==== 7.7 Entrada y salida de líneas ==== +
- +
-==== 7.8 Otras funciones ==== +
- +
-La biblioteca estándar proporciona una amplia variedad de funciones. Esta sección es una breve sinopsis de las más útiles. En el [[#apendice b|apéndice B]] pueden encon­trarse más detalles y muchas otras funciones. +
- +
-=== 7.8.1 Operaciones sobre cadenas === +
- +
-=== 7.8.2 Prueba y conversión de clases de caracteres === +
- +
-=== 7.8.3 Ungete === +
- +
-=== 7.8.4 Ejecución de órdenes === +
- +
-=== 7.8.5 Administración del almacenamiento === +
- +
-=== 7.8.6 Funciones matemáticas === +
- +
- +
-=== 7.8.7 Generación de números aleatorios === +
- +
- +
-===== Capítulo 8: La interfaz con el sistem a UNIX ===== +
- +
-El sistema operativo UNIX proporciona sus servicios a través de un conjunto de //llamadas al sistema//, que consisten en funciones que están dentro del sistema operativo y que pueden ser invocadas por programas del usuario. Este capitulo describe cómo emplear algunas de las más importantes llamadas al sistema desde programas en C. Si el lector usa UNIX, esto debe serle directamente útil, debido a que algunas veces es necesario emplear llamadas al sistema para tener máxima eficiencia, o para tener acceso a alguna facilidad que no esté en la biblioteca. +
- +
-Incluso, si se emplea C en un sistema operativo diferente el lector debería ser ca­paz de adentrarse en la programación estudiando estos ejemplos; aunque los de­talles varían, se encontrará un código semejante en cualquier sistema. Puesto que la biblioteca de C ANSI está en muchos casos modelada con base en las facilida­des de UNIX , este código puede ayudar también a su entendimiento. +
- +
-El capítulo está dividido en tres partes fundamentales: entrad a/salida, sistema de archivos y asignación de almacenamiento. Las primeras dos partes suponen una  modesta familiaridad con las características externas de los sistemas UNIX. +
- +
-El capítulo 7 tuvo que ver con una interfaz de entrada/salida uniforme entre sistemas operativos. En cualquier sistema las rutinas de la biblioteca estándar se tienen que escribir en términos de las facilidades proporcionadas por el sistema anfitrión. En las secciones de este capítulo describiremos las llamadas al sistema UNIX para entrada y salida, y mostraremos cómo puede escribirse parte de la biblioteca estándar con ellas.+
  
-==== 8.1 Descriptores de Archivos ====+[[El lenguaje de Programación C - Capitulo 1|Capitulo 1: Introducción General]]
  
-==== 8.2 E/S de bajo nivel — read write ====+[[El lenguaje de Programación C - Capitulo 2|Capítulo 2: Tipos, operadores expresiones]]
  
-==== 8.Open, creat, close, unlink ====+[[El lenguaje de Programación C - Capitulo 3|capitulo 3: Control de Flujo]]
  
-==== 8.Acceso aleatorio — lseek ====+[[El lenguaje de Programación C - Capitulo |Capitulo 4]]
  
-==== 8.5 Ejemplo - Una realización de fopen y getc ====+[[El lenguaje de Programación C - Capitulo 5 |Capitulo 5]]
  
-==== 8.6 Ejemplo — listado de directorios ====+[[El lenguaje de Programación C - Capitulo 6 |Capitulo 6]]
  
-==== 8.7 Ejemplo - Asignador de memoria ====+[[El lenguaje de Programación C - Capitulo 7 |Capitulo 7]]
  
-===== Apéndice A: Manual de Referencia =====+[[El lenguaje de Programación C - Capitulo 8 |Capitulo 8]]
  
-=== A1: Introducción ===+==== Apéndices ====
  
-Este manual describe al lenguaje C tal como se especifica en Draft Proposed American National Standard for Information Systems — Programming Language C, documento número X3J11/88-001, con fecha 11 de enero de 1988. Este borrador no es el estándar fi­nal, y todavía es posible que ocurran algunos cambios en el lenguaje. Así pues, este manual no describe la definición final del lenguaje. Más aún es una interpretación del borrador propuesto del estándar, no el estándar en sí, aunque se ha tenido cuidado de hacerlo una guía confiable.+[[El lenguaje de Programación C - Apéndice A|Apéndice A: Manual de Referencia]]
  
-En su mayor parte, este manual sigue la línea amplia del borrador estándar, que a su vez sigue la de la primera edición de este libro, aunque la organización difiere en el detalle. Ex­cepto por renombrar algunas producciones y porque no se formalizan las definiciones de los componentes léxicos o del preprocesador, la gramática dada aquí para el lenguaje es equivalente a la del borrador actual.+[[El lenguaje de Programación C - Apéndice B|Apéndice B: Biblioteca Estándar]]
  
->En este manual, el material comentado se encuentra sangrado y escrito en un tipo más pequeño, como este. A menudo estos comentarios resaltan las formas en las que el estándar ansí de C difiere del lenguaje definido por la primera edición de este libro, o de refinamientos introducidos posteriormente en varios compiladores.+[[El lenguaje de Programación C - Apéndice C|Apéndice C: Resumen de Modificaciones]]
  
-===== Apéndice B: Biblioteca Estándar ===== 
  
-===== Apéndice C: Resúmen de Modificaciones ===== 
  
-Desde la publicación de la primera edición de este libro, la definición del lenguaje C ha sufrido modificaciones. Casi todas fueron extensiones al lenguaje original, y fueron di­señadas cuidadosamente para permanecer compatibles con la práctica existente; algunas repararon ambigüedades de la descripción original, y otras representan modificaciones de la práctica existente. Muchas de las nuevas características se anunciaron en los documentos que acompañan a los compiladores disponibles de AT&T, y posteriormente se han adopta­do por otros proveedores de compiladores del lenguaje C. Recientemente, el comité ANSI incorporó más de esos cambios estandarizando el lenguaje, y también introdujo otras mo­dificaciones significativas. Su reporte fue en parte anticipado por algunos compiladores comerciales aún antes de la publicación del estándar formal. 
  
-Este apéndice resume las diferencias entre el lenguaje definido por la primera edición de este libro, y lo esperado como la definición del estándar final. Trata solamente al len­guaje en sí, no a su entorno ni a su biblioteca; aunque esas son partes importantes del estándar, hay poco con qué compararlas, puesto que en la primera edición no se intentó definirlas. 
  
-  * El preprocesamiento está definido más cuidadosamente en el Estándar que en la primera edición, y está extendido: está explícitamete basado en tokens (símbolos); existen nuevos operadores para la concatenación de tokens (# # ) y creación de cadenas (#); hay nuévas líneas de control como #elif y #pragm a; está explícitamente permitida la redeclaración de macros por la misma secuencia de tokens; ya no se reemplazan los parámetros que están dentro de cadenas. La separación de líneas por \ está permitida en cualquier lugar, no sólo en definiciones de cadenas y macros. Véase §A12. 
-  * El significado mínimo el más pequeño de todos los identificadores internos se incre­mentó a 31 caracteres; permitido para identificadores con liga externo permanece en 6 letras, sin importar sin son mayúsculas o minúsculas (muchas implantaciones propor­cionan más). 
-  * Las secuencias trigráficas introducidas por ?? permiten la representación de caracteres que no se encuentran en algunos conjuntos. Están definidos los escapes para ''#'' ''\'' '[' '']'' ''{'' ''}'' ''¡'' ''<nowiki>\</nowiki>''. Véase §A12.1. Obsérvese que la introducción de trigrafos puede cambiar el significado 
-de cadenas que contengan la secuencia ''??''. 
-  * Se introdujeron nuevas palabras reservadas (void, const, volatile, signed. enum). La palabra reservada entry, que nunca se puso en uso, fue retirada. 
-  * Se definen nuevas secuencias de escape para uso dentro de constantes de carácter y ca­denas literales. El efecto de seguir ''<nowiki>\</nowiki>'' con un carácter que no sea parte de una secuenciade escape aprobada está indefinido. Véase §A2.5.2. 

Este sitio web utiliza cookies para guardar datos esenciales de su actividad, como su autenticación. Al entrar acepta el uso de cookies.

Más información