You are on page 1of 153

SEGUNDA EDICION

EL
LENGUAJE DE
PROG
CION

BRIAN W. KERNIGHAN
DENNIS M. ruTCHIE
Pearson

--

Educacin

CONTENIDO

EDICION EN INGLBS
UNIX es una marca registrada AT & T

EL LENGUAJE ])E PROGRAMACION C


Traducido de la segunda edicin en ingls de:
THE C PROGRAMMING LANGUAGE
Prohibida la reproduccin total o parcial de esta obra, per cualquier medio o mtodo
sin autorizacin escrita del editor
DERECHOS RESERVADOS 1991 respecto a la segunda edicin en espaol por
PRENTICE-HALL HISPANOAMERICANA, S.A.
Atlacomulco Nm. 500-5 Piso
Col. Industrial Atoto
53519, Naucalpan de Jurez. Edo. de Mxico

Miembro de la Cmara Nacional de la Industria Editorial, Reg . Nm. 1524


ISBN 968-880-205-0
Original English language editiOn published by

Copyright MCMLXXXVIll by Prentice-Hall, ine.


AH Rights Reserved
ISBN 0-)3-110362-8
IMPRESO EN MEXICO I PRINTED IN MEXICO

Prefacio

ix

Prefacio a la primera edicin

xi

Introduccin
Captulo t. Introduccin general
l.l Comencemos
1.2 Variables y expresiones aritmticas
1.3 La proposicin for
1.4 Constantes simblicas
1.5 Entrada y salida de caracteres
1.6 Arreglos
1.7 Funciones
1.8 Argumentos-llamada por valor
1.9 Arreglos de caracteres
1.10 Variables externas y alcance
Captulo 2. Tipos, operadores y expresiones
2.1 Nombres de variables
2.2 Tipos y tamaos de datos
2.3 Constantes
2.4 Declaraciones
2.5 Operadores aritmticos
2.6 Operadores de re lacin y lgicos
2.7 Conversiones de tipo
2.8 Operadores de incremento y decremento
~9 Operadores para manejo de bits
2.10 Operadores de asignacin y expresiones
2.11 Expresiones condicionales
2.12 Precedencia y orden de evaluacin
Captulo 3. Control de flujo
3.1 Proposiciones y bloques
3.2 If-else
3.3 Else-if
3.4 Switch
3.5 Ciclos-while y ror
3.6 Ciclos-do-while

5
5
8

14
15
16

23
26
29
30
33
39
39

40
41
44
45

46
47
51
53
55
56

57
61
61
61

63
64
66
70

7.7
7.8
3.7
3.8

Break

continue

Goto y etiquetas

Captulo 4. Funciones}' la estructura del programa


4.1 Conceptos bsicos de funciones
4.2 Funcioncs que regresan valores no~enteros
4.3 Variables externas
4.4 Reglas de alcance
4.5 Archivos header
4.6 Variables estticas
4.7 Variables registro
4.8 Estructura de bloque
4.9 Inicializacin
4.10 Recursividad
4.11 El preprocesador de C

71
72

75
75
79
82
88
90
91
92
93
94
95
97

Captulo 5. Apuntadores y arreglos


5. 1 Apuntadores y direcciones
5.2 Apuntadores y argumentos de funciones
5.3 Apuntadores y arreglos
5.4 Aritmtica de direcciones
5.5 Apuntadores a caracteres y funciones
5.6 Arreglos de apuntadores; apuntadores a apuntadores
5.7 Arreglos multidimensionales
5.8 Inicializacin de arreglos de apuntadores
5.9 Apuntadores vs. arreglos multidimensionales
5.10 Argumentos en la lnea de comandos
5.11 Apuntadores a funciones
5. 12 Declaraciones complicadas

115
118
122
124
125
126
130
134

Captulo 6. Estructuras
6.1 Conceptos bs icos sobre estructuras
6.2 Estructuras y funciones
6.3 Arreglos de estructuras
6.4 Apuntadores o estructuras
6.5 Estructuras autorreferenciadas
6.6 Bsqueda en tablas
6.7 Typedef
6.8 Uniones
6.9 Campos de bits

141
141
143
146
151
153
158
161
162
164

Captulo 7. Entrada y salida


7.1 Entrada y salida estndar
7.2 Salida con formato-printf
7.3 Listas de argumentos de longitud variable
7.4 Entrada con formato-scanf
7.5 Acceso a archivos
7.6 Maneja de errores-stderr y exit

167
167
169
171

103
103
105
108
III

173
176
179

Entrada y salida de lneas


Otras funciones

181
183

Captulo 8. La interfaz del sistema UNrx


8.1 Descriptores de archivos
8.2 E/ S de bajo nivel-read y write
S.3 open, creat, clase, unlink
8.4 Acceso aleatorio-Iseek
8.5 Ejemplo-una implementacin de fopen y getc
8.6 Ejemplo-listado de directorios
8.7 Ejemplo-asignador de memoria

187
187
188
190
193
194
1'98
204

Apndice A. Manual de referencia


Al Introduccin
A2 Convenciones lxicas
A3 Notacin sintctica
A4 Significado de los identificadores
AS Objetos y valores-I
A6 Conversiones
A 7 Expresiones
AS Declaraciones
A9 Proposiciones
A 10 Declaraciones externas
AII Alcance y ligadura
A 12 Preprocesamiento
A13 Gramtica

211
211
215
215
217
217
220
232
245
249
251
253
258

211

Apndice B. Biblioteca estndar


B1 Entrada y salida: < stdio.h >
B2 Pruebas de clasificacin de caracteres: < ctype.h >
B3 Funciones para cadenas: < string.h >
B4 Funciones matemt icas: <math .h >
85 Funciones de utilera: <stdlib .h >
B6 Diagnsticos: <assert.h>
B7 Listas de argumentos variables: < stdarg.h >
88 Saltos no locales: <setjmp.h>
B9 Seales: < signal.h >
810 Funciones de fecha y hora: <time.h>
811 Lmites definidos en la implantacin: < Iim its.h > y < float.h >

278
279
279
281

Apndice C. Resumen de modificaciones

283

Indice

265
265
272
273
274

275
278
278

287

Prefacio

El mundo de la computacin ha sufrido una revolucin desde la publicacin,


en 1978, de El lenguaje de programacin C. Las grandes computadoras son ahora mucho ms grandes, y las computadoras personales tienen capacidades que rivalizan con los mainframes de hace una dcada. Tambin el lenguaje e ha
cambiado en ese tiempo, aunque slo en forma modesta, y se ha extendido ms
all de lo que fueron sus orgenes como el lenguaje del sistema operativo UNIX.
La creciente popularidad de e, los cambios en el lenguaje a lo largo de los
aos, y la creacin de compiladores por grupos no involucrados en su diseo, se
combinaron para demostrar la necesidad de una definicin del lenguaje ms precisa y contempornea que la que proporcion la primera edicin de este libro.
En 1983, el American Nalional Standards/nslilule (ANSI) estableci un comit
cuyos propsitos eran producir "una definicin no ambigua del lenguaje e e, independiente de la mquina", cuidando la conservacin de su espritu. El resultado es el estndar ANSI para el lenguaje C.
El estndar formaliza construcciones sugeridas pero no descritas en la primera
edicin, particularmente la asignacin de estructura y las enumeraciones. Proporciona una nueva forma de declaracin de funciones , que permite revisar com-

parativamente su definicin y uso. Especifica una biblioteca estndar, con un


conjunto extensivo de funciones para realizar la entrada y salida, la administracin de memoria, la manipulacin de cadenas y tareas semejantes. Precisa el
comportamiento de caractersticas que no se mencionaron en la definicin original, y al mismo tiempo establece explcitamente cules aspectos del lenguaje
tienen an dependencia de mquina.
Esta segunda edicin de E//enguaje de programacin C lo describe tal como lo
defini el estndar ANSI. (En el momento de escribir esta edicin, el estndar
se encontraba en la etapa final de revisin; se esperaba su aprobacin a finales
de 1988. Las diferencias entre lo que se ha descrito aqu y la forma final debern
ser mnimas.) Aunque hemos hecho anotaciones en los lugares donde el lenguaje
ha evolucionado, preferimos escribir excl usivamente en la nueva forma. En gen eral esto no hace una diferencia significati va; el cambio ms visible es la !lueva
forma de declaracin y defini cin de funciones. Los modernos compiladores manejan ya la mayora de las posibilidades del es tndar :
ix

PREFACIO

Hemos tratado de mantener la brevedad de la primera edicin. El lenguaje C


no es grande, y no le est bien un gran libro. Hemos mejorado la ex posicin de
caracters ticas crticas, como los apuntadores, que son parte central en la progra-

macin con C. Hemos redefinido los ejemplos originales y agregamos ejemplos


nuevos en varios captulos. Por ejemplo. se aument el tratamiento de declaraciones complicadas con programas que convierten declaraciones en palabras y vice-

ve rsa. Como antes, todos los ejemplos se han probado directamente a partir del
texto, el cual est diseado de manera que lo pueda leer la mqui na.
El apndice A, manual de referencia,

11 0

es el eSlndar, si no que nuestra

intencin fue trasladar la esencia del estndar a un espacio ms pequeo. Est


hecho con el nimo de que proporcione una fcil comprensin para los programadores, pero no como una definicin del lenguaje para quienes escriben compiladores -ese papel propiamente le corresponde al estndar en s. El apndice B
es un resumen de las posibilidades de la biblioteca estndar. Tambin tiene el propsito de ser una referencia para programadores, no para implantadores. En el
apndice C se ofrece un resumen de los cambios de la versin original.

Como mencionamos en el prefacio a la primera edicin, e "se lleva bien, en


la medida en que aum en la nuestra experiencia co n l". Con una dcada ms de
experiencia , aun lo sentimos as. Deseamos que este libro le ayude a aprender el
lenguaje e y lambin cmo usarlo.

Tenemos un profundo reconocimiento hacia los amigos que nos ayudaron a


producir esta segunda edicin. Jan Bentley , Doug Gwyn, Doug Mcllroy, Peter
Nelson y Rob Pike nos dieron valiosos comentarios sobre casi cada pgina del borrador de este manuscrito. Estamos agradecidos por [a cuidadosa [cctura de Al
Aho, Dennis Allison, Joe Campbell, G. R. Em[in, Karen Fortgang , AlIen Ho[ub , Andrew Hume, Dave Kristol, John Linderman, Dave Prosser, Gene Spafford, y Chris Van Wyk. Tambin recibimos tiles sugerencias de Bill Cheswick,
Mark Kernighan, Andy Koening, Robin Lake, Tom London, Jim Reeds, Clovis
Tondo y Peter Weinberger. Dave Prosser respondi muchas preguntas detalladas
acerca del estndar ANSI. Utilizamos extensivamente el intrprete de C + + de
Bjarne Stroustrup, para la prueba local de nuestros programas , y Dave Kristol
nos ofrec i un compilado r ANSI C para [as pruebas fin ales. Rich Drcchs[er nos
ayud grandemente co n la co mposici n.
N uestro sincero agradecimiento a todos.

Brian W. Kernighan
Dennis M. Ritchie

Prefacio a la primera edicin


C es un lenguaje de programacin de propsito general que ofrece como ventajas economa de expresin, control de flujo y estructuras de datos modernos
y un rico conjunto de operadores. Adems, C no es un lenguaje de "muy alto nive[" ni "grande", y no est especializado en alguna rea especial de aplicacin.
Pero su ausencia de restricciones y su generalidad lo hacen ms conveniente y
efectivo para muchas tareas que otros lenguajes supuestamente ms poderosos.
Originalmente, C fue diseftado para el sistema operativo UNIX y Dennis Ritchie
lo implant sobre el mismo en [a DEC PDP-ll. El sistema operativo, el compilador de C y esencialmente todos los programas de aplicacin de UN IX (incluyendo
todo el sofware utilizado para preparar este libro) estn escritos en C . Tambin
existen compiladores para [a produccin en otras mquinas, incluyendo la IBM
System/370, la Honeywell 6000 y la Interdata 8/32. El lenguaje C no est ligado
a ningn hardware o sistema en particular y es fcil escribir programas que corrern sin camb ios en cualquier mquina que maneje C.

La finalidad de este libro es ayudar al lector a aprender cmo programar en


C. Contiene una introduccin general para hacer que los nuevos usuarios se ini-

cien lo ms pronto posible, captulos separados sobre cada caracterstica importante y un manual de referencia. La mayora de las exposiciones estn basadas
en la lectura, escritura y revisin de ejemplos, ms que en el simple estab[ecimiento de reglas. En su mayora, los ejemplos son programas reales y completos, no
fragmentos aislados. Todos los ejemplos han sido probados directamente a partir
del texto, el cual est en forma legible para la mquina. Adems de demostrar
cmo hacer un uso efectivo del lenguaje, donde ha sido posible, tralamos de ilustrar algoritmos tiles y principios de buen estilo y diseo.
E[ libro no es un manual de introduccin a la programacin; se supone en l
familiaridad con los conceptos bsicos de programacin , como variables, proposiciones de asignacin, ciclos y funciones. No obstante, un programador no vato

deber ser capaz de leer y obtener los conceptos del lenguaje, aunque le ayudara
la coOperacin de un colega ms experimentado.
De acuerdo con nuestra experiencia, e ha demostrado ser un lenguaje agradable, expresivo y verstil para una amplia variedad de programas. Es fcil de aprender y se obtienen mejores resultados a medida que aumenta nuestra experiencia
con l. Deseamos que este libro le ayude al lector a usarlo correctamente.
xi

xii

PREFACIO A LA PRIMERA EDICION

Las crticas y sugerencias de muchos amigos y colegas han aumentado muchsimo los conceptos de este libro y ha sido un placer escribirlo. En particular nuestro agradecimiento a Mike Bianchi, lim Blue, Stu Feldman, Doug Mellroy, Bill
Roome, Bob Rosin y Larry Rosler que leyeron cuidadosamente las numerosas
versiones. Tambin agradecemos Al Aho, Steve Bourne, Dan Dvorak, Chuck
Haley, Debbie Haley, Marion Harrris, Rick Holt, Steve 10hnson, 10hn Mashey,
Bob Mitze, Ralph Muha, Peter Nelson, ElIiot Pinson, Bill Plauger, lerry Spivack, Ken Thompson y Peter Weinberger por sus valiosos comentarios a travs
de varias etapas; a Mike Lesk y loe Ossanna, por su invaluable ayuda en la impresin.

Brian W. Kernighan
Dennis M. Ritchie

Introduccin

C es un lenguaje de programacin de propsito 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 estn escritos en lenguaje C. Sin
embargo, este lenguaje no est ligado a ningn sistema operativo ni a ninguna
mquina, 'i aunque se le llama "lenguaje de programacin 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 travs del lenguaje B, el cual fue escrito por Ken Thompson en 1970 para
el primer ,istema UNIX de la DEC POP-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 nmeros de punto flotante de varios tamaos. Adems, existe una jerarqua de
tipos de datos derivados, creados con apuntadores, arreglos, estructuras y uniones. Las expresiones se forman a partir de operadores y operandos; cualquier
expresin, incluyendo una asignacin o una llamada a funcin, puede ser una
proposicin. Los apuntadores proporcionan una aritmtica de direcciones independiente de la mquina.
C proporciona las 'construcciones fundamentales de control de flujo que se requieren en programas bien estructurados: agrupacin de proposiciones, toma de
decisiones (I-else), seleccin de un caso entre un conjunto de ellos (switch),
iteracin con la condicin de paro en la parte superior (while, lar) O en la parte
inferior (do), y terminacin prematura de cielos (break).
Las funciones pueden regresar valores de tipos bsicos, estructuras, uniones
o apuntadores. Cualquier funcin puede ser llamada recursivamente. Las variables locales son normalmente "automticas", o creadas de nuevo con cada invocacin. la definicin de una funcin no puede estar anidada, pero las variables
Pueden estar deelaradas en una modalidad estructurada por bloques. las funciones de un programa en C pueden existir en archivos fuente separados, que se comPIlan de manera separada. Las variables pueden ser internas a una funcin,
externas pero conocidas slo dentro de un archivo fuente, o visibles al programa
Completo.

EL LENGUAJE DE PROGRAMACION
2

INTRODUCCION

Un paso de preprocesamiento realiza substitucin de macros en el texto del


programa, inclusin de otros archivos fuente y compilacin condicional.

e es un lenguaje de relativo "bajo nivel H . Esta caracterizacin no es peyorativa, simplemente significa que e trata con el mismo tipo de objetos que la mayora de las computadoras, llmense caracteres, nmeros y direcciones. Estos
pueden ser combinados Ycambiados de sitio con los operadores aritmticos y lgicos implantados por mquinas reales.
e no proporciona operaciones para tratar directamente con objetos compuestos, tales como cadenas de caractere~, conjuntos, li~tas o arrcglo~. No existen operaciones que manipulen un arreglo o ulla cadena complea, aunque las estructuras
pueden copiarse como una unidad. El lenguaje no define ninguna facilidad para
asignacin de almacenamiento que no sea la de definicin esttica y la disciplina
de pilas provista por las variables locales de funciones; no emplea heap ni recolector de basura. Finalmente, e en si mismo no proporciona capacidades de entrada/salida; no hay proposiciones READ o WRITE, ni mtodos propios de
acceso a archivos. Todos esos mecanismos de alto nivel deben ser proporcionados
por funciones llamadas explcitamente .
De manera semejante, e solamente ofrece un control de flujo franco, y lineal:
condiciones, ciclos, agrupamientos y subprogramas, pero no multiprogramacin,
operaciones paralcla~, ~incronizacin ni cor-rutinas.
Aunque la ausencia de alguna de esas capacidades puede parecer como una
grave deficiencia (" significa que se tiene que llamar a una funcin para comparar dos cadenas de caracteres?"), el mantener al lenguaje de un tamao modesto
tiene beneficios reales. Puesto que e es relativamente pequeo, se puede describir
en un pequeo c.,)J1acio y arrenderse con rapidez. Un programador puede razonablemente e~Jlerar conocer, cntender y utilizar en verdad la totalidad del
lenguaje.
Por muchos aos, la definicin de e fue el manual de referencia de la primera
edicin de El lenguaje de pr()~ram(/cin C. En 1983, el American Nalionol Slal/dards InslifUle (ANSI) estableci un comite para proporcionar una moderna y
comprensible definicin de C. La definicin resultante, el estndar ANSI o
"ANSI e", se esperaba fuera aprobada a fines de 1988. La mayora de las caractersticas del estndar ya se encuentran soportadas por compiladores modernos.
El estndar est basado en el manual de referencia original. El lenguaje ha
cambiado relativamente poco; uno de los propsitos del estndar fue asegu"r
que la mayora de los programas existentes pudiesen permanecer vlidos o, al menos, que los compiladores pudieran producir mensajes de advertencia acerca del
nuevo comportamiento.
Para la mayora de los programadores, el cambio ms importante es una nueva sintaxis para declarar y definir funciones. Una declaracin de funcin ahora
puede incluir una desc ripcin de los argumentos de la funcin; la sintaxis de la
definicin cambia para coincidir. Esta informacin extra permite que los compiladores detecten ms fcilmente los errores causado? por argumentos que no coinciden; de acuerdo con nuestra experiencia, es una adicin muy til al lenguaje.

Existen otros cambios de menor escala en el lenguaje. La asignacin de estructuras y enumeraciones, que ha estado ampliamente disponible, es ahora parte
oficial del lenguaje. Los clculos de punto Ootante pueden ahora realizarse con
precisin sencilla. Las propiedades de la aritmtica, especialmente para tipos sin
signo, estn esclarecidas. El preprocesador es ms elaborado. La mayor parte
de esas cambios slo tendrn efectos secundarios para la mayora de los progra~
madores.
Una segunda contribucin significativa dei estndar es la definicin de una biblioteca que acompae 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, asignacin de memoria, manipulacin de cadenas y otras actividades se~
mejantes. Una coleccin de encabczadores (headers) estndar proporcionan un
acceso uniforme a las declaraciones de funciones y tipos de datos. Los programas
que utilizan esta biblioteca para interaclUar con un sistema anfitrin estn asegurados de un comportamiento compatible. La mayor parte de la biblioteca esta
estrechamente modelada con base en la "biblioteca E/S estillldar" del si" cma
UNIX. Esta biblioteca se describi en la primera edicin y ha sido tambin
ampliamente utilizada en 'otros sistemas. De nuevo, la mayora de los prograll1a~
dores no notarn mucho el cambio.
Debido a que los tipos de datos y estructuras de control provistas por e son
manejadas directamente por la mayora de las computadoras, la biblioteca de
ejecucin (run-lime) requerida para implantar programas autocontenido.~ C\ pc~
quena. Las funciones de la biblioteca estndar nicamente se llaman en forma explicita, de manera que se pueden evitar cuando no se necesitan. La mayor parte
puede escribirse en e, y excepto por detalles ocultos del sistema operativo, ellas
mismas son porttiles.
Aunque e coincide con las capacidades de muchas computadoras, es independiente de cualquier arquitectura. Con un poco de cuidado es fcil escribir programas porttiles, esto es, programas que puedan correr sin cambios en una variedad
de mquinas . El estndar explica los problemas dc la transportabilidad, y prescribe un conjunto de constantes que caracterizan a la mquina en la quc ,>c ejecuta el programa.
e no es un lenguaje fuertemente tipincado, sino que, al evolucionar, \u vcrifi.
cacin de tipos ha sido reforzada. La definicin original de e desaprob, pero
permiti, el intercambio de apuntadores y enteros; esto se ha eliminado y el estndar ahora requiere la adecuada declaracin y la conversin explcita que ya ha
sido obligada por los buenos compiladores. La nueva declaracin de funciones
es otro paso en esta direccin. Los compiladores advertirn de la mayora de los
errores de tipo, y no hay conversin automtica de tipos de datos incompatibles.
mantiene la filosofa bsica de que los programadores saben lo
Sin embargo,
que estn haciendo; slo requiere que eSlablezcan ,- us intencionc ,> en forma
explcita.

Como cualquier otro lenguaje, e tiene sus defectos. Algunos de los operadores tIenen la precedencia equivocada; algunos elementos de la sintaxis pueden ser

INTRODUCCION

mejores. A pesar de todo, e ha probado ser un lenguaje extremadamente efectivo


y expresivo para una amplia variedad de programas de aplicacin.
El libro est organizado como sigue. El captulo 1 es una introduccin orien-

tada a la parte central de C. El propsito 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 programas en l. La introduccin supone un conocimiento prctico de los elementos bsicos de la programacin; no hay una explicacin de computadoras, de compilacin, ni del significado de una expresin como
n = n + 1. Aunque hemos tratado de mostrar tcnicas tiles de programacin en
donde fue posible, la intencin del libro no es la de ser un texto de consulta sobre
estructuras de datos y algoritmos; cuando nos vimos forzados a hacer una eleccin, nos hemos concentrado en el lenguaje.

En los captulos del 2 al 6 se discuten varios aspectos de C en mayor detalle


y ms formalmente de lo que se hace en el capitulo 1, aunque el nfasis est an
en los ejemplos de programas completos, ms que en fragmentos aislados. El captulo 2 trata de los tipos bsicos de datos, operaciones y expresiones. El captulo
3 trata sobre control de flujo: il-else, switch, while, for, etc. En el captulo 4
se cubren funciones y la estructura de un programa -variables externas, reglas
de alcance, archivos fuente mltiples y otros aspectos- y tambin abarca al preprocesador. El captulo 5 discute sobre apuntadores y aritmtica de direcciones.
El captulo 6 cubre estructuras y uniones.
El captulo 7 describe la biblioteca estndar, la cual proporciona una interfaz
comn con el sistema operativo. Esta biblioteca est definida por el estndar
ANSI y se intenla que se tenga en todas las mquinas 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 capilulo 8 describe una interfaz enlre los programas en C y el sislema operativo UNIX, concentrndose en entrada/salida, el sistema de archivos y la asignacin de memoria. Aunque algo de este captulo es especfico de sistemas UNIX,
los programadores que usen otros sistemas de todas maneras encontrarn aqu
material de utilidad, incluyendo alguna comprensin acerca de cmo est implantada una versin de la biblioteca estndar, as como sugerencias para obtener un
cdigo porttil.
El apndice A contiene un manual de consulta del lenguaje. El informe oficial
de la sintaxis y la semntica de C es en s el estndar ANSI. Ese documento, sin
embargo, est principalmente pensado para quienes escriben compiladores. El
manual de consulta de eSle libro transmite la definicin del lenguaje en una forma
ms concisa y sin el mismo estilo legalista. El apndice B es un resumen de la biblioteca estndar, de nuevo ms para usuarios que para implantadores. El apndice C es un breve resumen de los cambios del lenguaje original. Aunque, en caso
de duda, el estndar y el compilador en uso quedan como las autoridades finales
sob re el lenguaje.

CAPITULO 1

Cl)IllL'IlL'L'1ll0'"

COIl

Introduccin general

ulla illlrodul'l'ill r{pida a C. NUI.?'~lrll ohjL'li\l.1 l':- I1Hhlrar

lo.., l'k'lIlL' 11 I 0:-' e~el1L'iak:- del lenguajL' ell prograll1a~ reale ...,. pero :-.in p('rdL'l"I1o~

en detalles, reglas o excepciones. Por el momento, no intentamos ser completos

ni precisos (exceptuando en los ejemplos, que s lo son). Deseamos llevarlo tan


rpido como sea posible al punto en donde pueda escribir programas tiles, y
para hacerlo tenemos que concentrarnos en las bases: variables y constantes, arit-

mtica, control de flujo, funciones y los rudimentos de entrada y salida. Hemos


dejado intencionalmente fuera de este captulo las caractersticas de e que son
importantes para escribir programas ms grandes. Esas caractersticas incluyen
apu ntadores, estructuras, la mayor parte del rico conjunro de operadores de e,

varias proposiciones para control de flujo y la biblioteca estndar.


Este enfoque tiene sus inconvenientes. Lo ms notorio es que aqu no se enl'Ul'l1lra la descripcin completa dc ninguna c:lraL'tL'ri:-.lil'a panil'lllar dL'lll'ngllajl'.

y la introduccin, por su brevedad, puede tambin ser confusa. Y debido a que


los ejemplos no utilizan la potencia completa de e, no son tan concisos y elegantes como podran serlo. Hemos tratado de aminorar esos efectos, pero tenga cuidado. Otro inconveniente es que los captulos posteriores necesariamente repetirII algu de lo expue~(o en Sle. Esperamo:-. qUl' la rL'pcliL'''."lll. Ill:-' (JUl' Illolc ...,ar.
ayude.
En cualquier caso, los programadores con experiencia deben ser capaces de
extrapolar del material que se encuentra en este captulo a sus propias necesidades

de programacin. Los principiantes deben complementarlo escribiendo pequeflos


programas semejantes a los aqu expuestos. Ambos grupos pueden utiliza r este
captulo como un marco de referencia sobre el cual asociar las descripciones ms

detalladas que comienzan en el captulo 2.

1.1

Comencemos

La nica forma de aprender un lluevo lenguaje de programacin es escribiendo programas con l. El primer programa por escribir es el mismo para todos los

lenguajes :
Imprima las palabras
hola, mundo

lNTRODUCCION GENERAL

CAPITULO I

Este es el gran obstculo; para librarlo debe tener la habilidad de crear el texto
del programa de alguna manera, compilarlo con xito, cargarlo, ejecutarlo y descubrir a dnde fue la salida. Con el dominio de eslOs dClalles mecnicos, ludo lo
dems es relativamente fcil.
En C, el programa para escribir "hola, mundo" es

SECCION 1.1

CO\ IE NCHIOS

Un mtodo para comunicar dalOs entre las funciones es que la funcin que
llama proporciona una lista de valores, llamados argumentos, a id funcin que est
invocando. Los parntesis que estn de'spus del nombre de la funcin encierran
a la lista de argumentos. En este ejemplo , main est definido para ser una funcin
que no espera argumentos, lo cual est indicado por la lista vaca ( ).

#include <stdio.h>

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

# inelude <stdio.h>

incluye informacin acerca de la biblio/eea es/l1dar

main( )
La forma de ejecutar este programa depende del sistema que se est utilizando . Como un ejemplo especfico, en el sistema operativo UNIX se debe crear el
programa en un archivo cuyo nombre termine con" .c", como hola.c, y despus
compilarlo con la orden

define una funcin l/amada main


que 170 recibe valores de argumen/os
las proposiciones de main estn encerradas entre l/aves

printf("hola, mundo\n");

main llama a la funcin de biblioteca printL

para escribir esta secuencia de carae/eres;


\n represen/a el carcter nueva I/nea

ce hola.c

Si no se ha cometido algn error, como la omisin de un carcter o escribir algo


en forma incorrecta, la compilacin se har sin emitir mensaje alguno, y crear
un archivo ejecutab le llamado a.out. Si se ejecuta a.out escribiendo la orden

El primer programa en

a.out

se cscri bi r
hola, mundo

En otros sistemas, las reglas sern diferentes, consltelo con un experto.


Ahora algunas explicaciones acerca del programa en s. Un programa en C,
cualquiera que sea su tamao, consta defunciones y variables . Una funcin contiene proposiciones que especifican las operaciones de clculo que se van a realizar , y las variables almacenan los valores utilizados durante los clculos. Las funciones de e son semejantes a las subrutinas y funciones de Fortran o a los
procedimientos y funciones de Pascal. Nuestro ejemplo es una funcin llamada
main. Normalmente se tiene la ' libertad de dar cualquier nombre que se desee,
pero "main" es especial -el programa comienza a ejecutarse al principio de
Inain. Esto significa que todo programa debe tener un Inain en aJgn sitio.
Por lo comn Inain llamar a otras funciones que ayuden a realizar su trabajo, algunas que usted ya escribi, y otras de bibliotecas escritas previamente. La
primera lnea del programa.
#include < stdio.h >

indica al compilador que debe incluir informacin acerca de la biblioteca estndar de entrada/salida; esta lnea aparece al principio de muchos archivos fuente
de C. La biblioteca estndar est descrita en el captulo 7 y en el apndice B.

Las proposiciones de una funcin estn encerradas entre llaves { }. La funcin main slo contiene una proposicin,
printf ("hola, mundo\n");

Una funcin se invoca al nombrarla, seguida de una liSIa dc a rgumentos entrl.' parntes is; de esta manera se est llamando a la funcin printf con el argumento
"hola, Inundo\n". printf es una -undn de bibliott.'Gl qUL' L'~\'-'ribl..' la ..,alida, L'1l
este caso la cadena de caracteres que se encuentra entre comillas.
A una secuencia de caracteres entre comillas, como "hola, mundo\n", se le
llama carlcl/a de C(frac/eres o ('o"st(fl/te de cadel/a. Por d 11101lll'111o, 1l1lL"ll\l IIl1iL\l
uso de cadenas de caracteres ser como argumentos para printf y otras funciones.
La secuencia \n en la cadena representa el carcter nueva linea en la nOfacip
de e, y hace avanzar la impresin al margen izquierdo de la siguiente lnea. SI
se omite el \n (un experimento que vale la pena), encontrar que no hay avance
de lnea despus de la impresin. Se debe utilizar \n para incluir un carcter nue~
va lnea en el argumento de printf; si se intenta a lgo como
printf ("hola, mundo
");

el Compilador de

producir un mensaje de error.

CAP ITULO I

SECCION 1.2

printf nunca proporciona una nueva lnea automticamente, de manera que


se pueden utilizar varias llamadas para const ruir una lnea de salida en etapas.
Nuestro primer programa tambin pudo haber sido escrito de la siguiente manera.

240
260
280
300

INTRODUCCION GENERAL

#include < stdio.h >

main( )
{
printf("hola, ");
printf("mundo");

VAR IABLES Y EXPRESIONES ARITMETICAS

115
126
137
148

En s el programa an co nsiste de la definicin de una nica funcin llamada


main. Es ms largo que el que imprime "hola, mundo", pero no es complicado.
Introduce var ias ideas nuevas, incluyendo comentarios , declaraciones, variables,
expresiones aritmticas, ciclo s y salida con for mato.
#include < stdio.h >
/ * imprime la tabla Fahrenheit -Celsius
para fahr = O, 20, ... , 300 . j
main ( )

printf("\ n");

, producindose una salida idntica .


Ntese que \n representa un solo carcter. Una secuencia de escape como \n
proporciona un mecanismo general y extensible para representar caracteres invisibles o difciles de escribir. Entre otros que C proporciona estn \t para tabulacin, \b para retroceso, \" para comillas, y \ \ para la diagonal invertida. Hay una
lista completa en la seccin 2.3.

int fahr, celsius;


int lower , upper , step;
lower
upper

Ejercicio 1-1_ Ejecute el programa "hola, mundo" en su sistema. Experimente con

= O;
= 300;

step

20;

fahr

lower;

/ . lmite inferior de la tabla de temperaturas */


/ . lmite superior . /
/ * tamao del incremento ./

la omisin de partes del programa, para ver qu mensajes de error se obtienen. O

Ejercicio 1-2_ Experimente el descubrir qu pasa cuando la cadena del argumento


de printf contiene \c, en donde e es algn carcter no puesto en lista anteriormente. O

1_2

while (fahr < = upper) {


celsius = 5 (fahr- 32) j 9;
prinU(,'%d\t%d\n", fahr, celsius) ;
fahr = fahr + step;

Variables y expresiones aritmticas

El siguiente programa utiliza la frmula 0(7 = (5/9) (0 F-32) para imprimir


la siguiente tabla de temperaturas Fahrenheit y sus equivalentes centgrados o
Celsius:

o
20
40
60
80

- 17
-6
4
15
26

iOO 37
120
140
160
180
200
220

48
60
71
82
93
104

Las dos lneas


/ . imprime la tabla Fahrenheit -Celsius
para fahr = O, 20, ... , 300 . j
SOn

un comentario, que en este caso explica brevemente lo que hace el programa.

Cualesquier caracleres entre / . y . / son ignorados por el compilador, y pueden


ser utilizados libremente para hacer a un programa ms fcil de entender. Los comentarios pueden aparecer en cualquier lugar donde puede colocarse un espacio
en blanco, un tabulador o nueva lnea.
En C, se deben declarar todas las variables antes de su uso, generalmente al
principio de la funcin y antes de cualquier proposicin ejecutable . Una declaracin notifica las propiedades de una variable; consta de un nombre de tipo y una
liSIa de variables, como
int fahr, celsius;
int lower, upper, step;

10

CAPITU LO I

INTRODUCCION GENERA L

El tipo int significa que las variables de la lis!a son enteros, en contraste con float,
que significa punto flotante, esto es, nmeros que pueden tener una parte fraccionaria. El rango tanto de int como de float depende de la mquina que se est utilizando; los inl de 16 bits , que estn comprendidos entre el -3 2768 y + 32767, son
comUJ1c,,>, como lo son los intde 32 bits. Un numero float tpicamente es de 32 bil~,
por lo menos con seis dgitos significativos y una magnitud generalmente entre

10-" y lO - l8
Adems de inl y float, C proporciona varios tipos de datos bsicos, incluyendo:
-un solo hVTe

char
short

carcter

long

entero largo

double

punto notante de doble precisin

entero corto

Los tamaos de estos objetos tambin dependen de la mquina. Tambin existen


arreglos, estructuras y uniones de estos tipos bsicos, apuntadores a ellos y funciones
que regresan valores con esos tipos, todo lo cual se ver en el momento oportuno.
Los clculos en el programa de conversin de temperaturas principian con las

proposiciones de asignacin.
lower = O;
upper = 300;
step = 20;
fahr = lower;

que asignan a las variables sus valores iniciales. Las proposiciones individuales
se terminan con punto y coma.
Cada linea de la tabla se calcula de la misma manera por lo que se utiliza una
iteracin que se repite una vez por cada lnea de salida; este es el propsito del
ciclo while

while (fahr < = upper) {

El c'c1o while funciona de la siguiente manera : se prueba la condicin entre parntesis . De ser verdadera (fahr es menor o igual que upper), el cuerpo del ciclo
(las tres proposiciones entre llaves) se ejecuta . Luego la condicin se prueba nueva mente, y si es verdadera, el cuerpo se ejecuta de nuevo. Cuando la prueba resulta falsa (fahr excede a upper) la iteracin termina, y la ejecucin contina en la
proposicin que sigue al ciclo. No existe ninguna otra proposicin en este programa, de modo que termina.
El cuerpo de un while puede tener una o ms proposiciones encerradas entre
naves, como en el convertidor de temperaturas, o una sola proposicin sin llaves,
como en
while (i <

j)

i = 2 i;

SECC ION 1.2

VARIABLES Y EXP RESIONES ARITMETICAS

JI

En cualquier caso, siempre se sangra la proposicin controlada por el while con


una tabulacin (lo que se ha mostrado con cuatro espacios) par poder apreciar
de un vistazo cules proposiciones estn dentro del ciclo. El sangrado acenta la
estr uctura lgica del programa. Aunque a los compiladores de C no les importa
la apariencia del programa, un sang.rado y espaL' i a mi~nl o adecuado~ ~on Illuy importantes para hacer programas fciles de leer. Se recomienda escribir una sola
proposicin por lnea y utilizar espacios en blanco alrededor de los operadores
para dar claridad al agrupamiento. La posicin de las llaves es menos importante,
aunq ue la gente mantiene creencias apasionadas. Se eligi uno de los varios estilos populares. Seleccione un estilo que le satisfaga y selo en forma consistente.
La mayor parte del trabajo se realiza en el cuerpo del ciclo. La temperatura
Celsi us se calcula y se asigna a la variable celsius por la proposicin.

celsius

5 (fahr-32) I 9;

La razn de multiplicar por 5 y despus dividir entre 9 en lugar de solamente


multiplicar por 5/9 es que en C, como en muchos otros lenguajes, la divisin de
enleros {runca el resultado: cualquier parte fraccionaria se descarta. Puesto que
5 y 9 son enleros, 5/9 sera truncado a cero y as todas las temperaturas Celsius
se reportaran como cero.
Este ejemplo tambin muestra un poco ms acerca de cmo funciona printL
En realidad, printf es una funcin de propsito general para dar formato de salida, que se describir con detalle en el captulo 7 . Su primer argumento es una cadena de caracteres que sern impresos, con cada % indicando en donde uno de
los otros (segundo, tercero, ... ) argumentos va a ser sustituido, y en qu forma
ser impreso. Por ejemplo, %d especifica un argumento entero, de modo que la
proposicin
printf("%d\t%d\n", fahr, eelsius);
hace que los valores de los dos enteros fahr y eelsius sean escritos, con una tabulaci n (\1) entre ellos.
Cada construccin % en el primer argumento de printf est asociada con el
correspondiente segundo argumento, tercero, etc., y deben corresponder apropiadamente en nmero y tipo, o se tendrn soluciones in(:orrectas.
Con relacin a esto, printf no es parte del lenguaje C; no existe propiamente
una entrada o salida definida en C. printf es slo una til funcin de la biblioteca
es tndar de funciones que est accesible normalmente a los programas en C. Si n
embargo, el comportamiento de printf est definido en el estndar ANSI , por lo
que sus propiedades deben ser las mismas en cualquier compilador o biblioteca
que se apegue a l.
Para concentrarnos en e, no hablaremos mucho acerca de la entrada y la salida hasta el captulo 7. En particular, pospondremos el tema de la entrada con formato hasta emonces. Si se tiene que darle entrada a nmeros, lase la discusi n
de la fUIH.:in seanf en la seccin 7.4. La funcin seanf es como printf, exceptuando que lee de la entrada en lugar de escribir a la salida.

12

CAPITULO 1

INTROD UCC10N GENERAL

Existen un par de problemas con el programa de conversin de temperaturas.


El ms simple es que la salida no es muy esttica debido a que los nmeros no
estn justificados hacia su derecha . Esto es fcil de corregir; si aumentamos a
cada %d de la proposicin printf una amplitud. los numeras impresos sern justificados hacia su derecha dentro de sus campos. Por ejemplo, podra dec irse
printf("%3d %6d\n", fahr, celsius);
para escribir el primer nmero de cada lnea en un campo de tres dgitos de an-

cho, y el segundo en un campo de seis digitos, como esto:

o
20
40
60
80

100

13

Esto es mu y semejante a lo anterior, excepto que fahr y celsius estn declarados como float. y la frmula de conversin est escrita en una forma ms natural.

No pudimos utilizar 5/9 en la versin anterior debido a que la divisin entera lo


tr uncara a cero. Sin embargo, un punto decimal en una constante indica que sta

es de punto flotante, por lo que 5.0/9.0 no se trunca debido a que es una relacin
de dos valores de punto flotante.
Si un operador aritmtico tiene operandos enteros, se ejecuta una operacin
entera . Si un operador numrico tiene un operando de punto flotante y otro ente-

ro, este limo ser convertido a punto flotante antes de hacer la operacin. Si
se hubiera esc rito fahr - 32, el 32 sera convertido automticamente a punto
flota nte. Escribir co nstantes de punto flotante con puntos decimales explcitos,
aun cuando tengan valores enteros, destaca su naturaleza de punto flotante para
los lectores humanos.

-17
-6
4
15
26
37

Las reglas detalladas de cundo los enteros se convierten a punto flotante se


encuentran en el capitulo 2. Por ahora, ntese que la asignacin
fahr

El problema ms grave es que debido a que se ha utilizado aritmtica de enteros, las temperaturas Celsius no son muy precisas; por ejemplo , OF es en rea-

lidad aproximadamente _17.8C , no -17. Para obtener soluciones ms precisas, se debe utilizar aritmtica de punto flotante en lugar de entera. Esto requiere
de algunos cambios en el programa. Aqu est una segunda versin :
# include < stdio.h >

l. imprime la tabla Fahrenheit-Celsius


para fahr = O, 20, ... , 300; versin de punto flotante .1
main( )
{
float fahr, celsius;
int lower, upper, step;

lower = O;
upper ~ 300;
step ~ 20;

VARIABLES Y EXPRESIONES AR1TMETICA$

SECC ION 1.2

l . lmite superior de la tabla de temperaturas .1


l. lmite superior .1
/ . tamao del incremento . 1

fahr = lower;
while (fahr < ~ upper) {
celsius ~ (5.0/9.0) (fahr-32.0);
printf("%3.0f %6.lf\n", fahr, celsius);

fahr = fahr + step;

lower;

y la prueba
while (fahr <

upper)

tambin trabajan en la forma natural -el in! se convierte a flaa! antes de efectuarse la operacin.

La especificacin de conversin %3.01 del printf indica que se escribir un


nmero de punto flotante (en este caso lahr) por lo menos con tres caracteres de
ancho, sin punto decimal y sin dgitos fraccionarios; %6.lf describe a otro
nmero (celsius) que se escribir en una amplitud de por lo menos 6 caracteres ,
con 1 dgito despus del punto decimal. La salida se ver como sigue:

o
20

-17.8
-6 .7

40

4.4

La amplitud y la precisin pueden omitirse de una especificacin: %6f indica que


el nmero es por lo menos de seis caracteres de ancho; %.2f indica dos caracteres
despus del punto decimal , pero el ancho no est restringido; y %1 nicamente
indica escribir el nmero como punto flotante.
%d
%6d

escribe como entero decimal


escribe como entero decimal, por lo menos con 6 caracteres de

amplitud
%f
%6f

escribe como punto notante


escribe como punto notante , por lo menos con 6 carac[er.es de
amplitud

14

INTRODUCCION GENERAL

%.21
%6.21

CAPITULO 1

escribe como punto Ootante, con 2 caracteres despus del punto


decimal
escribe como punto flotante, por lo menos con 6 caracteres de
ancho y 2 despus del punto decimal

Entre otros, printf tambin reconoce %0 para octal, %x para hexadecimal, %c


para carcter, %5 para cadena de caracteres y % % para % en s.

Ejercicio 13. Modifique el programa de conversin de temperaturas de modo


que escriba un encabezado sobre la tabla. D
Ejercicio ]4. Escriba un programa que imprima la tabla correspondiente Celsius
a Fahrenheit. O

1.3

La proposicin tor

Existen suficientes formas distintas de escribir un programa para una tarea en


particular. Intentemos una variacin del programa de conversin de temperaturas.
#include < stdio.h>

CONSTANTES 51MBOLlCAS

SECCION 1.4

15

se ejecuta una vez, antes de entrar propiamente al ciclo. La segunda seccin es

la condicin o prueba que controla el ciclo:


ahr < = 300

Esta condicin !)e evala; si es verdadera, el cuerpo del ciclo (en este caso un sim-

ple printf) ,e ejecuta. Despus el incremento de avance


lahr = ahr + 20

se ejecuta y la condicin se vuelve a evaluar. El ciclo termina si la condicin se


hace falsa. Tal como con el while, el cuerpo del ciclo puede ser una proposicin
sencilla o un grupo de proposiciones encerradas entre llaves. La inicializacin, la
condicin y el incremento pueden ser cualquier expresin.

La seleccin entre while y for es arbitraria, y se basa en aquello que parezca


ms claro . El for es por lo general apropiado para ciclos en los que la inicializacin yel incremento son proposiciones sencillas y lgicamente relacionadas, puesto que es ms compacto que el while y mantiene reunidas en un lugar a las proposiciones que controlan al ciclo.

Ejercicio ]5. Modifique el programa de conversin de temperaturas de manera


que escriba la tabla en orden inverso, esto es, desde 300 grados hasta O. D

/ . imprime la tabla Fahrenheit -Celsius ./


main( )

{
int fahr;

lar (Iahr = O; lahr < = 300; lahr = lahr + 20)


printl("'%3d %6.lUn", lahr, (5.0/9.0)(lahr- 32));

Este produce los mismos resultados. pero ciertamente se ve diferente. Un cambio


importante es la eliminacin de la mayora de las variables; slo permanece fahr
y la hemos hecho in!. Los lmites inferior y superior y el tamao del avance slo
aparecen como constantes dentro de la proposicin for, que es una nueva

construccin, y la expresin que calcula la temperatura Celsius ahora aparece


como el tercer argumento de printf en vez de una proposicin de asignacin separada.

Este ltimo cambio ejemplifica una regla general -en cualquier contexto en
el que se permita ulilil.ar el valor de una variable de algn tipo, c!-, posible usar

una expresin ms complicada de ese tipo. Puesto que el tercer argumento de


printf debe ser un valor de punto Ootante para coincidir con %6.11, cualquier ex
presin de punto flotante puede estar all.
La proposicin for es un ciclo, una forma generali zada del while. Si se compara

con el while anterior, su operacin debe ser clara. Dentro de los parntesis existen
tres secciones, separadas por punto y coma. La primera, la inicializacin
lahr = O

1.4

Constantes simblicas

Una observacin final antes de dejar definitivamente el tema de la conversin


de temperaturas . Es una mala prctica poner "nmeros mgicos" como 300 y 20
en un programa, ya que proporcionan muy poca informacin a quien tenga que

leer el programa, y son difciles de modificar en un forma sistemtica, Una manera de tratar a esos nmeros mgicos es darles nombres significativos. Una lnea

#define define un nombre simblico o constante simblica como una cadena de


caracteres especial:
#define I/ombre

lexlO de reelllp!o::u

A partir de esto, cualquier ocurrencia de nombre (que no est entre comillas ni


como parte de otro nombre) se sust ituir por el texto de reemplazo correspondiente. El nombre tiene la misma forma que un nombre de variable: una secuen-

cia de letras y dgitos que comienza con una letra. El

lex/O

de reemplazo puede

ser cualquier secuencia de caracteres; no est limitado a nmeros.


#include < stdio.h >
#dehne
#define
#define

LOWER
UPPER
STEP

O
300
20

/. lmite inferior de la tabla


/. limite superior .1
/ * tamao del incremento . /

*/

16

INTRODUCC ION GENERAL

CAPrTUI O I

/ . imprime la tabla Fahrenheit-Celsius /


main( )
{
int fahr;

ENTRADA Y SALIDA DE CA RACTERES

SECCION 1.5

17

1.5.1 Copia de archivos

Con getehar y putehar se puede escribir una cantidad sorprendente de cdigo


til sin saber nada ms acerca de entrada y salida. El ejemplo ms sencillo es un

programa que copia la entrada en la salida, un carcter a la vez:


Jor (Jahr = LOWER; Jahr < = UPPER; fahr = fahr + STEP)
printf("%3d %6.H\ n", fahr, (5.0/9.0). (fahr-32));

lee un carcter
while (carcter no es indicador de fin de archivo)

Las cantidades LOWER, UPPER y STEP son constantes si mblicas, no variables,


por lo que no aparecen entre las declaraciones. Los nombres de constantes
simblicas, por convencin, se escriben con letras ma yscu las, de modo que se

puedan distinguir fcilmente de los nombres de variables escritos con minsculas.


Ntese que no hay punto y coma al final de una lnea #deline.

1.5

Entrada y salida de caracteres

Ahora vamos a considerar una familia de programas relacionados para el procesamiento de datos de tipo carcter. Se encontrar que muchos programas slo

son versiones ampliadas de los prototipos que se tratan aqu;.


El modelo de entrada y sali da manejado por la biblioteca c~lnrlar es muy
simple. La entrada y salida de texto, sin importar dnde fue originada o hacia
donde se dirige, se tratan como flujos (streollls ) de caracteres. Un fllljo de texto
es una secuencia de caracteres divididos entre lneas, cada una de las cuales consta
de cero o ms caracteres seguidos de un carcter nueva linea. La biblioteca es
responsable de hacer que cada secuencia de entrada o salida est de acuerdo con este

modelo; el programador de C que utiliza la biblioteca no necesita preocuparse de


cmo estn representadas las lneas fuera del programa.
La biblioteca estndar proporciona varias funciones para leer o escribir un
carcter a la vez, de las cuales ge!ehar y pu!ehar son las ms simples. Cada vez
que se invoca, getchar Ice el siguiente carcter de en/rada de una secuencia de texto y lo devuelve como su valor. Esto es, despu s de
e = getchar()

la variable e contiene el siguiente carcter de entrada. Los caracteres provienen


normalmente del teclado; la entrada de archivos se trata en el captulo 7.
La funcin putchar escribe un carcter cada vez que se invoca:
putchar(c)

escribe el contenido de la variable entera c como un carcter, generalmente en

manda a la salida el carcter recin leMo


lee un carcter

Al convertir esto en C se obtiene


#include <stdio.h>
/ . copia la entrada a la salida; la. versin . /
m.in( )
{
int c;

e = getchar( );
while (e ! = EOF) {
putch.r( e);
e = getchar( );

El operador de relacin! = significa "no igual a" .


Lo que aparece como un carcter en el teclado o en la pantalla es, por supuesto, como cualquier otra cosa, almacenado internamente como un patrn de bits.

El tipo ehar tiene la funcin especfica de almacenar ese tipo de dato, pero tambin puede ser usado cualquier tipo de entero. Usamos in! por una sutil pero importante razn.
El problema es distinguir el fin de la entrada de los datos vlidos. La solucin
es que getehr devuelve un valor distintivo cuando no hay ms a la entrada, un
valor que no puede ser confundido co n ningn otro carcter. Este valor se llama

EOF, por "end af file (fin de archivo)". Se debe declarar e con un tipo que
sea lo suficientemente grande para almacenar cualquier va lor que le regrese getcharo No se puede utilizar char puesto que e debe ser suficientemente grande

como para mantener a EOF adems de cualquier otro carcter. Por lo tanto, se
emplea in!.
EOF es un entero definido en <stdio .h>, pero el valor numrico especfico
no importa mientras que no sea el mismo que ningn va lor tipo chaL Utilizando

la pantalla. Las llamadas a putehar y a printf pueden estar alternadas; la salida

la constante simblica, hemos asegurado que nad a en el programa depende del

aparecer en el orden en que se realicen las llamadas.

valor numrico especfico.

18

CAPITULO 1

INTRODUCC ION GE NERAL

El programa para copiar podra escribirse de modo ms conciso por programadores experimentados de C. En lenguaje e, cualquier asignacin, tal como
c

SECCJON J.5

ENTRADA Y SALIDA DE CARACTERES

19

1.5.2 Conteo de caracteres

El siguiente programa cuenta caracteres y es semejante al programa que copia.

getchar( )

#include < stdio .h >


1 cuenta los caracteres de la entrada; la. versin *1

es una expresi n y tiene un valor, el del lado izquierdo luego de la asignaci n. Esto signi fica que una asignacin puede aparecer como parte de una expr e~ i n ms
larga. Si la asi gnacin de un carcter a e se coloca dentro de la seccin de prueba
de un cicld while, el programa que copia puede esc ribirse de la si guieme man era:

main( )
{

long ne;
ne = O;
while (getchar( ) r = EOF)
+ +nc
printf("%ld\n", nc);

#include < stdio.h>


1* copia la entrada a la salida; 2a. versin *1

main( )
{

La proposicin

int e;

while c = getchar( )) !
putchar(c);

+ +nc;

EOF)

El while obtiene un carcter, lo asigna a e , y entonces prueba si el carcter fue


la seal de fin de archivo. De no serlo, el cuerpo del while se ejecuta, escribiendo
el carcter; luego se repite el while. Luego, cuando se alcanza el final de la entrada, el while termina y tambin lo hace main.
Esta versin centraliza la entrada -ahora hay slo una referencia a getehary reduce el programa . El programa resultante es ms compacto y ms fcil de leer
una vez que se domina el truco. Usted ver seguido este estilo. (Sin embargo,
es posible descarriarse y crear cdigo impen etrable. una tendencia que trataremos de reprimir.)
Los parntesis que estn alrededor de la asignacin dentro de la condicin son
necesarios . La precedencia de ! = es ms alta que la de = . lo que significa que
en ausencia de parntesis la prueba de relacin! = se realizara antes de la asignaDe esta manera, la proposicin
cin
c

#include < stdio.h >

getchar( ) ! ~ EOF

l . cuenta los caracteres de la entrada; 2a. versin */

es equivalente a
c

presenta un nuevo operador, + +, que significa incrementa en uno. Es posible


escribir ne = ne + 1, pero + + ne es ms conciso y muchas veces ms cficiente.
Ha y un operador .correspondiente -- para disminuir en 1. Los operadores + + y
- - pueden ser tanto operadores prefijos (+ + nc) como post fijo s (nc + +); esas
dos formas tienen diferentes valores dentro de las expresiones, como se demos trar en el captulo 2, pero ambos + + nc y ne + + incrementan a ne. Por
el momento adoptaremos la forma de prefijo.
El programa para contar caracteres acumula su cuenta en una variable long
en lugar de una int. Los enteros long son por lo menos de 32 bits . Aunque en
algunas mquinas int y long son del mismo tamao, en otras un int es de 16 bits,
con un valor mximo de 32767, Y tomara relativamente poca lectura a la entrada
para desbordar un contador int. La especificacin de conversin %Id indica a
printf que el argumento correspondiente es un entero long.
Sera posible tener la capacidad de trabajar con nm eros mayores empleando
un double (float de doble precisin). Tambin se utilizar una proposicin for en
lugar de un while, para demostrar otra forma de escribir el ciclo.

main( )

= (getchar() r = EOF)

Esto tiene el efecto indeseable de hacer que e sea O 1, dependiendo de si la llamada de getchar encontr fin de archivo. (En el capitulo 2 se trata este tema con
ms detalle).
Ejercicio 1-6. Verifique que la expresin getchar ( ) ! ~ EOF es O o l. O
Ejercicio 1-7. Escriba un programa que imprima el valor de EOF. O

double ne;
for (nc = O; getchar( ) r = EOF; + + nc)
printf("% .Of\ n", nc) ;

20

CAPITULO I

INTRODUCCION GENERAL

ENTRADA Y SALIDA DE CARACTERES

SECCION 1.5

21

printf utiliza %1 tanto para flcat como para double; %.01 suprime la impresin
del punto decimal y de la parte fraccionaria, que es cero.
El cuerpo de este ciclo lar est vaco, debido a que todo el trabajo se realiza
en las secciones de prueba e incremento. Pero las reglas gramaticales de C requieren que una proposicin for tenga un cuerpo. El punto y coma a islado se llama
proposicin nula, y est aqu para satisfacer este requisito. Lo colocamos en una
linea aparte para que sea visible.
Antes de abandonar el programa para contar caracteres, obsrvese que si la
entrada no contiene caracteres, la prueba del while o del lor no tiene xito desde
la primera llamada a getchar, y el programa produce cero, el resultado correcto .
Esto es importante. Uno de los aspectos agradables acerca del while y del lar es
que hacen la prueba al inicio del ciclo, antes de proceder con el cuerpo. Si no hay
nada que hacer, nada se hace, aun si ello significa no pasar a travs del cuerpo
del ciclo. Los programas deben actuar en forma inteligente cuando se les da una
entrada de longitud cero. Las proposiciones while y lor ayudan a asegurar que los
programas realizan cosas razonables con condiciones de frontera.

distinguir la prueba de igualdad del = simple que utiliza e para la asignacin.


Un mensaje de alerta: los principiantes de e ocasionalmente escriben = cuando
en realidad deben usar = = . Como se ver en el captulo 2, el resultado es por
lo general una expresin legal, de modo que no se obtendr ninguna advertencia.
Un carcter escrito entre apstrofos representa un valor entero igual al valor
numrico del carcter en el conjunto de caracteres de la mquina. Esto se llama
una constante de carcter, aunque slo es otra forma de escribir un pequeo enterO. As, por ejemplo' A' es una constante de carcter; en el conjunto ASCn de
caracteres su valor es 65 , esto es, la representacin interna del carcter A. Por supuesto 'A' es preferible que 65: su significado es obvio, y es independiente de un
conjunto de caracteres en particular.
Las secuencias de escape que se utilizan en constallles de cadena tambin son legales en constantes de carcter; as, '\n' significa el valor del carcter nueva lnea,
el cual es 10 en cdigo ASCII. Se debe notar cuidadosamente que '\n' es un
carcter simple, y en expresiones es slo un entero; por otro lado," \ n" es una
constante cadena que contiene slo un carcter. En el captulo 2 se trata el tema
de cadenas versus caracteres.

1.5.3 CQnteo de lineas

Ejercicio 1-8. Escriba un programa que cuente espacios en blanco, tabuladores


y nuevas lineas. O

El sigu iente programa cuenta lneas a la entrada. Como se mencion anteriormente, la biblioteca estndar asegura que una sec uencia de texto de entrada parezca una sec uencia de lneas, cada una terminada por un carcter nueva linea.
Por lo tanto, contar lneas es solamen te contar caracteres nueva lnea:
#include <stdio.h>

Ejercicio 1.9. Escriba un programa que copie su entrada a la salida, reemplazando


cada cadena de uno o ms blancos por un solo blanco. O
Ejercicio 1-10. Escriba un programa que copie su entrada a la salida, reemplazando cada tabulacin por \1, cada retroceso por \b y cada diagonal invertida por
\ \. Esto hace que las tabulaciones y los espacios sean visibles sin confusiones. O

/ . cuenta las lneas de la entrada . /


main( )

1.5.4 Conteo de palabras

{
int c, nI;
ni = O;
while c = getchar(
il (c = = 'In')

! =

EOF)

El cuarto en nuestra serie de programas tiles cuenta las lneas, palabras y caracteres, usando la definicin de que una palabra es cualquier secuencia de caracteres que no contiene espacio en blanco ni tabulacin ni nueva lnea. Esta es una
versin reducida del programa wc de UNIX.

+ +nl;
printf("%d\n", ni);

#include < stdio.h >


#d~line

El cuerpo del while consiste ahora en un if, el cual a su vez controla el incremento + + nI. La proposicin il prueba la condicin que se encuentra entre parmesis y, si la condicin es verdadera, ejecuta la proposicin (o grupo de proposiciones entre llaves) que le sigue. Hemos sangrado nuevamente para mostrar lo
que controla cada elemento.
El doble signo de igualdad = = es la notacin de C para expresar "igual a"
(como el = si mple de Pascal o el .EQ. de Fortran). ESle simbolo se emplea para

IN 1

#deline OUT O

j ...

en una palabra

.j

l. fuera de una palabra . /

/ . cuenta lneas, palabras, y caracteres de la entrada . /


main( )
{

int c, nI, nw, nc, state;

22

CAPITULO 1

INTRODUCCION GENERAL

state = OUT,
nI = nw = ne = O;
while ((e = getehar( ! = EOF) {
+ +ne,
f (c = = '\n')
+ +nl;
ii (e = = " JI e = = ,\n' :: e
state = OUT;
else f (state = = OUT) {
state = IN;
+ +nw;

ARREGLOS

SECC ION 1.6

23

panicular imponancia en este caso, pero es significativo en situaciones ms complicadas, como se ver ms adelante.
El ejemplo muestra tambin un else, el cual especifica una accin alternativa

si la condicin de una proposicin i/ es falsa. La forma general es


if (expresin)
proposicin
else
proposicin z

'\t')

printf ("%d %d %d\n", ni, nw, ne);

Cada vez que el programa encuentra el primer carcter de una palabra, contabiliza una palabra ms. La variable state registra si actualmente el programa est
o no sobre una palabra; al iniciar es "no est sobre una palabra", por lo que se
asigna el valor OUT. Es preferible usar las constantes simblicas IN y OUT que
los valores literales 1 y 0, porque hacen el programa ms legible. En un programa

Una y slo una de las dos proposiciones asociadas con un i/else se realiza. Si
la expresin es verdadera, se ejecuta proposicin 1; si no lo es, se ejecuta proposicin,. Cada proposicin puede ser una proposicin sencilla o varias entre llaves . En el programa para contar palabras, la que est despus del else es un if
que controla dos proposiciones entre llaves.
Ejercicio 1-11. Cno probara el programa para contar palabras? Qu clase
de entrada es la ms conveniente para descubrir errores si stos existen? o

Ejercicio 1-12. Escriba un programa que imprima su entrada una palabra por
lnea. O

tan pequeo como ste, la diferencia es mnima, pero en programas ms grandes

el incremento en claridad bien vale el esfuerzo extra que se haya realizado para
escribir de esta manera desde el principio. Tambin se descubrir que es ms fci l
hacer cambios extensivos en programas donde los nmeros mgicos aparecen
slo como constantes simblicas.

La lnea
nI

nw

ne

O;

inicializa a las tres variables en cero. Este no es un caso especial sino una consecuencia del hecho de que una asignacin es una expresin con un valor, y que

las asignaciones se asocian de derecha a izquierda. Es como si se hubiese escrito

1.6

Arreglos

Escribamo!-. un programa para contar el nmero ele

if (e =

= ' , II e = = ,\n' II e = = '\t')

dice "si e es un blanco o e es nueva lnea o e es un tabulador". (Recuerde que

la secuencia de escape \t es una representacin visible del carcter tabuladoL)


Existe un correspondiente operador && para AND; su precedencia es ms alta
que la de ::. Las expresiones conectadas por && O :: se evalan de izquierda a

de L'i.H.la digito.

Existen doce categoras de entrada, por lo que es conveniente utilizar un arre-

glo para mantener el nmero de ocurrencias de cada dgito, en lugar de tener diez
variables individuales. Esta es una versin del programa:

nI = (nw = (ne = O));

El operador :: significa "O" (o bien "OR"), por lo que la linea

Ol,.'lIITenLia~

de caracteres espaciadores (blancos, tabuladores, nueva lnea), y de todos los


otros caracteres . Esto es artificioso, pero nos permite ilustrar varios aspectos de
C en un programa.

#include <stdio .h>


/ * cuenta dgitos, espacios blancos, y otros */
main( )
{
int e, i, nwhite, nother;
int ndiqitIIO];

derecha, y se garantiza que la evaluacin terminar tan pronto como se conozca

nwhite

la verdad o falsedad . Si e es un blanco, no hay necesidad de probar si es una nueva lnea o un tabulador, de modo que esas pruebas no se hacen . Esto no es de

for (i = O; i < !O; + + i)


ndiqitli] = O;

nother

O;

24

CAPITULO 1

INTRODUCCION GENERAL

while ((e = gelehar( )) ! = EOF)


ti (e > = 'O' && e < = '9')
+ + ndigit[e-'O');
else if (c = = ' , :; c = = ,\n' JI c
+ +nwhite;
else
+ + nother;

'\1')

SECC ION 1.6

ARREGLOS

else if (e = = " :: e
+ + nwhite;
else
+ + nother;

'\n' :: e

2S

'\1')

El patrn
if (condicin l )
proposicin I
else if (condicin~)
proposicil1 1

printf ("dgitos = ");


lor (i = O; i < 10; + + i)
printf(" %d", ndigit[i]);
%d, otros
printf(", eGpacios blancos
nwhite, nother);

%d\n",
else

proposicinl/

La salida de este programa al ejecutarlo sobre s mismo es


digilos

93 O O O O O O O 1, espacios blancos

123, otros = 345

La declaracin
inl ndigil [101;

declara ndigit como un arreglo de 10 enteros. En e, los subndices de arreglos


comienzan en cero, por lo que los elementos son ndigit[O], ndigit [1], ... , ndigit[9]. Esto se refleja en los ciclos lor que inicializan e imprimen el arreglo.
Un subndice puede ser cualquier expresin entera, lo que incluye a variables
enteras como i, y constantes enteras.
Este programa en particular se basa en las propiedades de la representacin
de los dgitos como caracteres. Por ejemplo, la prueba
il (e > = 'O' && e < = '9') ...

determina si el carcter en c es un dgito. Si lo es, el valor numrico del dgito es


e - 'O'

Esto slo funciona si 'O', '1', ... , '9' tienen valores consecutivos ascendentes. Por
fortuna, esto es as en todos los conjuntos de caracteres.

Por definicin, los char son slo pequeos enteros, por lo que las variables
las constantes char son idnticas a las int en expresiones aritmticas . Esto es
natural y conveniente; por ejemplo, e-'O' es una expresin entera con un valor
entre O y 9, correspondiente a los caracteres 'O' a '9' almacenados en e, por 10
y

que es un subndice vlido para el arreglo ndigit.


La decisi n de si un carcter es dgito, espacio en blanco u olra cosa se realiza
con la secuencia
il (e >

'O' && e < = '9')


+ + ndigil[e-'O');

se encuentra frecuentemente en programas como una forma de expresar una decisi n mltiple . Las condiciones se evalan en orden desde el principio hasta que
se satisface alguna condicin; en ese punto se ejecuta la proposicin correspondicntc. y la cOll~lrllL\:ill Cllll1plcla termina. (Cualquier I'I'(J/}()S!c'hJll puede constar de
varias proposiciones entre llaves.) Si no se satisface ninguna de las condiciones,
se ejecuta la proposicin que est despus del else final. si sta existe. Cuando se
omiten el else y la proposicin finales, tal como se hizo en el programa para COIllar palabras, no tiene lugar ninguna accin. Puede haber cualquier nmero de
grupos de
else if (condicin)
proposicin

entre el if inicial y el else final.


Se recomienda, por estilo, escribir ~sta construccin tal como se ha mostrado;
si cada if est uviesc sangrado despus del else anterior. una larga secuencia de decisiones podra rebasar el margen derecho de la pgina.
La proposicin switch, que se tratar en el captulo 3, proporciona otra forma
de escribir una decisin mltiple, que es particularmente apropiada cuando la
condicin es determinar si alguna expresin entera o de carcter corresponde con
algn miembro de un conjunto de constantes. Para contrastar, se presentar una
versin ele este programa, usando switch, en la seccin 3.4.
Ejercicio 1- J3. Escriba un programa que imprima el histograma de las longitudes
de ti'. palabras de:-.u e!ltrada. E:-. fL'i dibujar el hi:-.togralll<l L'on la:- barra:- Ilori/olltate . . . : la oricntacin \lTtL'al L' ... U!l rL'to mi. . intcrc:-anlL'. O

Ejercicio 1-14. Escriba un programa que imprima el histograma de las frecuencias con que se presentan diferentes caracteres ledos a la entrada. O

26

1.7

CAPITULO t

INTRODUCCION GENERAL

SECC ION 1.7

FUNCIONES

27

Una definicin de funcin tiene la forma siguiente:

Funciones

fipo-de-reforno nombre-de-funcin (declaracin de parmetros. si los hay)


{
declaraciones
proposiciones

En lenguaje e, una furrcin es el equivalente a una subrutina o funcin en


Fortran, o a un procedimiento o funcin en Pascal. Una funcin proporciona

una forma conveniente de encapsular algunos clculos, que se pueden emplear


despus sin preocuparse de su implantacin. Con funciones diseadas adecuadamente, es posible ignorar cmo se realiza un trabajo; es s uficiente saber qu se
hace. El lenguaje C hace que el uso de funciones sea fcil, conveniente yeficienporque eso esclarece alguna parte del cdigo.
Hasta ahora slo se han utilizado funciones como printf, getchar y putchar,

Las definiciones de funcin pueden aparecer en cualquier orden y en uno O varios


archivos fuente, pero una funcin no puede separarse en archivos diferentes. Si
el programa fuente aparece en varios archivos, tal vez se tengan que especificar
ms cosas al compilar y cargarlo que si estuviera en uno solo, pero eso es cosa

que nos han sido proporcionadas; ya es el momento de escribir unas pocas nosot ros mismos. Dado que e no posee un operador de exponenciacin co mo el * *

del sistema operativo, no un atributo del lenguaje . Por ahora supondremos que
ambas funciones estn en el mismo archivo y cualquier cosa que se haya aprendi-

de Fortran, ilustremos el mecanismo de la definicin de una fun cin al escribir la

do acerca de cmo ejecutar programas en e, an funcion ar n.


La funcin power se invoca dos veces por main, en la lnea

te; es comn ver una fun cin cona definida y empicada una so la vez, nicamente

funcin power(m,n), que eleva un enlero m a una potencia entera y positiva n .

Esto es, el valor de power(2,S) es 32. Esta funcin no es una rutina de exponenciacin prctica, puesto que slo maneja potencias positivas de enleros pequeos, pero es suficiente para ilustracin (la biblioteca estndar contiene una
funcin pow(x,y) que calcula x Y).

A continuacin se presenta la funcin power y un programa main para utilizarla, de modo que se vea la estructura completa de' una vez.

printf("%d %d %d\n", i, power(2,i). power{-3,i));

Cada ll am ada pasa dos argument os a power, que cada vez reg resa UIl cntcro, al
quc se pone formato y se imprime. En una expresin , power(2,i) es un cl1lero tal
como lo son 2 e i. (No todas las funciones producen un valor cntero; lo que se ve-

r en el capit ulo 4.)


La primera lnea de la funcin power,

#include <stdio.h>

int power(in base, int n)


int power(nt m, int n);

declara los tipos y nombres de los parmetros, as como el tipo de resultado que
la funcin devuelve. Los nombres que emplea power para sus parmetros son locales a la funcin y son invisibles a cualquier otra funcin: otras rutinas pueden
utilizar los mismos nombres sin que exista problema alguno. Esto tambin es cierto para las variables i y p: la i de power no tiene nada que ver con la i de main.

l . prueba la funcin power . 1

main( )
{
int i;

Generalmente usa.remos parmetro para una variable nombrada en la lista entre parl1lcsis de la defi nici n de una funcin, y argumen/o para el valor emplea-

for (i = O; i < 10; + + i)


printf("%d %d %d\n", i, power(2,i), power(-3,i));
return O;

l o. power:

eleva la base a la n'3sima palencia; n >


int power(int base, int n)

{
int i, p;
p -= 1i

= 1; i < = n; + +i)
P = P o. base;
return p;

for (i

= 0. 1

do al hacer la llamada de la funcin. Los trminos argumento formal y argumenlo real

se emplean en ocasiones para hacer la misma distincin.

El valor que calcula power se regresa a main por medio de la proposicin return, a la cual le puede seguir cualquier expresin:
return expresin

Una fu ncin no necesita regresar un valor; una proposicin return sin expresin

hace que el control regrese al programa, pero no devuelve algn valor de utilidad,
como se hara al "caer al final" de una funcin al alcanzar la llave derecha de

terminacin . Adems, la funcin que llama puede ignorar el valor que regresa
una funcin.

Probablemente haya notado que hay una proposicin return al final de main.
Puesto que main es una funcin como cualquier otra, tambin puede reg resar un
valor a quien la invoca, que es en efecto el medio ambiente en el que el programa

28

CAPITULO 1

INTRODUCCION GENERAL

se ejecuta. Tpicamente, un valor de regreso cero implica una terminacin normal; los valores diferentes de cero indican condiciones de terminacin no comunes o errneas. Para buscar la simplicidad, se han omitido hasta ahora las proposiciones return de las funciones main, pero se incluirn ms adelante, como un
recordatorio de que los programas deben regresar su estado final a su medio ambiente.
La declaracin
int power(int m, int n);

precisamente antes de main, indica que power es una funcin que espera dos argumentos inl y regresa un int. Esta declaracin, a la cual se le llama funcin prototipo, debe coincidir con la definicin y uso de power. Es un error el que la definicin de una funcin o cualquier uso que de ella se haga no corresponda con
su prototipo.
Los nombres de los parmetros no necesitan coincidir; de hecho, son optativos en el prototipo de una funcin, de modo que para el prototipo se pudo haber
escrito
int power(int, int);

No obstante, unos nombres bien seleccionados son' una buena docu~entacin,


por lo que se emplearn frecuentemente.
Una nota histrica: La mayor modificacin entre ANSI C y las versiones anteriores es cmo estn declaradas y definidas las funciones. En la definicin original de C, la funcin power se pudo haber escrito de la siguiente manera:
/ . power: eleva la base a n-sima potencia; n
/.
(versin en estilo antiguo) */
power(base, n)
int base, n;

>=

O./

ARGUMENTOS-LLAMADA POR VALOR

SECC ION 1.8

29

que por omisin se poda suponer que power regresaba un entero, toda la declaracin podra haberse omitido.
La nueva sintaxis de los prototipos de funciones permite que sea mucho ms
fcil para el compilador detectar errores en el nmero o tipo de argumentos. El
viejo estilo de declaracin y definicin an funciona en ANSI C, al menos por
un periodo de transicin, pero se recomienda ampliamente que se utilice la nueva
forma si se tiene un compilador que la maneje.

Ejercicio 1-15. Escriba de nuevo el programa de conversin de temperatura de


la seccin 1.2, de modo que utilice una funcin para la conversin. O

1.8

ArgumentQs-lIamadas por valor

Hay un aspecto de las funciones de C que puede parecer poco familiar a los
programadores acostumbrados a otros lenguajes, particularmente Fortran. En C,
todos los argumentos de una funcin se pasan "por valor". Esto significa que
la funcin que se invoca recibe los valores de sus argumentos en variables temporales y no en las originales. Esto conduce a algunas propiedades di rerentes a las
que se ven en lenguajes con "llamadas por referencia" como Fortran o con parmetros varen Pascal, en donde la rutina que se invoca tiene acceso al argumento
original, 110 a una copia local.
La diferencia principal es que en C la funcin que se invoca no puede alterar
directamente una variable de la funcin que hace la llamada; slo puede modificar su copia privada y temporal.
Sin embargo, la llamada por valor es una ventaja, no una desventaja. Por lo
com n, esto conduce a elaborar programas ms compactos con pocas variables
extraas, debido a que los parmetros se tratan en la funcin invocada como variables locales convenientemente inicializadas. Por ejemplo, he aqui una versin
de power que utiliza esta propiedad.

int i, p;
p

/ * power: eleva la base a la n-sima potencia;


int power(int base, int n)

1;

for (i = 1; i

<

= n;

versin 2 ./

+ + i)

P = P * base;
return p;

int p;
for (p = 1; n

Los parmetros se nombran entre los parntesis y sus tipos se declaran antes de
abrir la llave izquierda; los parmetros que no se declaran se toman como int.
(El cuerpo de la funcin es igual a la anterioL)
La declaracin de power al inicio del programa pudo haberse visto como sigue:
int power( );

n> = O;

No se permiti ninguna lista de parmetros, de modo que el compilador no pudo


revisar Can facilidad que power fuera llamada correctamente. De hecho, puesto

> O;

--n)

p = p base;

return p;

El parmetro n se utiliza como una variable temporal, y se decrementa (Ul' ciclo


for que se ejecuta hacia atrs) hasta que llega a cero; ya no es necesaria la variable
i. Cualquier cosa que se le haga a n dentro de power no tiene efecto sobre el argumento con el que se llam originalmente power.

30

INTRODUCCION GENERAL

CAPITULO I

Cuando sea necesario, es posible hacer que una funcin modifique una variable dentro de una rutina invocada. La funcin que llama debe proporcionar
la direccin de la variable que ser cambiada (tcnicamente un apuntador a la variable), y la funcin que se invoca debe declarar que el parmetro sea un apuntador y tenga acceso a la variable indirectamente a travs de l. Los apuntadores
se tratarn en el captulo 5.
La historia es diferente con los arreglos . Cuando el nombre de un arreglo se
emplea como argumento, el valor que se pasa a la funcin es la localizacin o
la direccin del principio del arreglo -no hay copia de los elementos del arreglo.
Al colocarle subndices a este valor, la funcin puede tener acceso y alterar cualquier elemento del arreglo. Este es el tema de la siguiente seccin.

1,9

#include <stdio.h>
#define MAXLINE 1000
/ " tamao mximo de la lnea de entrada */
int getline(char line[J, int maxline);
void eopy(ehar to[], ehar from[]);
/ " imprime la linea de entrada ms larga ,,/
main( )
{
int len;
/ * longitud actual de la lnea ./
/ ...xima longitud vista hasta el momento .. /
int max;
/ .. lnea de entrada actual .. /
ehar line[MAXLINEJ;
ehar longest[MAXLINE]; / .. la lnea ms larga se guarda aqu */
max = O;
while ((len = getline(line, MAXLINE)) > O)
if (len > max) {
max = len;
eopy(longest, line);

Arreglos de caracteres

El tipo de arreglo ms comn en e es el de caracteres, Para ilustrar el uso de


arreglos de caracteres y funciones que los manipulan, escriba un programa que
lea un conjunto de lneas de texto e imprima la de mayor longitud . El pseudocdigo es bastante simple:
while (hay otra lInea)
if (es ms larga que la anterior ms larga)
gurdala
guarda su longitud
imprime la I[nea ms larga

Este pseudocdigo deja en claro que el programa se divide naturalmente en partes. Una trae una nueva lnea, otra la prueba y el resto controla el proceso,
Puesto que la divisin de las partes es muy fina, lo correcto ser escribirlas
de ese modo. As pues, escribamos primero una funcin getline para extraer la
siguiente lnea de la entrada. Trataremos de hacer a la funcin til en otros contextos, Al menos, getline tiene que regresar una seal acerca de la posibilidad de
un fin de archivo; un diseo de ms utilidad deber retornar la longitud de la lnea, o cero si se encuentra el fin de archivo. Cero es un regreso de fin de archivo
aceptable debido a que nunca es una longitud de lnea vlida, Cada lnea de texto
tiene al menos un carcter; incluso una lnea que slo contenga un carcter nueva
lnea tiene longitud 1,
Cuando se encuentre una lnea que es mayor que la anteriormente ms larga,
se debe guardar en algn lugar. Esto sugiere una segunda funcin copy, para copiar la nueva lnea a un lugar seguro.
Finalmente, se necesita un programa principal para controlar getline y copy.
El resultado es el siguiente:

ARREGLOSDECARAcrERES

SECCION 1.9

if (max > O)
/" hubo una lnea ,,/
printf("%s", longest);
return O;

/" getline: lee una lnea en s, regresa su longitud


int getline(char s[]. int lim)

{
int e, i;

for (i=O; i<lim-l && (e = getehar( )) !=EOF && e!='\n'; + +i)


s[i] = e;
if (e = = '\n') {
s[i] = e;

+ + i;
s[i] = '\0';
return i;
/ " copy: copia 'from' en 'to'; supone que to es suficientemente grande * /
void eopy(ehar to[]. ehar from[])
{
int ii
i = O;

while (to[i]
+ +i;

from[iJ) ! = ,\0')

3]

32

CAPITULO 1

INTRODUCCIONGENERAL

Las funciones getline y copy estn declaradas al principio del programa, que
se supone est contenido en un archivo.
main y getline se comunican a travs de un par de argumentos y un valor de

retorno. En getline los argumentos se declaran por la lnea


int getline(char sr]. int lim)

que especifica que el primer argumento, s, es un arreglo, y el segundo, lim, es

un entero. El propsito de proporcionar el tamao de un arreglo es fijar espacio


de almacenamiento contiguo. La longitud del arreglo 5 no es necesaria en getline,
puesto que su tamao se fija en main. En getline se utiliza retum para regresar

un valor a quin lo llama, tal como hizo la funcin power. Esta lnea tambin
declara que getline regresa un in!; puesto que in! es el valor de retorno por omisin, puede suprimirse.
Algunas funciones regresan un valor til; otras, como copy, se emplean ni-

camente por su efecto y no regresan un valor. El tipo de retorno de copy es void,


el cual establece explcitamente que ningn valor se regresa.
En getline se coloca el carcter '\0' (carcter nulo, cuyo valor es cero) al final
del arreglo que est creando, para marcar el fin de la cadena de caracteres. Esta convencin tambin se lIlili/a por el lenguaje C: cuando Ulla constante de carcter C0l110
"hola\n"

aparece en un programa en C, se almacena como un arreglo que contiene los

caracteres de la cadena y termina con un ,\0' para marcar el fin.

[h I

I \n I

SECCION 1.10

VARIABLES EXTERNAS Y ALCANCE

33

Ejercicio 116. Corrija la rutina principal del programa de la lnea ms larga de


modo que imprima correctamente la longitud de lneas de entrada arbitrariamen-

te largas, y tanto texto como sea posible. O


Ejercicio 1-17. Escriba un programa que imprima todas las lneas de entrada que
sean mayores de 80 caracteres. O
Ejercicio 118. Escriba un programa que elimine los blancos y los tabuladores
que estn al final de cada lnea de entrada, y que borre completamente las lneas
en blanco. O
Ejercicio 1-19. Escriba una funcin reverse(5) que invil'rta la cadena de caracteres s. Usela para escribir un programa que invierta su entrada, lnea a lnea. O

1.10

Variables externas y alcance

Las variables que estn en main, tal como line, longest, etc., son privadas

o locales a ella. Debido a que son declaradas dentro de main, ninguna otra funcin puede tener acceso directo a ellas. Lo mismo tambin es vlido para variables de otras funciones; por ejemplo, la variable i en getline no tiene relacin

con la i que est en copy. Cada variable local de una funcin comienza a existir
slo cuando se llama a la funcin, y desaparece cuando la funcin termina. Esto
es por lo que tales variables son conocidas como variables automticas, siguiendo
la terminologa de otros lenguajes. Aqu se utilizar en adelante el trmino automtico para hacer referencia a esas variables locales. (En el captulo 4 se discute
la categora de almacenamiento esttica, en la que las variables locales s conser-

\0

La especificacin de formato %5 dentro de printf espera que el argumento correspondiente sea una cadena representada de este modo; copy tambin se basa
en el hecho de que su argumento de entrada se termina con '\0', y copia este carcter dentro del argumento de salida. (Todo esto implica que '\0', no es parte
de un texto normaL)
Es til mencionar de paso que aun un programa tan pequeo como ste presenta algunos problemas de diseo. Por ejemplo, qu debe hacer main si encuentra una lnea que es mayor que sV lmite? getline trabaja en forma segura,
en ese caso detiene la recopilacin cuando el arreglo est lleno, aunque no encuentre el carcter nueva lnea. Probando la longitud y el ltimo carcter devuelto, main puede determinar si la linea fue demasiado larga, y entonces realiza el
tratamiento que se desee. Por brevedad, hemos ignorado el asunto.
Para un usuario de getline no existe forma de saber con anticipacin cun

larga podr ser una linea de entrada, por lo que getline revisa un posible desbordamiento (o ve/Jlo 11'). Por otro lado, el usuario de copy ya conoce (o lo puede
averiguar) cul es el tamao de la cadena, por lo que decidimos no agregar
comprobacin de errores en ella.

van sus valores entre llamadas.)


Debido a que las variables locales aparecen y desaparecen con la invocacin de
funciones, no retienen sus valores entre dos llamadas sucesivas, y deben ser inicializadas explcitamente en cada entrada. De no hacerlo, contendrn "basura".
Como una alternativa a las variables automticas, es posible definir variables
que son externas a todas las funciones, esto es, variables a las que toda funcin

puede tener acceso por su nombre. (Este mecanismo es parecido al COMMON


de Fortran O a las variables de Pascal declaradas en el bloque ms exterior.) Debido a que es posible tener acceso global a las variables externas, stas pueden ser
usadas en lugar de listas de argumentos para cpmunicar datos entre funciones.
Adems, puesto que las variables externas se mantienen permanentemente en
existencia, en lugar de aparecer y desaparecer cuando se llaman y terminan las
funciones, mantienen sus valores aun despus de que regresa la funcin que los

r"
IJO.

Una variable externa debe definirse, exactamente una vez, fuera de cualquier
funcin; esto fija un espacio de almacenamiento para ella. La variable tambin
debe declararse en cada funcin que desee tener acceso a ella; esto establece el
lipo de la variable. La declaracin debe 'ier una propo\icin extern cxplicia, o bien
Puede estar implcita en el contexto. Para concretar la discusin, reescribamos el

34

CAPITULO I

INTRODUCCION GENERAL

VARIABLES EXTERNA S Y ALCANCE

SECCION 1.10

3S

il (c = = '\n') {
line[i] = e;

programa de la lnea ms larga con line, 10ngest y max como variables externas.
Esto requiere cambiar las llamadas , declaraciones y cuerpos de las tres funciones.

+ +i;
#inc1ude <stdio.h>

#define MAXUNE 1000

/ .. maximo tamao de una lnea de entrada .. /

int max;

/ .. mxima longitud vista hasta el momento .. /


/ .. lnea de entrada actual .. /
/ .. la linea ms larga se guarda aqu .. /

ch.r line IMAXLINE];


char longestlMAXUNEI;

lineli} = '\0';
return i;

/ .. copy: versin especializada .. /

void cOPy(void)
{

int getline(void);
void copy(void);

int i;
extern char lina [], longest[];
i

/ .. imprime la lnea de entrada ms larga; versin especializada" /

= O;

while ((longestli}

lineli]) ! = ,\0')

+ +i;

m.in( )
{
int len;
extern int max;
extern char longest[];
max

= O;

while len = gelline( )) > O)


il (len > max) {
max

= len;

cOPY( );
/ .. hubo una lnea .. /
il (m.x > O)
prinU("%s", longest);
return O;

/ .. gelline: versin especializada ./


int getline(void)

{
int e , i;
extern char line[];

for (i = O; i < MAXLlNE-l


&& (c = getchar( )) ! = EOF && c ! = ,\n'; ++i)
lineli} = c;

Las variables externas de main, gelline y copy estn definidas en las primeras
lneas del ejemplo anterior, lo que establece su tipo y causa que se les asigne espacio de almacenamiento. Desde el punto de vista sintctico, las definiciones externas son exactamente como las definiciones de variables locales, pero puesto que
ocurren fuera de las funciones, las variables son externas. Antes de que una funcin pueda usar una variable externa, se debe hacer saber el nombre de la variable
a la funcin. Una forma de hacer esto es escribir una declaracin extem dentro
de la funcin; la declaracin es la misma que antes, excepto por la palabra reservada externo
Bajo ciertas circunstancias, la declaracin exlem se puede omitir. Si la definicin de una variable externa ocurre dentro del archivo fuente antes de su uso por
una funcin en particular, entonces no es necesario el uso de una declaracin exlem dentro de la funcin . La declaracin exlern en main, gelline y copy es, por
lo tanto, redundante . De hecho, una prctica comn, es poner las definiciones
de todas las variables externas al principio del archivo fuente y despus omitir todas las declaraciones externo
Si el programa est en varios archivos fuente y una variable se define en archivol y se utiliza en arch;vo2 y archivo3, entonces se necesitan declaraciones extern
en archivo2 y archivo3 para conectar las ocurrencias de la variable. La prctica
comn es reunir declaraciones extern de variables y funciones en un archivo separado, llamado histricamente header, que es incluido por #include al principio
de cada archivo fuente. El sufijo .h se usa por convencin para nombres de headers. las funciones de la biblioteca estndar, por ejemplo, estn declaradas en
headers como <sldio.h> . Este tema se trata ampliamente en el capitulo 4, y la
biblioteca en el capitulo 7 y en el apendice B.

36

INTRODUCCION GENERAL

CAPITULO 1

Puesto que las versiones especializadas de getline y copy no tienen argumentos, la lgica sugerira que sus prototipos al principio del archivo deben ser getline( l y copy( l. Pero por compatibilidad con programas de C anteriores, el estndar toma a una lista vaca como una declaracin al viejo estilo, y suspende toda
revisin de listas de argumentos; para una lista explcitamente vaca debe emplearse
la palabra void. Esto se discutir en el captulo 4.
Se debe notar que empleamos cuidadosamente las palabras definicin y declaracin cuando nos referimos a variables externas en esta seccin. La palabra "definicin" se refiere al lugar donde se crea la variable o se je asigna un lugar de
almacenamiento; "declaracin" se refiere al lugar donde se establece la naturaleza de la variable pero no se le asigna espacio.
A propsito, existe una tendencia a convertir todo en variables extern, debido
a que aparentemente simplifica las comunicaciones -las listas de argumentos son
cortas y las variables existen siempre, cuando se les requiere. Pero las variables
externas existen siempre, aun cuando no hacen falta. Descansar fuertemente sobre variables externas es peligroso , puesto que lleva a programas cuyas conexiones entre datos no son completamente obvias -las variables pueden cambiarse en forma inesperada e inadvertida, y el programa es dificil de modificar. La
segunda versin del programa de la lnea mayor es inferior a la primera, en parte
por las anteriores razones y en parte porque destruye la generalidad de dos tiles
funciones, introduciendo en ellas los nombres de las variables que manipula.
Hasta este punto hemos descrito lo que podra llamarse los fundamentos convencionales de C . Con estos fundamentos, es posible escribir programas tiles de
tamao considerable, y probablemente sera una buena idea hacer una pausa suficientemente grande para realizarlos. Estos ejercicios sugieren programas de complejidad algo mayor que los anteriores del capitulo.
Ejercicio 1-20. Escriba un programa detab que reemplace tabuladores de la entrada con el nmero apropiado de blancos para espaciar hasta el siguiente paro
de tabulacin. Considere un conjunto fijo de paros de tabulacin, digamos cada
n columnas. Debe ser n una variable o un parmetro simblico? O
Ejercicio 1-21. Escriba un programa entab que reemplace cadenas de blancos
por el mnimo nmero de tabuladores y blancos para obtener el mismo espaciado.
Considere los paros de tabulacin de igual manera que para detab. Cuando un
tabulador o un simple espacio en blanco fuese suficiente para alcanzar un paro
de tabulacin, a cul se le debe dar preferencia? O
Ejercicio 1-22. Escriba un programa para "doblar" lneas grandes de entrada en
dos o ms lneas ms cortas despus del ltimo carcter no blanco que ocurra antes de la n-sima columna de entrada. Asegrese de que su programa se comporte
apropiadamente con lneas muy largas, y de que no hay blancos o tabuladores
antes de la columna especificada. O
Ejercicio 1-23. Escriba un programa para eliminar todos los comentarios de un
programa en C. No olvide manejar apropiadamente las cadenas entre comillas y
las constantes de carcter. Los comentarios de e no se anidan. O

SECC ION 1.10

VARIABLES EXTERNAS Y ALCANCE

37

Ejercicio 1-24. Escriba un programa para revisar los errores de sintaxis rudimentarios de un programa en e, como parntesis, llaves y corchetes no alineados. No
olvide las comillas ni los apstrofos, las secuencias de escape y los comentarios.
(Este programa es difcil si se hace completamelHe genera1.) D

CAPITULO 2:

Tipos, operadores y expresiones

Las variables y las constantes son los objetos de datos bsicos que se manipulan en un programa. Las declaraciones muestran las variables que se van a

utilizar y establecen el tipo que tienen y algunas veces cules son sus valores iniciales. Los operadores especifican lo que se har con las variables. Las expresiones combinan variables y constantes para producir nuevos valores. El tipo de un
objeto determina el conjunto de valores que puede tener y qu operaciones se pueden realizar sobre l. Estos son los temas de este captulo.
El estndar ANSI ha hecho muchos pequeos cambios y agregados a los tipos bsicos y a las expresiones. Ahora hay formas signad y unsigned de todos
los tipos enteros, y notaciones para constantes sin signo y constantes de carcter
hexadecimales. Las operaciones de punto flotante pueden hacerse en precisin sencilla; tambin hay un tipo long double para precisin extendida. Las constantes
de cadena pueden concatenarse al tiempo de compilacin. Las enumeraciones

son ya parte del lenguaje, formalizando una caracterstica pendiente por mucho
tiempo. Los objetos pueden ser declarados const, lo que impide que cambien.
Las reglas para conversin aUlomlica enlre tipos aritmticos se aumentaron para manejar el ahora ms rico conjunto de tipos.

2.1

Nombres de variables
Aunque no lo mencionamos en el captulo 1, existen algunas restricciones en

los nombre de las variables y de las constantes simblicas. Los nombres se componen de letras y dgitos; el primer carcter debe ser una letra. El carcter de subrayado" -_ti cuenta como una letra; algunas veces es til para mejorar la legibili-

dad de nombres largos de variables. Sn embargo, no se debe comenzar los


nombres de variables con esle carcter, puesto que las rutinas de biblioteca con
frecuencia usan tales nombres. Las letras maysculas y minsculas son distintas,
de tal manera que x y X son dos nombres diferentes. La prctica tradicional de e
es usar letras minsculas para nombres de variables, y todo en maysculas para
Constantes simblicas.

39

40

TIPOS, OPERADORES y EXPRESIONES

CAPITULOZ

Al menos los primeros 31 caracteres de un nombre interno son significativos.


Para nombres de funciones y variables externas el nmero puede ser menor que
31, puesto que los nombres externos los pueden usar los ensambladores y los cargadores, sobre los que el lenguaje no tiene control. Para nombres externos, el estndar garantiza distinguir s lo para 6 caracteres y sin diferenciar maysculas de
minsculas, Las palabras clave como if, else, int, float, etc., estn reservada:.-: no
se pueden utilizar como nombres de variables. Todas ellas deben escribirse Con
minsculas.
Es conveniente elegir nombres que estn relacionados con el propsito de la
variable, que no sea probable confundirlos tipogrficamente, Nosotros tendemos a utilizar nombres cortos para variables locales, especialmente ndices de iteraciones, y nombres ms largos para variables externas,

SECCION 2.3

CONSTANTES

41

signo o sin l depende de la mquina, pero los caracteres que se pueden imprimir
son siempre positivos.
El tipo long double especifica punto flotante de precisin extendida. Igual
que con los enteros, los tamaos de objetos de punto flotante se definen en la
implantacin; /loal, double y long double pueden representar uno, dos o tres tamaos distintos.
1.0.\ an..:hi'o . . . de ~Ili..'abc/ado lIeuders estndar <limits .h > y <float.h> ronticllell (OI1 ... [al1tl'\ :.-illlbliL'as para lodo . . c!-to . . tamaflo..,. jullto l'OI1 otra . . propicdat!l's dc la mquina y del cOIll[Jilador, lo ... cuaJe.'" !-oC' cli"CLlICIl ell l'l ap~'l1diL'l' B.

Ejercicio 2-1. Escriba un programa para determinar los rangos de variables char,
short, int y long, tanto signed como unsigned, imprimiendo los valores apropia-

dos de los /caders estndar y por clculo dircclo. Es ms dificil si los calcula: determine los rangos de los varios tipos de punto flotante. O

2.2 Tipos y tamaos de datos


Hay unos cuantos tipos de datos bsicos en C:
char
int

Hoo!
double

un solo byte, capaz de contener un carcter del conjunto de


caracteres local.
un entero, normalmente del tamai'io natural de los enteros en la
mquina en la que se ejecuta,
punto flotante de precisin normal.
punto flotante de doble precisin.

Adems, existen algunos calificadores que se aplican a estos tipos bsicos.


short y long se aplican a enteros:
short int sh;

long int counter;

La palabra in! puede omitirse de tales declaraciones, lo que tpicamente se hace.


La intencin es que short y long puedan proporcionar diferentes longitudes
de enteros donde sea prctico; in! ser normalmente el tamafio nat u ral para una
mquina en particular. A menudo short es de 16 bits y long de 32; in! es de 16
o de 32 bits. Cada compilador puede seleccionar libremente los tamafios apropiados para su propio hardware, sujeto slo a la restriccin de que los shor!s e in!s
son, por lo menos de 16 bits, los longs son por lo menos de 32 bits y el short no
es mayor que int, el cual a su vez no es mayor que feng.
El calificador signed o unsigned puede aplicarse a char o a cual quier entero.
Los nmeros unsigned son siempre positivos o cero y obedecen las leyes de la
aritmtica mdulo 2", donde n es el nmero de bits en el tipo. As, por ejemplo,
si los char son de 8 bits, las variables unsigned char tienen valores centre O y 255,
en lanto que las variables signed char tienen valores entre -128 'JI 127 (en una
mquina de complemento a dos). El hecho de que los chars ordinarios sean con

2.3

Constantes

Una constante entera como 1234 es un int. Una constante long se escribe con
una 1 (ele) o L terminal, como en 123456789L; un entero demasiado grande para
caber dentro de un int tambin ser tomado como long. La:.. constantes sin ~ig
no se escriben con una u o U, terminal y el sufijo ul o UL indica unsigned
long.
Las constantes de punto rIotante eonricnen un punto decimal (123.4) o un exponente (le-2) o ambos; su tipo es double, a menos que tengan sufijo. Los sufijos f o F indican una constante /loa!; 1 o L indican un long double.
El valor de un entero puede especificarse en forma octal o hexadecimal enJugar de decimal. Un O (cero) al principio en una constante entera significa octal;
Ox OX al principio significa hexadecimal. Por ejemplo, el decimal 31 puede escribirse como 037 en octal y Oxlf OX1F en hexadecimal. Las constantes octales
y hexadecimales tambin pueden ser seguidas por L para convertirlas en long y
U para hacerlas unsigned: OXFUL es una constante unsigned long con valor de
15 en decimal.
Una ('onSlal1le de carcter es un Cillero, csnilo C0l110 un carcter dentro de arostrofos, lal como 'x'. El valor de una constante de caracter es el valor nUIlH:rico del
carcter en el conjunto de caracteres de la mquina, Por ejemplo, en el conjunto
de caracteres ASCII el carcter constante 'O' tiene el valor de 48, el cual no est
relacionado con el valor numrico O. Si escribimos '0' en vez de un valor numrico
como 48 que depende del conjunto de caracteres, el programa es independiente
del valor particular y ms fcil de leer. Las conSlantes de car":lcr participan en operaciones numricas tal como cualesquier otros enteros, aunque se utilizan ms comnmente en comparaciones con otros caracteres.
Ciertos caracteres pueden ser represcnlado~ en con~tantc de caraclcr y de cadena,
Por medio de secuencias de escape como \n (nueva lnea); esas secuencias se ven

42

CAPITULO 2

TIPOS, OPERADORES Y EXPRESIONES

como dos caracteres, pero representan slo uno. Adems, un patrn de bits arbitrario de tamao de un byte puede ser especificado por
'\000'

en donde

000

son de uno a tres dgitos octales (0 ... 7) o por

'\xhb'

SECCION 2.3

CONSTANTES

Las comillas no son parle de la cadena, slo sirven para delimitarla. Las mismas
secuencias de escape utilizadas en constantes de carcter se aplican en cadenas;
\" representa el carcter comillas . Las constantes de cadena pueden ser concatenadas en tiemp o de compilacin:
"hola," "mundo"

es equivalente a

en donde hh son uno O ms digitos hexadecimales (0 ... 9, a .. . f, A ... F). Asi podriamas escribir
#define VTAB '\013'
#define BELL '\007'
0,

/ . tah vertical ASCII ./


/. carcter campana ASCII. /

#define VTAB '\xh'


#define BELL '\x7'

/. tah vertical ASCII ./


/. carcter campana

ASCII ./

El conjunto completo de secuencias de escape es


\\

\h
\f

carcter de alarma (campana)


retroceso

\?

interrogacin

avance de hoja

\'

apstrofo

\n
\r

nueva lnea
regreso de carro

\tI

\t

tabulador horizontal
tabulador vertical

\v

"hola, mundo"

Esto es til para separar cadenas largas entre varias lneas fuente.
Tcnicamente, una constame de cadena es un arreglo de caracteres. La represen-

tacin interna de una cadena tiene un carcter nulo '\0' al final, de modo que

en hexadecimal,

\a

43

diagonal invertida

comillas
\000
nmero octal
\xhh nmero hexadecimal

La constante de carcter ,\0' represema el carcter con valor cero, el carcter


nulo. ,\0' a menudo se escribe en ve7 de O para enfatizar la naturaleza de carcter
de algunas expresiones. pero el valor numrico es precisamente O.

Una expresin constante es una expresin que slo inmiscuye constantes. Tales expresiones pueden ser evaluadas durante la compilacin en vez de que se haga
en tiempo de ejecucin, y por tanto pueden ser utilizadas en cualquier lugar en

el almacenamiento fsico requerido eS uno ms del nmero de caracteres escritos


entre las comillas . Esta representacin significa que no hay lmite en cuanto a qu
tan larga puede se r una cadena, pero los programas deben leer completamente
una cadena para determinar su longitud. La funcin strlen (s) de la biblioteca

estndar regresa la longitud de su argumento s de tipo cadena de caracteres, excluyendo el ,\0' terminal. Aqu est nuestra versin:
/+ strJen: regresa la longitud de s +/
int strlen(char s[ ])
{
int i;
i = O;

while (s[J !

'\0')

, +,

return i;

que pueda encontrarse una constante, como en


#define MAXLINE 1000
char line [MAXLINE + 1J;

o
#define LEAP 1

/ * en aos bisiestos* /

int days [31 + 28 + LEAP + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30 + 31J;

Una constanle de cadena o cadena literal, es una secuencia de cero o ms ca-

racteres encerrados entre comillas, como en


"Soy una cadena"

o
""/- la cadena vaca . /

strlen y otras funciones para cadenas estn declaradas ell el header estndar

< string.h > .


Se debe ser cuidadoso al di st inguir entre una constante de carcter y una cadena
que contiene un slo carcter: 'x' no es lo mismo que "x". El primero es un entero, utilizado para producir el valor numrico de la letra x en el conjunto de car.lcteres de la mquina. El himo es un arreglo de caracteres que contiene un

carcter (la letra xl y un '\0'.


Existe otra dase de constanle. la cOIISlallle de enumeracin. Una enumeracin
es una lista de valores enteros constantes, como en
enum boolean {NO, YES};

El primer nombre en un enum tiene valor O, el siguiente 1, y as sucesivamente,


a menos que sean especificados valores explcitos. Si no todos los valores son es-

44

TIPOS, OPERADORES Y EXPRESIONES

CAPITUL02

char esc =
int i = O;

pecificados, los valores na especificados continan la progresin a partir del


ltimo valor que s lo fue, como en el segundo de esos ejemplos:
enum escapes {BELL = ,\a', RETROCESO = '\b', TAB
NVALIN = '\n', VTAB = '\v', RETURN

in!

= '\1',
= '\r'};

enum months {ENE = 1, FEB, MAR, ABR, MAY, JUN,


JUL, AGO, SEP, OCT, NOV, DIC};
/- FEB es 2, MAR es 3, etc. - /
Los nombres que estn en enumeraciones diferentes deben ser distintos. Los valores no necesitan ser distintos dentro de la misma enumeracin.
Las enumeraciones proporcionan una manera conveniente de asociar valores
constantes con nombres, una alternativa a #define con la ventaja de que los valores pueden ser generados pa ra uno. Aunque la s variables de tipos enum pueden

ser declaradas , los compiladores no necesitan revisar que lo que se va a almacenar en tal variable es un valor vlido para la enumeracin. No obstante, las va-

mir los valores de variables de enumeracin en su forma simblica .

45

'\ \';

limi! = MAXLINE + 1;
= 1.0e-5;

float eps

Si la variable en cuestin no es automtica, la inicializacin es efectuada slo


una vez, conceptualmente antes de que el programa inicie su ejecucin, y el inicializador debe ser una expresin constante. Una variable automtica explcitamente
inicializada es inicializada cada vez que se entra a la funcin o bloque en que
se encuentra; el inicializador puede ser cualquier expresin . Las variables estticas y externas son inicializadas en cero por omisin. Las variables automticas

para las que no hay un inicializador explcito tienen valores indefinidos (esto es,
basura) .
El calificador cons! puede aplicarse a la declaracin de cualquier variable para especificar que su valor no ser cambiado. Para un arreglo, e l calificador
canst indica que los elementos no sern alterados.

cons! double e = 2.71828182845905;

riables de enumeracin ofrecen la oportunidad de revisarlas y tal cosa es a

menudo mejor que # define. Adems, un depurador puede ser capaz de impri-

OPERADORES ARITMETlCOS

SECC ION 2.5

const char msq[]

= "precaucin:

JI.

La declaracin const tambin se puede utilizar con argumentos de tipo arreglo,


para indicar que la funcin no cambia ese arreglo:
int strlen(const char[]);

Si se efecta un intento de cambiar un const, el resultado est definido por la


implantacin .

2.4

Declaraciones

Todas las variables deben ser declaradas antes de su uso, aunque ciertas decla.
raciones pueden ser hechas en forma implcita por el contexto. Una declaracin
especifica un po, y contiene una lista de una o ms variables de ese tipo, como en
int lower, upper, step;

char e, line [1000];

Las variables pueden ser distribuidas entre las declaraciones en cualquier forma;
la lista de arriba podra igualmente ser escrita como
int
int
int
char

lower;
upper;
step;
c;

char line[1000];
Esta ltima forma ocupa ms espacio, pero es conveniente para agregar un co-

mentario a cada declaracin o para modificaciones subsecuentes .


Una variable tambin puede ser inicializada en su declaracin. Si el nombre
es seguido por un signo de igual y una expresin, la expresin sirve como un inicializador, como en

2.5

Operadores aritmticos

Los operadores aritmticos binarios son +, -, .,1, Yel operador mdulo %.


La divisin entera trunca cualquier parte fraccionaria. La expresin
x % y

produce el residuo cuando x es dividido entre y, por lo que es cero cuando y divide a x exactamente. Por ejemplo, un ao es bisiesto si es divisible entre 4 pero
no entre 100, excepto aquellos aos que son divisibles entre 400, que sr son bisies-

tos. Por lo tanto


if year % 4 = = O && year % lOO! = O) :: year % 400
printf("%d es un ao hisiesto\n ", year);
else
printf("%d no es un ao bisiesto\n", year);

==

O)

El operador % no puede aplicarse a operandos !loa! o double. La direccin de


truncamiento para / y el signo del resultado de % son dependientes de la mquina
para operandos negativos, as como la accin que se toma en caso de sobrcflujo

o subflujo.

46

CAPITULO 2

TIPOS. OPERADORES Y EXPRESIONES

Los operadores binarios + y - tienen la misma precedencia, la cual es menor


que la precedencia de *, 1, Y %, que a su vez es menor que + y - unarios . Los
operadores aritmticos se asocian de izquierda a derecha.

La tabla 2-1 que se encuentra al final de este captulo, resume la precedencia


y asociatividad para todos los operadores.

SECCION 2.7

CONVERSIONES DE TIPO

47

Por definicin, el valor numrico de una expresin de relacin o lgica es 1


si la relacin es verdadera, y si la relacin es falsa.
El operador unario de negacin! convierte a un operando que no es cero en
0, y a un operando cero en l. Un uso comn de ! es en construcciones como

if (! vlido)

en lugar de
f

2.6

Es difcil generalizar acerca de cul es la mejor. Construcciones como !vlido se


leen en forma agradable ("si no es vlido"), pero otras ms complicadas pueden
ser difciles de entender.

Operadores de relacin y lgicos

Los operadores de relacin son


>

>=

<

Ejercicio 2-2. Escriba un ciclo equivalente a la iteracin for anterior sin usar &&

<=

Todos ellos tienen la misma precedencia. Precisamente bajo ellos en precedencia


estn los operadores de igualdad:
!=

o ti- O

2.7

Los operadores de relacin tienen precedencia inferior que los operadores aritmticos, as que una expresin como i < lim-l se toma como i < (lim-l),
como se esperara.
Ms interesantes son los operadores lgicos && y tr. Las expresiones conectadas por && O :: son evaluadas de izquierda a derecha, y la evaluacin se detiene

tan pronto como se conoce el resultado verdadero o falso . La mayora de los


programas en C descansan sobre esas propiedades. Por ejemplo, aqu est un ciclo de la funcin de entrada getline que escribimos en el captulo 1:
for (i = O; i < lim-l && (c = getchar( )) ! = '\0' && c
s[i) = c;

(vlido = = O)

,=

EOF; + + i)

Antes de leer un nuevo carcter es necesario verificar que hay espacio para almacenarlo en el arreglo s, as que la prueba i < lim-l debe hacerse primero. Adems, si esta prueba falla, no debemos seguir y leer otro carcter.
De manera semejante, sera desafortunado si e fuese probada contra EOF antes

Conversiones de tipo
Cuando un operador tiene operandos de tipos diferentes, stos se convierten

a un tipo comn de acuerdo con un reducido nmero de reglas. En general, las


nicas conversiones automticas son aquellas que convierten un operando "angosto" en uno "amplio" sin prdida de informacin, tal como convertir un entero a punto flotante en una expresin como f + i. Las expresiones que no tienen
sentido, como utilizar un float como subndice, no son permitidas. Las expresiones que podran perder informacin, como asignar un tipo mayor a uno ms corto, o un tipo de punto flotante a un entero, pueden producir una advertencia, pero

no son ilegales.
Un char slo es un entero pequeo, por lo que los char se pueden utilizar libremente en expresiones aritmticas. Esto permite una flexibilidad considerable
en ciertas clases de transformacin de caracteres. Una es ejemplificada con esta ingenua implantacin de la funcin atoi, que convierte una cadena de dgitos en
su equivalente numrico.

de que se llame a getchar; por lo tanto, la llamada y la asignacin deben ocurrir


antes de que se pruebe el carcter c.
La precedencia de && es ms alta que la de [[, y ambas son menores que los
operadores de relacin y de asignacin, as que expresiones como
i<lim-l && (c

getchar( )) ! = '\n' && c ! = EOF

no requieren de parntesis adicionales. Pero puesto que la precedencia de ! = es


superior que la asignacin, los parntesis se necesitan en
(c = getchar( )) ! = '\n'

para obtener el resultado deseado de asignacin a c y despus comparacin con

'\n'.

/- atoi: convierte s en entero _/


int atoi(char si))
{
int i, n;

= O;

for (i

O; s[i]

>

n = 10 n

retum n;

'O' && s[i] <

+ (s[i] - 'O');

'9';

+ + i)

48

CAPITULO 2

TIPOS, OPERADORES Y EXPRESIONES

Tal como se discuti en el captulo 1, la expresin

sli] - 'O'
da el valor numrico del carcter almacenado en sli]' debido a que los valores
de 'O', '1', etc., forman una secuencia ascendente contigua.
Otro ejemplo de conversi n de chara int es la fun~in lower, que eOIl\ enc Un
carcter sencillo a minscula para el conjunto de caracteres ASelf. Si el carcter
no es una letra mayscula, lower lo regresa sin cambio.
l. lower:

convierte e a minscula; solamente


int lower(int e)

ASen .1

{
if (c > = 'A' && c < = 'Z')
return e + 'a' - 'A';
else

return c

Esto funciona para ASCII debido a que las correspondientes letras maysculas
y minsculas estn a una distancia fija como valores numricos y cada alfabeto
es contiguo -no hay sino letras entre A y Z. Sin embargo, csta ltima observa-

cin no es ciena para el conjunto de caracteres EBCDIC, asi que este cdigo
podria convenir algo ms que slo letras en EBCDIC.
El header estndar <clype.h>, que se describe en el apndice B, define una
familia de funciones que proporcionan pruebas y conversiones independientes de
los juegos de caracteres. Por ejemplo, la funcin tolower(e) regrc\a el va lor de la
letra minscula dce si ce~ una mayscu la , de modo que to]ower e~ un reelllplalo

transportable para la funcin lower most rada an les. De modo semejante, la


prueba.
c > = 'O' && c

<=

49

aparecer como negativos en algunas mquinas, aunque sean positivos en otras.


por transportabilidad. se debe especificar signed o unsigned si se van a almacenar datos que no so n caracteres en variables tipo charo
Las expresiones de relacin como i > j y las expresiones lgicas conectadas
por && y :: estn definidas para tener un valor de 1 siendo verdaderas, y O al
ser falsas. De este modo, la asignacin
d = e > = 'Q'&&c < = '9'

hace I a d si c es un dgito, y O si no lo es. Sin embargo, las funciones como isdigil pueden regresar cualquier valor diferente de cero como verdadero. En la parte
de validacin de if, while, for, etc., "verdadero" es slo "diferente de cero", por

lo que esto no hace diferencia.


Las conversiones aritmticas implcitas trabajan como se espera. En general,
si un operador como + O que toma dos operandos (operador binario) tiene
operandos de diferentes tipos, el tipo "menor" es promovido al tipo "superior"
antes de que la operacin proceda. El resultado es el del tipo mayor. La seccin
6 del apndice A establece las reglas de conversin en forma precisa. Si no hay
operandos unsigned, sin embargo, el siguiente conjunto informal de reglas bastar:

Si cualquier operando es long double, convirtase el otro a long double.


De otra manera, si cualquier operando es double, convirtase el otro a

doub!e.
De otra manera, si cualquier operando es floal, convirtase el otro a floal.
De otra manera, convirtase char y short a int.

'9'

puede reemplazarse por

Despus, si cualquier operando es long, convirtase el otro a long.


Ntese que los f]oat que estn en una expresin no se convierten automtica-

isdigit(c)

mente a double; esto es

U:1

cambio de la definicin original. En general, las fun-

Nosotros utilizaremos las funciones de < clype.h > en adelante.


Existe un sutil punto acerca de la conversin de caracteres a enteros ~ El lenguaje no especifica si las variables de tipo char son valores con o sin signo. Cuan-

ciones matemticas como las de < math.h > utilizarn doble precisin. La razn

do un char se convierte a int, puede producir alguna vez un entero negativo?

tica de doble precisin es particularmente costosa.

La respuesta vara de una mquina a otra, reflejando diferencias en la arquitectura. En algunas mquinas un char cuyo bit ms a la izquierda es 1 se convertir
a un entero negativo ("extensin de signo"). En otras, un char es promovido a
un int agregando ceros del lado izquierdo. as que siempre es positivo.
La definicin de
garantiza que ningn ca rcter que es t en el conjunto
estndar de caracteres de impresin de la mquina ser negativo, de modo que
esos caracteres siempre ser n cantidades positivas en las expresiones. Pero hay
patrones arbitrarios de bits almacenados en variables de tipo carcter que pueden

CONVERSIONES DE TIPO

SECCION 2.1

principal para usar floal es ahorrar espacio de almacenamiento en arreglos grandes o, con menor frecuencia, ahorrar tiempo en mquinas en donde la aritmLas reglas de conversin son ms complicadas cuando hay operandos un'

signed. El problema es que las comparaciones de valores con signo y sin signo
Son dependientes de la mquina, debido a que dependen de los tamaos de
los va rios tipos de enteros. Por ejemplo, supngase que inl es de 16 bits y long
de 32. Entonces -lL < lU, debido a que lU, que es un inl, es promovido a
Signed long. Pero -lL > lUL, debido a que -lL es promovido a unsigned long
y as parece ser un gran nmero positivo.

50

TIPOS, OPERADORES y EXPRESIONES

CAPITULO 2

Las conversiones tambin tienen lugar en las asignaciones; el valor del lado

derecho es convertido al tipo de la izquierda, el cual es el tipo del resultado .

SECC ION2.8

OPERADORES DE INCREMENTO Y DECREMENTO

SI

Si un prototipo de funcin declara argumentos , C0l110 debe ser normalmente,


la declaracin produce conversin forzada automtica de los argumentos cuando

Un carcter es converudo a un entero, tenga O no extensin de signo, como


se describi anteriormente.
Los enteros ms largos son convertidos a cortos o a char desechando el exceso

la funcin es llamada. As, dado el prototipo de la funcin sqr!:

de bits de ms alto orden. As en

la llamada

double sqrt(double);

r.z2 = sqrt(2);
iot i;
char e;
i

C;

c = i;

obliga al entero 2 a ser el valor double 2.0 sin necesidad de ningn caSI.
La biblioteca estndar incluye una implantacin transportable de un generado r de nmeros pseudoaleatorios , y una funcin para iniciali zar la semilla; lo
primero ilustra un casI:
unsigned long int next = 1;

el valor de c no cambia . Esto es verdadero ya sea que se inmiscuya o no la extensin de signo. Sin embargo, el invertir el orden de las asignaciones podra
producir prdida de informacin.
Si x es float e i es int, entonces x = i e i = x producirn conversiones; de float
a in! provoca el truncamiento de cualquier parte fraccionaria . Cuando double se
convierte a !loa!, el que se redondee o trunque el valor es dependiente de la implantacin.
Puesto que un argumento de la llamada a una funcin es una expresin, tam-

/ - rand: regresa un entero pseudoaleatorio en 0 .. 32761 .. ;


int rand(void)

{
next = next 1103515245 + 12345;
return (unsiqned int)(next/65536) % 32768;
l . srand: fija la semilla para rand( ) . /
void srand(unsiqned int seed)

bin suceden conversiones de tipo cuando se pasan argumentos a funciones. En

ausencia del prototipo de una funcin, char y shor! pasan a ser in!, y IIoa! se
hace doble. Esta es la razn por la que hemos declarado los argumentos a funciones como in! y double, aun cuando la funcin se llama con char y !loa!.
Finalmente, la conversin explcita de tipo puede ser forzada ("coaccionada") en cualquier expresin, con un operador unario llamado casto En la
construccin

next

seed;

Ejercicio 2-3. Escriba la funcin h!oi(s), que convierte una cadena de dgitos
hexadecimales (incluyendo Ox OX en forma optativa) en su valor entero equivalente. Los dgitos permitidos son del O al 9, de la a a la f, y de la A a la F. O

(nombre-de-tipo) expresin

la expresin es convertida al tipo nombrado, por las reglas de conversin anteriores. El significadoprecisode un cast es como si la expresin fuera asignada a una
variable del tipo especificado, que se utiliza entonces en lugar de la construccin completa. Por ejemplo, la rutina de biblioteca sqr! espera un argumento de
doble precisin y producir resultados sin sentido si maneja inadvertidamente
algo diferente. (sqr! est declarado en <ma!h.h>.) As, si n es un entero, pode-

2.8

Operadores de incremento y decremento

El enguaje C proporciona dos operadores poco comunes para incrementar y


decrementar variables. El operador de aumento + + agrega 1 a su operando, en
tanto que el operador de disminucin - - le resta l. Hemos usado frecuentemente + + para incrementar variables , como en

mos usar
sqrtdouble) n)

para convertir el valor de n a doble antes de pasarlo a sqr!. Ntese que la conversin forzosa produce el valor de n en el tipo apropiado; n en s no se altera. El
operador caSI tiene la misma alta precedencia que otros operadores unarios, co-

mo se resume en la tabla del final de este captulo.

if (e = = '\ n ')
+ +01;

El aspecto poco comn es que + + y - - pueden ser utilizad o como prcfij os


(an te, de la variable , como en + + n), o como post fijo s (despus de la variable:
n + + ). En ambos casos, el efecto es incrementar n. Pero la expresin + + n in crementa a n antes de que su valor se utilice, en tanto que n + + incrementa a

52

TIPOS, OPERADORES y EXPRESIONES

CAPITUL02

n despus de que su valor se ha empleado. Esto significa que en un contexto donde el valor eSl siendo utilizado, y no slo el efecto, + + n y n + + so n diferentes.
Si n es S, entonces
x

n+ +;

asigna 5 a x, pero
x = + +n;

hace que x sea 6. En ambos casos, n se hace 6. Los operadores de incremento


y decremento slo pueden aplicarse a variables; una expresin como (i + j) + +
es ilegal.
Dentro de un contexto en donde no se desea ningn valor, sino slo el efecto
de incrcmento, como en

OPERADORES PARA '\IANEJO DE BITS

SECCION 2.9

53

por algo mas compacto como


il (e

= = '\n')
s[i+ + 1 = c;

Como un tercer ejemplo, considrese que la funcin estndar strcat{s, t), quc
(om:alcna la cadena t al final de la cadena s. strcat suponc que ha~ ,",ut"kicnlc l',",pacio en 5 para almacenar la combinacin. Corno la herno~ escrito. strcat no
rcgl'C"ia un \alor: la versin de la biblioteca estndar regresa UIl apulltador a la
cadella rC.':Iultante.
/ * slrcat: concatena t al final de s; s debe ser suficientemente grande *
void streat(ehar si], ehar t[))
{
int i, j;

if (e = = ,\n')
nl+ +;

i = j = O;
while (s[i) ,= ,\0')
/ .. encuentra el fin de s .. /
i+ +;
while s[i + +) = t[j + + J) ! = '\0')
/ .. copia t */

prefijos y postfijos son iguales. Pero existen situaciones en donde se requiere especficamente uno u otro. Por ejemplo, considrese la funcin squeeze(s,e),
que elimina todas las ocurrencias del carcter e de una cadena s.
/ .. squeeze: borra todas las c de s .. /
void squeeze(char sIL int c)
int i, j;

lor (i = j = O; s[i)!= '\0'; i+ +)


if (s[i) ! = e)
s[j+ +) = s[i);
s[j] = '\0';

Cada vez que se encuentra un valor diferent e de e, ste se copia en la posicin

actual j, y slo entonces j es incrementada para prepararla para el siguiente carcter. Esto es exactamente equivalente a
if (s[i) ! = e) {
s[j) = s[i];
i+ +;

Otro ejemplo de construccin semejante viene de la funcin getline que escribimos en el captulo 1, en donde podemos reemplazar
if (e = = '\n') {
s[i] = e'
+ +i;

Como cada caracter se copia de t a s, el + + post fijo se aplica tanto a i como


a j para estar seguros de que ambos estn en posicin para la siguiente iteracin.
Ejt.rt:kio 2-4. Escriba una versin alterna de squeeze(sl,52) quc borrl.' lada
carcter de 51 que coincida con cualquier carcter de la cadena 52. O

Ejercicio 2-5. Escriba la funcin any(sls2), que regresa 13 primcra po~icill lIl'

la cadena sI en donde se encuentre cualquier carcter de la cadena s2, o -1 si


sI no contiene caracteres de s2. (La funcin de biblioteca estndar strpbrk hace
el mismo trabajo pero regresa un apuntador a la posicin encontrada.) O

2.9

Operadores para manejo de bits

El lenguaje e proporciona seis operadores para manejo de bits: solo puedcn


"il'r aplicados a operandos integrales, eSIO es, char, -,hor!, int, y long. con o ... in
"iigno.
&

AND de bits
OH inclusivo de bits
OR exclusivo de bits
corrimiento a la izquierda
cQrrimienio a la derecha
complemento a uno (unario)

54

TIPOS. OPERADORES Y EXPRESIONES

CAPITULO 2

El operador AND de bits & a menudo es usado para enmascarar algn conjunto de bits; por ejemplo,
n = n & 0177;

hace cero todos los bits de n, menos los 7 de menor orden.


El operador OR de bits: es empleado para encender bits:
x = x : SET_ON;

fija en uno a todos los bits de x que son uno en SET_ON.


El operador OR exclusivo" pone un uno en cada posicin en donde sus operandos tienen bits diferentes, y cero en donde son iguales .
Se deben distinguir los operadores de bits & y : de los operadores lgicos &&
que implican evaluacin de izquierda a derecha de un valor de verdad. Por
ejemplo, si x es 1 y Y es 2, entonces x & y es cero en tanto que x && y es uno.
Los operadores de corrimiento < < y > > realizan corrimientos a la izquierda y a la derecha de su opcrand0 que est a la izquierda, el numero de posiciones de bits dado por el operando de la derecha, el cual debe ser positivo. As x
< < 2 desplaza el valor de x a la izquierda dos posiciones, llenando los bits
vacantes con cero; esto es equivalente a una multiplicacin por 4. El correr a la
derecha una cant idad unsigned siempre llena los bits vacantes con cero. El correr
a la derecha una cantidad signada llenar con bits de signo ("corrimiento aritmtico") en algunas mquinas y con bits O ("corrimiento lgico") en otras.
El operador unario - da el complemento a uno de un entero; esto es, convierte cada bit 1 en un bit O y viceversa. Por ejemplo,

OPERADORES DE ASIGNACION y EXPRESIONES

SECCJON 2.10

55

Ejercicio 2-6. Escriba una funcin setbits(x,P,n,y) que regresa x con los n bits
que principian en la posicin p iguales a los n bits ms a la derecha dc y. dejando
los otros bits sin cambio. O
Ejercicio 2-7. Escriba una funcin invert(xpn) que regresa x con los n bits que
principian en la posicin p invertidos (esto es, 1 cambiado a O y vi,:eversa), dejando
los otros sin cambio. O

Ejercicio 2-8. Escriba una funcin rightrot(x, n) que regresa el valor del entero
x rotado a la derecha n posiciones de bils . D

y::,

x=x&-077

fija los ltimos seis bits de x en cero. Ntese que x & '077 es independiente de
la longitud de la palabra, y por lo tanto, es preferible a, por ejemplo, x &
0177700, que supone que x es una cantidad de 16 bits. La forma transportable
no involucra un costo extra, puesto que ~077 es una expresin constante que puede ser evaluada en tiempo de compilacin.
Como ilustracin de algunos de los operadores de bit s, considere la funcin getbits(x,p,n) que regresa el campo de n bits de x (ajustado a la derecha)
que principia en la posicin p. Se supone que la posicin del bit O est en el borde
derecho y que n y p son valores positivos adecuados. Por ejemplo, getbits(x,4,3)
regresa los tres bits que estn en la posicin 4, 3 Y 2, ajustados a la derecha.
/. getbits: obtiene n bits desde la posicin p .1
unsigned getbits(unsigned x, int P, int n)

Operadores de asignacin y expresiones

2.10

Las expresiones tales como

+ 2

i = i

en las que la variable del lado izquierdo se repite inmediatamente en el derecho,


pueden ser escritas en la forma compacta
i

El operador + = se llama operador de asignacin.


La mayora de los operadores binarios (operadores como + que tienen un
operando izquierdo y otro derecho) tienen un correspondiente operador de asignacin op::::, en donde op es uno de
+-*1%&"

Si expr 1 Y expr2 son expresiones, entonces


exp'l op ::; expr 2

es equivalente a
expr = (expr l ) op (expr~ )

except uando que expr se calcula slo una vez. Ntense los parl1les is alrededor
de expr 2:
l

x*=y+l

significa

x = x (y

return (x > > (p + l-n)) & '('O < < n);

1)

y no

La expresin x > > (p + 1-n) mueve el campo deseado al borde derecho de la palabra. -Oes todos los bits en 1; corriendo n bits hacia la izquierda con -0< <nco]oca ceros en los n bits ms a la derecha; complementado con hace una mscara
de unos en los n bits ms a la derecha.

x*y+l

Como ejemplo, la funcin


memo entero.

/);f('OIlIlf cuenta el nmero de

bits ell 1 en su argu-

SECCION 2_12

56

TIPOS, OPERADORES Y EXPRESIONES

l. bitcount:

cuenta bits 1 en x 1

int bitcount(unsigned x)

expr 1 ? expr2

for (b = O; x ! = O; x > > = 1)

if(x&OI)
b+ +;

return b;

yyval[yypv[p3 + p4] + yypv[pl + p211 + = 2

el operador de asignacin hace al cdigo ms fcil de entender, puesto que ellector no tiene que verificar arduamente que dos expresiones muy largas son en realidad iguales, o preguntarse por qu no lo son, y un operador de asignacin puede
incluso ayudar al compilador a producir cdigo ms eficiente.
Ya hemos visto que la proposicin de asignacin tiene un valor y puede estar
dentro de expresiones; el ejemplo ms comn es

Ejercicio 2-9. En un sistema de nmeros de complemento a dos, x & = (x-l) borra el bit 1 de ms a la derecha en x. Explique el porqu . Utilice esta observacin
para escribir una versin ms rpida de bitceunt. O

2.11

exprJ

> b) ? a : b;

l z

mx(a,b)1

Se debe notar que la expresin condicional es en s una expresin, y se puede


utilizar en cualquier lugar donde otra expresin pueda. Si e.\1'r, y (',\P/'l' son
de tipos diferentes, el tipo del resultado se determina por Ia:- regla" dc l'l)Il\' l'l"~
sin discutidas anteriormente en este captulo. Por ejemplo, si f es un float y n
es un int, entonces la expresin
(n

> O)

f :n

es de tipo Heat sea n positivo o 110.


Los parntesis no son necesarios alrededor de la primera expresin de una expresin condicional, puesto que la precedencia de?: (':- Illuy baja . ..,ln arril"\a
de la asignacin. De cualquier modo son recomendables, puesto que hacen ms
fcil de ver la parte de condicin de la expresin.
La expresin condicional frecuentemente lleva a un cdigo conciso. Por ejemplo, este ciclo imprime n elementos de un arreglo, lO por linea, con cada columna
separada por un blanco, y con cada lnea (incluida la :tima) terminada por una
nueva lnea.

getehar( )) ! = EOF)

Los otros operadores de asignacin (+ = ,- =, etc.) tambin pueden estar dentro


de expresiones, aunque esto es menos frecuente.
En todas esas expresiones, el tipo de una expresin de asignacin es el tipo
de su operando del lado izquierdo, y su valor es el valor despus de la asignacin .

'

la expresin expr es evaluada primero. Si es diferente de cero (verdadero), entOllces la expresin expr~ es evaluada. y se es el \'alo]" dc la c\pr(':..ill Londicio~
na!. De otra forma, expr J se evala, y se es el valor. Slo llllL1 ele cntre e.\"pr~ y
expr,. se evala. As, para hacer z el mximo de a y b,
z = (a

Declarar al argumento x como unsigned asegura que cuando se corre a la derecha, los bits vacantes se llenarn con ceros, no con bits de signo, sin importar
la mquina en la que se ejecute el programa.
Muy aparte de su concisin, los operadores de asignacin tienen la ventaja de
que corresponden mejor con la forma en que la gente piensa. Decimos "suma 2 a
i" o "incrementa i en 2", no "toma i, agrgale 2, despus pon el resultado de
nuevo en il/. As la expresin i + = 2 es preferible a i = i + 2. Adems, para una
expresin complicada como

57

calculan en z el mximo de a y b. La expresin condicional, escrita con el operador ternario "?:", proporciona una forma alternativa para escribir sta y otras
construcciones semejantes. En la expresin

int b;

while e

PRECEDENCIA Y ORDEN DE EVALUAC ION

CAPITUL02

far (i = O; i < n; i + +)
printf("%6d%c", a[i], (i%lO= =9 :: i= =n-l) ? '\n' :

');

Se imprime un carcter nueva lnea despus de cada die! clCIllCllIO:", y lk!\pllL'-' dd


n-simo. Todos los otros elementos son seguidos por un espacio en blanco. Esto
podra verse oscuro, pero es ms compacto que el if-else l,tjui\akllll'. Ot]"ll
buen ejemplo es
printf("Hay %d elemento%s. \n", n, n =

1 ? "" : "s");

Ejercicio 2-10. Reescriba la funcin Ilower[, que convierte letras maysculas e


minsculas, con una expresin condicional en vez de un if-else. O

Expresiones condicionales

Las proposiciones
if(a>b)
z
a;

eIse
b

2.12

Precedencia y orden de evaluacin

La tabla 2-1 resume las reglas de precedencia y asociatividad de todos los operadores, incluyendo aquellos que an no se han tratado. Los operadores que
estn en la misma lnea tienen la misma precedencia; los renglones estn en orden

58

TIPOS. OPERADORES Y EXPRESIONES

CAPITULO 2

de precedencia decreciente, as, por ejemplo, *,/, y % tienen todos la misma precedencia, la cual es ms alta que la de + y - binarios. El "operador" ( ) se refiere
a la llamada a una funcin. Los operadores - > y . son utilizados para tener acce~o

a miembros de estructuras; sern cllb i erto~ en el capitulo 6. junto con sizeof


(Iamao de un objeto). En el captulo 5 se discuten * (indireccin a Iravc~ de un
apunlador) y & (direccin de un objelo), ye n el capilulo 3 ,e Irala al operador

coma.
Nese que la precedencia de los operadores de bir~ &, ~. Y r est debajo de
y 1=. Esto implica que las expresiones de prueba de bits como
if x

& MASK)

= = O)

deben ser completamente colocadas entre parntesis para dar los resultados apropiados.
TABLA 2-1. PRECEDENCIA y ASOCIATlVIDAD DE OPERADORES

ASOCIATIVIDAD

OPERADORES

()

[[

- >

- + + -- + - &
I
%

(tipo) sizeof

<

<= > >=


!=

&

&&

""?:
+=

1= % = &=

l= = =

izquierda a derecha
derecha a izquierda
izquierda a derecha
izquierda a derecha
izquierda a derecha
izquierda a derecha
izquierda a derecha
izquierda a derecha
izquierda a derecha
izquierda a derecha
izquierda a derecha
izquierda a derecha
derecha a izquierda
derecha a izquierda
izquierda a derecha

Los +, -, y * unarios, tienen mayor precedencia que las formas binarias.


Como muchos lenguajes, C no especifica el orden en el cual los operandos de
un operador sern evaluados. (Las excepciones son &&, ::.7: y' .:) Por ejemplo,
en proposiciones como
x = f( ) + g( );

f puede ~cr e\ aluada allles de 9 o \'ice\'ersa~ de este modo si fa 9 alteran una \'ariabk de la que la OIra depende, x puede depender del orden de c\aluacin. Se
pueden allllaL'enar resultados intermedios en variables temporales para asegurar
una \ecuencia panicular.

SECC ION 2.12

PRECEDENCIA Y ORDEN DE EVALUACION

59

Oc manera selllejame, el orde n en el que- se c\alan los argumento:.. de ul1a


funcin no est especificado, de modo que la propo,icin
printf("%d %d\n", + +n, power(2, nI);

/. EQUIVOCADO ./

puede producir resultados diferentes con distintos compiladores, dependiendo de


si n es incrementada antes de que se llame a power. La solucin, por supuesto,
es escribir
+ +n;
printf("%d %d\n", n, power(2, n));

Las llamadas a funciones, proposiciones de asignacin anidadas, y los operadores de incremento y decremento provocan "efectos colaterales" -alguna variable es modificada como produclo de la evaluacin de una expresin. En cualquier
expresin que involucra efectos colaterales, pueden existir sutiles dependen cias
del orden en que ra~ \ariablc!-' ill\olucrada~ ell la L'\pn:"in ~e aCluali/<lll. La infortunada situacin c~ lipifif".'ada por la pruposicin
a[i] = i+ +;

La pregunta es si el subndice es el viejo o el nuevo valor de i. Los compiladores


pueden interpretar esto en formas diferentes, y generar diferentes respuestas dependiendo de su interpretacin. El estndar deja intencionalmente sin especificacin la mayoria de talc:'> aspcrtos. Cuanclo hay ekclo" co latcra!c!-' (a~igllaf".'in
a \ariablcs) delllro de una expresion, ~(' deja a la prudellcia del compilador,
puesto que el mejor orden depende grandemente de la arquitectura de la mquina.
(El c...,tndar s cspeL'iI'ica que lodo:.. lo.'" efectos clllall'rales ~obre arglllllenln~ ~UCl'
dan ,1111(':.. de que la funcin sea llamada, pero l ...,O podria 110 ayudar ell la llamada
a printf mostrada anteriormente.)
La moraleja es que escribir un cdigo que dependa del orden de evaluacin es
una mala prctica de programacin en cualquier lenguaje. Naturalmente, es necesario conocer qu cosas evitar, pero si no sabe cmo se hacen las cosas en varias
mquinas, 110 ddll' intelltar apnl\ecllar una illlplalll<lcin l'1I particula r.

CAPITULO 3

Control de flujo

Las proposiciones de control de flujo de un lenguaje especifican el orden en


,,1,.' rl'~lIi/a el proL'l. . . amicl1lo. Ya hCll1o.., \..,IO la mayora de las cOllslruccioncs de
control de flujo en ejemplos anteriores; aqui completaremos el conjunto, y seremos ms precisos acerca de las discutidas con anterioridad.
qUl'

3.1

Proposiciones y bloques

Una expresin como x = O i + + O printf( ... ) se convierte en una proposicin cuando va seguida de un punto y coma, como en
x = O;

1+ +;

pnntf ( ... );
En e, el punto y coma es un te rminador de proposicin, en lugar de un separa-

dar, como lo es en un lenguaje tipo Pascal.


l.as llaves {y I se emplean para ag rupar declaraciones y proposiciones dentro
de una proposicin complles/( o bloque, de modo que ... un ..,iJ1lcticamente cqui\alentes a una proposicin sencilla. Las llave.., que encierran la . . propot.,icionc..,

de una funcin so n un ejemplo obvio; otros ejemplos son las ll aves alrededor de
proposiciones mltip les despus de un if, else, while o fo r o (Pueden declararse
variables dentro de cualquier bloque; esto se expondr en el capitulo 4.) No hay
pUnto y coma despus de la llave derecha que termina un bloque.

3.2

If-else
La proposicin if-else se utiliza para expresar decisiones. Formalmente, la

sintaxis es
f (expresin)

proposicin I

el se
proposicin~

61

62

CONTROL DE FLUJO

CAPITUL03

donde la parte del else es optativa. La expresin se evala; si es verdadera (esto


es, si la expresin tiene un valor diferente de cero), la proposicin! se ejecuta. Si es
falsa (expresin es cero) y si existe una parte cleelse, laproposicin 2, se ejecuta en su
lugar.
Puesto que un if simplemente prueba el valor numrico de una expresin, son
posibles ciertas abreviaciones de cdigo. Lo ms obvio es escribir

ELSE-IF

SECCION 3.3

A propsito, ntese que hay un punto y coma despus de z

63

a en

if (a > b)
a;

else
z = b;

Esto se debe a que gramaticalmente al [ITl le sigue una proposicin, y una expresin
como "z :::= a;" siempre se termina con punto y coma.

if (expresin)

en lugar de
if (expresin!

O)

3,3

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 ambigedad
cuando un else se omite de una secuencia if anidada. Esto se resuelve al asociar
el else con el if anterior sin else ms cercano. Por ejemplo, en
f

a'

proposicin

else if (expresin)
proposicin

el else va con el if ms interno, como se muestra con el sangrado. Si eso no es


10 que se desea, se deben utilizar llaves para forzar la asociacin correcta:
if (n > O) {
if (a > b)
a;

else

z = b;

La ambigedad es especialmente perniciosa en situaciones como esta:


if (n > = O)
for (i = O; i < n; i + +)
if (sli] > O) {
printfe' ... ");

else
proposicin

ocurre de modo tan frecuente que bien vale una pequea discusin aparte. Esta
secuencia de proposiciones if es la forma ms general de escribir una decisin
mltiple. Las expresiones se evalan en orden; si cualquier expresin es verdadera, la proposicin asociada con ella se ejecuta, y esto termina toda la cadena. Como siempre, el cdigo para cada proposicin es una proposicin simple o un
grupo dentro de llaves.
La parte del ltimo else maneja el caso "ninguno de los anteriores" o caso
por omisin cuando ninguna de las otras condiciones se satis face. En algunos casos no hay una accin explcita para la omisin; en ese caso el
else
proposicin

return i:

l. MAL ./
printf("error -- n es negativo

proposicin
proposicin

z = b;

else

if (expresin)

else if (expresin)

else

La construccin

else if (expresin)

(n > O)
if (a > b)
z

Else-if

\n

Jl
);

El sangrado muestra en forma inequvoca lo que se desea, pero el compilador nO


entiende el mensaje y asocia el else con el if ms interno. Puede ser difcil encon~
trar esta ciase de errores; es una buena idea utilizar llaves cuando hay varios if
anidados.

del final puede omitirse, O puede utilizarse para deteccin de errores al atrapar
una condicin "imposible".
Para ilustrar una decisin de tres vas, se muestra una funcin de bsqueda binaria 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 funcin regresa la posicin (un nmero entr-e O y n-l) si x est en v, y -1 si no es as.
La bsqueda 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 bsqueda se enfoca
sobre la mitad inferior de la tabla; de otra manera lo hace en la mitad superior .
En Cualquier caso, el siguiente paso es comparar a x con el elemento medio de

64

CONTROL DE FLUJO

CAPITULO)

SWITCH

SECC 10N 3.4

la mitad seleccionada. Este proceso de dividir en dos co ntina hasta que se encuentra el valor o ya no hay elementos .
/ * binsearch: encuentra x en v[O]
int binsearch(int x, int v[]. int n)

< = v[l] < =

<~

v[n-l1 ./

int low, high, mid;

65

l)plaivo ; si no e~t y ninguno de los casos coincide, no se (oma accin alguna. Ln~ dju ..,u las rC~lSCl y Idefaultj pueden ocurrir en cualquier orden .
En el captulo 1 se escribi un programa para contar las ocurrencias de cada
dgito, espacio en blanco y todos los dems caracteres, usando una secuencia de
if ... else if ... else. Aqu est el mismo programa con un switch:
1......

#include < stdio.h >


low

= O;

high ~ n - 1;
while (low < ~ high) {
mid ~ (low + high) / 2;
if (x < v[midJ)
high ~ mid - 1;
else if (x > v [midJ)
low
else

= mid + 1;

/ * el elemento fue encontrado * /


return mid;

return -1;

/* no se encontr */

main()

/ * cuenta dgitos, espacios blancos, y otros ,

{
int c, i, nwhite, nother, ndigit[10);
nwhite = nother

= O;

for (i ~ O; i < 10; i + +)


ndigit[i] ~ O;
while ((e ~ getehar( )) ! ~ EOF) {
switch (e) {
case 'O': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':

ndigit[e-'O'I + +;
break;

La decisin fundamental es si x es menor que, mayor que o igual al elemento medio v[mid] en cada paso; esto es un lelse-ifl natural.
Ejercicio 3-1. Nuestra bsqueda binaria realiza dos pruebas dentro del ciclo,
cuando una podra ser suficiente (al precio de ms pruebas en el exterior). Escriba
una versin con slo una prueba dentro del ciclo y mida la diferencia en tiempo
de ejecucin. D

3.4

case
case '\n':
case '\t':
nwhite+ +;
break;

default:
nother+ +;
break;

Switch

La proposicin switch es una decisin mltiple que prueba si una expresin


coincide con uno de un nmero de valores constantes enteros, y traslada el control adecuadamente.
switch (expresin) {
case exp-consl: proposiciones
case exp-consl: proposiciones
default: proposiciones

printf("djgitos ~ ");
for (i ~ O; i<10; i+ +)
printf(" %d", ndigit[iJ);
printf(", espacios blancos = <%d, otros
nwhite, nother);
return O;

%d\n",

la proposicin break provoca una salida inmediata del switch. Puesto que
Cada Icasel se etiqueta con uno o ms valores constantes enteros o expresiones
constantes enteras. Si un Icasel coincide con el valor de la expresin, la ejecucin
L'o1l1icIlLa alli. Toda~ las expresiones !case I deben ser diferentes. El etiquetado como default se ejecuta si ninguno de los otros se sa ti ~face. El default

los @iJ sirven slo como etiquetas, despus de que \e ejeL'wa el codigo para
Uno, la ejecucin pasa al sigu iente , a menos que se tome una accin e\pecfica para terminar el switch . Las forma", ms comune\ de dejar un switch son break y re-

turno Una proposicin break tambin se puede emplear para forzar una salida
inmediata de los ciclos while, lor y do, como se ver ms adelante en este captulo.

66

CONTROL DE FLUJO

CAPITULO]

Pasar a trms de 10.<" It"asel e!' en parle bueno y en parte no. Por el lado PO~ilivo,
permile t"onet"tar var ios Icasel a una accin simple, como con los dgito!' dc
e<.,te ejemplo. Pero eso tambin implica que cada case no rm almente debe terminar co n un break para pre\'enir pasar al siguiente. Pasar ele un t case la Q(ro no
e<., una prih.:tica Illuy ,-olida y e,- <.,u<.,(cptiblc a la dcsintcgra(in cuando se modifica
el programa. Con la cxcepcin de etiqueras mltiples para un dtkulo simple, lo
anterior <.,e debe ulili/ar (o n caulela y empicar comentarios.
Como formalidad. coloque un break despus del ltimo Icase I (en estc caso el
defauIt) aun ", e,- lgicamcnte innece:-,ario. Algn da, cuando se agregue otro
I casel al final, c..,ta prclica de programacin defen~iva lo salvara.
C<.,IO

Ejercicio 3-2. Escriba una funcin escape(stt) que convierte caracteres como
nueva lnea y tabulacin en secuencias de escape visibles como \n y \t mientras
copia la cadena t a s. Utilice un switch. Escriba tambin una funcin para la direccin inversa, convirtiendo secuencias de escape en caracteres reales. O

3.5

Ciclos-while y for

SECCION 3.5

ClCLOS-W ILE y FOR

67

expansin. Si la prueba exprz no est presente, se toma como permanentemente


verdadera, as que
for (;;) {

es una iteraClOn "infinita", que presumiblemente ser interrumpida por otros


medios, como un break o un return.
El usar while o lor es principalmente cuestin de preferencia personal. Por
ejemplo, en
while (c = getchar( )) = = " :: c = = '\n' :: c
l . ignora caracteres espaciadores . 1

'\t')

no hay inicializacin o reinicializacin, por lo que el while es ms natural.


El for se prefiere cuando existe una inicializacin simple e incrementos, puesto
que mantiene las proposiciones de control del ciclo juntas y visibles al principio
del mismo. Esto es ms obvio en
for (i = O; i < n; i + +)

Ya hemos encontrado los ciclos while y lor. En


while (expresin)
proposicin

la expresin se evala. Si es diferente de cero, se ejecuta la proposicin y se reeva


la la expresin. Este ciclo contina hasta que la expresin se hace cero, punto
en el cual se suspende la ejecucin para continuar despus de la proposicin.
La proposicin lor
for (expr 1 ; exprz; exprJ )
proposicin

es equivalente a
expr);
w hile (expr ,) {

proposicin
expr 3;

que es la forma caracterstica de procesar los primeros n elementos de un arreglo


en e, lo anlogo al ciclo DO de Fortran o al lor de Pascal. Sin embargo, la analoga no es perfecta puesto que tanto el ndice como el lmite de un ciclo for en C
pueden ser alterados desde dentro del ciclo, y la variable del ndice i retiene su valor
cuando las iteraciones terminan por cualquier razn. Debido a que las compo nentes del lar son expresiones arbitrarias, sus ciclos no estn restringidos a progresiones aritmticas. Por otra parte, considere que es un mal estilo inclu ir en las
secciones de inicializacin e incremento operaciones no relacionadas con esas actividades, que ms bien se reservan para acciones de control del ciclo.
Como un ejemplo ms amplio, aqu est otra versin de atoi para convertir
una cadena a su equivalente numrico. Esta es ligeramente ms general que la del
captulo 2; trata tambin lo s espacios en blanco previos al nmero, y los signos
+ o -. (El captu lo 4 muestra atof, que realiza la misma conversin para numeras de punto flotante.)
La estructura del programa refleja la forma de la entrada:
1J!.l1ora espacios en blanco, si
roma el signo, si lo hay

excepto por el comportam iento de continue que se describe en la sec<.: in 3.7.


Gramaticalmente, las tres componentes de un ciclo for son expresiones . por
lo comn, expr 1 Y expr] son asignaciones o llamadas a funcin y expr2 es una eXpresin de relacin. Cualquiera de las tres partes se puede omitir, aunque deben
permanecer los punto y coma. Si expr) o exprJ se omite, slo se desecha de la

10\

hay

roma la parre enrera y convirrela

Cada paso realiza su parte, y deja las cosas en forma clara para el siguiente. La
tOtalidad del proceso termina con el primer carcter que no pueda ser parte de
Un nmero.

68

CAPITULO 3

CONTROL DE FLUJO

#include < ctype.h >


1* atoi: convierte s a entero; versin 2 */
int atoi(char s[])
int i, n, sign;
O; isspace(s[i)); i + +)

for (i

sign

(s[i]

if (s[i] == ==

~ ~

1* ignora espacio en blanco. /

'-') ? -1 . 1;

'+':: s[i]

== == '_')

/. ignora el signo . /

i+ +;

fo, (n

(s[~~

- 'O');

zeturn sign * n;

La biblioteca estndar proporciona una funcin ms elaborada, strtol, para la


conversin de cadenas a enteros largos; vase la seccin 5 del apndice B.
Las ventajas de mantener centralizado el control del ciclo son an ms obvias
cuando existen ciclos anidados. La siguiente funcin es una clasificacin Shell
para ordenar un arreglo de enteros. La idea bsica de esle algoritmo de ordenamiento, que fue inventado en 1959 por D.L. Shell, e~ que en las primeras etapas
sean comparados elementos lejano~, en lugar de los adyacentes, como en los ordenamientos de intercambio ms simples. E~lo tiende a eliminar rpidamente
gran cantidad de desorden, as que los estados posteriores tienen menos trabajo
por hacer. El intervalo entre los elementos comparados disminuye en forma gradual hasta uno, punto en el que el ordenamiento viene a ser efectivamente un mtodo adyacente de intercambio.
ordena v[OJ ... v[n-11 en orden ascendente *1

void shellsort(int vI], int n)


int gap, i, j, temp;
fOl

(gap

CICLOS~WILE

nl2; gap> O; gap / ~ 2)

for (i = gap; i < n; i + + )


fm (j~i-gap; j> ~O && vii] >v[j + gap]; j-~gap) {

temp ~ vii];
vii] ~ v[j + gap];
vii + gap] ~ temp;

Existen tres ciclos anidados. El ms externo controla el espacio entre los elementos comparados. reducindolo desde n/2 por un factor de dos en cada paso hasta

y FOR

69

que llega a cero. El ciclo intermedio recorre los elementos. El ciclo ms interno
compara cada pareja de elementos que est separada por el espacio gap e invierte
a las que estn desordenadas. Puesto que gap finalmente se reduce a uno, lodos
los elementos se ordenan correctamente. Ntese cmo la generalidad del for hace
que el ciclo ms externo coincida con la forma de los otros, aun cuando no es
una progresin aritmtica.
Un ltimo operador de e es la coma", JI, que frecuentemente encuentra uso
en la proposicin for. Una pareja de expresiones separadas por una coma se evaloa de izquierda a derecha, y el tipo y valor del resultado son el tipo y valor del
operando derecho. As, en una proposicin for es posible colocar expresiones
moltiples en las diferentes partes, por ejemplo, para procesar dos ndices en paralelo. Esto se ilustra en la funcin reverse(s), que invierte a la cadena s en el mismo lugar.

O; isdigit(s[i]); i+ +}

n == 10. n +

l. shellsort:

SECCION 3.5

< sfring.h >

#include

l. reverse: invierte la cadena s en el mismo lugar 1


void reverse(char s[])

{
inf

C,

fm (i

i, j;
~

O, j

stden(s}-1; i <

j;

i+ +, j--) {

s[i];
s[i]
slj];
slj] ~ e;
e

Las comas que separan a los argumentos de una funcin, las variables en declaraciones, etc., no son operadores coma, y no garantizan evaluacin de izquierda
a derecha.
Los operadores coma debern utilizarse poco. Los usos ms adecuados son
en construcciones fuertemente relacionadas una con la otra, como en el ciclo for
de reverse, yen macros en donde un clculo de paso mltiple debe ser una expresin simple. Una expresin coma podra tambin ser apropiada para el intercambio de elementos en reverse, donde el intercambio puede ser a travs de una
operacin simple:
fo, (i ~ O, j ~ stden(s)-l; i < j; i+ +, j--)
e ~ s[i], sli] ~ s[j], s[j] ~ e;

Ejercicio 3-3. Escriba la funcin expand(sl,s2) que expande notacin abreviada


como a-z, que viene en la cadena sI, en la lista equivalente \,:"omplcla abe ... xyz,
en s2. Permita letras maysculas y minsculas, as como dgitos, y est preparado
para manejar casos como a-b-e y a-zO-g y -a-z. Haga que los guiones al
inicio o al final se tomen literalmente. O

70

3.6

CAPITULO 3

CONTROL DE FLUJO

Ciclos-do-while

Como ya se expuso en el captulo 1, los ciclos while y lor verifican al principio


la condicin de trmino. En contraste, el tercer ciclo en e, el do-while, prueba
al final despus de realizar cada paso a travs del cuerpo del ciclo, el cual se ejecuta siempre por lo menos una vez.
La sintaxis del do es

SECCION 3.7

BREAK Y CONT INUE

71

n igual a _(2tamanopalabra-I). Explique por qu. Modifquelo para imprimir el valor


correctamente , sin importar la mquina en que ejecute. o
Ejercicio 3-5. Escriba la funcin itob(n,s,b) que convierte al entero n en una representacin de caracteres con base b dentro de la cadena s. En panicular, itob(n,
s, 16) da formalO a n como un entero hexadecimal en S. D
Ejercicio 3-6. Escriba una versin de litoal que acepte tres argumentos en lugar
de dos. El tercer argumento es un ancho mnimo de campo; al nmero convertido se deben agregar blancos a la izquierda si es necesario para hacerlo suficientemente ancho. D

do
proposicin
while (expresin);

La proposicin se ejecuta y despus se evala la expresin. Si es verdadera, la


proposicin se evala de nuevo, y as sucesivamente . Cuando la expresin se hace
falsa, el ciclo termina. Excepto por el sentido de la prueba, el dowhile es equivalente a la proposicin repeatuntil de Pascal.
La experiencia demuestra que el dowhile es mucho menos utilizado que el
while y el fOfo Aunque de cuando en cuando es valioso, como en la siguiente
funcin itoa, que convierte un nmero a una cadena de caracteres (lo inverso de
atoi) . El trabajo es ligeramente ms complicado de lo que podra pensarse en un
principio, debido a que los mtodos fciles para generar dgitos los generan en
el orden incorrecto. Hemos elegido generar la cadena al revs y despus invertirla.
1* itoa: convierte n a caracteres en s */
void itoa(int n, char s[])

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 proposicin break proporciona una salida anticipada de un for, while y do, tal como lo hace el switch.
Un break provoca que el ciclo o switch ms interno que lo encierra termine inmediatamente.
La siguiente funcin , trim, elimina espacios blancos, tabuladores y nuevas lneas al final de una cadena, utilizando un break para salir de un ciclo cuando
se encuentra el no-blanco, no-tabulador o no-nueva lnea de ms a la derecha.
1* trim:

elimina blancos, tabuladores y nueva lnea al final * 1

int trim(char sI})

int, i, sign;

n) < O)

if ((sign
n

-n;

1* registra el signo *1
vuelve a n positivo *1

i = O;

1* genera dgitos en orden inverso */


s[i+ + 1 = n % 10 + 'O';
/* toma el siguiente dgito *1
I while n 1= 10) > O);
l. brralo 1
if (sign < O)
s[i+ +]

do {

s[i]

int n;

lor (n = strlen(s)-l; n > = O; n--)


if (s[n] !
&& sin]
'\t' && sin]
break;
sin + 11 = '\0';
return n;

=' ,

,=

,=

'\n')

'\0';

reverse(s);

El dowhile es necesario, o al menos conveniente, puesto que por lo menos se debe instalar un carcter en el arr~glo s, aun si n es cero. Tambin empleamos llaves
alrededor de la proposicin simple que hace el cuerpo del do-while, aunque son
innecesarias, y as el lector apresurado no confundir la seccin del while con el
principio de un ciclo while.
Ejercicio 3-4. En una representacin de nmeros en complemento a dos, nuestra
versin de itoa no maneja el nmero negativo ms grande, esto es, el valor de

,trien regresa la longitud de la cadena. El ciclo lor inicia al final y rastrea hacia atrs, buscando el primer carcter que no sea blanco o tabulador o nueva lnea.
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 componamiento es correcto, aun cuando la cadena est vaca o slo contiene es pacios en blanco .
La proposicin continue est relacionada con el break, pero se utiliza menos;
p.ro\"oca que inicie la siguiente iteracin del ciclo for, while o do que la COI1tiene. Dentro de while y do, esto significa que la parte de la prueba se ejecuta
In", d'
e latamente; en el for, el control se traslada al paso de incremento. La pro po-

72

CAPITULO)

CONTROL DE FLUJO

sicin continue 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 iteracin del ciclo .
Como un ejemplo, el siguiente fragmento procesa slo los elementos no
negativos que estn en el arreglo a; los valores negativos son ignorados .
for ( ~ O; < n; i+ +) {
if (a{iJ < O)
/ . ignora elementos negativos . /
continue;
/ . trabaja con elementos positivos . /

La proposicin continue se emplea a menudo cuando la parte del ciclo que sigue
es complicada, de modo que invertir la p rueba y sangrar otro nivel podria anidar
profundamente el programa.

3. 8

Goto y etiquetas

C proporciona la infinitamente abusable proposicin gota, y etiquetas para


saltar hacia ellas . Formalmente, el gota nunca es necesari o, y en la prctica es
casi siempre ms fcil escribir cdigo sin l. En este li bro no se ha usado gota
algu no.
Sin embargo, hay algunas situaciones donde los gota pueden encontrar un lugar. La ms comn es abandonar el procesamiento en alguna estructura profundamente anidada, tal como salir de dos o ms ciclos a la vez. La proposicin
break no se puede uti lizar directamente, puesto que s610 sale del cic lo ms interno. Asi:
for( ... )
for ( ... ){

if (desastre)
goto error;

error:
arregla el desorden

Esta organizacin es til si el cdigo de manejo de error no es trivial y si los errores 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 proposicin de la misma funcin
en la que est el gota. El alcance de una etiqueta es toda la funcin.
Como otro ejemplo, considrese el problema de determinar si dos arregloS , 8
y b, tienen un elemento en comn. Una posibilidad es

SECCION 3.8

GOTa y ETIQUETAS

73

for (i = O; i < n; i + +)
for (i ~ O; i < m; i + +)
f (al] ~ ~ bli]
goto encontrado;
/ . no se encontr ningn elemento en comn . /
encontrado:
!- se tiene uno:

al]

~ ~

bli]- !

El cdigo que involucra un gota siempre puede escribirse sin l, aunque tal
vez al precio de algunas pruebas repetidas o variables extra. Por ejemplo, la bsq ucda en los arreglos quedar
encontrado = O;
for (i = O; i < n && !encontrado; i + +)
for (j ~ O; j < m && 'encontrado; i + +)
if (al] ~ ~ bli])
encontrado = 1;
if (encontrado)
! - se tiene uno: al-l] ~ ~ bli-l]- !
else
/. no se encontr algn elemento en comn . /

Con pocas excepciones, como las citadas aqu, el cdigo que se basa en pro-

posiciones gota es generalmente ms difcil de entender y de mantener que el cdigo sin ellas. Aunque no somos dogmticos acerca del asunto, se ve que las
proposiciones gota deben ser utilizadas raramente, si acaso.

CAPITULO 4:

Funciones y la estructura
del programa

Las funciones dividen tareas grandes de computacin en varias ms pcqueii.as,


y permiten la posibilidad de construir sobre lo que otros ya han hecho. en
lugar de comenzar desde cero. Las funciones apropiadas ocultan os detalles de
operacin 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 fciles
de usar; los programas escritos en e se componen de muchas funciones pequeas
en lugar de slo algunas grandes. Un programa puede residir en uno o ms archivos fuente, los cuales pueden compilarse por separado y cargarse junto con funciones de biblioteca previamente compiladas. No trataremos aqu tales procesos,
puesto que los detalles varan de un sistema a otro .
La declaracin y definicin de funciones es el rea donde el estndar ANSI
ha hecho los cambios ms visibles a C. Tal como mencionamos en el captulo 1,
ahora es posible declarar los tipos de los argumentos cuando se declara una funcin. La sintaxis de la definicin de funciones tambin cambia, de modo que las
declaraciones y las definiciones coincidan. Esto hace posible que el compilador
pueda detectar muchos ms errores de lo que poda anteriormente. Adems,
cuando los argumentos se declaran con propiedad, se realizan automticamente
las conversiones convenientes.

El estndar clarifica las reglas sobre el alcance de los nombres; en particular,


requiere que slo haya una definicin de cada objeto externo. La inicializacin
es ms general: los arreglos y las estructuras automticas ahora se pueden inicializar.
El preprocesador de C tambin se ha mejorado. Las nuevas facilidades del
procesador incluyen un conjunto ms completo de directivas para la compilacin
condicional, una forma de crear cadenas entrecomilladas a partir de argumentos
de macros y un mejor control sobre el proceso de expansin de macros.

4.1

Conceptos bsicos de funciones


Para comenzar, diseemos y escribamos un programa que imprima cada lnea

de su entrada que contenga un "patrn" o cadena de caracleres en particular.

75

76

FUNCIONES Y LA ESTRUCTURA DEL PROGRAMA

CAPITULO 4

(Este es un caso especial del programa grep de UNIX.) Por ejemplo, al buscar
el patrn de letras "ould" en el conjunto de lneas
Ah Lovel could you and 1 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 Desirel

producir la salida
Ah Lovel could you and 1 with Fate conspire
Would not we shatter it to bits -- and then
Re-mould it nearer to the Heart's Desirel

# inelude <stdio.h>
#define MAXILINE 1000

/* longitud mxima por lnea de entrada */

int getline(char line[ L int max);


int strindex(char soulce[], chal searchfor[])

char pattero [ ]

"ouId";

1* palrn por buscar /

-encuentra todas las lneas que coincidan con el patrn */


main ( )
{
char linelMAXLINEI;
int found = O;

El trabajo se ajusta ordenadamente en tres parles:

while (getline(line, MAXLINE) > O)


if (strindex(line, paUern) > = O) {
printf("%s", line);
found+ +;

while (hay olra lInea)


if (/a lnea contiene el patrn)
imprfmela

Aunque ciertamente es posible poner el cdigo de todo esto en main, una mejor forma es aprovechar la estructura haciendo de cada parte una funcin separada. Es ms fcil trabajar con tres piezas pequeas que con una grande, debido a
que los detalles irrelevantes se pueden ocultar dentro de las funciones, y minimizar as el riesgo de interacciones no deseadas. Los fragmentos incluso se pueden
emplear en otros programas.
"Mientras hay otra lnea" es getline, funcin que ya escribimos en el captulo
1, e "imprmela" es printf, que alguien ya nos proporcion. Esto significa que
slo necesitamos escribir una rutina para decidir si la lnea contiene una ocurrencia del patrn.
Podemos resolver ese problema escribiendo una funcin slrindex(s,t), que regresa la posicin 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 e principian en la posicin cero,
los ndices sern cero o positivos, y as un valor negativo como -1 es conveniente
para sealar una falla ..Cuando posteriormente se necesite una coincidencia de patrones ms elaborada, slo se debe reemplazar strindex; el resto del cdigo puede permanecer igual. (La biblioteca estndar provee una funcin slrstr que es
semejante a strindex, excepto en que regresa un apuntador en lugar de un ndice.)
U na vez definido todo este diseo, llenar los detalles del programa es simple.
Aqu est en su totalidad, de modo que se puede ver cmo las piezas quedan
juntas. Por ahora, el patrn que se buscar es una cadena literal, lo cual no es
el mecanismo ms general. Regresaremos en breve a una discusin sobre cmo
inicializar arreglos de caracteres, yen el captulo 5 mostraremos cmo hacer que
el patrn de caracteres' sea un parmetro fijado cuando se ejecuta el programa.
Tambin hay una versin ligeramente diferente de getline, que se podr campa
rar con la del captulo l.

CONCEPTOS BASICOS DE FUNCIONES

SECCION 4. 1

retum found;

/* getline: trae lnea y la pone en


in! getline(char si ]. int lim)

S,

regresa su longitud */

{
int

C,

i;

i = O;

while (--lim > 0&& (c=getchar())!= EOF && c !='\n')


sli+ +) = c;
if (c = = ,\n')
sli+ +) = c;
sli) = '\0';
retuIn i;

/* strindex: regresa el ndice de t en

S, -1

si no existe */

int strindex(char si ]. char ti ))


{
int i , j, k;
for (i = O; sli) != '\0'; i+ +) {
for (j=i, k=O; tlk)!='\O' && slj)= =tlkJ; j+ +, k+ +)

77

78

CAPITULO 4

FUNCIONES Y LA ESTRUCTURA DEL PROGRAMA

if (k > o && tlk]


return i;

FUNCIONES QUE REGRESAN VALORES NO-ENTEROS

SECCION 4.2

79

tres funciones se almacenan en tres archivos llamados main.c, getUne.c, y strin~


dex.c . Entonces la orden

'O')

ce main.c getline.c strindex.c


return -1;

compila los tres archivos, sita el cdigo objeto resultante en los archivos
main .o , getline.o, y strindex.o, Ydespus los carga todos dentro de un archivo
ejecutable llamado a.out. Si existe un error, digamos en main.c, ese archivo puede volverse a compilar por s mismo y el resultado cargado con los archivos objeto previos, con la orden.

Cada dcrinicin de funcin tiene la forma


lipo~regresado nombre~de-funcin(declaraciones

de argumentos)

ce main.c getline.o strindex.o

{
declaraciones y proposiciones

Varias partes pueden omitirse; una funcin mnima es

cC

emplea la convencin ".c" contra

".0"

para distinguir los archivos fuente de

los archivos objelo.


Ejercicio 4-1. Escriba la funcin strrindex(s,t), que regresa la posicin de la ocurrencia de ms a la derecha de t en s, o -1 si no hay alguna. O

nada( )( }

que no hace ni regresa nada. Una funcin hacer-nada, como sta, es algunas veces til para reservar lugar al desarrollar un programa. Si el tipo que regresa se
omite, se supone int.
Un programa es slo un conjunto de definiciones de variables y funciones. La
comunicacin entre funciones es por argumentos y valores regresados por las fun~
ciones, ya travs de variables externas. Las funciones pueden presentarse en cualquier orden dentro del archivo fuente, y el programa fuente se puede dividir en
varios archivos, mientras las funciones no se dividan .
La proposicin return es el mecanismo para que la funcin que se llama regrese un valor a su invocador. Al retum le puede seguir cualquier expresin:
return expresin

La expresin se convertir al tipo de retorno de la funcin si es necesario. Con


frecuencia se utilizan parntesis para encerrar la expresin, pero son optativos.
La funcin que llama tiene la libertad de ignorar el valor regresado. Incluso,
no hay necesidad de una expresin despus de retum; en tal caso, ningn valor
regresa al invocador. Tambin el control regresa, sin valor, cuando la ejecucin
"cae al final" de la funcin al alcanzar la llave derecha que cierra. No es ilegal,
aunque probablemente un signo de problemas, el que una funcin regrese un valor desde un lugar y ninguno desde otro. En cualquier caso, si una funcin no
regresa explcitamente un valor, su "valor" es ciertamente basura.
El programa de bsqueda del patrn regresa un estado desde main, el nmero
de coincidencias encontradas . Este valor est disponible para ser empleado por
el medio ambiente que llam al programa.
El mecanismo de cmo compilar y cargar un programa en C que reside en varios archivos fuente vara de un sistema a otro. En el sistema UNIX, por ejemplo,
la orden ce mencionada en el caplulo 1 hace el trabajo. Supongase que las

4.2

Funciones que regresan valores no enteros

Hasta ahora los ejemplos de funciones han regresado o ningn valor (void)
o un inl. Qu pasa si una funcin debe regresar algo de otro tipo? Muchas funciones numricas como sqrt, sin y cos regresan double; otras funciones especializadas regresan tipos diferentes . Para ilustrar cmo tratar con esto, escribamos y
usemos la funcin atol(5), que convierte la cadena 5 a su valor equivalente de
punto flotante de doble precisin. La funcin atol es una extensin de atoi, de la
que mostramos versiones en los capitulos 2 y 3. Maneja signo y punto decimal
optativos. y presencia o ausencia de parte entera o fraccionaria. Nuestra versin
no es una rutina de conversin de alta calidad; tomara ms espacio del que podemos dedicarle. La biblioteca estndar incluye un atol; el header < math.h > la
declara.
Primero, atol por s misma debe declarar el tipo del valor que regresa, puesto
que no es inl. El nombre del tipo precede al nombre de la funcin:
#include < ctype .h >
/- atof: convierte la cadena s a double -/
double atof(char 51 ])
{
double val, power
int i, sign;
for (i

siqn

O; isspace(s[i]); i+ +)

(sli)

= = '-')

? -1 : 1;

/- ignora espacios blancos -/

80

FUNCIONES Y LA ESTRUCTURA DEL PROGRAMA

if (s[i[ = =

CAPITULO 4

'+'::

s[i[ = = '-')
i+ + i
lor (val = 0.0; isdigil(s[i ]); i+ +)
val = 10.0 val + (s[i] - 'O');
if (s[i] = = '.')
i+ +;
lor (power = 1.0; isdigil (s[i)); i + +) {
val = 10.0 val + (sli] - 'O');
power = 10.0;
retum siqn * val / power;

Segundo, e igualmente importante, la rutina que llama debe indicar que ala!
regresa un valor que no es inl. Una forma de asegurar esto es declarar ala! explcitamente en la rutina que la llama. La declaracin se muestra en esta primitiva calculadora (apenas adecuada para un balance de chequera), que lee un nmero por
lnea, precedido en forma optativa por un signo, y lo acumula, imprimiendo la
suma actual despus de cada entrada:

FUNCIONES QUE REGRESAN VA LORES NO-ENTEROS

SECCION 4.2

81

regresara un valor double que main tratara como int, y se produciran resultadoS incongruentes.

A la luz de lo que hemos mencionado acerca de cmo deben coincidir las declaraciones con las definiciones, esto podra ser sorprendente. La razn de que
ocurra una falta de coincidencia es que, si no existe el prototipo de una funcin,
sta es declarada implcitamente la primera vez que aparece en una expresin, co-

mo
sum + = atol(line)

Si en una expresin se encuentra un nombre que no ha sido declarado previamente y est seguido por parntesis izquierdo, se declara por contexto, de modo que
se supone que es el nombre de una funcin que regresa un int, y nada se supone
acerca de sus argumentos. An ms, si la declaracin de una funcin no incluye
argumentos como en
double atol( );

tambin es tomada de modo que no se supone nada acerca de los argumentos de


atol; se desactiva toda revisin de parmetros. Este significado especial de la lista
de argumentos vaca se hace para permitir que los programas en e viejos se compilen con los nuevos compiladores. Pero es una mala tctica usar esto con pro-

#include <stdio.h>
#define MAXLINE 100
/ * calculadora rudimentaria" /
maine )

{
double sum, ato!(char I ]);
char line[MAXLINE];
int qetline(char line[ ], int max);

gramas nuevos . Si la funcin toma argumentos, declrelos; si no los toma, use


void.
Dado ato!, propiamente declarado, podemos escribir atoi (convierte una cadena a int) en trminos de l:
/* atoi: convierte la cadena s a entero usando atof */
int atoi(char si ])
{
double alo!(char s[ ]);

return (inl) atol(s);


sum = O;
while (getline(line, MAXLINE) > O)
printf("\t%g\n", suro + = ato!(line));
retum O;

Ntese la estructura de las declaraciones y la proposicin return. El valor de la


expresin en
return expresin;

La declaracin
double sum, ato!(char

I ));

seala que sum es una variable double, y que ala! es una funcin que toma un
argumento char[ 1 y regresa un double.
La funcin ato! se debe declarar y definir consistentemente. Si ato! en s misma y la llamada a ella en main tienen tipos inconsistentes dentro del mismo archivo fuente, el error ser detectado por el compilador. Pero si (como es probable)
ato! fuera compilada separadamente, la falta de consistencia no se detectara, atol

Se Convierte al tipo de la funcin antes de que se tome el retum . Por lo tanto,


el valor de ato!, un double, se convierte automticamente a int cuando aparece
en este return, puesto que la funcin atoi regresa un int. Sin embargo, esta opera-

cin potencialmente descarta informacin, as que algunos compiladores lo previenen. El cas( esrablece explcitamente lo que la operacin intenta y suprime
las advertencias.
Ejercicio 4-2. Extienda atof para que maneje notacin cientfica de la forma
123.45e-6

82

FUNCIONES Y LA ESTRUCTURA DEL PROGRAMA

CAPITULO 4

donde un nmero de punto flotante puede ir seguido por e o E y opcionalmente


un exponente con signo. O

VARIABLES EXTERNAS

SECCIN 4.3

83

En notacin polaca inversa, cada operador sigue a sus operandos; una expresin infija como
(1 - 2) (4

+ 5)

se intrOduce como
12-45 +.

4.3

Variables externas

Un programa en C consta de un conjunto de objetos externos, que son va.


riables o funciones. El adjetivo "externo" se emplea en contraste con "interno",
que describe los argumentos y las variables definidas dentro de las funciones. Las
variables externas se definen fuera de cualquier funcin, y por lo tanto, estn po.
tencialmente disponibles para muchas funciones. Las funciones en s mismas son
siempre externas, puesto que C no permite definir funciones dentro de otras fun.
ciones. Por omisin, las variables y funciones externas tienen la propiedad de que
todas las referencias a ellas por el mismo nombre, incluso desde funciones compiladas separadamente, son referencias a la misma cosa. (El estndar llama a esta
propiedad ligado externo.) En este sentido, las variables externas son anlogas a
los bloques COMMON de Fortran o a las variables del bloque ms externo de
Pascal. Ms adelante veremos cmo definir variables y funciones externas que sean
visibles slo dentro de un archivo fuente.
Debido a que las variables externas son accesibles globalmente, proporcionan
una alternativa a los argumentos en funciones y a los valores de retorno para comunicar datos entre funciones. Cua lquier funcin puede tener acceso a variables
externas haciendo referencia a ellas solamente por su nombre, si ste ha sido
declarado de alguna manera.
Si un gran nmero de variables se debe compartir entre funciones, las variables externas son ms covenientes y eficientes que las largas listas de argumentos. Sin embargo, como se seal en el captulo 1, este razonamiento se deber
aplicar con precaucin, pues puede tener un efecto negativo sobre la estructura
del programa y dar lugar a programas con demasiadas conexiones de dalOs entre
funciones.
Las variables externas son tambin tiles debido a su mayor alcance y tiempo
de vida. Las variables automticas son internas a una funcin y su existencia se
inicia cuando se entra a la funcin y desaparecen cuando sta se abandona. por
otro lado, las variables externas son permanentes, de modo que retienen sus valores de la invocacin de una funcin a la sigui<:l1te. As, si dos funciones deben
compartir algun0s datos, aun si ninguna llama a la otra, con frecuencia es ms
conveniente que los datos compartidos se mantengan en variables externas, en lugar de que sean pasados como argumentos de entrada y salida.
Examinemos ms a fondo este tema con un ejemplo ms amplio. El problema
es escribir el programa de una calculadora que provea los operadores +, -, y
/. Por ser ms fcil su implantacin, la calculadora utilizar notacin polaca inversa en lugar de infija. (La polaca inversa es utilizada por algunas calculadoras
de bolsillo, y en lenguajes como Forth y Postscript.)

LOS parntesis no son necesarios; la notacin no es ambigua mientras sepamos


cuntoS operandos espera cada operador.
La implantacin es simple. Cada operando se introduce en una pila o
srack; cuando un operador llega, el nmero correcto de operandos (dos para
operadores binarios) son extrados, se aplica el operador y el resultado se regresa
a la pila. En el ejemplo anterior, se introducen 1 y 2, despus se reemplazan por
su diferencia, -1. En seguida se introducen 4 y 5 Y luego se reemplazan por su
suma, 9. El producto de -1 y 9, que es -9, los reemplaza en la pila. El valor que
se encuentra en el tope de la pila se extrae e imprime cuando se encuentra el fin
de la lnea de entrada.
La estructura del programa es as un ciclo que realiza las operaciones adecuadas sobre cada operador y operando que aparece:

while (siguiente operador u operando no es fin de archivo)


if (nmero)
introducirlo

else if (operador)
extraer operandos
hacer operaciones
introducir el resultado
else if (nueva linea)
extrae e imprime el tope de la pila

else

error

Las operacioncs de introducir (Pllsh) y extraer dc una pila (pop) son sencillas,
pero cuando se les agrega deteccin y recuperacin de errores, son suficientemenle largas como para que sea mejor ponerlas en funciones separadas en lugar del
Cdigo a lo largo de todo el programa. Adems, debe existir una funcin separada para buscar el siguiente operador u operando.
. La principal decisin de diseo que an no se ha cxplicado es dnde est la
PIla, esto es, cules rutinas tienen acceso a ella directamente. Una posibilidad es
mantenerla en main, y pasar la pila y la posicin actual a las rutinas que introducen y extraen elementos. Pero main no necesita saber acerca de las variables que
~OntrOlan a la pila; slo efecta operaciones de introducir y extraer. As, hemos
b~Cldldo almacenar la pila y su informacin asociada en variables externas accesie\a las ~unciones push y pop, pero no a main.
m faduclr este bosquejo a cdigo es suficientemente fcil. Si por ahora pensaOs qUe el programa existe en un archivo fuente, se ver as:

84

FUNCIONES Y LA ESTRUCTURA DEL PROGRAM A

CAPITULO 4

SECCION 4.3

VARIABLES EXTERNAS

85

case '.':

#includes
#defines

push(pop( ) pop( ));


break;
case '-':

declaracin de funciones para man

op2 = pop( );
push(pop( ) - op2);

main( ) { ... }

break;
case '/':

variables externas para push y pop

op2 = pop( );
if (op2 ! = 0.0)
push(pop( ) I op2);

void push( double f) { .. . }


double pop(void) { ... }

else

int getop(char si [) { .. . }

printf("error: divisor cero\n");


break;
case '\n':

rutinas llamadas por getop

printf("\t%.8g\n", pop( ));

Ms adelante se ver cmo esto se puede dividir entre dos o ms archivos fuente.
La funcin main es un ciclo que contiene un gran switch sobre el tipo de operador y operando; ste es un uso del switch ms tpico que el mostrado en la seccin 3.4.
#include <stdio.h>
#include <math.h>

l para atof( ) 1

#define MAXOP 100


#define NUMBER 'O'

/. mx tamao de operando u operador ./


/. seal de que un nmero se encontr ./

int getop(char I [);


void push(double);
double pop(void);

break;

defau1t:
printf("error: comando desconocido %s\n", s);
break;

return O;

Puesto que + y son operadores conmutativos, el orden en el que se combinan


los operandos extrados es irrelevante, pero para - y / deben distinguirse los operandos izquierdo y derecho. En
push(pop( ) - pop( ));

/. calculadora polaca inversa ./

main( )
{
int type;

double op2;
char sIMAXOP];
while type = getop(s)) ! = EOF) {
switch (type) {
case NUMBER:
push( atof(s));
break;
case '+':

push(pop( ) + pop( ));


break;

l INCORRECTO ./

no se define el orden en el que se evalan las dos llamadas de popo Para garantizar el orden correcto, es necesario extraer el primer valor en una variable temporal, Como se hizo en main.
#define MAXVAL 100
int sp

= O;

double valIMAXVAL];
l. push:

/ . mxima profundidad de la pila val ./


/. siguiente posicin libre en la pila ./
/. valores de la pila . /

introduce f a la pila ./

void push( double f)


{
if (sp < MAXV AL)
vallsp+ + 1 = f;

86

CAPITULO 4

FUNCIONES Y LA ESTRUCTURA DEL PROGRAMA

VARIABLES EXTERNAS

SECCION 4.3

87

s[i] = '\0';
if (e ! =EOF)
unqeteh(e);
return NUMBER;

else
printf("erro r: pila llena, no puede efectuar push %g/n", f);

/. pop: extrae y regresa el valor superior de la pila ./

Qu son gelch y ungetch? Por lo comn se da el caso de que un programa

double pop( void)


{
if (sp > O)
returo val[--sp];
else {
printf("error: pila vaca\n");
retum 0.0;

puede determinar si ha leido suficiente de la entrada hasfa que ha ledo demasiado. Un ejemplo es reunir los caracteres que forman un nmero: hasta que
se vea el primer no-dgito, el nmero no est completo. Pero entonces el progranO

ma ha leido un carcter de ms, para el cual no est preparado.


El problema podra ser resuelto si fuera posible "desleer" el carcter no deseado. Entonces, cada vez que el programa lea un carcter de ms, podra regresar-

Una variable es externa si se encuentra definida fuera de cualquier funcin.


As, la pila y el ndice de la pila que deben ser compartidos por push y por pop
se definen fuera de estas funciones. Pero main en s misma no hace referencia
a la pila o a la posicin de la pila -la representacin puede estar oculta.
Pasemos ahora a la implantacin de galop, la funcin que toma el siguiente
operador u operando. La tarea es fcil. Ignorar blancos y tabuladores. Si el siguiente carcter no es un dgito o punto decimal, regresarlo. De otra manera, reu-

nir una cadena de dgitos (que pueda incluir un punto decimal), y regresar
NUMBER, la seal de que ha sido reunido un nmero .

lo a la cntrada. as que el resto del cdigo se podra comportar como si nunca se


hubiese leido. Afortunadamente. es fcil simular el regreso de un cara<.:ter, escribiendo un par de fun<.:iones cooperativas, getch cntrega el siguierHe caracter de la
entrada que va a ser considerado; ungetch reintegra el caracter devuelto a
la entrada. de modo que llamadas po steriores a getch lo regresarn antes de leer
algo nuevo de la entrada.
Cmo trabajan juntas es sencillo, ungetch coloca el carcter regresado

en un buffer compartido -un arreglo de caracteres . getch lee del buffer si hay
algo all y llama a gelchar si el buffer est vaco. Tambin debe existir una variable ndice que registre la posicin del carcter actual en el buffer temporal.
Puesto que el buffer y el ndice son compartidos por getch y ungelch y deben
retener . . w. valore~ entre llamadas, deben ser externos a ambas rutilla~. As. pode-

mos escribir gelch, unqetch y sus variables compartidas como:

#include < etype.h >

int qeteh(void);
void unqeleh(inl) ;

#define BUFSIZE 100

/. getop : obtiene el siguiente operador u operando numrico ./


int qetop(ehar s[ ])
{
int i, e;

ehar buf[BUFSIZE];
nt bufp = O;

while ((s [O]

e = qeteh( ))

" 1: e

int getch(void) /. obtiene un (posiblemente ya regresado) carcter - /

relurn (bufp > O) ? buf[--bufp] : qetehar ( );

'\1')

s[l] = '\0';
il (!isdiqit(e) && e ! = ':)
/ . no es un nmero . /
return e;
i = O;
if (isdigit(e))
/. rene la parte entera ./
while (isdiqil(s[ + + i] = e = qeleh( )))

;- buffer para unqeteh -;


/ . siguiente posicin libre en buf . /

void ungeteh(int e) /. regresa carcter a la entrada . /

il (bufp > = BUFSIZE)


printf("ungetch: demasiados earaeteres\n");
else
buf[bufp + +] = e;

}
if (e= ='.') / . rene la parte fraccionaria . /
while (isdiqit(s[ + + i] = e = qeteh( )))

La bibl ioteca estndar incluye una funcin ungetc que proporciona el regreso de
Un Glrlter; e~lo:-.e ver en el captulo 7. Se ha utilil.ado un arreglo para lo que se
regresa a la entrada, en lugar de un carcter sencillo, para dar una idea ms general.

88

FUNCIONES Y LA ESTRUCTURA DEL PROGRAMA

CAPITU LO 4

Ejercicio 4-3. Dada la estructura basica, es fcil extender la calcu ladora. Agre-

gue el operador mdulo ("70) y consideraciones para nmeros negativos. O


Ejercicio 4-4. Agregue rdenes para imprimir el elemento al tope de la pila si n sa-

carlo de ella, para duplicarlo y para intercambiar los dos elementos del tope.
Agregue una orden para limpiar la pila. O
Ejercicio 4-5. Agregue acceso a funciones de biblioteca como sin, exp y pow.
Consulte < math.h > en el apndice B, seccin 4. O
Ejercicio 4-6. Agregue rdenes para mani pular variables. (Es fcil proporcionar veintisis variables con nombres de una letra.) Aada una varia ble para el va-

lor impreso ms reciente. O


Ejercicio 4-7. Escriba una rutina ungets(s) que regresa a la entrada una cadena
completa. Debe ungets conocer acerca de bul y bulp, o slo debe usar ungetch?

o
Ejercicio 4-8. Suponga que nunca existir ms de un carcter de regreso.
Modifique getch y ungetch de acuerdo con eso. O
Ejercicio 4-9. Nuestros getch y ungetch no manejan correctamente un EOF que
se regresa. Decida cules deben ser sus propiedades si se regresa un EOF, y despus realice su diseo. O
Ejercicio 4-10. Una organizacin alternativa emplea gelline para leer una lnea
completa de entrada; esto hace innecesarios a getch y a ungetch. Corrija la calculadora para que use este planteamiento. O

SECC ION 4.4

REGLAS DE ALCANCE

89

(OI1\enga separarla, pero es una excclelHc ilustracin de los conceptos que surgen
en programas mayores.
El alcance de un nombre es la parte del programa dentro del cual se puede

usar el nombre . Para una variable automtica declarada al principio de una funcin, el alcance es la funcin dentro de la cual est declarado el nombre. Las variables locales con el mismo nombre que estn en fun cione~ diferentes no tienen
relacin. Lo mismo es vlido rara los parmetros de una funcin, que en efecto
son variables locales.

El alcance de una variable o funcin externa abarca desde el punto en que se


declara hasta el fin del archivo que se est compilando. Por ejemplo, si main, sp,
val, push, y pop estn definidas en un archivo, en el orden expuesto anteriormente, esto es,
main( ) {... }

int sp = O;
double val[MAXVAL];

void push(double f) { ...


double pop(void) { ... }

entonces las variables sp y val se pueden utilizar en push y pop simplemente nombrndolas; no se necesita ninguna otra declaracin. Pero estos nombres no son
visibles en main, ni push ni popo
Por otro Jado, si se va a hacer referencia a una variable externa antes de su

definicin, o si est definida en un archivo fuente diferente al que se est utilizando, entonces es obligatoria una declaracin externo
Es importante distinguir entre la declaracin de una variable externa y su definicin. Una declaracin expone las propiedades de una variable (principalmente

4.4

Reglas de alcance

Las funciones y variables externas que constituyen un programa en C no necesitan ser compiladas a la vez; el texto fuente del programa se puede tener en varios
archivos y se pueden cargar rutinas previamente compiladas de biblioteca. Entre
las preguntas de inters estn
Cmo se escriben las declaraciones de modo que las variables sean declaradas
adecuadamente durante la compilacin?
Cmo se arreglan las declaraciones de modo que todas las piezas se conecten
adecuadamente cuando se carga el programa?
Cmo se organizan las declaraciones de modo que slo haya una copia?
Cmo se inicializan las variables externas?
Discutamos estos temas reorganizando el programa de la calculadora en varioS
archivos. En terminos practicos, la calculadora es demasiado pequea para que

su tipo); una definicin tambin provoca que reserve un espacio para almacenamiento. Si las lneas
int sp;

double val[MAXV AL];

aparecen fuera de cualquier funcin, definen las variables externas sp y val, reservan un espacio para almacenamiento y tambin sirven como declaracin para
el resto de ese archivo fuente. Por otro lado, las lneas
extem in sp;

extem double val[ ];

declaran para el resto del archivo que sp es un int y que val es un arreglo double
(cuYO tamao se determina en algn otro lugar). pero 110 crean las variables
nI les reservan espacio.
Slo debe existir una definicin de una variable externa entre todos los archivos que forman un programa fuente; otros archivos pueden contener declaraciones

90

FUNCIONES Y LA ESTRUCTURA DEL PROGRAMA

CAPITULO 4

VARIABLE ESTATICAS

SECCION 4.6

extem para tener acceso a ellas. (Tambin puede haber declaraciones extem en
el archivo que contiene la definicin.) Los tamaos de los arreglos deben ser especificados con la definicin, pero es optativo hacerlo en una declaracin extern o
La inicializacin de una variable externa slo va con su definicin.
Aunque no es una organizacin idnea para este programa, las funciones
push y pop pueden definirse en un archivo, y las variables val y sp podran ser
definidas e inicializadas en otro. Entonces se necesitaran las siguientes definicio_
nes y declaraciones para enlazarlas:
En el archivo 1:

#define NUMBER 'O'


void push(double);
double pop(vOid);
inl getop(char I J);
inl getch(void);
void ungelch(inl);

#inelude <sldio.h>

#inelude <sldio.h>

#inelude <math.h>

#inelude <dype.h>

#indude "calc.h"

#inelude "cale.h"

#inelude "calc.h "

#define MAXVAL 100

#doline MAXOP 100

gelop() {

main ()

slack.e:

gelop.c:

#inelude <stdio. h>

void push(double f) { ...


double pop(void) { ...

calc.h

main,c:

extern ini sp;


exlem double val! J

91

mI sp

O;

double vaI IMAXVAL);

void push(double) {

En el archivo 2:
getch.c:

int sp = O
double val!MAXVAL];

#deline BUFSIZE 100


chal" buJIBUFSIZE);

Debido a que las declaraciones exlem que se encuentran en el archivo 1 estn situadas antes y afuera de las definiciones de funciones, se aplican a todas las funciones; un conjunto de declaraciones basta para todo el archivo l. Esta misma
organizacin tambin sera necesaria si las definiciones de sp y val se encontraran
despus de su uso en un archivo.

4.5

double pop(void) {

#melude <stdio.h>

inl bufp

-----------

= O;

inl gotch(void) {

void ungetch(int) {

Archivo de encabezamiento ~de!J

Consideremos ahora la divisin del programa de la calculadora en varios archivos fuente, como podra ser si cada uno de los componentes fuera sustancialmente mayor. La funcin main ir dentro de un archivo, al que llamaremos
main.c; push, pop y sus variables van dentro de un segundo archivo, stack.c;
gelop va en un tercero, gelop.c . Finalmente, gelch y ungelch van dentro de un
cuarto archivo, gelch.c; las separaremos de las otras debido a que podran venir
de una biblioteca compilada separadamente en un programa realista.
Hay algo ms de qu preocuparse -las definiciones y declaraciones compartidas entre archivos. Debemos central izarlas hasta donde sea posible, de modo que
haya slo una copia por mantener mientras se desarrolla el programa. En consecuencia, situaremos esre material comn en un archivo tipo lIeade,., cale.h, que se
incluir donde sea necesario. (La linea # include se describe en la seccin 4.11.)
Enfonces, el programa resultante se ve como sigue:

Existe un compromiso entre el deseo de que cada archivo slo tenga acceso
a la informacin que necesita para su trabajo y la realidad prctica de que es ms
difkil mantener ms archivos tipo header. Hasta un tamai'lo moderado de prograrna, probablemente es mejor tencr un archivo de encabezamienro que contel1ga todo lo que ser compartido cntre las partes del programa; sta cs la dct.:isin
que tomamos aqu. Para un programa mucho ms gra nde. se necesita da ll1~ organizad n y ms archivos lipa Ileade/".

4.6

Variables estticas

. Las varia bles sp y val en slack.c, ylbuflyIbufplen getch.c, son para el uso
Pnvado de las funciones que estn en sus respectivos archivos fuente, y se supone

92

FUNCIONES Y LA ESTRUCTURA DEL PROGRAMA

CAPITULO 4

que nada ms tiene acceso a ellas. La declaracin static, aplicada a una variable
o funcin externa, limita el alcance de ese objeto al resto del archivo fuente que
se est compilando. As las variables static externas proporcionan una rorma de
ocultar nombres como huf y hufp en la combinacin getch-ungetch, que deben
ser externas para que puedan ser compartidas, aunque no deben ser visibles a los
usuarios de getch y ungetch.
El almacenamiento esttico se especifica al anteponer a la declaracin normal
la palabraestatic. Si las dos rutinas y las dos variables se compilan en un archivo, C0l110 en
statie ehar buf[BUFSIZE];
static int bufp = O;

l' buffer para ungeteh '1


/* eiguiente posicin libre en buf */

int geteh(void) { ... }


void ungeteh(int e) { ... }

entonces ninguna otra rutina ser capaz de tener acceso a huf ni a b ufp, yesos
nombres no entrarn en conflicto con los mismos nombres que estn en otros archivos del mismo programa. De la misma manera, las variables quepush y pop
utilizan para la manipulacin de la pila se pueden ocultar, declarando sp yval
como static.
La declaracin static externa se usa con ms frecuencia en variables, pero
tambin se puede aplicar a funciones . Normalmente, los nombres de funciones
son globales, visibles a cualquier parte del programa completo. Sin embargo, si
una funcin se declara como static, su nombre es invisible fuera del archivo en
el que est declarada .
La declaracin stalic tambin puede aplicarse a variables internas. Las variables internas static son locales a una funcin en particular, tal como lo son las
variables automticas, pero a diferencia de ellas, mantienen su existencia en lugar
de ir y venir cada vez que se activa la funcin. Eso significa que las variables internas static proporcionan almacenamiento privado y permanente dentro de una
runcin.

Ejercicio 4-11. Modifique getop dc modo que no necesile utilizar ungelch . Sugerencia: emplee una variable static interna. O
4.7

ESTRUCTURA DE BLOQUE

SECCION 4.8

93

etctera. La declaracin Iregisterl slo se puede aplicar a variables automticas y


a los parmetros formales de una funcin. En este ltimo caso, aparece como
f(register unsigned m, register long n)
register int i;

En la prctica existen restricciones en las variables tipo registro, que reflejan la


realidad del equipo donde se opera. Slo algunas variables de cada funcin se
pueden mantener en registros, y slo se permiten ciertos tipos. Sin embargo, el
exceso de declaraciones tipo registro no provoca daos, puesto que la palabra re
gister se ignora en las declaraciones excesivas o no permitidas. Adems, no es posible tomar la direccin de una variable de tipo registro (tema que se tratar en el
captulo 5), sin importar si la variable est o no realmente en un registro. Las restricciones especficas sobre el nmero y tipo de estas variables varan de una
mquina a otra.

4.8

Estructura de bloques

e no es un lenguaje estructurado en bloques en el sentido de Pascal o lenguajes


semejantes, puesto que las funciones no se pueden definir dentro de otras funciones. Por otra parte, las variables se pueden definir en una modalidad de estructura
de bloques dentro de una funcin. Las declaraciones de variables (incluyendo la
inicializacin) pueden seguir a la llave izquierda que indica cualquier proposicin compuesta, no slo la que inicia a una funcin. Las variables declaradas de
esta manera ocultan cualquier nombre idntico de variables en bloques externos,
y permanecen hasta que se encuentra la llave derecha que corresponde con la inicial. Por ejemplo, en
if (n > O) {
int i; /* declara una nueva i */

for (i = O; i

< n; i + +)

Variables tipo registro

Una declaracin register indica al compilador que la variable en cuestin se


emplear intensamente . La idea es que las variables register se coloquen en registros de la mquina, lo que puede dar como resultado programas ms pequeOS
y rpidos. Pero los compiladores tienen la libertad de ignorar esta sugerencia.
La declaracin register se ve as:
register int Xi
register char e;

el alcance de la variable i es la rama "verdadera" del if; esta[I]no tiene nada que
ver COn alguna i fuera del bloque. Una variable automtica declarada e inicializada en un bloque se inicializa cada vez que se entra al bloque. Una variable
static se inicializa slo la primera vez que se entra al bloque .
Las va riables automticas. inclu yendo los parmetros rormales, tambin esCOnden a las variables y fun ciones externas del mismo nombre. Dada\ las declaraciones

FUNCIONES Y LA ESTRUCTURA DEL PROGRAMA

94

CAPITULO 4

SECCION 4.10

RECURSIVIDAD

95

en lugar de

int x;
int y;

int low, high, midi

f (double x)
{
double y;

low

O;

high =

n -

1;

En efecto, las inicializaciones de variables automticas son slo abreviaturas de

en la funcin f, las ocurrencias de x se refieren al parmetro, que es un double;


fuera de f, se refieren al inl externo. Lo mismo es vlido para la variable y.

Por estilo, es mejor evitar nombres de variables que coinciden con nombres
de un alcance exterior; la potencialidad de confusin y error es muy grande.

proposiciones de asignacin. La eleccin es en gran medida cuestin de gusto.


NosotrOS hemos empleado generalmente asignaciones explcitas, debido a que los
inicializadores en las declaraciones son difciles de ver y lejanos del lugar de uso.
Un arreglo puede ser inicializado al seguir su declaracin con una lista de inicializadores encerrados entre llaves y separados por comas. Por ejemplo, para
inicializar un arreglo das con el nmero de das de cada mes:
inl das

4.9

Inicializacin

La incializacin ya se ha mencionado muchas veces, pero siempre alrededor


de algn otro tema. Esta seccin resume algunas de las reglas, ahora que ya se
han discutido las diferentes categoras de almacenamiento.
En ausencia de una inicializacin explcita, se garantiza que las variables externas y estticas se inicializarn en cero; las variables automticas y tipo reg istro
tienen valores iniciales indefinidos (esto es, basura).

Las variables escalares se pueden inicializar cuando se definen, siguiendo al


nombre con un signo de igual y una expresin :
int x = 1;
char apstrofo = '\";
long da = 1000L 60L 60L 24L;

L int

n)

= { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 );

Cuando se omite el tamao de un arreglo, el compilador calcular la longitud


contando los inicializadores, los cuales son 12 en este caso.
Si existen menos inicializadores para un arreglo que los del tamao especificado, los otros sern cero para variables externas o estticas, pero basura para automticas. Es un error tener demasiados inicializadores. No hay forma de indicar
la repeticin de un inicializador, ni de inicializar un elemento que eSl a la mitad

de un arreglo sin proporcionar tambin todos los valores precedentes.


Los arreglos de caracteres son un caso especial de inicializacin; se puede utili-

zar una cadena en lugar de la notacin de llaves y comas :


char patrn[ ]

= "ouId";

es ms corto pero equivalente a


l milisegundoslda '1

Para variables externas y estticas, el inicializador debe ser una expresin constante; la inicializacin se realiza una vez, conceptualmente antes de que el programa inicie su ejecucin. Para variables automticas y tipo registro, se hace cada
vez que se entra a la funcin o bloque.
Para variables automticas y tipo registro, el inicializador no se limita a una
constan te: puede ser cualquier expresin que corllenga valores previamente defi nidos, incluso llamadas a funciones. Por ejemplo, la inicializacin del programa
de bsqueda binaria de la seccin 3.3 podria escribirse como
int binsearch(int x, int v [
{
int low = O;
int high = n - 1;
int mid;

[J

char patrn[]

= { 'o',

'u', '1', 'd', ,\O'}i

En este caso, el tamao del arreglo es cinco (cuatro caracteres ms el terminador


'\0').

4.10 Recursividad
Las funciones de e pueden emplearse recursivamente; esto es, una funcin
Puede llamarse a s misma ya sea directa o indirectamente. Considere la impresin
de Un nmero como una cadena de caracteres. Como ya se mencion anteriormente, los dgitos se generan en orden incorrecto: los dgitos de orden inferior
estn disponibles antes de los dgitos de orden superior, pero se deben imprimir
en el orden invertido.
Existen dos soluciones a este problema . Una es almacenar los dgitos en un
arreglo lal como se generan, y despus imprimirlos en orden inverso, corno se hizo

96

FUNCIONES Y LA ESTRUCTURA DEL PROGRAMA

CAPITULO 4

SECCION 4.11

EL PREPROCESADOR DE

con itoa en la seccin 3.6. La alternativa es una solucin recursiva, en la que

swap(v, left, (left + right)/2);

printd primero se llama a s misma para tratar co n los primero":. dgitos, y des pues

lasl = left;
/- a viO] -/
lor (i = 1ell+ 1; 1 < = right; i+ +)
/- particin -/
il (vii] < vllell])

imprime el dgito del final. De nuevo, esta versin puede fallar con el nmero negativo ms grande.

swap(v, + + last, i);


swap(v, 1eft, last);

#include < stdio.h >


j ., printd: imprime n en decimal ., /
void printd(int n)
{
if (n < O) {
putchar('-');

/ * mueve el elemento de particin

j"

regresa el elemento de particin

97

*j

*j

qsorl(v, left, lasl-l);


qsort(v, lasl + 1, righl);
Pasamos la operacin de intercambio a una funcin separada swap, puesto que
ocurre tres veces en qsort.
j * swap: intercambia v[il y v [j] */
void swap(int vf J, int i, int j)

-n;

il (n / 10);

int temp;

prinld(n / 10);
pulchar(n % 10 + 'O');

temp = vii];
vii] = viii;
viii = lemp;

Cuando una funcin se llama a s misma recursivamente, cada invocacin obtiene


un conjunto nuevo de todas las variab les automticas, independiente de l co njun-

to previo. As, en printd(123) el primer printd recibe el argumento n = 123. Pasa


12 al segundo printd, que a su vez pasa 1 a un tercero . El printd del tercer nivel
imprime 1, despus regresa al segundo nivel. Ese printd imprime 2, despus regresa al primer nivel. Ese imprime 3 y termina.
Otro buen ejemplo de recursividad es quicksort, un algoritmo de ordenamiento desarrollado en 1962 por C. A. R. Hoare. Dado un arreglo, un elemento se

selecciona y los otros se particionan en dos subconjuntos -aquellos menores que


el elemento de la particin y aquellos mayores o iguales a l. El mismo proceso
se aplica despus recursivamente a los dos subconjuntos. Cuando un subconjunto
tiene menos de dos elementos no necesita ya de ningn ordenamielllo; esto detie-

ne la recursividad .
Nuestra versin de quicksorl no es la ms rpida posible, pero es una de las
ms simples. Empleamos el elemento intermedio de cada subarreglo para particionar.

La biblioteca estndar incluye una versin de qsort que puede ordenar objetos
de cualquier tipo.
La recursividad no puede proporcionar un ahorro en almacenamiento, puesto

que en algn lugar se debe mantener una pila de los valores procesados. Ni ser
mas rapida. Pero el cdigo recursivo es ms compacto y frecuentemente mucho
ms fcil de escribir y de entender que su equivalente no recursivo. La recursividad
es especialmente conveniente para estructuras de datos definidas en forma recur-

siva, como rboles; veremos un agradable ejemplo en la seccin 6.5.


Ejercicio 4-12. Adapte las ideas delprintdlal escribir la versin recursiva del programa~ esto es, convierta un entero en una cadena llamando a una rUlina re-

cursiva. O

Ejercicio 4-13. Escriba una versin recursiva de la funcin reverse(s), que invierte la cadena s en su lugar. o

/., qsort: ordena v[left] ... v[right] en orden ascendente ., j

void qsort(inl viI. inl left, inl righl)


{

4.11

int i, last;

void swap(int vi 1. int i, int j);


il (left >
return;

righl)

j.,

no hace nada si el arreglo contiene ., /

1" menos de dos elementos .,/

El prep rocesador de

C proporciona ciertas facilidades de lenguaje por medio de un preprocesador,


que Conceptualmente es un primer paso separado en la compilacin. Los dos elementos que se usan con ms frecuencia son #include, para incluir el contenido
de Un archivo durante la compilacin, y #define, para reemplazar un smbolo por

98

FUNCIONES Y LA ESTRUCTURA DEL PROGRAMA

CAPITULO 4

una secuencia arbitraria de caracteres. Otras caractersticas que se describen en


esta seccin incluyen compilacin condicional y macros con argumentos.

4.11.1 Inclusin de archivos

La inclusin de archivos facilita el manejo de grupos de #define y declaracio_


nes (entre otras cosas). Cualquier linea fuente de la forma

EL PREPROCESADOR DE C

SECCION 4.11

99

de cada lnea que va a continuar. El alcance de un nombre definido con #define


va desde su punto de definicin hasta el fin del archivo fuente que se compila.
Una definicin puede emplear definiciones previas. Las substituciones se realizan
slo para elementos, y no sucede dentro de cadenas delimitadas por comillas.

por ejemplo, si AFIRMA es un nombre definido, no habr Subslitucin en printf


("AFIRMA") ni en AFIRMATIVO.
Cualquier nombre puede definirse con cualquier texto de reemplazo . Por
ejemplo .
#define porsiempre for (;;) / * ciclo infinito */

#include "nombre"

define una nueva palabra, porsiempre, para un ciclo infinito.


Tambin es posible definir macros con argumentos, para que el texto de reemplazo pueda ser diferente para diferenles llamadas de la macro. Como un ejem-

o
#include <nombre>

plo, defina una macro llamada max;

se reemplaza por el contenido del archivo nombre. Si el nombre se encierra entre


comillas, la bsqueda del archivo comienza normalmente donde se encontr el programa fuente; si no se encuentra all, o si el nombre se delimita por < y > , la
bsqueda sigue una ruta predefinida por la implantacin para encontrar el archivo.

Un archivo incluido puede contener lneas #include.


Frecuentemente existen varias lneas #include al principio de un archivo
fuente, para incluir proposiciones #define y declaraciones extern comunes , o

para tener acceso a la declaracin del prototipo de una funcin de biblioteca desde 1If!(fders como < stdio.h >. (El:.trictamentc hablando, no ncccsitan ser arc hi\'o~; los dcalle\ de cmo se tiene accc<.,o a
cin.)

10\

lIeuders dependen de la implan ta-

#include es la mejor manera de enlazar las declaraciones para un programa


grande. Garantiza que todos los archivos fuente se suplirn con las mismas definiciones y declaraciones de variables, y as elimina un tipo de error particularmente desagradable. Por supuesto, cuando se cambia un archivo include, se deben recompilar lodos los archivos que dependen de l.

#define max (A, B) ((A) > (B) ? (A) : (B))


Aunque aparenta ser una llamada a funcin, el uso de max se expande a cdigo.

Cada ocurrencia dc un parmelro formal (A o B) ser reemplazada por el argumento real correspondiente. As, la lnea
x

max(p+q, r+s};

ser reemplazada por la lnea


x = ((p+q)

> (r+s) ? (p+q) : (r+s));

En tanto que los argumentos se traten consistentemente, esta macro servir para
cualquier tipo de datos; no hay necesidad de diferentes tipos de max para diferentes tipos de datos, como la habra con las funciones.
Si examina la expansin de max, notar algunos riesgos. Las expresiones se
evalan dos veces; esto es malo si involucra efectos colaterales como operadores
incrementales o de entrada y salida . Por ejemplo,
max(i + +, j + + )

/ . INCORRECTO . /

incrementar el valor ms grande dos vece~. Tambin debe tenerse algn cuidado Con los parntesis, para asegurar que se preserva el orden de evaluacin; considere qu pasa cuando la macro
4.11.2 Substitucin de macros

Una definicin tiene la forma


#define

nombre

texto de reemplazo

Pide la subs ti tucin de una macro del tipo ms sencillo ~Ias siguientes ocurrencias de nombre ser n substi tuida s por el texto de reempla::.o. El nombre en un
#define tiene la mi sma forma que un nombre de variable; el texto de reemp lazo
es arbitrario. Normalmente el texto de reemplazo es el resto de la lnea, pero una
definicin extensa puede cont inuarse en varia;; lneas, colocando una \SJ al fina l

#define cuadrado(x}

:x * x

/ . INCORRECTO . /

Se invoca como cuadrado (z + 1).


Sin embargo, las macros son valiosas. Un ejemplo prctico viene de
< stdio.h >, donde gelchar y pulchar se definen frecuentemente como macros
Para evitar el exceso de tiempo de ejecucin de una llamada a funcin por cada
carcter procesado. Las funciones en <ctype.h> tambin se realizan generalmente como macros.

Los nombres se pueden hacer indefinidos con #undef, para asegurar que una
rutina es realmente una funcin, no una macro:

100

CAPITULO 4

FUNCIONES Y LA ESTRUCTURA DEL PROGRAMA

#undef getchar

EL PREPROCESADOR DE

SECCJON 4. [1

101

La lnea #if evala una expresin constante entera (que no puede incluir sizeof, cas{S o constantes enum). Si la expresin es diferente de cero, se incluyen las

int getchar (void) { ... }

parmetro reemplazado por el argumento real. Esto puede combinarse con Con_

siguientes lneas hasta un #endif, #elif o #else. (La proposicin de procesador


#elif es como else if). La expresin defined(nombre) en un #if es 1 si el nombre
se ha definido, y O de otra manera.
por ejemplo, para asegurarse de que el contenido de un archivo hdr.h se in-

catenacin de cadenas para hacer, por ejemplo, una macro de impresin para de~
puracin:

sta:

Los parmetros formales no se reemplazan dentro de cadenas entre comillas

Sin embargo, si un nombre de parmetro est precedido por un # en el text


de reemplazo, la combinacin se expandir en una cadena entre comillas, con el

#define

dprint(expr)

printf(#expr"

cluya slo una vez, el contenido del archivo se del imita con una condicional como
#if 'defined (HDR)
#define HDR

= %g\n", expr)

Cuando se invoca, como en


/* el contenido de hdr.h va aqu */

dprint(x/y);

la macro se expande en
printf("x/y"

11

#endif

= %g\n", x/y);
La primera inclusin de hd r .h define el nombre HDR; las siguientes inclusiones
encontrarn definido al nombre y pasarn hacia el #endif. Un estilo semejante

y las cadenas se concatenan, as el efecto es


printf("x/y = %g\n", x/y);

Dentro del argumento real, cada[J se reemplaza por

IS'J y cada l'Il por ~ , as!

que el resultado es una constante de cadena legtima.

El operador ## del preprocesador proporciona una forma de concatenar aro


gumentos reales durante la expansin de una macro . Si un parmetro que est
en el texlO de reemplazo es adyacente a un ##, es reemplazado por el arg umento real, se eliminan el ## y los espacios en blanco que lo rodean, y el resultado
se rastrea de nuevo. Por ejemplo, la macro paste concaLena sus dos argumentos:
#define paste(front, back) front ## back

as, paste(nombre, 1) crea el token nombre!.


Las reglas para el uso anidado de ## son misteriosas; en el apndice A se pueden encontrar mayores detalles.
Ejercicio 4-14. Defina una macrolswap(t,x,y)1 que intercambie dos argumentOS
de tipo (La estructura de bloques ayudar.) O

rn

puede emplearse para evitar incluir archivos varias veces. Si este estilo se utiliza
en forma consistente, entonces cada header puede en s mismo incluir cualquier
otro del que dependa, sin que el usuario tenga que tratar con la interdependencia.
La siguiente secue ncia prueba el nombre SYSTEM para decidir cul versin
de un header inclui r:
#if SYSTEM = = SYSV
#define HDR "sysv.h"

#elif SYSTEM = = ESD


#define HDR "bsd.h"
#elif SYSTEM = = MSDOS
#define HDR "msdos.h"
#else

#define HDR "defaulLh"


#endif

#include HDR

Las lneas #ifdef e #ifndef son formas especializadas que prueban si un nombre est definido. El primer ejemplo de #if de ms arriba pudo haberse escrito
#ifndef HDR
#define HDR

4.11.3 Inclusin condicional

Es posible controlar el preprocesamiento mismo con proposiciones

condici~

nales que se evalan durante esa etapa. Esto proporciona una forma de inclU;e
cdigo selectivamente, dependiendo del valor de condiciones evaluadas duran
la compilacin.

/ * contenido de hdr.h va aqu */

#endif

CAPITULO 5

Apuntadores y arreglos

Un apuntador es una variable que contiene la direccin de una variable. Los


apuntadores se utilizan mucho en

e, en parte debido a que ellos son en ocasiones

la nica forma de expresar una operacin, yen parte debido a que por lo general
llevan un cd igo ms compacto y eficiente de lo que se puede obtener en otras

formas. Los apuntadores y los arreglos estn relacionados lllimameme; este


('aptulo tambin explora estas relaciones y muestra cmo explotarlas.

Los apuntadores se han puesto junto a la proposicin goto como una forma
maravillosa de crear programas ininteligibles. Esto es verdadero cuando se utilizan en forma descuidada, y es fcil crear apuntadores que sealen a algn lugar
inesperado. Sin embargo, con disciplina, los apuntadores pueden tambin emplearse para obtener claridad y simplicidad. Este es el aspecto que trataremos de ilustrar.

El principal cambio en ANSI e es hacer explcitas las reglas acerca de cmo


pueden manipularse los apuntadores, obligando a lo que los buenos programadores ya practican y lo que los buenos compiladores ya imponen. Adems, el tipo
void (apuntador a void) reemplaza a char como el tipo apropiado para un
apuntador genrico.

5.1

Apuntadores y direcciones
Empecemos con un dibujo simplificado de cmo se organiza la memoria. Una

mquina tpica tiene un arreglo de celdas de memoria numeradas o direccionadas

Consecutivamente, que pueden manipularse individualmente o en grupos contiguos. Una situacin comn es que cualquier byte puede ser un char, un par de cel-

~as

de Un byte pueden tratarse como un entero short, y cuatro bytes adyacentes


Orman un long. Un apuntador es un grupo de celdas (generalmente dos o cuatro)

que pueden mantener una direccin. As, si e es un char y p es un apuntador que


apunta a l, podra representarse la situacin de esta manera:
103

104

CAP ITULO 5

APUNTADORES Y ARIU::GLOS

P~

]!jIEN~[

tOS

LOS operadores unarios * y & se ligan ms estret-hamente que los operado res
tmticos; as, la asignacin

II!TII!III!III!II

APUNTADORES Y ARGU M ENTOS DE FUN C IONES

SECCION 5.2

atl

y = *ip

n.

+ 1

tOma aqucllo a lo que apullte p, le agrega 1, Y a~igna el resultado a y. mientras


El operador unario & da la direccin de un objeto, de modo que la Proposi.
cin.
p = &c;

asigna la direccin de e a la variable p, y se dice que p "apunta a" c. El Operador


& slo se aplica a objetos que estn en memoria: vari ables y elementos de arreglos.
No puede aplicarse a expresiones. constantes o variab les tipo registro.
El operador unario * es el operador de indireccin O desreJerenda; cuando se
aplica a un apuntador, da acceso al objeto al que seala el apuntador. Supngase
que x y y so n enteros e ip es un apntador a int. Esta secuencia art ificial
muestra como declarar un apuntador y como emplear & y
~t

x = 1, y = 2,

.II~;

int *ip;

/* ip es un apuntador a int */

ip = &x;
y = *ip;
*ip = O;

/ * ip ahora apunta a x */
/ * y es ah o ra 1 */
/ * x es ahora O */
/* ip ahora apunta a z[O] */

ip =

&.101;

que

*ip + = 1
incren1enla en UIl O aquello a que ip apunta, como lo hace

+ + *ip

y
(-ip)++

Los parntesis son necesarios en este ltimo ejemplo; sin ellos, la expresin incre~
mentara ip en luga r de a lo que apunta, debido a que los operadores linarias como
* y + + se asocia n de derecha a izquierda.
Por ltimo, puesto que los apuntadores son variables, se pueden emplear sin
desrl~/rellci(/lIIi(,I//O. PUl' \.'jctllplo . .'ti iq es otro apuntador a int,
iq = ip

copia el contenido de ip en iq; as, hace que iq apunte a lo que ip est apuntando.

Las declaraciones de x, y y son lo que hemos visto todo el tiempo . La declaracin del apuntador p,
int *ip;

funciona como mnemnico; di ce que la expresin *ip es un int. La sin taxis de la


dcclaracion para una variab le imita la sintaxis de expresiones en las que la va
riable puede aparecer. Este razonamienw se aplica tambin a la declaracin de
funciones. Por ejemplo,
double -dp, atof(char o);

indica que en una expresin 'dp y atol(s) tiene n valores de tipo double, y que el
argumento de atof es un apuntador a charo
Tambin se debe notar la implicacin que tiene el hecho de que un apuntador
est restringido a sealar a una clase particular de objeto: cada apuntado r seala
a un tipo especfico de datos. (Hay una excepcin: un "apuntador a void" se ern~
pica para mantener cualquier tipo de apuntador. pero en s mismo no puede ser
desreferellciadu. EstO se voher a tratar en la secc in 5.11.)
Si ip apunta al entero x, entonces *ip pued~ presentarse en cualquier contexto
donde x pueda hacerlo, asi que
*ip = "ip

10;

incrementa *ip en 10.

5.2

Apuntadores y argumentos de funciones

Puesto que e pasa los argu mentos de funciones por valor , no existe una forma
directa para que la funcin que se invoca altere una variab le de la funcin que la
llama. Por ejemplo, una rutina de ordenamiento podra intercambiar dos e lemen~
tos desorde nados con una funcin llamada swap. No es suficiente escribir
swap(a, b);

dond e la funcin swap est definida como


void ,wap(int x, inl y) / - INCORRECTO -/
{
int temp;
temp = x;
x = y;
y = temp ;

106

APUNTADORES Y ARREGLOS

CAPITULO S

Debido a la llamada por valor, swap no puede afectar los argumentos a y b que
estn en la rutina que la llam. La funcin anterior slo intercambia copias de a
y de b.
La forma de obtener los resultados que se desean es que el programa invoca.
dar pase apunladores a los valores que se cambiarn:

107

Una solucin es hacer que getint regrese el estado de fin de archivo como su
valor de funcin, usando un argumento apuntador para almacenar el emero convertido Y tenerlo en la funcin invocadora. Este esquema tambin es utilizado
por scanf, corno se ver en la seccin 7.4 .
El siguiente ciclo llena un arreglo con enteros por medio de llamadas a gelin!:

in! n, array[SIZEJ, getint(int .);

swap(&a, &b);
Puesto que el operador & produce la direccin de una variable, &a es un apunta_
dor a a. Dentro de la misma funcin swap, los parmetros se declaran para ser
apuntadores, y se tiene acceso a los operandos indirectamente a travs de ellos.
void swap(int px, int .py)

/. intercambia px y .py . /

int temp;
temp = px;

px
.py

APUNTADORES Y ARGUMENTOS DE FUNCIONES

SECCION 5. 2

.py;
temp;

for (n = O; n < SIZE && getint(&array[nJ) ! = EOF; n + +)


Cada llamada pone en array[n) el siguiente entero que se encuentra a la entrada
e incrementa n. Obsrvese que es esencial pasar la direccin de array[n] a getint.
De otra manera no hay forma de que Igetintlcomunique el entero convertido hacia
la funcin invocadora.
Esta versin de getint regresa EOF como fin de archivo, cero si la siguiente entrada no es un nmero. y un valor positivo si la entrada contiene un nmero vlido.

#include <ctype.h>

Grficamente;
int getch(void);
void ungeteh(int);
en el invocador:

a:D
b:D

/ . getint: obtiene el siguiente entero de la entrada y lo asigna a pn . /


int getint(int pn)

int e, sign;

while (isspace(c = getch()))

/. ignora espacios en blanco - /

en Iswapl:
ro

- - - ,

px:

py:

if (Iisdigit(c) && c ,= EOF && c!= '+' && c!= '-') {


/. no es un nmero ./
ungetch( e);
retum O;

sign = (e = = '-') ? -1 : 1;
il (c = = '+' :: c = = '-')

Los argumemos tipo apuntador permiten a una funcin tener acceso y cambiar objelOs que estn en la funcin que la llam. Como ejemplo, considere una funcin ge!in! que realiza una conversin de entrada en formato libre, desglosando
un flujo de caracteres en valores enteros, un entero por llamada. As, getin! tiene
que regresar el valor encontrado y tambin una seal de fin de archivo cuando ya
no hay ms que tomar. Esos valores tienen que regresarse por rutas separadas,
para que sin importar qu valor se emplea para EOF, tambin pueda ser el valor
de un entero de la entrada.

e = getch( );
for ('pn = O; isdigit(c); e = getch( ))
pn = 10 pn + (e - '0');
pn = sign;
if (e ! = EOF)

ungetch( e);
retum c;

108

APUNTADORES Y ARREGLOS

CAPITULO 5

A lo largo de getint, *pn se emplea como una variable int ordinaria . Tambin se
utiliz gelch y ungelch (descritas en la seccin 4.3) para que el carcter extra
que debe leerse puede regresar a la entrada.
Ejercicio 51. Como se escribi, getint trata a un + o un - no seguido por un
dgito como una representacin vlida de cero. Corrjala para que regrese tal ca.
rcter a la entrada. O

Ejercicio 5-2. Escriba gelfloal. la analoga de punto flotante de getint. Qu tipo


regresa gelfloal como su valor de funcin? O

APUNTADORES Y ARREGLOS

SECCION 5.3

109

Ahora la asignacin
x

pa

copiar el contenido de atO) en x.


Si pa apunta a un elemento en particular de un arreglo, entonces por definicin
pa + 1 apunta al siguiente elemento, pa + i apunta i elementos despus de pa, y
pa- i apunta i elementos antes. As. si pa apunta a atO).
-(pa + 1)
se refiere al contenido de a[l). pa + i es la direccin de a[i) y -(pa + iJ es el contenido de a[i).

5.3

p~\Pa7

Apuntadores y arreglos

En C existe una fuerte relacin entre apuntadores y arreglos. tan fuerte que
deben discutirse simultneamente. Cualquier operacin que pueda lograrse por
indexacin de un arreglo tambin puede realizarse con apuntadores. La versin Con
apuntadores ser por lo general ms rpida. pero. al menos para los no iniciados.
algo ms dificil de entender.
La declaracin
inl arlO];

define un arreglo a de tamao 10. esto es. un bloque de 10 objetos consecutivos


llamados a[0].a[I] ... a[9].

I [

a[C]CllIJ

I I
0[91

La notacin a[i) se refiere al i-simo elemento del arreglo. Si pa es un apuntador


a un entero, declarado como
int pa;

entonces la asignacin

pa = &a[O]'

./
hace que pa apunte al elemento cero de a; esto es. pa contiene la C:ireccin de atO).
,

pa:

~Ir'--l----r----,----,---,---,-,---,----.--,
a:

a[OI

I I I I
arO]

Lo anterior es verdadero sin importar el tipo o tamao de las variable ~


del arreglo a. El significado de "agregar 1 a un apuntador". y por extensin, toda
la aritmtica de apuntadores. es que pa + 1 apunta al siguiente objeto. y pa + i
apunta al i-simo objeto adelante de pa.
La correspondencia entre indexacin y aritmtica de apuntadores es muy estrecha. Por definicin. el valor de una variable o expresin de tipo arreglo es la direccin del elemento cero del arreglo. As. que despus de la asignacin

pa = &a[OJ;
pa y a tienen valores idnticos. Puesto que el nombre de un arreglo es un sinnimo
para la localidad del elemento inicial. la asignacin pa = &a[O) puede escribirse
tambin como
pa

a;

Ms sorprendente. al menos a primera vista, es el hecho de que una referencia


a a[i) tambin puede escribirse como -(a + iJ. Al evaluar a[i)' C la convierte inmediatamente a '(a + i~; las dos formas son equivalentes. Al aplicar el operador & a
ambas partes de esta equivalencia. se deriva que &a[i) ya + i tambin son idnticas:
a + i es la direccin del i-simo elemento delante de a. Por otra parte, si pa es un
apuntador. las expresiones pueden usarlo con un subndice; pa[i) es identico a
-(pa + iJ. En resumen. cualquier expresin de arreglo e ndice es equivalente a una
expresin escrita como un apuntador y un desplazamiento.
Existe una diferencia entre un nombre de arreglo y un apuntador. que debe tenerse en mente. Un apuntador es una variable. por esto pa =a y pa + + son legales. Pero un nombre de arreglo no es una variable; construcciones como a = pa
y a + + son ilegales.

110

CAPITULO 5

APUNTADORES Y ARREGLOS

Cuando un nombre de arreglo se pasa a una funcin, lo que se pasa es la

locali~

dad del elemento inicial. Dentro de la funcin que se llama, este argumento es una

/ .. strlen: regresa la longitud de la cadena s .. /


int strlen(char "s)
{
int n;
for (n = O; *s!= '\0'; s+ +)
n+ +;
returo n;

Puesto que s es un apuntador, es perfectamente legal incrementarlo; s+ + no tiene


efecto alguno sobre la cadena de caracteres de la funcin que llam a strlen, sino
que simplemente incrementa la copia privada del apuntador de strlen. Eso significa que llamadas como
strlen("hola, mundo");
strlen(array);

strlen(ptr);

/* constante de cadena */
/ - char array[lOOJ; -/
/ * char ptr; */

s funcionan.

Puesto que los parmetros formales en una definicin de funcin,


char s[ ];

y
char *s;

son equivalentes, preferimos el ltimo, porque indica ms explcitamente que el


parmetro es un apuntador. Cuando un nombre de arreglo se pasa a una funcin,
sta puede interpretar a su conveniencia que se ha manejado un arreglo o un apuntador, y manipularlo en consecuencia. Puede incluso emplear ambas notaciones si
ello lo hace apropiado y claro.
Es posible pasar parte de un arreglo a una funcin, pasando un apuntador al
inicio del subarreglo. Por ejemplo, si ~es un arreglo,
f(&a[2])

y
f(a + 2)

III

ambas pasan a la [uncin f la direccin del subarreglo que inicia en a[2}. Dentro
de f, la declaracin de parmetros puede ser

variable local, y por lo tanto, un parmetro de nombre de arreglo es un apuntadOr


esto es, una variable que contiene una direccin. Se puede utilizar este hecho par~

escribir otra versin de strlen, que calcula la longitud de una cadena.

ARITMETICA DE DIRECC IONES

SECCION 5.4

f(int arr[ ]) { ... }

o
f(int -arr) { ... }

As, hasta donde a I le concierne, el hecho de que el parmetro se refiera a parte

de un arreglo ms grande no es de consecuencia.


Si se est seguro de que los elementos existen, tambin es posible indexar hacia
atrs en un arreglo; p[-l). p[-2]. etc., son legitimas desde el punto dc viSla
sintctiCO, Y se refieren a elementos que preceden inmediatamente a p[O]. Por supuesto, es ilegal hacer referencia a objetos que no estn dentro de los lmites del
arreglo.

5.4

Aritmtica de direcciones

Si p es un apuntador a algn elemento de un arreglo, entonces p + + incrementa


p para apuntar al siguiente elemento, y p +

i la incrementa para apuntar i

elemen~

tos adelante de donde actualmente lo hace. Esas y otras construcciones semejantes


son las formas ms simples de aritmtica de apuntadores o de direcciones.
El lenguaje C es consistente y regular en su enfoque a la aritmtica de direcciones; su integracin de apuntadores, arreglos y aritmtica de direcciones es uno de
los aspectos que le dan fuerza. Lo ilustraremos al escribir un rudimentario asignadar de memoria. Hay dos rutinas: la primera, a11oe(n), regresa un apuntador p a
n posiciones consecutivas, que pueden ser empleadas por el invocador de a110c
para almacenar caracteres. La segunda, alree(p), libera el almacenamiento adquirido en esta forma, de modo que pueda ser reutilizado posteriormente. Las rutinas
son rudimentarias, puesto que las llamadas a afree deben realizarse en el orden
opues to a las llamadas realizadas a alloc. Es decir, el almacenamiento maneja-

do por alloe y airee es una pila o lista del tipo ltimo-que entra, primeroque-sale. La biblioteca estndar proporciona funciones anlogas llamadas ma110e
y free que no lienen tales restricciones; en la seccin 8.7 se mostrar cmo se
pueden reali zar.

La implantacin ms sencilla es hacer que a110e maneje piezas de un gran arreglo de caracteres al que llamaremos a11oebuf. Este arreglo est reservado para
alIoe y para airee. Puesto que stas hacen su trabajo con apuntadores, no con ndices, ninguna otra rutina necesita conocer el nombre del arreglo, el cual puede ser
declarado como slatic en el archivo fuente que contiene a alloc y a afree, y as
ser invisible hacia afuera. En la implantacin prctica, el arreglo puede incluso no
tener Un nombre; podra obtenerse llamando a malloc o pidiendo al sistema operat i\'o Un apuntador hacia algn bloque ~ in nombre de memoria.

112

APUNTAlJORES y ARREGLOS

CAPITULO S

La otra informacin necesaria es cunto de allocbuf se ha utilizado . Emplea.


mas un apuntador, llamado allocp, que apunta hacia el siguiente elemento libre.
Cuando se requieren n (,:aracteres a alloc, primero re visa si hay suficiente espado
libre en allocbuf. Si lo hay, alloc regresa e l valor aClual de allocp (CSIO es, el prin.
cip io del bloque libre), despus lo incrementa en n ;1ara apuntar a la siguiente
rea libre. Si no hay espacio, alloe regresa cero, en tanto que afree(p) si mple.
mente hace allocp igual a p si p est dent ro de allocbuf.

ARITMETICA DE DIRECCIONES

SECClON 5.4

define a allocp como un apuntador a caracteres y lo inicializa para apuntar al


principia de aYocbuf, que es la siguiente posicin libre cuando el programa comienza . Esto tambin podra haberse escrito
static char *allocp = &allocbuf[Ol;

puesto que el nombre del arreglo es la direccin del elemento cerosimo .


La prueba
if (allocbuf + ALLOCSIZE - allocp

antes de llamar a alloc:


allocp: \

II

allocbuf :

-en

li.t ,e

l.::n_ E

despus de llamar a alloc:


allocp: \

a llocbuf:

[ I
en uso

__ I i h
libyo
re _ _ _

#deline ALLOCSIZE 10000 1- lamao del espacio disponible -1

113

>

~ n) { 1-

si cabe -1

comprueba si existe suficiente espacio para satisfacer la peticin de n caracteres.


Si lo hay, el nuevo valor de allocp seria, cuando mucho, uno adelante del fin de
alloebuf. Si la peticin puede satisfacerse, alloc regresa un apuntador al princi
pio de un bloque de caracleres (nlese la declaracin de la funcin). De lo co ntra
rio, alloc debe regresar alguna seal de que no queda espacio. El len guaje C ga
rantiza que ce ro nunca es una direccin vlida para datos y por lo tanto puede
usarse un valor de ccro como retorno para sealar un suceso anormal, en este ca
so, falla de espacio.
Los apuntadores y los enteros no son intercambiables. Cero es la nica excepcin: la constan le cero puede ser asignada a un apuntador, y ste puede campa
rarse contra la constante cero . La constante simblica NULL se emplea con fre
euencia en lugar de cero, como un mnemnico para indicar ms claramente que
es un valor especial para un apuntador. NULL est definido en < stdio.h >. De
aqu en adelante se utilizar NULL.
Pruebas como
if (allocbuf + ALLOCSIZE - allocp >

static char alloebuf[ALLOCSIZE1; /* almacenamiento para alloe */


staHc char *allocp = allocbuf; /* siguiente posicin libre */

n) { 1- si cabe -1

char *alloc(int n) /* regresa UD apuntador a n caracteres */


{
if (allocbuf + ALLOCSIZE - allocp > ~ n) { 1- s cabe -1
allocp + = n;
return allocp - n; /* antigua p */
} else
/ * no hay suficiente espacio */
return O;
void afree(char *p) / * almacenamiento libre apuntado por p */
{
if (p > ~ allocbuf && p < allocbuf + ALLOCSIZE)

allocp

p;

En general, un apuntador puede ser inicializado tal como cualquier otra va#
riable, aunque normalmente los nicos valores significativos son cero o una eX#
presin que involucre la direccin de un dato previamente definido y de un tipo
apropiado. La declaracin
static char *allocp

allocbuf;

if

(p >

allocbuf && p < allocbuf + ALLOCSIZE)

muestran varias facetas importantes de la aritmtica de apuntadores. Primero,


los apuntadores pueden compararse bajo ciertas circunstancias. Si p Y q apuntan
a miembros del mismo arreglo, entonces re:aciones como = = !:::: < > =
etc. , funcio nan correctamente. Por ejemplo,
I

<

es verdadero si p apunta a un elcmenlO que est antes en cl arreglo de Jo que c~t


al que apunla q. Cualquier apuntador puede ser comparad o por .su igualdad D
desigualdad con cero. Pero est indefinido el comportam iento para la aritmtica
o comparaciones con apuntadores que no apuntan a miembro~ del mismo arrcg.la. (Existe una excepcin: la direccin del primer elemento que C'.;t de~J1u\ del
fin de un arreglo puede emplearse en arilml iea de apuntadores.)
Segundo, ya se ha observado que un apuntador y un entero pueden sumarse
o restarse. La construccin
P + n

114

APUNTADORES Y ARREGLOS

CAPITULO S

significa la direccin del n-simo objeto adelante del que apunta actualmente P.
Esto es verdadero sin importar la clase de objeto al que apunta p; n es escalada
de acuerdo con el tamao de los objetos a los que apunta p, lo cual est determi_
nado por la declaracin de p . Si un in! es de cuat ro bytes, por ejemplo, la esca la
para el l in! 1ser de cuatro.
La resta de apuntadores tambin es vlida: si p y q apuntan a elementos del
mismo arreglo, y p<q, entonces q-p + 1 es el nmero de elementos desde p hasta
q, inclusive. Este hecho puede usarse para escribir todava otra versin de strlen:
/* strlen: regresa la longitud de la cadena s */
int strlen(char *5)
{
char *p
5;

while (.p ! ~ '\0')


p+ +;

return p - s;

APUNTADORES A CARACTERES Y FUNCIONES

SECCJON S.S

5.5

115

Apuntadores a caracteres, y funciones


Una

conS/al1le

de cadena, escrita como

"Soy una cadena"

s un arreglo de caracteres. En la representacin interna, el arreglo termina con

:1 carcter nulo '\0', de tal manera que los programas puedan encontrar el fin.
La longitud de almacenamiento es as uno ms que el nmero de caracteres entre
las comillas.
posiblemente la ms comn ocurrencia de cadenas constantes se encuentra
como argumentos a funciones, como en
printf("hola, mundo\n");

Cuando una cadena de caracteres como sta aparece en un programa, el acceso


a ella es a travs de un apuntador a caracteres; printf recibe un apuntador al inicio
del arreglo de caracteres. Esto es, se tiene acceso a una cadena constante por un
apuntador a su primer elemento .
Las cadenas constantes no necesitan ser argumentos de funciones. Si
pmessage se declara como
char *pmessage;

En su declaracin, p se inicializa en s, esto es, para apuntar al primer carcter


de la cadena. En el ciclo while, cada carcter se examina en su turno hasta que
al final se encuentra el '\0'. Debido a que p apunta a caracteres, p + + avanza
p al siguiente carcter cada vez, y p-s da el nmero de caracteres que se avanza
ron, esto es, la longitud de la cadena. (El nmero de caracteres en la cadena puede
ser demasiado grande como para almacenarse en un in!. El/eader <s!ddef.h>
define un tipo ptrdifLt, que es suficientemente grande para almacenar la diferencia
signada de dos valores apuntadores . Sin embargo, si se es muy cauteloso, se debe
usar size_! para el tipo de retorno de slrlen, para coincidir con la versin de la
biblioteca estndar, size.! es el tipo de entero sin signo que regresa el operador
sizeof.)
La aritmtica de apuntadores es consistente: si estuviramos tratando Cod
floats, que ocupan ms espacio de memoria que los chars, y si p fuera un apunta
dor a noa!, p + + avanzara al siguiente noa!. As, podemos escribir otra versin
de alloc que mantenga noa!s en lugar de chars, simplemente cambiando de chal
a noa! en todo alloc y afree. Todas las manipulaciones de apuntadores tOmarn automticamente en cuenta el tamao de los objetos apuntados.
Las operaciones vlidas de apuntadores son asignacin de apuntadores del
mismo tipo, suma y substraccin de un apuntador y un entero, resta o comparactJI
de dos apuntadores a miembros del mismo arreglo, y asignacin o comparacin 000
cero. Toda otra aritmtica de apuntadores es ilegal. No es legal sumar dos apuotadores, multiplicarlos o dividirlos, enmascararlos o agregarles un noa! O un
dauble, O an, excepto para void * I asignar un apuntador de un tipo a un apuo
tador de otro tipo sin una conversin forzosa de tipo.

entonces la proposicin
pmessage

"ya es el Hempo";

asigna a pmessage un apuntador al arreglo de caracteres. Esta no es la copia de


una cadena; slo concierne a apuntadores. El lenguaje e no proporciona ningn
operador para procesar como unidad una cadena de caracteres .
Existe una importante diferencia entre estas definiciones:
char amessageI 1 = "ya es el tiempo";
char *pmessage = "ya es el tiempo";

/* arreglo */
/* apuntador */

atnessage es un arreglo, suficientemente gralide como para contener la secuencia

de caracteres y el ,\0' que lo inicializa. Se pueden modificar caracteres individuales dentro del arreglo, pero amessage siempre se referir a la misma localidad de
alrnacenamiento. Por otro lado, pmessage es un apuntador, inicializado para
apUntar a una cadena constante; el apuntador puede modificarse posteriormente
~ara que apunte a algn otro lado, pero el resultado es indefinido si trata de moIrlCar el conten ido de la cadena.

pmessage:
amessage:

~ I ahora es el tiempo\ O

ahora es el tiempo\O

116

APUNTADORES Y ARREGLOS

CAPITUlO

Ilustraremos ms aspectos de los apuntadores y los arreglos, estudiando ver..


siones de dos tiles funciones adaptadas de la biblioteca estndar. La primera
funcin es slrcpy(s,I), que copia la cadena 1 a la cadena s. Sera agradable decir
simplemente s=t, pero esto copia el apuntador, no los caracteres. Para copiar lOs
caracteres se requiere de un ciclo . Primero est la versin con un arreglo:
/* strcpy: copia t hacia Si versin de subndices ./
void strcpy(char *s, char *t)

APUNTADORES A CARACTERES Y FUNCIONES

SECCION 5.5

117

a controlar el ciclo. El efecto real es que los caracteres se copian de t a 5, haspar l '\0' final, incluyndolo .
la eComo resumen f'ma,
l
b
" contra '\0 'es redundante,
o serve
que una comparaClOn
puesto que la pregunta es simplemente si la expresin es cero. As , la funcin podra escribirse correctamente como
/ * strcpy: copia t haca s; versin 3 con apuntadores */
void strcpy(char *s, char *t)

int i;

while (>s+ +
i = O;
while ((s[i)

>t+ +)

1 Ji)) 1= '\0')

i+ +;

En contraste, aqu est una versin de strcpy con apuntadores:


/* strcpy: copia t hacia s; versin 1 con apuntadores */
void strcpy(char *s, char *t)
{
while ((>s = >1) = '\0') {
s+ +;

t+ +;

Puesto que los argumentos se pasan por valor, slrcpy puede utilizar los parmetros s y 1 en la forma que le parezca mejor. Aqu hay apuntadores convenientemente inicializados, que se desplazan a lo largo del arreglo un carcter a la vez,
hasta que el '\0' con que term ina 1 se ha copiado a s.
En la prctica, strcpy no se escribira como se mostr anteriormente. Los pro-gramadores expertos de e preferiran
/* strcpy: copia t hacia s; versin 2 con apuntadores */
void strcpy(char *s, char *t)
{

while ((>s+ +

>1+ +) 1= ,\0')

Esto traslada el incremento de s y de 1 hacia dentro de la parte de prueba del ciclo. El valor de *1+ + es el carcter al que apunta 1 antes de incrementarse; el ++
postfijo no modifica t sino hasta despus de que se ha tomado el carcter. En la
mi~ma forma, el carcter se almacena en la posicin anterior de s antes de qU,e ;
se Incremente. Tamb in este carcter es el va lor contra el cual se compara \

Aunque esto puede parecer misterioso a primera vista, la conveniencia de esta


notacin es considerable, y debe dominarse el estilo, puesto que se encontrar
frecuentemente en programas de C.
En la biblioteca estndar string.h strcpy, devuelve la cadena objetivo
como el valor de la funcin.
La segunda rutina que examinaremos es strcmp(s,t), que compara las cadenas
de caracteres s y t, Y regresa un valor negativo, ceTO o positivo si s es lexicogrficamente menor que, igual a, o mayor que t. El valor se obtiene al restar los caracleres de la primera posicin en que 5 y t no coinciden.
/* strcmp: regresa <O si s<t, O si s= =t, >0 si s>t */
int strcmp(char *s, char *t)
{
int i;

lor (i = O; s[i) = = t [il; i + +)


il (s[i) = = '\0')
retum O;
return s[i) - t[i);

La versin con apuntadores de strcmp :


/* strcmp: regresa <O si s<t, O si s= =t, >0 si s>t */
int strcmp(char *s, char *t)
{
for ( ; *S = = *t; 5 + + , t + +)
il ('S = = '\0')
retum O;
return 5 - *t;

118

APUNTADORES Y ARREGLOS

CAP ITULO 5

Puesto que + + y -- son operadores prefijos o postfijos, se presentan Otras


combinaciones de ,., + + y --, aunque con menos frecuencia. Por ejemplo,
*--p

dismi nu ye p antes de traer el ca racter al que apu nta. En efecLo , la pareja de Cx


presiones
.p+ + = val;
val = *-- Pi

/* mete val en la pila */


/ * saca el tope de la pila y lo pone en val -/

son expresiones idiomticas estndar para meter y sacar algo de una pila; vase
la seccin 4.3.
El "cader <string.h> contiene declaracIOnes para las funciones que se mencionan en esta seccin, adems de una variedad de otras funciones para manipu.
lacin de cadenas en la biblioteca estndar.

Ejercicio 5-3. Escriba una versin con apuntadores de la funcin strcat que se
muestra en el captulo 2: strcat(s,t) copia la cadena t al final de s. O
Ejercicio 5-4. Escriba la funcin strend(s,t), que regresa 1 si la cadena t se presenta al final de la cadena s, y cero si no es as. O
Ejercicio 5-5_ Escriba versiones de las funciones de biblioteca

SECCION 5.6

ARREGLOS DE APUNTADORES: APUNTADORES A APUNTADORES

119

sU lado, los apuntadores se pueden almacenar en un arreglo. Dos lneas se pueden


comparar pasando sus apuntadores a strcmp. Cuando dos lneas desordenadas
tienen que intercambiarse, se intercambian los apuntadores en el arreglo de apun-

tadores, no las lneas de texto.


--;-defghi 1

defqhi

jklmnopgrst

'klmnooor st

abc

Esto elimna el doble problema de un manej o complicado de almacenamiento y


exceso de procesa mi ento que se producira al mover las ln eas.

El proceso de ordenamiento tiene tres pasos:


lee todas las lineas de entrada
ordnalas
impdmelas en orden

Como es usual, es mejor dividir el programa en funciones que coincidan co n esta


divisin natural, con la rutina principal controlando a las otras funciones . Abandonemos por un momento el paso de ordenamiento, y concentrmonos en las es-

stmcmp, que operan co n hasta los n primeros caracteres de sus argumentos de


cadena. Por ejemplo, strncpy(s,t,n) co pia hasta n caracteres de t hacia s . En el

tructuras de datos y la entrada y salida.

apndice B se exponen descripciones ms completas. O

y construir un arreglo de apuntadores hacia las lneas. Tambin debe contar el

Ejercicio 5-6. Reescriba los programas y ejerccios apropiados de los captulos

nmero de lneas de entrada, puesto que esa informacin se requiere para el orde-

anteriores, empleando apuntadores en lugar de ndices de arreglos. Buenos prospectos son getline (capt ulos 1 y 4), a toi, itoa, y sus variantes (captulos 2, 3 Y

4). reverse (capitu lo 3) y strindex y getop (captulo 4). O

5.6

Arreglos de apuntadores; apuntadores a apuntadores

Puesto que en s mismos los apuntadores son variables, pueden almacenarse


en arrcglos tal como o tras va riables. Ilust raremos esto al escribir un programa
que ordena un co njunt o de lneas de texlo en orden alfabtico, que es una vers in

La rutina de entrada tiene que reunir y guardar los caracteres de cada lnea,

namiento y la impresin. Debido a que la funcin de entrada slo puede tratar


con un nmero finto de lneas, puede regresar alguna cuenta de lneas ilegal ,
como -1, si se presenta demasiado texto a la entrada.
La rutina de salida slo tiene que imprimir las lneas en el orden en que aparecen en el arreglo de apuntadores.
#include < stdJ.o.h >
#include <string.h >

#define MAXLINES 5000

re'tringda del programa sor! de UN IX.


En el captulo 3 se present una funcin de ordenamiento Shell que poda orclenar un arreglo de enteros, y en el capitulo 4 se mejor co n un quicksorl. El
mismo algoritmo funcionar, excepto que ahora se debe tratar con lneas de texto
de diferentes longitudes, y que , a diferencia de los enteros, no se pueden compa-

char -lineptrIMAXLINESJ;

rar ni cambiar en una simple operacin. Se necesita una representacin de datOS


que maneje cfkiel1le y convenientemente lineas de tex to.

void qsort(char -!ineplr!

Aqu es donde entran los arreglos de apuntadores. Si las lneas que se van a
ordenar se almacenan juntas en un gran arreglo de caracteres, entonces se puede
tener acceso a cada lnea por medio de un apuntador a su primer carcter. por

/ * mx # de lineas por ordenar */


/ . apuntadores a lneas de texto - /

int readlines(char *Uneptr[ ]. int nlines);


void writelines(char *lineptr[ L inl nlines) i

L inl

j. ordena lneas de entrada */


lnain( )
{

left, int right);

120

APUNTADORES Y ARREGLOS

int nlines;

CAPITULO

/* nmero de lneas de entrada leidas */

printf("error: entrada demasiado grande para ordenarla\n");


return 1;

/ * max longitud de cualquier lnea de entrada_,

int len, nlines;


char -p, line[MAXLEN];
nlines = O;

NULL)

lineIlen-lJ = '\0'; / * elimina carcter nueva lnea */


strcpy(p, line);
lineptr[nlines + + J
p;
return nlines;

/* writelines: escribe lineas de salida */


void writelines(char *lineptr[ ], int nlines)
int i;
for (i = O; i < nlines; i + + )
printf("%s\n", lineptrfi})

La funcin getline se trat en la seccin 1.9.


El principal nuevo elemento es la declaracin para lineptr:
char -lineptr[MAXLINESj

121

/ * writelines: escribe lineas de salida */


void writelines(char *!ineptrl ], int nlines)

while (nlines-- > O)


printf("%s\n", *lineptr+ +);

Inicialmente "lineptr apunta a la primera lnea; cada incremento lo avanza al siguiente apuntador a lnea mientras nlines se disminuye.
Teniendo la entrada y la salida bajo control, podemos proceder a ordenar. El
(fllicksorl del captulo 4 necesita so lo cambios de poca imponallL'ia: la.., del."lar<.l ciones deben modificarse, y la operacin de comparacin debe hacerse llamando
a strcmp. El algoritmo permanece igual, lo que nos da cierta confianza de que
an trabajar.

/ * readlines: lee lneas de entrada */


int readlines(char *lineptr[ ], int rnaxlines)

while ((len ~ getline(line, MAXLEN)) > O)


if (nlines > = maxlines Ir (p
alloc{len
return -1;
else {

ARREGLOS DE APUNTADORES; APUNTADORES A APUNTADORES

ue indica que lineptr es un arreg lo de MAXLINES elementos, cada uno de los


quaJes es un apuntador a chal. Esto es, lineptr[i] es un apuntador a carcter, y
:lineptr[i] es el carcter al que apunta, el primer carcter de la i-sima lnea de
tC\iO a Imacenacla.
pue>., o que lineptr c.., por s mi smo el nombre de un arreglo, puede lratar~ e
corno un apuntador en la misma forma que en nuestros ejemplos anteriores, y
writelines puede escribirse en su lugar como

if ((nlines ~ readlines(lineplr, MAXLINES)) > ~ O) {


qsort(linepfr, O, nlines- 1);
writelines(lineptr, nlines);
return O;
} else {

#define MAXLEN 1000


int getline(char *, int);
char *alloc(int);

SECCION 5.6

/ * qsort: ordena v[leftl ... v[right] en orden ascendente */


void qsorl(char *v[ ], int left, int right)
(
int i, last;
void swap(char *v[ ], int i, int j);

if (left > = right)

/ * no hace nada si el arreglo contiene ., /


return;
/ ., menos de dos elementos */
swap(v, lelt, (left + right)/2);
lasl ~ left;
for (i = left + 1; i < = right; i + +)
if (strcmp(v[i}, v[left]) < O)
swap(v, + +last, i);
swap(v, left, last);
qsort(v, left, last-l);
qsort(v, lasl + 1, righl);

De nlanera semejante, la rutina de int ercambio slo requiere modiricacionel


POco signi ficativas:
/ .. swap:
intercambia vii] y vUJ . /
void swap(char *v[ 1. int i, int j);
{

122

APUNTADORES Y ARREGLOS

CAPITULO S

ARREGLOS MULT1DIMENSIONALES

SECCIN 5.7

char *temp;

leap = year%4 = = O && year%lOO


for (i = 1; i < month; i+ +)

temp = v[i];
vii] = v[iI;
v[i] = temp;

retum day;

Puesto que cualquier elemento individual de v (alias lineptr) es un apuntador a


carcter, temp tambin debe serlo, de modo que uno pueda copiarse al otro .

= O:: year%400 =

123

O;

day + = daytab[leaplli];

/* montlLday: obtiene mes, y da a partir de da del ano .,,/


void montlLday(int year, int yearday, int *pmonth, int *pday
int i, leap;

Ejercicio 5-7. Reescriba readlines para almacenar lneas en un arreglo propor_


cionado por main, en lugar de llamar a alloc para obtener espacio de almacena_
miento. Cunto ms rpido es el programa? O

leap

= year%4

= 0&&

year%lOO ! = O :: year%400

O;

for (i = 1; yearday > daytab[leaplli]: i+ +)


yearday - = daytab[leaplli];
*pmonth = i;
"pday = yearday;

5.7

Arreglos multidimensionales

El lenguaje C proporciona arreglos multidimensionales rectangulares, aunque


en la prctica se usan menos que los arreglos de apuntadores. En esta seccin
mostraremos algunas de sus propiedades.
Considrese el problema de la conversin de fechas, de da del mes a da del
ao y viceversa. Por ejemplo, el I de marzo es el 60 0 da de un ao que no es
bisiesto, y el 61 0 da de uno que s lo es. Definamos dos funciones para hacer la
conversin: day_oLyear convierte mes y da en el da del ao, y monl.-d.y
convierte el da del ao en mes y da. Puesto que esta ltima funcin calcula dos
valores, los argumentos de mes y da deben ser apuntadores:
montlLd.y(1988, 60, &m, &d)
hace m igual a 2 y d igual a 29 (29 de febrero).
Ambas funciones necesitan la misma informacin, una tabla de los nmeros de
das de cad a mes ("treinta das tiene septiembre ... "). Puesto que el nmero de das

por mes difiere para aos bisiestos y ';'0 bisiestos, es ms fcil separarlos en dos
renglones de un arreglo bidimensional que siga la pista de lo que le pasa a febrero
durante los clculos. El arreglo y las funciones que realizan las transformaciones
son como se muestra a continuacin:

staUc ch.r d.ytab[2] [13] = {


{O, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31),
{O, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31),
);
/ * day_oLyear: obtiene da del ao a partir de mes y ao */
int day_oLyear(int year, int month, int day)

{
int i, leap;

Recurdese que el va lor aritmtiC'o de una expresin lgica, ,;omo la de leap, es cero
(fabo) o uno (verdadero), asi que puede emplearse como indice del arreglo daytab.
El arreglo daytab iene que ser eXCrIlO tanto a day_of_year

como a

montlLday, para que ambas puedan utilizarlo. Lo hicimos char para ilustrar un
uso legtimo de char para almacenar enteros pequeos que no son caracteres.

daytab es el primer arreglo de caracteres de dos dimensiones con el que hemos


tratado. En C, un arreglo de dos dimensiones es en realidad un arreglo unidimensional, cada uno de cuyos elementos es un arreglo. Por ello, los subndices se escriben como

daytab[i] [iI

l [rengln] [columna] 1

en lugar de
l INCORRECTO ./
daytab[i, iI
Aparte de esta diferencia de notacin, un arreglo de dos dimensiones puede tratarse en forma muy semejante a la de los otros lenguajes. Los elementos se almacenan por re nglones, as que el ndice de ms a la derecha, o columna, varia m~
rapido cuando se tiene acceso a los elementos en orden de almacenamiento.
Un arreglo se iniciali za con una lista de inicializadore~ entre lIave~; cada renglon de un arreglo de dos dimensiones se inicializa CO I1 una subli sta. El arreglo

daytab se inicia con una columna de ceros, de modo que los nmeros de mes puedan variar entre I y 12 en lugar de O a 11. PuestO que el espacio no es apremiante
aqui, esto es ms claro que ajustar los ndices.
Si un arreglo de dos dimensiones se pasa a una funcin, la declaracin de parmetros en la funcin debe incluir el nmero de columnas; el nmero de renglo-

124

APUNTADORES Y ARREGLOS

CAPITULO 5

APUNTADOR ES VS. ARREGLOS MULTIDIMENSIONALES

SECCION 5.9

nes es irrelevante, puesto que lo que se pasa es, como antes, un apuntador a Un

};

arreglo de renglones, donde cada rengln es un arreglo de 13 ints . Es este caso

retum (n < 1 :: n

particular, es un apuntador a objetos que son arreglos de 13 ints. Entonces, si


el arreglo daytab se pasara a la funcin f, la declaracin de f sera
f(inl daylab[2J [13]) { ... }

>

125

12) ? name[O] ; name[nl;

La declaracin de name, que es un arreglo de apuntadores a caracteres, es la mis-

ma que la de lineptr en el ejemplo del ordenal11ienlo. El inicializador es una li sta


de cadenas de caracteres; cada una se asigna a la posicin correspondiente dentro

Tambin podra ser


f(inl daylab[ J [13]) { ... }

porque el nmero de renglones es irrelevanre, O podra ser

del arreglo. Los caracteres de la i-sima cadena se colocan en algn lugar, y en


name[i] se almacena un apuntador a ellos. Puesto que el tamao del arreglo
name no est especificado, el compilador cuenta los inicializadores y completa
el nmero correcto.

(inl (-daylab) [13]) { ... }

que indica que el parmetro es un apuntador a un arreglo de 13 enteros. Los parntesis son necesarios, puesto que los corchetes [ ] tienen ms alta precedencia
que .... Sin parntesis, la declaracin

int -daytab [13]


es un arreglo de 13 apuntadores a entero. De modo ms general, slo la primera
dimensin (subndice) de un arreglo queda abierta; todas las otras deben especificarse.

En la seccin 5.12 se discute ms acerca de declaraciones complicadas.


Ejercicio 5-8. No existe deteccin de errores en day_oLyear ni en monllLday.
Solucione ese defecto. O

5.9 Apuntadores vs. arreglos multidimensionales


Los nu evos usuarios de C algunas veces se confunden con la diferencia entre
un arreg lo de dos dimensiones y uno de apuntadores, como name en el ejemplo
anterior. Dadas las definiciones
int a[lOJ [20];
int -b[lOJ;

entonces tanto a[3] [4] como b[3] [4] son referencias sintcticamente legtimas a
un nico int. Pero a es verdaderamente un arreglo de dos dimensiones: se le han

asignado 200 localidades de tamao de un int, y se emplea el clculo convencional


de subndices rectangulares 20 x rengln + columna para encontrar elemento
a[rengln,columna]. Para b, sin embargo, la definicin slo asigna 10 apuntadores y no los inicializa; la inicializacin debe realizarse en forma explicita, ya sea

5.8

Inicializacin de arreglos de apuntadores

estticamente o con cdigo. Suponiendo que cada elemento de b apunta a un


arreglo de veinte elementos, entonces existirn 200 ints reservados, ms diez cel-

Considrese el problema de escribir una funcin month...name(n), que regrese


un apuntador a una cadena de caracteres que contengan el nombre del n-simo

das para los apuntadores. La ventaja importante del arreglo de apuntadores es


que los renglones del arreglo pueden ser de longitudes diferentes. Esto es, no es

mes. Esta es una aplicacin ideal para un arreglo static interno, month.....name
contiene un arreglo reservado de cadenas de caracteres, y regresa un apuntador

necesario que cada elemento de b apunte a un vector de veinte elementos; alguno


puede apuntar a dos elementos, otro a cincuenta y algn otro a ninguno.
Aunque hemos basado esta discusin en trminos de enteros, el uso ms fre
cuente de arreglos de apuntadores es para almacenar cadenas de caracteres de
longitudes diversas, como en la funcin month....name. Compare la declaracin

a la cadena apropiada cuando se llama. Esta seccin muestra como se inicializa


ese arreglo de nombres.
La sintaxis es semejante a la de inicializaciones previas:
/ - montlLname:

regresa el nombre del n-simo mes -/

y la grfica para un arreglo de apuntadores:


char -name[

char - month..name(int n)

{ "Mes ilegal", "Ene", "Feb", "Mar" };

{
stabc char "name[

"Mes ilegal",
"Enero", "Febrero", "Marzo",

nombre:

Mes ilegal.o

"Abril", "Mayo", "Junio",

Ene,o

"Julio", "Aqosto", "Septiembre",

Feb o

"Octubre", "Noviembre", "Diciembre"

Maf\o

126

APUNTADORES Y ARREGLOS

CAPITULO $

con la de un arreglo bidimensional:


char aname(

(15]

{ "Mes ilegal ll , llEne", "Feb", "Mar" };

aname:

..

~-s-iloe-q-.'l'
~------"E'n~e~------------F'e'b-\o ------------M a-~-o-----------:J

15

30

ARGUMENTOS EN LA LINEA DE COMANDOS

SECCION 5. 10

127

La primera versin de echo rrata a argv como un arreglo de apuntadores a ca


raete r es :

#include <stdio.h>
/ - eco de los argumentos de la lnea de rdenes; la. versin - /
main(int argc, char -argv[ ])

45
int i;

Ejercicio 5-9. Reescriba las rutinas day_oLyear y monlh..d.y empleando apun_


tadores en lugar de ndices. O

5.10

Argumentos en la lnea de rdenes

Dentro de un medio ambiente que maneje C hay una forma de pasar arg umentos en la lnea de rdenes O de parmetros a un programa cuando em pieza su
ejecucin. Cuando se llama a main se le invoca con dos argumentos. El pri mero
(llamado por convencin argc, por argument counf) es el nmero de argumentos
cn la linea de rdenes con los que se invoc el programa; el segundo (argv, por argUlI1ent vector) es un apuntador a un arreglo de cadenas de caracter~s que cont ie.
ne los argumentos, uno por cadena. Se acostumbra utilizar niveles m ltip les de
apuntadores para manipular esas cadenas de caracteres.
El ejemplo ms sencillo es el programa echo, que despliega sus argumentos
de la linea de rdenes en una linea, separados por blancos. Esto es, la orden
echo hola, mundo

for (i = 1; i < argc; i + +)


printf("%s%s", arqv(i], (i < .r9v- 1) ? " ": "");
printf("\n");
return O;

Como argv es un apuntador a un arreglo de apuntadores, se pueden manipular


al apuntador en lugar de indexar al arreglo . Esta siguiente variacin se basa en
incrementar argv, que es un apuntador a un apuntador a char, en talllo se di s
minu ye argc:
w

#include <stdio.h>
/ "' eco de los argumentos de la linea de rdenes; 2a. versin "'/
main(int argc, char "'argv[ ])
{
while (--.rqc > O)
printf("%s%s", "' + +argv, (argc > 1) ?" " :"");
printf("\n");
return O;

Imprime
hola, mundo

Por convencin, argv[O] es el nombre con el que se invoc el programa, por lo


que argc es por lo menos 1. Si argc es 1, entonce no hay argumentos en la Unea
despus del nombre del programa. En el ejemplo anterior, argc es 3, yargv[O],
argv[l] y argv[2} son "echo", "hola" y "mundo", respectivamente. El primer ar
gumento optativo es argv[l] y el ltimo es argv[argc-l]; adems, el estndarrequiere que argv[argc) sea un apumador nulo.
arqv:

printf(argc > 1) ? "%s " : "%s", "' + + argv);

Esto demuestra que el argumento de formato del printf tambin puede ser una
expresin.
'. Como un segundo ejemplo, hagamos algunas mejoras al programa de la ,ce

echo\O
hola,\O
mundo\O

l.

Puesto que argv es un apuntador al inicio del arreglo de cadenas de argumentos,


incrementarlo en 1 (+ + argv) lo hace apuntar hacia argv[l] en lugar de apuntar
a .rgv[O]. Cada incremento sucesivo lo mueve al siguiente argumento; entonces
t argv es el apuntador a ese argumento. Al mismo tiempo, argc disminuye; cuando llega a cero, no quedan argumentos por imprimir.
En forma alternativa, podemos escribir la proposicin prinlf como

~ton 4 . 1 que encuentra un patrn. Si se recuerda, se fij el patrn de bsqued a cn


o prOfu nd o del programa, un esquema que ob viamente no es satisfactorio. SigUiendo la guia del programa grep de UNIX, cambiemos el programa dc modo
~ue el pat r n que se debe encontrar se especifique por el primer argumento en la
Inea d .
e Ordenes .

128

APUNTADORES Y ARREGLOS

CAPITULO S

ARGUMENTOS EN LA LINEA DE COMANDOS

SECCION 5.10

129

#include < stdio. h >


#include <strinq.h>
#define MAXUNE 1000

#include <stdio.h >


#include <string.h >
#define MAXLlNE 1000

int getline(char *!ine, int max);

int getline(char -line, int max);

/ * find:
imprime lineas que coinciden con el patrn del ler. argumento _/
main(int arqc, char *argv[ ])

/ * find:
imprime lneas que coinciden con el patrn del ler. argumento */
main(int argc , char -argv[ ])

ehar line[MAXLlNE);
int found = O;
if (arge ! = 2)
printf("Uso: find patrn\ n");
else
while (geUine(line, MAXU NE) > O)
if (,Irslr(line, argv[lj ! = NULL) {
printf("%s", line);
found+ +;
return found;

La funcin ,1"lr("I) de la biblioteca estndar regresa un apuntador a la primera


ocurrencia de la cadena I dent ro de la cadena s, o NULL si no existe. La cadena
est declarada en <slring. h >.
Ahora se puede extender el modelo para ilustrar cOI1!,trucciones adicionales de
apuntadores. Suponga que deseamos permitir dos argumentos optativos . Uno in
dica "imprime todas las lneas excepto aquellas que coincidan con el patrn" ; el
segundo dice 'p recede cada lnea impresa con su nmero de lnea".
Una convencin co mn para programas en e en sistemas UN IX es que un argumento que principia con un signo de menos introduce una bandera o parmetro
op tativo. Si seleccionamos -x (por "excepto") para ind icar la inversin, Y - n
("nmero") para so licitar la numeracin de lneas, entonces la orden
find - x - n patrn

imprimir cada linea que no coincida con el patrn, precedida por su nmero de
linea.
Los argumentos para opciones deben ser permitidos en cualquier orden, Yel
resto del programa debe ser independiente del nmero de argumentos que estuvieran presentes. Adems, es conveniente para los usuarios que los argumentoS de
las opciones puedan combinarse, como en
find -nx patrn

Aqu est el programa:

ehar line[MAXLlNEj;
long lineno = O;
int c, except = O, number

0, found

O;

'-')
while (--arge > 0&& (. + + argv)[Oj
while (e = + + argv[Oj)
,witeh (e) {
case 'x':
except
l
break;
case 'n':
number
1;
break;
default:
printf("find: opcin ilegal %c\n", c);
argc = O;
found = -1;
break;

if (arge ! = 1)
printf("Uso: find - x -n patrn\ n");
else
while (getline(line, MAXLlNE) > O) {
lineno+ +;
if ,trslr(line, argv) ! = NULL) ! = exeepl) {
if (number)
printf("%ld:", !ineno);
printf("%s", line);
found + +;

retum found;

AJ argc se disminuye y argv se incrementa antes de cada argumento opcional.


final del ciclo, si no hay errores, argc dice cuntos argumentos permanecen

130

APUNTADORES Y ARREGLOS

CAPITULO S

sin procesar y argv apunta al primero de stos. As, argc debe ser I y *argv debe
apuntar al patrn. Ntese que * + + argv es un apuntador a un argumento tipo
cadena, as que (* + + argv)[OJ es su primer carcter. (Una forma alternativa vlida
seria + + argv.) Debido a que [ ) tiene ms prioridad que' y que + +, los pa.
rntesis son necesarios; sin ellos, la expresin seria tomada como' + + (argv[O)).
En efecto, esto es lo que empleamos en el ciclo ms interno, donde la tarea es
proceder a lo largo de una cadena especfica de argumentos. En el ciclo ms interno, la expresin' + + argv[O) incrementa el apuntador argv[O)
Es raro que se empleen expresiones con apuntadores ms complicadas que s.
tas; en tal caso, ser ms intuitivo separarlas en dos o tres pasos.
Ejercicio 510. Escriba el programa expr, que evala una expresin polaca inver.
de la lnea de ordenes, donde cada operador u operando es un argumento por
separado. Por ejemplo,

~a

expr

3 4

se evala como 2 x (3 + 4). O


Ejercicio 511. Modifique el programaentab y detab(escritos como ejercicios en
el capitulo 1) para que acepten una lista de puntos de tabulacin como argumen
tos. Utilice los tabuladores habituales si no hay argumentos. O

APUNTADORES A FUNCIONES

SECCION 5.1 I

dones, regresados por funciones y otras cosas ms. Ilustraremos esto modificando el procedimiento de ordenamiento descrito anteriormente en este captulo, de
nod o Que si se da el argumento opcional -n, ordenar las lneas de entrada 11l1
~ricamente en lugar de lexicogrficamente.
Frecuentemente un ordenamiento consiste de tres partes -una comparacin
que determina el orden de cualquier par de objetos, un intercambio que invierte
SU orden, Y un algoritmo de ordenamiento que realiza comparaciones e intercambios hasta que los objetos estn en orden. El algoritmo de ordenamiento es independien te de las operaciones de comparacin e imercambio; as, al pasarle diferentes funcio nes de comparacin e imercambio, se pueden oblener clasificaciones co n diferentes criterios. Esta es la tctica que se sig ue en nuestro nuevo
mtodo.
La com paracin lexicogrfica de dos lineas es realizada por strcmp, como an
tes; tambin requeriremos de una rutina numcmp que compare el valor numrico
de dos lineas y regrese la misma clase de indicacin que hace strcmp. Estas fun
ciones se declaran antes de main, y a qsort se le pasa un apuntador a la funcin
apropiada. Se ha hecho un procesamiento deficiente de los errores en los argumentos, co n el fin de concentrarnos en los elementos principales.
#include <stdio.h>
#include < strinq.h >

Ejercicio 512. Extienda entab y detab de modo que acepten la abreviatura


entah-m +n

que indica puntos de tabulacin cada n columnas, iniciando en la columna m. Se


leccione el comportamiento por omisin ms conveniente (para el usuario). O
Ejercicio 513. Escriba el programa tail, que imprime las ltimas n lneas de su
ent rada. Por omisin, n es 10, digamos, pero puede modificarse con un argu men
to optativo, de modo que
tail -n

imprime las ltimas n lineas. El programa debe comportarse en forma racional


sin importar cun poco razonable sea la entrada o el valor de n. Escriba el progra
ma de manera que haga el mejor uso de la memoria disponible; las lneas deben
almacenarse como en el programa de ordenamiento de la seccin .\.6, no en un
arreglo de dos dimensiones de tamao fijo. O

#define MAXUNES 5000


char *lineptr[MAXLINES);

1* mx # de lneas a ordenar *1
1* apuntadores a lineas de texto *1

int readlines(char *lineptr[ ), int nlines);


void writelines(char *lineptr[ J. int nlines);

void qsort(void -lineptr[ ). int left, inl righl,


int (*comp) (void *, void
int numcmp(char *, char *);

e~;

/ * ordena lineas de entrada */


main{int arqc, char *arqv[ ])
j
int nlines;
/ * nmero de lineas de entrada ledas */
int numeric
/ * 1 si es ordenamiento numrico -/
O;
iJ (a rgc > 1 && strcmp(argvll). "-n") = = O)
numeric = 1;

5.11

Apuntadores a fu nciones

En e, una funcin por s sola no es una variable, pero es posible definir apuo
ladores a funciones, que pueden asignarse, ser colocados en arreglos, pasadOS a fu o

131

il nlines = readlines(lineptr, MAXLINES)) > = O) j


qsorlvoid .. ) lineplr, 0, nlines- l ,
(int (e) (void-, vOid- (numeric? numcmp: strcmp);
writelines(lineptr, nlines);
retum O;
) else j

132

APUNTADORES Y ARREGLOS

CAPITULO S

printf ("entrada demasiado grande para ser ordenada\n");


retum 1;

APUNTADORES A FU NCIONES

SECCI ON 5.11

133

es consistente con la declaracin: comp es un apuntador a una funcin, *comp es

la funcin, y
(ocomp) (vii]. vlloft])

En la llamada a qsort, slrcmp y numcmp son direcciones de funciones. Como


se sabe que son funciones, el operador & no es necesario, en la misma forma que
no es necesario antes del nombre de un arreglo.

Hemos escrito qsort de modo que pueda procesar cualquier tipo de dato, no
slo cadenas de caracteres. Como se indica por la funcin prototipo, qsort espera
un arreglo de apuntadores, dos enteros y una funcin con dos argumentos de tipo
apuntador. Para los argumentos apuntadores se emplea el tipo de apuntador genrico void *. Cualquier apuntador puede ser forzado a ser void * y regresado
de nuevo sin prdida de informacin, de modo que podemos llamar a qsort forzando los argumentos a void*. El elaborado casI del argumento de la funcin
fuerza los argumentos de la funcin de comparacin. Esto generalmente no

eS la llamada a ella. Los parntesis son necesarios para que los componentes sean
correctamente asociados; sin ellos,
inl ocomp( void o, void o)
l o INCORRECTO 01
indica que comp es una funcin que regresa un apuntador a int, lo cual es muy

diferente.
Ya hemos mostrado strcmp, que compara dos cadenas. Aqu est numcmp,
que compara dos cadenas numricamente, valor que se calcula llamando a atof:
#include <math.h>

tendr efecto sobre la representacin real, pero asegura al compilador que todo

/ * numcmp:
compara 51 y 52 numricamente . /
int numcmp(char *sl, char *s2)

est bien.

/* qsort: clasifica v[left} ... v[rightJ en orden ascendente */


void qsort(void *v[ L int leH, int riqht,
int (*comp) (void *, void *))
int i, last;
void swap(void *v[ }, int, int);

iI (left > = right)


/. no hace nada si el arreglo contiene /
return;
/. menos de dos elementos */
swap(v, lelt, (left + righl)/2);
lasl = left;
lor(i = left+l;i <= righl;i++)
iI ((ocomp) (vii]. vlleft]) < O)
swap(v, + +last, i);
swap(v, left, lasl);
qsort(v, leH, Jast-l, comp);
qsort(v, last + 1, riqht, comp);

Las declaraciones deben estudiarse COIl cuidado. El cuarlo parmetro de qsort es

double vI, v2;

vI = alol(sl);
v2 = alol(s2);
j (vI < v2)
return - 1;
else iI (vI> v2)
retum 1;
else
return O;

La funcin swap, que intercambia dos apuntadores, es idntica a la que presentamos anteriormente en este captulo, excepto en que las declaraciones se han
cambiado a void *.
void swap(void *v[ 1 , int i, int j)
void "temp;

lemp = vii];
vii]
vii];
vii] = lemp;

inl (ocomp) (void o, void o)

que indica que comp es un apuntador a una funcin que tiene dos argumenlOS
void * y regresa un int.

El uso de comp en la lnea


if ((ocomp) (vii]. vlleft]

< O)

Puede agregarse una variedad de otras opciones al programa de ordenamiento; algunas se convierten en ejercicios interesantes .

Ejercicio 5-14_ Modifique el programa de ordenamiento de modo que maneje


una bandera -r, que indica ordenar en orden inverso (descendente). Asegrese
de que -r, trabaja con -no O

134

APUNTADORES Y ARREGLOS

CAPITU LO 5

Ejercicio 5-15_ Agregue la opcin -1 para igualar las letras maysculas y minscu_
las, de modo que no se haga distincin entre ellas durante el o rd enamiemo; POr
ejemplo, al comparar, a y A son iguales. O
Ejercicio 5-16_ Agregue la opcin -d ("orden de directorio"), que compara slo
letras, nmeros y blancos. Asegrese de que trabaja en conjuncin con -l. O
Ejercicio 5-17. Agregue capacidad de manejo de campos, para que el ordena_
miento se haga sobre campos de las lneas, cada campo ordenado de acuerdo con un
conj unto independiente de opciones. (El indice de este libro fue ordenado con
-di para las entradas y -n para los nmeros de pgina.) O

DECLARACIONES COMPLICADAS

SECCION 5.12

void ('comp) ( )
comp:

x:
funcin que regresa un apuntador a un arreglo [ ] de
apuntadores a una funcin que regresa char

char ((xJ3]) ( ))151


x:
arreglo[3] de apuntadores a una funcin que regresa
un apuntador a un arreglo[5] de char

del est basada en la gramtica que especifica un declarador, que se define


en forma precisa en el apndice A, seccin 8.5; sta es una forma simplificada:

Declaraciones complicadas

Al lenguaje C se le reprueba algunas veces por la sintaxis de sus declaraciones,


particularmente las que involucran apuntadores a funciones. En la sintaxis hay
un intento de hacer que coincidan las declaraciones con su uso; trabaja bien para
casos simples. pero puede ser confusa para los dificiles, debido a que las declara~
ciones no pueden leerse de izquierda a derecha, y debido al exceso de uso de parntesis. La diferencia entre

apuntador a una funcin que regresa void

char (. (ox( ))[ ])( )

del:
dc/-directa:

5.12

135

*s optativos del-directa
nombre
(del)
del-direelo( )
del~directa[tamao optativo]

Con palabras, una del es una dc/~directa, tal vez precedido por * 5. Una de/directa es un nombre, o una del entre parntesis. o una de/-directa seguida por
parntesis , o una de/-directa seguida por corchetes con un tamao optativo.
Esta gramtica puede emplearse para reconocer declaraciones. Por ejemplo,
considere este declarador:
('plal ]) ( )

inl 'I( );

/* f: funcin que regresa un apuntador a int */

y
inl ('pl)( );

/* pf: apuntador a una funcin que regresa un int */

ilustra el problema: es un operador prefijo y'tiene menor precedencia que ( ), de


modo que los parntesis son necesarios para obligar a una asociacin apropiada,
Aunque en la prctica es extrao que aparezcan declaraciones verdaderamen~
te complicadas, es importante saber cmo entenderlas y, si es necesario, emo
crearlas . Una buena forma de sintetizar declaraciones es en pequeos pasos con
Iypedel, que se discute en la seccin 6.7. Como una alternativa, en esta seccin
presentaremos un par de programas que convienen de e vlido a una descrip~
ci n verbal y viceversa. La descripcin verbal se lee de izquierda a derecha.
La primera, del, es la ms compleja, Convierte una declaracin de e en una
descripcin hecha con palabras, como en estos ejemplos:
char **argv
argv:
apuntador a un apuntador a char
inl ('daytab) 113J
daytab:
apuntador a un arreglo[13] de int
inl 'daytabl131
daytab:
arreglo[13] de apuntadores a int
void *comp{ )

comp:

fU.lcin que regresa apuntador a void

pfa se identificar como un nombre y por ende como una dc/-dircc/a. Entonces

pla[ J es tambin una del-directa. Luego 'pla[ J se reconoce como una del, de
modo que ('pla[ ]) es una del-directa. Entonces ('pla[ ]) ( ) es una de/-directa y
por tanto ulla de/. Tambin podemos ilustrar el anlisis con un rbol de estructu~
ra gramatical como ste (en donde de/-directa se ha abreviado como de/~dir):
pla

nOI~bre/
det-dir

I
del-dir
del

I
del-dir

I
de/-dir

del

[J

()

136

APUNTADORES Y ARREGLOS

CAPITULO S

El corazn del programa del es un par de funciones, del y dirdel, que descri.
ben una declaracin de acuerdo con esta gramtica. Debido a que la gramtica
est definida recursivamente. las funciones se llaman recursivamente una a la
otra, mientras reconocen piezas de una declaracin; el programa se conoce corno
analizador sintctico por descenso recursivo.

DECLARAC IONES COMPLICADAS

SECCION 5.12

137

AqU estn las variables globales y la rutina principal:


#include <stdio.h>
#include <string.h>
#include <ctype.h>

#define MAXTOKEN 100

/* del: reconoce una declaracin */


void del(void)

enum { NAME, PARENS, BRACKETS };

{
int ns;

for (ns = O; gettoken( )


'*' ;
ns+ +;
dirdel( );
while (ns- - > O)
strcat(out, "apuntador a");

void del(void);
void di Tfl r 1(void);
/* cuenta *S */

/* dirdcl: reconoce un declarador directo */


void dirdcl(void)
{
int type;

if (tokentype = = '(') {
;- ( del ) -;
del( );
iJ (tokentype ! = ')')
printf "error: falta )\n");
} else if (tokentype = = NAME) /* nombre de variable */
strcpy(name, token);
eIse
printf("error: nombre o (del) esperado\n");
while ((type=gettoken( )) = = PARENS :: type
BRACKETS)
iJ (type = = PARENS)
strcat(out, "funcin que regresa");
else {
strcat(out, " arreglo"};
strcat(out, token);
strcat(out, "de");

int gettoken(void);
int tokentype;
ehar tokenIMAXTOKEN];
ehar nameIMAXTOKEN];
ehar datatypeIMAXTOKEN];
ehar outllOOO];

1* tipo del ltimo token -/


/ * cadena del ltimo token * /
/* nombre del identificador */
/* tipo de dato = char, int, etc. */
1* cadena de salida * /

maine ) /* convierte una declaracin a palabras */


/* ler. token en la linea */
while (gettoken( ) ,= EOF) {
/*
es
el tipo de dato *1
strcpy(datatype, token);
out 10] = '\0';
1* reconoce el resto de la lnea * /
del( );
iJ (tokentype ! = '\n')
printf("error de sintaxis\n");
printf("%s: %s %s\n", name, out, datatype);

return O;

La funcin gettoken ignora blancos y tabuladores, y encuentra el siguiente


token de la e ntrada ; un "token" es un nombre, un nar de parentesis, un par de

corchetes que tal vez incluyen un nmero, o cualquier otro carcter simple.
int gettoken(void)

/* regresa el siguiente token */

{
int C , getch(void);
void ungetch(int);
char *P = token;

Puesto que se intenta que el programa sea ilustrativo, no a prueba de balas,


hay restricciones sobre del, que slo puede manejar un tipo simp le de datOS
como char o int. No maneja tipos de argumentos dentro de funciones, o califi~
cadores como const. Los espacios en blanco inadecuados lo confunden. No se re~
cupera mucho ante los errores, de modo que las declaraciones invlidas tambin
lo confunden. Esas mejoras se dejan como ejercicios.

while ((e = getch( ))

iJ (e = = '(') )
if ((e = geteh( = = ')' {
strepy(token, "( )");

'\t')

138

APUNTADORES Y ARREGLOS
CAPITULO S

retum tokentype

DECLARACIONES COMPLICADAS

SECCJON 5. 12

PARENS;

sprintf(temp, "(*%s)", out);


strcpy(out, temp);
} else i1 (type ~ ~ NAME) {
sprintf(temp, "%s %s", token, out);
strcpy(out, temp);

} else (
ungeteh(e);
retum tokentype

'(';

) else if (e ~ ~ '[') {
for (op+ + ~ e; (op+ +
.p

139

} else
geteh( )) ! ~ 'j'; )

printf "entrada invlida en %s\n", token);


printf("%s\n", out);

= '\0';

return tokentype ~ BRACKETS;


{ else if (isalpha(e)) {

retum O;

for (Op+ + ~ e; isalnum(e ~ geteh( )); )


.p+ + = C;
.p = '\0';

Ejercicio 5-19. Modifique undel de modo que no agregue parntesis redundantes


a las declaraciones . O

ungeteh( e);
return tokentype

NAME;

Ejercicio 5-20. Extiende del para que maneje declaraciones con tipos de argumentos de funciones, calificadores como const, etctera. O

} else
return tokentype

Ejercicio 5-18. Haga que del se recupere de errores en la entrada. O

e;

gelch y ungeteh se discutieron en el captulo 4.


Es ms fcil ir en la direccin inversa, especialmente si no nos preocupamos
por la generacin de parntesis redundantes. El programa undel convierte una
descripcin verbal como \Ix es una funcin que regresa un apuntador a un arreglo
de apuntadores a funciones que regresan char", que se expresar como
x()o[]o()ehar
y se convertir en

ehar (o(-x( ))[ ] )( )

La sintaxis abreviada de la entrada nos permite reutilizar a la funcin geltoken. La


funcin undel tambin emplea las mismas variables externas que del.
l undd: convierte una descripcin verbal a declaracin ./
main( )
{
int type;

ehar temp[MAXTOKEN];
while (gettoken( ) ! ~ EOF) (
strcpy(out, token);

while ((type ~ gettoken( )) ! ~ '\n')


if (type = = PARENS ti type
strcat(out, token);
else i1 (type ~ ~ 'o'){

BRACKETS)

CAPITULO 6:

Estructuras

U na estructura es una coleccin de una o ms variables, de tipos posiblemente


diferentes, agrupadas bajo un solo nombre para manejo conveniente. (La s es-

tructuras se conocen como "records" en algunos otros lenguajes, principalmente

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 separada s.

Un ejemplo tradicional de estructura es el registro de una nmina: un empleado est descrito por un conjunto de atributos, como nombre, domicilio, nmero

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

an un salario. Otro ejemplo, ms tpico para e, procede de las grficas: un punto es un par de coordenadas, un rectngulo es un par de puntos, y otros casos
semej antes.
El principal cambio realizado por el estndar ANSI es la definicin de la asignacin de estructuras -las estructuras se pueden copiar y asignar, pasar a funciones y ser regresadas por funciones. Esto ha sido manejado por muchos compiladores
durante varios aos, pero las propiedades estn ahora definidas en forma precisa.

Las estructuras y los arreglos automticos ahora tambin se pueden inicializar.

6.1

Conceptos bsicos sobre estructuras


Definamos algunas estructuras propias para graficacin. El objeto bsico es

Un

punto, del cual supondremos que tiene una coordenada x y una coordenada

y , ambas enteras.

(4 ,3)

x
(0,0)

142

CAPITULO 6

ESTRUCTURAS

Los dos componentes pueden ser colocados en una estructura declarada as:
struet point {
int x;
int y;
};

ESTRUCTURAS Y FUNCIONES

SECCION 6.2

143

El operador miembro de estructura "." conecta al nombre de la estructura con


el nombre del miembro. Por ejemplo, para imprimir las coordenadas del punto pt,
printf("%d, %d", pt.x, pt. y};

o para calcular la distancia del origen (0,0) a pI,

La palabra reservada struct presenta la declaraClOn de una estructura, que


es una lista de declaraciones entre llaves. Un nombre optativo, llamado rtulo de
estructura, puede seguir a la palabra strue! (como aqu lo hace poin!). El rtulo
da nombre a esta clase de estructura, yen adelante puede ser utilizado como una
abreviatura para la parte de declaraciones entre llaves.
Las variables nombradas dentro de la estructura se llaman miembros. Un
miembro de estructura o rtulo, y una variable ordinaria (esto es, no miembro)
pueden tener el mismo nombre sin conflicto, puesto que siempre se pueden distingu ir por el contexto. Adems, en diferentes estructuras pueden encontrarse los mismos nombres de miembros, aunque por cuestiones de estilo se deberan de usar
los mismos nomb res slo para objetos estrechamente relacionados.
Una declaracin struet define un tipo. La llave derecha que termina la lista
de miembros puede ser seguida por una lista de variables, como se hace para cualquier tipo bsico. Esto es,

double disl, sqrl(double);


di,l

sqrt((double)pt.x pl.x + (double)pt.y pt.y);

Las estructuras pueden anidarse. Una represemacin de un rectngulo es como un par de puntos que denotan las esquinas diagonalmente opuestas:
y

pl2

pI!

struet rect {
struet point ptl;
stmet point pt2;

struct { ... } x, y, z;

es sintcticamente anlogo a

};

int x, y, z;

La estructura rect contiene dos estructuras point. Si decla ramos screen como

en el sentido de que cada proposicin declara a x, y y z como variables del tipo


nombrado y causa que se les reserve espacio contiguo .
Una declaracin de estructura que no est seguida por una lista de variables
no reserva espacio de almacenamiento sino que simplemente describe una plantilla o la forma de una estructura. Sin embargo, si la declaracin est rOLUlada, el
rtulo se puede emplear posteriormente en definiciones de instancias de la estruCtura. Por ejemplo, dada la declaracin anterior de point,

struet reet sereen;

entonces
screen.ptl.x

se refiere a la coordenada x del miembro ptl de screen.

struct point pt;

define una variable pt que es una estructura de tipo slruct point. Una estructura
se puede inicializar al seguir su definicin con una lista de inicializadores, cada
uno una expresin constante, para los miembros:
,Irue! poinl maxpl

6.2

Estructuras y funciones

{ 320, 200 };

Una estructura automtica tambin se puede inicializar por asignacin o llamando a una funcin que regresa una estructura del tipo adecuado.
Se hace referencia a un miembro de una estructura en particular en una ex~
presin con una construccin de la forma
nombre-estructura. miembro

Las nicas operaciones legales sobre una estructura son copiarla o asignarla
como unidad, tomar su direccin con &, y tener acceso a sus miembros. La copia
y la asignacin incluyen pasarlas como argumentos a funciones y tambin regresar valores de funciones . Las estructuras no se pueden comparar. Una estructura
Se puede inicializar con una lista de valores constantes de miembros; una estructUra automtica tambin se puede inicializar con una asignacin.

144

ESTRUCTU RA S

CAPITULO 6

In vestiguemos las es tru cturas escribi endo algunas fun ciones para manipular
puntos y rectngulos. Hay por lo menos tres acercamientos posibles: pasar sepa~
radamente los componentes, pasar una estructura completa o pasar un apuntador
a ella . Cada uno tiene sus puntos buenos y malos.
La primera fun cin, makepoint, to ma dos enteros y regresa una es tru ctura
point:
/ * makepoint: crea un punto con las componentes x, y */
struet point makepoint(int x, int y)
{
struct point temp;
temp.x = x;
temp.y = y;
return temp;
Ntese que no hay conflicto entre el nombre del argumento y el miembro con el
mismo nombre; incluso la reutilizacin de los nombres refuerza el vnculo.
makepoint ahora se puede usar para inicializar dinmicamente cualquier es~
tructura, o para proporcionar los argumentos de la estructura a una funcin:

ESTRUCTU RAS Y FUNCIO NES

SECCION 6.2

145

Como otro ejemplo, la funcin ptinrect prueba si un punto est dentro de un


rectngulo , dopde hemos adoptado la convencin de que un rectngulo incluye
sus lados izquierdo e inferior pero no sus lados superior y derecho:
l . ptinreet:

regresa 1 si p est en r, O si no lo est *1


int ptinrect(struct point p , struet reet r)
return p.x > = r.ptl.x && p.x < r.pt2 .x
&& p.y > ~ r.ptl.y && p.y < r.pt2.y;

Esto supone que el rectngulo est representado en una forma estndar en donde
las coo rdenadas ptl son menores que las coordenadas pt2. La siguiente funcin
regresa un rectngulo, garantizando que est en forma cannica :
#define min(a, b) a) < (b) ? (a) : (b))
#deline max(a, b) a) > (b) ? (a) : (b))
1* eanonreet: pone en forma cannica las coordenadas de un rectngulo *1

struct reet eanonrect(struct rect r)


struet rect temp;

struct rect screen;


struct point middle;
struet point makepoint(int, int);
screen.ptl = makepoint(O, O);
screen.pt2 ~ makepoint(XMAX, YMAX);
middle ~ makepointscreen.ptl.x + screen.pt2.x)/2,
(sereen.ptl. y + screen.pt2.y)/2);
El siguiente paso es un conjunto de funciones para hacer operaciones aritm
ticas sobre los puntos. Por ejemplo ,

temp.ptl.x
temp.ptl.y
temp.pt2.x
temp.pt2.y
return temp;

min(r.ptl.x,
min(r.ptl.y,
max(r.ptl.x,
max(r.ptl.y,

r.pt2.x);
r.pt2.y);
r.pt2.x);
r.pt2.y);

Si una estructura grande va a ser pasada a una funcin , generalmente C ~ mr.,


eficiente pasar un apuntador qu e copiar la estructura completa. Lo ~ apu ntad o re r.,
a CstruelU ras son C0l11 0 los apuntadores a variables ordinari as . La d eclaraci n
s1ruct point *pp;

/ * addpoint: suma dos puntos */


struet point addpoint(struct point pl, s1ruct point p2)
{
pl.x + ~ p2.x;
pl.y + ~ p2.y;
return pI ;
Aqu, tanto los argumentos como el valor de retorno son estructuras. Incrementamos los componentes en pI en lugar de utili zar explcitamente una variable tem
poral para hacer nfasis en que los parmetros de la estructura son pasados por
valor como cualesquiera otros.

dice que pp es un apuntador a una estructura de tipo struct point. Si pp apunta


a una estructura point, 'pp es la estructura, y (pp).x y ('pp).y son los miembros. Para emplear pp, se podra escribir, por ejemplo,
struct point origin,

.pp;

pp = &origin;
printf ("el origen es (%d,%d) \ n", (pp).x, (.pp). y;
l os parntesis son necesarios en (pp).x debido a que la precedencia del operadOr miembro de estructura. es mayor que la de *. La expresin *pp.x significa
(PP.x), lo cual es ilegal debido a que x no es un apuntador.

146

ESTRUCTURAS

CAPITULO 6

Los apuntadores a estructuras se usan con tanta frecuencia que se ha propor_


cionado una notacin alternativa como abreviacin. Si p es un apuntador a estructura, entonces
p->miembro de estructura

se refiere al miembro en particular. (El operador - > es un signo menos seguido


inmediatamente por>.) De esta manera podramos haber escrito
printf("el origen es (%d,%d)\n", pp->x, pp->y);

Tanto. como -

> se asocian de izquierda a derecha, de modo que si tenemos

struct rect r, "rp

r;

entonces estas cuatro expresiones son equivalentes:


r .ptl.x
rp->ptl.x
(r.ptl).x
(rp->ptl).x

y [

Los operadores de estructuras. y - > , junto con ( ) para llamadas a funciones


1para subndices, estn hasta arriba de la jerarqua de precedencias y se aso-

cian estrechamente. Por ejemplo, dada la declaracin


struct {
int len;
ehar "str;
) 'p;

entonces
+ +p->len

incrementa a len, no a p, puesto que los parntesis implcitos son + + (p- > len).
Los parntesis se pueden emplear para alterar la asociacin: (+ + p)-> len incrementa a p antes de tener acceso a len, y ( + + p)- > len incrementa a p despus
del acceso. (Este ltimo conjunto de parntesis es innecesario.)
De la misma manera, "p->str obtiene cualquier cosa a la que str apunte;
"p- >str + + incrementa a str despus de hacer el acceso a lo que apunta (exactamen~
te como 's + +); ('p- > str) + + incrementa cualquier cosa a la que str apunte; Y
'p + + - > str incrementa a p despus de hacer el acceso a lo que str apunta.

6.3

Arreglos de estructuras

ARREGLOS DE ESTRUCTURAS

SE.CCION 6.3

los nombres, y un arreglo de enteros para las cuentas. Una posibilidad es usar
dos arreglos paralelos, keyword y keyeonunt, como en

ehar 'keyword[NKEYS];
inl keyeounl[NKEYS];
pero el hecho de que los arreglos sean paralelos sugiere una organizacin diferen~
te. un arreglo de estructuras. Cada entrada de una palabra C~ una pareja:
ehar "word;
int count;

y hay un arreglo de parejas. La declaracin de estructura


struet key {
char "word;
int count;

) keylab [NKEYS];
declara un tipo de estructura key, define un arreglo keytab de estructuras de
ese tipo, y reserva espacio de almacenamiento para ellas. Cada elemento del arreglo es una estructura. Esto tambin se podra escribir como
struct key {
char "word;
int count;
);

,Iruet key keylab[NKEYS];


Como la estructura keytab contiene un conj unto constante de nombres, es
ms fcil convertirla en una variable externa e inicializarla de una vez cuando se
define. La inicializacin de la estructura es anloga a otras anteriores -la definicin es seguida por una lista de inicializadores entre llaves:

struct key {
char "word;
int eount;

} keytab[ 1 = {
"auto", O,
"break", O,
"case", O,
"char", O,
"canst", O,
"eontinue", O,
"default ll , 0,
/..

Considrese escribir un programa para contar las ocurrencias de cada palabra


reservada de C. Se requiere de un arreglo de cadenas de caracteres para mantener

147

*/

"unsigned", 0,
"void", 0,

148

CAPITULO 6

ESTRUCTURAS

SECCION 6.3

"volatile", O,
"while", O

ARREGLOS DE ESTRUCTURAS

149

retum O;

};
/* binsearch: encuentra una palabra en tab[O]. .tab[n- 1] */
int binsearch(char *word, struct key tab[ ] int n)
{
int cond;
int low, high, mid;

Los inicializadores se listan en parejas correspondientes a los miembros de las estructuras. Podra ser ms preciso encerrar los inicializadores para cada "rengln" o estructura entre llaves, como en

{ "auto", O },
{ "break", O },
{ "case", O },

low = O;
high = n - 1;
while (low < = high) {
mid = (low + high) / 2;
if con<l = strcmp(word, tab[mid].word)) < O)
high = mid - 1;
else if (cond > O)
low = mid + 1;
else
return mid;

pero las llaves internas no son necesarias cuando los inicializado res son variable'!'
simples o cadenas de caracteres, y cuando todos estn presentes . Como es usual,
el nmero de entradas en el arreglo keytab se calcular si los inicializado res estn
presentes y el [ 1 se deja vaco,
El programa que cuenta palabras reservadas principia con la definicin de
keytab. La rutina principal lee de la entrada con llamadas repetidas a la fu ncin
getword, que trae una palabra a la vez. Cada palabra se consulta en keytab con
una versin de la funcin de bsqueda binaria que se escribi en el captulo 3.
La lista de palabras reservadas debe estar ordenada en forma ascendente en la tabla.
#include <stdio.h>
#include <ctype.h>
#include <string.h>

Mostraremos la funcin getword en un momento; por ahora es suficiente decir


que cada llamada a getword encuentra una palabra, que se copia dentro del arreglo refe rido como su primer argumento.
La cantidad NKEYS es el nmero de palabras en keytab_ Aunque las
podriamos contar manualmente, es mucho ms fcil y seguro que lo haga la mquina, especialmente si la list a est sujeta a cambios. Una posibilidad sera terminar la lista de inicializadores con un apumador nulo y luego hacer un ciclo a 10
largo de keytab hasta que se encuentra el fin.
Pero esto es ms de lo que se requiere, puesto que el tamao del arreglo est
determinado completamente al tiempo de compilacin. El tamao del arreglo es
el tamailo de una enlrada multiplicado por el nmero de ent radas, asi que el nmero de ent radas es

#define MAXWORD 100


int getword(char *, int);
int binsearch(char *, struct key * int);
I

/* cuenta palabras reservadas de


maine )
{
int n;
char word[MAXWORD];

return -1;

*j

size o/ keytab / size o/ struct key

,=

while (getword(word, MAXWORD)


EOF)
if (isalpha(word[O]))
if n = binsearch(word, keytab, NKEYS)) > = O)
keytab[n].count + +;
for (n = O; n < NKEYS; n + +)
if (keytab[n].count > O)
printf("%4d %s\n",
keytab[n].count, keytab[n].word);

e proporciona un operador unario a


Se

tiempo de compilacin llamado sizeof que


puede emplear para calcular el tamao de cualq uier objeto. Las expresiones
sizeof objeto

y
sizeof (nombre de tipo)

150

ESTRUCTURAS

CAP ITULO 6

dan un entero igual al tamao en bytes del tipo u objeto especificado. (Estricta_
mente, sizeol produce un valor entero sin signo cuyo tipo, size_t, est definido
en el header <slddel.h>.) Un objeto puede ser una variable, arreglo o estructu _
ra. Un nombre de tipo puede ser el nombre de un tipo bsico como inl o double

ISI

w ~ '\0';
return word[O];
getwor~, utiliza ge,tch y ungetch, ~l~e se escri?ieron en el captulo . .L Cuando la

'

rccolecclon de un slmbolo alfanul11cnco se detiene, getword se ha colocado un

En nuestro caso, el nmero de palabras es el tamao del arreglo dividido entre


el tamao de un elemento. Este clculo se utiliza en una proposicin # deline
para fijar el valor de NKEYS:

carcter adelante . La llamada a ungetch regresa el carcter a la entrada para la

un tipo derivado como una estructura o un apuntador.

APUNTADORES O ESTRUCTURAS

SECCION 6.4

#deline

NKEYS

(sizeol keylab I sizeol(struel key))

Otra forma de escribir esto es dividir el tamao del arreglo entre el tamao de
un elemento especfico:
#deline

NKEYS

(sizeol keylab I sizeol keylab[O])

Esto tiene la ventaja de que no necesita ser modificado si el tipo cambia.


Un sizeol no se puede utilizar en una lnea #if, debido a que el preprocesador
no analiza nombres de tipos. Pero la expresin del #define no es evaluada por
el preprocesador, y aqu el cdigo es legal.
Ahora la funcin gelword . Hemos escrito una funcin gelword ms general de lo que se requiere para este programa, pero no es complicada. getword
obtiene la siguiente "palabra" de la entrada, donde una palabra es cualquier ca-

dena de letras y dgitos que principia con una letra, o un solo carcter que no sea
espacio en blanco . El valor de la funcin es el primer carcter de la palabra, o
EOF para fin de archivo, o el carcter en si mismo cuando no es alfabtico.
/* getword:
obtiene la siguiente palabra o carcter de la entrada */
int getword(char *word, int lim)

Ejt'rcicio 6-1. Nuestra vers in de getword no maneja adecuadamcllle la\ \ubra


yas, cadenas constantes, comentarios o lneas de control para el [1reprocC'sador.
Escriba una versin mejorada. O

6_4 Apuntadores a estructuras


Para ilustrar algunas de las consideraciones involucradas con apuntadores y

arreglos de estructuras, escribamos de nuevo el programa de conteo de palabras


reservadas, esta vez utilizando apuntadores en lugar de subndices.

La declaracin externa de keytab no requiere de cambios, pero main y binsearch


s necesitan modificaciones .
#include < stdio.h >
#include < ctype.h >
#include < string.h >
#define MAXWORD 100
int getword(char *, int);
struct key .binsearch(char *, struct key *, int);

inl e, geleh(void);
void ungetch(int);
char "'w = word;
while (isspaee(e

siguiente llamada. getword tambin usa isspace para ignorar espacios en blanco,
isalpha para identificar letras, e isalnum para identificar letras y dgitos; todas
provicnen del header estndar < ctype.h > .

geteh(

/ * cuenta palabras reservadas de C; versin con apuntadores . /


rnain( )
{
ehar word[MAXWORD];
struct key *P;

il (e ! ~ EO F)
*w+ + = c'
iI (!isalpha(e)){
w ~ '\0';
retum c;
lor (; --lim > O; w+ +)
il (!isalnum(w ~ getch(
ungetch( *w);
break;

while (getword(word, MAXWORD) ! ~ EOF)


if (isalpha(word[O]))
~ binseareh(word, keylab, NKEYS ! ~ NULL)
il
p- >count + +;
lor (p ~ keylab; p < keytab + NKEYS; p + +)
il (p~ > eounl > O)
printf("%4d %s/n", p- >count, p->word);
return O;

) {

152

ESTRUCTURAS

CAPITULO 6

/ * binsearch:
encuentra una palabra en tab[O) ... tab[n- 1J */
struct key *binsearch(char *word, struct key -tab, int n)
int cond;
slruel key 'low = &lab[O);
slruct key 'high = &Iab[n];
struct key *mid;

while (low < high) {


mid = low + (high-Iow) / 2;
if eond = sllemp(wold, mid- > wOld)) < O)
high = mid;
else if (eond > O)
low = mid + 1;
alse
return mid;
leluID NULL;

SECC ION 6.5

ESTRUCTURAS AUTORREFERENCIADAS

153

res que involu cra el primer elemento despus del fin de un arreglo (e~to es,

&tabln] trabajar correctameme.


En main escribimos

fOI (p = keylab; p < keylab + NKEYS; p + +)


S p es un apuntador a una estruct ura, la artmtica con p toma en cuenta el
tamao de la estructura, as que p + + incrementa p co n la cantidad correcta
para obtener el siguiente elemento del arreglo de estructuras, y la prueba detiene
el ciclo en el momento correcto.
Sin embargo , no hay que suponer que el tamao de una estructura es la suma
de los tamaos de sus miembros. Debido a requisitos de alineacin para diferentes objetos , podra haber "huecos" no identificados dentro de una estructura.
As por ejemplo, si un char es de un byte y un int de cuatro bytes, la estructura

slIUe! {
char e;
int i;
};

bien podra requerir ocho bytes, no cinco. El operador sizeol regresa el valor
apropiado.
Finalmente, un comentario acerca del formato del programa: cuando una
funcin regresa un tipo complicado como un apuntador a estructura, como en

Aqu hay varas cosas que amertan nota. Primero , la declaracin de binsearch

debe indicar que regresa un apuntador a struct key en lugar de un entero; esto se declara tanto en el prototipo de la funcin como en binsearch _ Si binsearch encuentra la
palabra, regresa un apuntador a eUa; si no, regresa NUll_
Segundo, ahora se tiene acceso a los elementos de keytab por medio de apuntadores. Esto requiere de cambios significativos en binsearch.
Los inicializado res para low y high son ahora apuntadores al inicio y justo
despus del final de la tabla.
El clculo del elemento intermedio ya no puede ser simplemente
mid

(low + high) / 2

struct key *binsearch(char *word, struct key -tab, int n)

el nombre de la func in puede ser difcil de leer y de encontrar con un editor de


tex to. Por eso, algunas veces se emplea un estilo alternativo:

struct key binsearch(char *word, struct key *tab, ini n)

Esto es algo de gusto personal; seleccione la forma que prefiera y mantngala.

/. INCORRECTO ./

6.5
puesto que la suma de dos apuntadores es ilegal. Sin embargo, la resta es legtima, por lo que high-low es el nmero de elementos, y as
mid

low + (high-Iow) / 2

hace que mid apunte al elemento que est a la mitad entre low y high.
El cambio ms importante es ajustar el algoritmo para estar seguros de que
no genera un apuntador ilegal o intenta hacer acceso a un elemento fuera del arreglo. El problema es que &tab{-l] y &tab]n] estn ambas fuera de los lmites del
arreglo tab . La primera es estrictamente ilegal, y es ilegal desreferenciar la segunda.
Sin embargo, la definicin del lenguaje garantiza que la aritmtica de apu ntado-

Estructuras autorreferenciadas

Supngase que deseamos manejar el problema ms general de contar las ocurrencias de lodas las palabras en alguna entrada. Como la lista de palabras no se
COnoce por anticipado, no podemos ordenarlas convenientemente y utilizar una

busqueda binaria. No podemos hacer una bsqueda lineal para cada palabra que
llega, para ver si ya se ha visto, puesto que el programa tomara demasiado tiemPO. (En forma ms precisa, su (iempo de ejecucin tiende a crecer en proporcin

Cuadrtica co n el nmero de palabras de entrada.) Cmo podemos organizar los


datos para tratar eficientemente una lista de palabras arbitrarias?
Una solucin es mantener siempre ordenado el conjunto de palabras que ya
se han vis to, colocando cada una en su posicin correcta cua ndo llega. Esto,

154

ESTRUCTURAS

CAPITULO 6

de cualquier manera, no se podra realizar recorriendo las palabras en un arreglo


lineal -tambin tomado demasiado liempo. En lugar de ello utilizaremos una

estructura de datos llamada rbol binario.

ESTRUCTURAS AUTORREFERENCIADAS

SECC ION 6.5

155

Esta declaracin recursiva de un nodo podra parecer riesgosa. pero es correcta.


Es ilegal que una estructura contenga una instancia de s misma, pero
struct tnode "left;

El rbol contiene un "nodo" por capa palabra distinta; cada nodo Contiene
declara a 1eft como un apuntador a tnode, no como un tnode en s.
ocasionalmente, se requiere de una variacin de estructuras autorreferenciadas:
doS estructuras que hagan referencia una a la otra. La forma de manejar esto es:

un apuntador al txto de la palabra


una cuenta del nmero de ocurrencias
un apuntador al nodo hijo de la izquierda
un apuntador al nodo hijo de la derecha

slruet I {

Ningn nodo puede tener ms de dos hijos; slo puede tener cero o uno.
Los nodos se mantienen de tal manera que en cualquier nodo el subrbol izquierdo contiene slo palabras que son lexicogrficamente menores que la palabra que est en el nodo, y el subrbol de la derecha slo contiene palabras que
son mayores. Este es el rbol para la oracin "Es tiempo de que todos los hom-

struct s {

bres buenos vengan al auxilio de su partido", como se construy al insertar cada

);

palabra tal como fue encontrada .

/"de
tiempo
/
/
" -todos
buenos
que
/

auxilio

los

/ .. p apunta a una s */

);

struct t "q;

/ .. q apunta a una t */

El cdigo de todo el programa es sorprendentemente pequeflo, dado un


nmero de rutinas de soporte, como getword, que ya hemos descrito. La rutina

es

al

struct s *p;

/ \

su vengan

hombres partido

Para descubrir si una nueva palabra ya est en el rbol, inicie en la raz y comp-

re la con la que est almacenada en ese nodo. Si coincide, la pregunta se responde


afirmativamente. Si la nueva palabra es menor que la palabra del rbol, contine
buscando en el nodo hijo de la izquierda o, de otra manera, en el nodo hijo de la
derecha. Si ya no hay un hijo en la direccin requerida, la palabra nueva no est
en el rbol, y de hecho la entrada vacia es el lugar apropiado para agregar la palabra nueva. Este proceso es recursivo, ya que la bsqueda desde cualquier nodo
emplea una bsqueda desde uno de sus hijos . Por ello, unas rutinas recursivas
para insercin e impresin sern lo ms nat ural.
Regresando a la descripcin de un nodo, se representa convenientemente como una estructura con cualro componentes:
struct tnode {
/ - el nodo del rbol: - /
/ * apunta hacia el texto .. /
char *word;
int count;
/ * nmero de ocurrencias .. /
/ .. hijo a la izqUierda .. /
struct tnode *left;
struct tnode *right;
/ * hijo a la derecha .. /
);

principal lee palabras con gelword y las instala en el rbol con addtree
#include < stdio.h>
#include < ctype.h>
#include < string.h >

#defne MAXWORD 100


struct tnode *addtree(struct tnode *, chal .. );
void treeprint(struct tnode *);
int getword(char *, int);
; . conteo de frecuencia de palabras .. /
main( )

{
struct tnode *TOol;
ehar word[MAXWORD];

root ~ NULL;
while (getword(word, MAXWORD) ! ~ EOF)
if (isalpha(word[OJ))
root = addtree(root, word);
treeprint(root);
return O;

La funcin addtree es recursiva. main presenta una palabra al nivel superior

del rbol (la raz). En cada etapa, la palabra se compara con la que ya est alma-

156

CAPITULO 6

ESTRUCTURAS

SECCION 6.S

ESTRUCTURAS AUTORREFER"NClADAS

157

treeprint(p- > left);


printf("%4d %s\n", p- > count, p- > word);

cenada en el nodo, y se filtra bajando hacia el subrbol izquierdo o derecho con


una llamada recursiva a addtree. Finalmente la palabra coincidir con algo

treeprint(p- > right);

que ya est en el rbol (en cuyo caso la cuema se incremema), o se encuemra Un


apuntador nulo, indicando que se debe crear un nodo y agregarlo al rbol. Si Se
crea un nuevo nodo, addlree regresa un apuntador a l, y lo instala en el nodo padre.
struct tnode "talloc(void);
char "strdup(char ,,);

Una nota ..rctica: si el rbol se "desbalancea" debido a que las palabras no


llegan en orden aleatorio, el tiempo de ejecucin puede aumentar demasiado. En

/ " addtree:
agrega un nodo con w, en o bajo p ,,/
struct tnode "addtree(struct tnode "p, char *w)

costosa simulacin de bsqueda lineal. Existen generalizaciones del rbol binario

el peor de los casos, si las palabras ya estn en orden, este programa reali za una

inl cond;
/" lleg una nueva palabra ,, /

iI (p = = NULL){

/ - crea un nuevo nodo - /


p = talloe( );
p- > word = strdup(w);
p- > count = 1;

p->Ieft = p->right = NULL;


) else iI eond = stremp(w, p-> word = = O)
p->count+ +;
/ " palabra repetida ,, /
else f (cond < O)
/ - menor que el contenido del subrbol izquierdo -/

p- > left = addtree(p- > left,


else

w);

/ " mayor que el contenido del subrbol derecho -/

p- > right = addtree(p- > right, w);


return p;

que no padecen de este comportamiento del peor caso, pero no las describiremos

aqu.
Antes de dejar este ejemplo, tambin es deseable una breve exposicin sobre
un problema relacionado con los asignadores de memoria. Es claramente deseable que slo exista un asignador de almacenamiento en un programa, aun cuando
asigne diferentes clases de objetos. Pero si un asignador va a procesar peticiones
de, digamos, apuntadores a ehar y apuntadores a slruel Inodes, surgen dos preguntas . Primera, cmo cumple los requisitos de la mayor pacte de las mquinas
reales, de que los objetos de ciertos tipos deben satisfacer restricciones de alineacin (por ejemplo, generalmente los enteros deben ser situados en localidades pares)? Segunda, cules declaraciones pueden tratar con el hecho de que un asignadar de memoria necesariamente debe regresar diferentes clases de apuntadores?
Los requisitos de alineacin por lo general se pueden satisfacer fcilmente, al
costo de algn espacio desperdiciado, asegurando que el asignador siempre regrese un apuntador que cumpla con todas las restricciones de alineacin. El anoe
del captulo 5 no garantiza ninguna alineacin en particular, de modo que emplearemos la funcin malloe de la biblioteca estndar, que si lo hace. En el capitulo 8
se mostrar una forma de realizar malloc.

El espacio de almacenamiento para el nuevo nodo se obtiene con la rutina


tanoe , la cual regresa un apuntador a un espacio libre adecuado para mantener
un nodo del rbol, y la nueva palabra se copia a un lugar acuito con slrdup. (Hablaremos de esas rutinas en un momento.) La cuenta se inicializa y los dos hijos
se hacen nulos. Esta parte del cdigo se ejecuta slo para las hojas del rbol,
cuando est siendo agregado \Jn nuevo nodo. Hemos omitido (imprudencialmente) la revisin de errores en los valores regresados por slrdup y lanoe .
Ireeprint imprime el rbol en forma ordenada; para cada nodo escribe el subrbol izquierdo (todas las palabras menores que sta), despus la palabra en s, Y
posteriormente el subrbol derecho (todas las palabras mayores). Si se siente
il1l)cguro sobre la forma en que trabaja la recursin, sim ule la operacin de
treeprint sobre el rbol mostrado anteriormente.
/ " treeprint :
impresin del rbol p en orden ,, /
void treeprint(struct tnode *p)

iI (p ! = NULL) {

La pregunta acerca del tipo de declaracin para una funcin como manoe es
dificil para cualquier lenguaje que tome con seriedad la revisin de tipos. En
e, el mtodo apropiado es declarar que manoe regresa un apuntador a void, despus forzar explicitamente con un caSI al apuntador para hacerlo del tipo deseado, manoe y las rutinas relativas estn declaradas en el header estndar
< sldlib.h >. As, lanoe se puede escribir como
#include < stdlib.h>
/* talloc:

crea un tnode -/

struet tnode otalloe(void)


{
retum (struct tnode *) malloc(sizeof(struct tnode});

strdup simplemente copia la cadena dada por su argumento a un lugar segu~

ro, obtenido por una llamada a mance:

158

ESTRUCTURAS

char *strdup(char *s)


{
char ''p;

CAPITULO 6

/* crea un duplicado de s */

p ~ (char -) malloc(strlen(s) + 1); - + 1 para ,\0' -


if (p ! ~ NULL)
strcpy(p, s);
return p;

Ejercicio 6-4. Escriba un programa que imprima las distintas palabras de su entrada, ordenadas en forma descendente de acuerdo con su frecuencia de ocurrencia . Precede a cada palabra por su conteo. O

nombre
defn

Ejercicio 6-3. Escriba un programa de referencias cruzadas que imprima una liSIa
de todas las palabras de un documento, y para cada palabra, una lista de los nmeros de linea en los que aparece. Elimine palabras como "el", "y", etctera. O

159

slo cadenas de caracteres. lookup(s) busca s en la labia y regresa un apuntador


al lugar en donde fue encontrado, o NULL si no est.
El algoritmo es una bsqueda hash -el nombre que llega se convienc a un
pequeo entero no negativo, que despus se usa para indexar un arreglo de apuntadores. Un elemento del arreglo apunta al principio de una lista ligada de bloques
que describen nombres que ti enen ese valor de hash. El elemento es NULL ..,i ningn nombre ha obtenido ese valor .

malloe regresa NULL si no ha y espacio dispo nible; strdup pasa ese valor, dejando el manejo de error a su invocador.
El espacio obtenido al llamar a malloe puede liberarse para su reutilizacion
llamando a free; vanse los caplulos 7 y 8.
Ejercicio 6-2. Escriba un programa que lea un programa en e imprima en orden alfabtico cada grupo de nombres de variable que sean idnticas en sus primeros 6 caracteres, pero diferentes en el resto. No cuente palabras dentro de cadenas ni comentarios. Haga que 6 sea un parmetro que pueda fijarse desde la
linea de rdenes. O

BUSQUEDA EN TABLAS

SECCION 6.6

nombre
defn

Un bloque de la lista es una estructura que contiene apuntadores al nombre,


al texto de reemplazo y al siguiente bloque de la lista. Un siguiente apuntador
nulo marca el final de la lista.
struct nlist {
/ ,. entrada de la tabla: */
struct nlist ,.next; / ,. siguiente entrada en la cadena */
char ,.name;
/ ,. nombre definido */
char -defn;
/* texto de reemplazo */

};
El arreglo de apuntadores es slo
#define HASHSIZE 101

6.6

Bsqueda en tablas

En esta seccin escribiremos los componentes de un paquete de bsqueda en


tahta~, para ilustrar ms aspectos acerca de estructuras . Este cdigo es (pico de
lo que podra encontrarse en las rutinas de manejo de tablas de smbolos de un
macroprocesador o compilador. Por ejem plo, considere la proposicin #define.
Cuando se encuentra una lnea co mo
#define

IN

el nombre IN y el texto de rccmpla/o 1 se almacenan en una tabla. Despus,


cuando el nombre IN aparcce en una proposicin como
state = IN;

se debe reemplazar por 1.


Existen dos rutinas que manipulan los nombres y textos de reemplazo .
install(s, t) registra el nombre s y el texto de reemplazo t en una tabla; s y t son

static strue! nlis -hashtab[HASHSIZE}; - tabla de apuntadores

La funcin de hash, que se utiliza tanto en lookup como en install, agrega


cada valor de carcter de la cadena a una combinacin mezclada de los anteriores
y regresa el mdulo del residuo entre el tamao del arreglo. Esta no es la mejor
funcion de hash posible, pero es pequea y efectiva.
/ * hash: forma un valor hash para la cadena s ,. /
unsiqned hash(char s)
{
unsigned hashval;
for (hashval ~ O; -s ! ~ '\0'; s + +)
hashval = S + 31 * hashval;
return hashval % HASHSIZE;

La aritmtica sin signo asegura que el valor de hash no el, negativo.

160

ESTRUCTURAS

CAPITULO 6
SECC ION 6.7

El proceso de hash produce un ndice inicial en el arreglo hashtab; si la cadena


se encontrara en algn lugar, ser en la lista de bloques que empieza all. La bs_
queda se realiza por lookup. Si lookup encuentra que la entrada ya est presente
'
regresa un apuntador a ella; de otra manera, regresa NULL.
/ * lookup:
busca s en hashtab */
struct nlist *lookup(char *s)
{
struct nlist *np;

lor (np = hashtab[hash(s)l; np ! = NULL; np


il (stremp(s, np- > name) = = O)
return np;
retum NULL;

TYPEDEF

161

Ejercicio 6-5. Escriba una funcin undef que borre un nombre y una defini-

cin de la tabla mantenida por lookup e instal!. o


Ejercicio 6-6. Haga una versin simple del procesador #define (esto CS, sin
argumentos) adecuada para usarse con programas en e, basada en las rutinas de
esta seccin. Tambin podr encontrar tiles getch y ungetch. O

np->next)

/ * se encontr */
/ * no se encontr */

El ciclo for que est en lookup es la expresin idiomtica estndar para moverse
sobre una lista ligada:
lor (ptr = head; ptr ! = NULL; ptr = ptr- > next)
install usa a lookup para determinar si el nombre que se va a instalar ya est
presente; de ser asi, la nueva definicin toma el lugar de la anterior. De otra manera, se crea una nueva entrada. install regresa NULL si por cualquier razn no
hay espacio para una nueva entrada.
struct nlist *lookup(char *);
char *strdup(char *);
/ * install: coloca (name, defn) dentro de hashtab */
struct nlist *install(char *name, char *defn)

6.7

Typedef

e proporciona una facilidad llamada typedef para crear nuevos tipos de datos. Por ejemplo, la declaracin
typedef int Longitud;

hace del nombre Longitud un sinnimo de in!. El tipo Longitud puede emplearse
en declaraciones, cas(s, etc., exactamente de la misma manera en que lo podra
ser int.
Longitud

len, maxlen;

Longitud

'lengths[ 1;

De modo semejante, la declaracin


typedef char *Cadena;

hace a Cadena un sinnimo para char * o apuntador a carcter, que despus puede usarse en declaraciones y caslS:

Cadena p, lineptr[MAXLINES]. alloe(int);


int slrcmp(Cadena, Cadena);

struct nlist *np;


unsigned hashval;

if ((np = lookup(name = = NULL) { l' no lue encontrado '1

np = (strue! nlist .) malloe(sizeol('np;


il (np = = NULL :: (np- > name = strdup(name = = NULL)
return NULL;

hashval = hash(name);
np-> next = hashtab[hashval];
hashtab[hashval] = np;
] else
l ' ya est alli ' 1
free((void *) np->defn); / * libera la anterior defn */
iI ((np- > deln = strdup(deln) = = NULL)
retum NULL;
return np;

p = (Cadena) malloc(lOO);
Ntese que el tipo que se declara en un typedef aparece en la posicin de un
nombre de variable, no justo despus de la palabra typedel. Sintcticamente,
tYpedef es como las clases de almacenamiento extern, static, etc. Hemos empleado nomb res con mayscula para los typedef, para destacarlos.
Como un ejemplo ms complicado, podramos declarar mediante typedef los
nodos del rbol mostrados anteriormente en este captulo:
Iypedef struct Inode Treeptr;
typedef struct tnode {
char *word;
int count;
Treeptr left;
Treeptr riqht;
} Treenode

l ' el nodo del rbol: ' 1


/ * apunta hacia el texto -/
/ * nmero de ocurrencias */
/ * hijo izquierdo */
/ * hijo derecho */

162

ESTRUCTURAS

CAPITULO 6

Esto crea dos nuevas palabras reservadas para tipos. llamados Treenode (una es-

tructura) y Treeplr (un apuntador a la estructura). Entonces, la rutina talloc


podra ser

Treeptr talloc(void)
{
return (Treeptr) malloc(sizeof(Treenode;

union u_tag {
int ival;
float fval;
char *sval;

te. Tampoco es alguna nueva semntica : las variables declaradas de esta manera
tienen exactamente las mismas propiedades que las variables cuyas declaraciones

se escriben explicitamente. En efecto, Iypedef es como #define, excepto que al


ser interpretado por el compilador puede realizar substituciones textuales que
estn ms all de las capacidades del preprocesador. Por ejemplo,

crea el tipo AAF, de "apuntador a funcin (de dos argumentos char *) que regresa int" , el cual se puede usar en contextos como
AAF strcmp, numcmp;

dentro del breve programa del capitulo 5.


Adems de las razones puramente estticas, hay dos razones principales para
emplear typedef. La primera es paramctrizar un programa contra los proble-

mas de transportabilidad . Si se emplea typedef para tipos de datos que pueden ser
dependientes de la mquina, cuando un programa se traslada, slo los typedef
requieren de cambios. Una situacin comn es usar nombres de typedef para varias cantidades enteras, y entonces hacer un conjunto apropiado de selecciones de

short, int y long para cada mquina. Tipos como size_t y ptrdifLt de la biblioteca
estndar son ejemplos.
El segundo propsito dc los Iypedef es proporcionar mejor docume ntacin para un programa -un tipo llamado Treeplr puede ser ms fcil de entender
que uno declarado slo como un apuntador a una estructura complicada.

163

Como un ejemplo, que podra ser encontrado en el manejador de la tabla de


smbolos de un compilador. supngase que una constante podra ser un [], un
Boat, o un apuntador a carcter. El valor de una constante en particular debe ser
guardado en una variable del tipo adecuado. No obstante, es conveniente para el
manejador de tablas si el valor ocupa la misma cantidad de memoria y es guardado en el mismo lugar sin importar su tipo. Este es el propsito de una "unin"
-una sola variable que puede legtimamente guardar uno de varios tipos. La sintaxis se basa en las estructuras:

Se debe destacar que una declaracin Iypedef no crea un nuevo tipo en


ningn sentido; simplemente agrega un nuevo nombre para algn tipo ya existen_

typedel int ('AAF)(char " char .);

UNIONES

SECC ION 6.8

} u;

La variable u ser suficientemente grande como para mantener al mayor dc los


tres tipos: el tamao especfico depende de la implantacin. Cualquiera de estos tipos puede ser as ignado a u y despus empicado en expresiones, mientras que el uso
sea consistente: el tipo recuperado debe ser el tipo que se almacen ms reciente

mente. Es responsabilidad del programador llevar el registro del tipo que est almacenado actualmente en una unin; si algo se almacena como un tipo y se recu-

pera como otro, el resultado depende de la implantacin.


Sintcticamente, se tiene acceso a los miembros de una unin con
nombre-unin. miembro

o
apunlador-unin- > miembro

precisamente como a las estructuras. Si la variable utype se emplea para llevar


el registro del tipo actualmente almacenado en u, entonces se podra ver el cdigo
como
if (utype = = INT)
printf("%d\n", u.ival);

else il (utype = = FLOAT)


printf("%f\n", u.fval);

else il (utype

= =

STRING)

printf("%s\n", u .sval);
else
printf("dato incorrecto %d en utype\n", utype);

6.8

Uniones

Una unin es una variable que puede contener (en momentos diferentes) objetos de diferentes lipos y tamaos, y el compilador hace el seguimiento del tamaO
y requbitos de alineacin. Las uniones proporcionan una forma de manipular diferemes clases de datos dentro de una sola rea de almacenamiento, sin incluir en
el programa ninguna informacin dependiente de la mquina. Son anlogas a los
varia/1l records de Pascal.

Las uniones pueden presentarse dentro de estructuras y arreglos, y viceversa.


La notacin para tener acceso a un miembro de una unin en una estructura (o

Viceversa) es idntica a la de las estructuras anidadas. Por ejemplo, en el arreglo


de estructuras definido por
strue! {
char "name;
int flags;

164

ESTRUCTURAS

CAPITULO 6

int utype;
union {
int ival;
float val;
char *sval;

CAMPOS DE BITS

SECCION 6.9

165

#define KEYWORD 01
#define EXTERNAL 02
#define STA TIC
04

o
enum { KEYWORD = 01, EXTERNAL = 02, STATIC = 04 };

} u;

} symtabI NSYM};

al miembro ival se le refiere como


symtab[i].u.ival

y al primer carcter de la cadena sval por cualquiera de


*symtab[i].u.sval
symtabli}. u.svaIIO}

En efecto, una unin es una estructura en la cual todos los miembros tienen
un desplazamiento de cero a partir de la base, la estructura es suficientemente
grande para mantener al miembro "ms ancho", y la alineacin es la apropiada

para todos los tipos de la unin. Estn permitidas las mismas operaciones sobre
las uniones como sobre las estructuras : asignacin o copia como unidad, to mar
la direccin, y hacer el acceso a un miembro.

Una unin slo se puede inicializar con un valor del tipo de su puimer miembro, as que la unin u descrita anteriormente s lo se puede inicializar co n un valor entero.

El asignador de almacenamiento del captulo 8 muestra cmo se puede usar


una unin para ob ligar a que una variable sea alineada para una clase particular
de lmites de almacenam iento.

6.9

Campos de bits

Cuando el espacio de almacenamiento es escaso, puede ser necesario em-

paquetar varios objetos dentro de una sola palabra de mquina; un uso comn
es un conjunto de banderas de un bit en aplicaciones como tablas de smbolos
para comp iladores. Los formatos de datos impuestos externamente, como inter-

faces hacia dispostivos de hardware, frecuentemente requieren la capacidad de


tomar partes de una palabra.
Imagnese un fragmento de un compilador que manipula una tabla de smbolos . Cada identificador dentro de un programa tiene cierta informacin asociada
a l, por ejemplo, si es o no una palabra reservada, si es o no externa y/ o esttica
y otros aspectos. La forma ms compact a de codificar tal informacin es con un
conjunto de banderas de un bit dentro de un char o int.

La forma usual en que esto se realiza es definiendo un conjunto de "mscaras" correspondientes a las posiciones relevantes de bits, como en

LO" nmeros deben ser potencias de dos . El acceso a lo s bits \ iene a ser cosa de
"jugar" con los operadores de corrimiento, enmascaramiento y co mplemento,
que se describieron en el ca ptulo 2.
Ciertas expresiones aparecen frecuentemente:
f1ags : = EXTERNAL : STATIC;

enciende los bits EXTERNAL y STATIC en flags, en tanto que


flags & = -(EXTERNAL : STATIC) ;

los apaga, Y
if ((flag s & (EXTERNAL : STATIC))

O)

es verdadero si ambos bits estn apagados.


Aunq ue estas expresiones se dominan fcilmente, como alternati va

ofre-

ce la capacidad de definir y tener acceso a campos de una palabra ms directameJl{e que por medio de operadores lgicos de bits. Un campo de bits, o simplemente campo, es un conjunto de bits adyacentes dentro de tina unidad de almacenamiento definida por la imp lantacin, al que llamaremos "pa labra". La sintaxi s

para la definicin y acceso a campos est basada en estructuras. Por ejemplo, la anterior tabla de smbolos #define podra haber sido reemplazada por la definicin
de tres campos:
struct {
unsigned int is..-keyword : 1;
l'
unsigned int is_ extern
unsigned int is~tatic
: 1;
} flags;

Esto define una variable llamada f1ags, que contiene tres campos de un bit. El
numero que sigue al carcter dos puntos representa el ancho del campo en bits.
lo~ ca mpos son declarados unsigned int para asegurar que sean cantidades si n
signo.

Los cam pos individuales son referidos en la misma forma que para otros
miembros de estructuras: f1ags.isJeyword, f1ags-is_ extern, etc. Los campos
se Comportan como pequeos enteros y pueden participar en expresiones aritmticas, como lo hacen otros enteros. As, el ejemplo previo pudo escribirse ms naturalmente como
flags.is_ extern = flaqs.is~tatic = 1;

166

ESTRUCTURAS

CAPITULO 6

para encender los bits;


flags.is._extern

flags.is_static

O;

para apagarlos; y
if (flag.is_exiern

o && flaqs.is-static

O)

para probarlos.
Casi IOdo acerca de los campos es dependiente de la implantacin. El que un cam.
po pueda traslapar al limite de una palabra se define por la implantacin . Los

CAPITULO 7

Entrada y salida

campos no necesitan tener nombre; los campos sin nombre (dos puntos y su am-

plitud solamente) se emplean para llenar espacios. El ancho especial O puede em.
plearse para obligar a la alineacin al siguiente lmite de palabra .
Los campos se asignan de izquierda a derecha en algunas mquinas y de dere.
cha a izquierda en otras. Esto significa que aunque los campos son tiles para
el mantenimiento de estructuras de datos definidas internamente, la pregunta de
qu punta viene primero tiene que considerarse cuidadosamente cuando se selec.

cionan datos definidos externamente; los programas que dependen de tales cosas
no son transportables. Los campos slo se pueden declarar como enteros ; por
tran sportabilidad, se dcbe especificar explcitamente signed o unsigned. No
son arreglos y no tienen direccioncs, de modo que el operador & no puede apl icarse a ello~ .

Las operac iones de entrada y sa lida no SOI1 en si parte del lenguaje e, por 10
que hasta ahora no las hemos destacado. Sin embargo, los programas interactan
con su medio ambiente en forma~ lllucho lll~ complicadas de las que hemos mostrado antes. En este capitulo describircmos la bibliOleca estndar, un co njunto
de funciones que proporcionan entrada y salida, manipulacin de cadenas, manejo de memoria, rutinas matemticas y una variedad de otros serv icios para
programas en e, aunque haremos hincapi en la entrada y sa lida.
El e. . tndar ANSI define demallera prel:isae:-,Ias fUIll'iones de biblioteca, de
modo que pueden existir en forma t'ol11[1alible en cualquier sistema en donde
exista C. Los programas que restringen su inleraccill con el sistema a las facilidade~ provistas por la biblioteca cs tilllda r pueden '-Ier Ile \'ados dc un siste ma a
otro ... in cambios.

Las propiedades de las funciones de biblioteca estn especi ficadas en ms de


ulla docena de headers; ya helllo.., \ i~to a lguno::.. incluyendo < stdio.h > .
<stri ng.h> y <ctype.h>. No presentaremo'-l aqui b 100alidad de la biblioteca,
PUC'-,O que estamos ms interesados en csnibir programas en e que los usan. La
biblioteca se describe e n detalle cn el apcndicc il.

7.1

Entrada y salida estndar

Co rno sealamos en el captu lo 1, la biblioteca consiste en un modelo simple


ele cntrada y salida de lexto. Un flujo de (exto consiste en una secuencia de lneas.
cada una de las cuales termina con un carcter nueva lnea. Si el sistema no opera
de C'-le modo, la biblioteca hace lo que sea necesario para simular que as funciona . Por ejemplo, la biblioteca podra convenir el regreso de carro y a\'ance de
lnea a una nu eva lnea en la e ntrada y de IlUC\'O en la sa lida.
El mecanismo de entrada ms simple es leer un carcter a la vez de la ellfrada
estndar, normalmente el teclado, con getchar:
ini getchar(void)
167

168

ENTRADA Y SALIDA

CAP ITULO 7

getchar regresa el siguiente carcter de la entrada cada vez que se invoca, o EOF
cuando encuentra fin de archivo. La constante simblica EOF est definida en
< stdio.h > . El valor es tpicamente -1, pero las pruebas se deben escribir en funcin de EOF, de modo que sean independientes del valor especifico.
En muchos medios ambiemes, un archivo puede lOmar el lugar del teclado
empleando la convencin < para redireccionamiento de entrada: si un programa
prog usa getchar, entonces la lnea de rdenes

SECClON 7.2

SALIDA CON FORMATO-PRINTF

169

programa a la entrada de otro. Por ejemplo, considrese el programa lower, que


convierte su entrada a minsculas:
#inclu::le < stdio.h >
#include <ctype.h>
main(}

/* lower:

convierte la entrada a minsculas '* /

int c;

prog < archent


provoca que prog lea caracteres de archent. El cambio de la entrada se realiza
de tal manera que prog mismo es ajeno al cambio; en particular, la cadena "< archent" no est incluida entre los argumentos de la linea de rdenes en a rgv. El
cambio de la entrada es tambin invisible si la. entrada viene de otro programa
va un mecanismo de interconexin (pipe): en algunos sistemas, la lnea de rdeotroprog : prog

nes ejecuta tanto al programa otr oprog como a prog, e interconecta la salida esde otroprog con la entrada estndar para prog.
La funcin

~tndar

int putchar(int)
se emplea para salida: putchar(c) coloca el carcter c en la sq/ida estndar, que
por omisin es la pantalla. putchar regresa el carcter escrito, EOF si ocurre
algn error. De nuevo, la salida puede ser dirigida hacia algn archivo con
> nombrearch: si prog utiliza putchar,

while e = getchar( )) ! = EOF)


pu tehar( tolower( e));
return O;

La funcin tolower est definida en < ctype.h >; convierte una letra mayscula a minscula, y regresa los otros caracteres intactos. Como mencionamos antes, las "funciones" como getchar y putchar en <stdio.h> y tolower en
<ctype .h> son a menudo macros, evitndose as la .)obrecarga de una llamada a
funcin por cada carcter. En la seccin 8.5 se mostrar cmo se hace esto. Sin
importar cmo sean las funciones de <ctype.h> en una mquina dada, los
programas que las emplean estn aislados del juego de caracteres.
de caracteres.
Ejercicio 7~ 1. Escriba un programa que convierta maysculas a minsculas o viceversa , dependiendo del nombre con que se invoque, dado en argv[O]. O

prog > archsal


escribir la salida estndar hacia archsaL Si se permite la interconexin,
prog

otroprog

deja la salida estndar de prog en la entrada estndar de otroprog.


La salida producida por printf tambin encuentra su camino hacia la salida estndar. Las llamadas a putchar y a printf pueden estar traslapadas -la salida
aparece en el orden en que se hicieron las llamadas.
Cada archivo fuente que se refiera a una funcin de biblioteca de entrada/ salida debe contener la linea
#include < stdio.h >
antes de la primera referencia. Cuando un nombre se delimita por < y > se realiza una bsqueda del header en algunos lugares estndar (por ejemplo, en los si s~
lemas UNIX, tpicamente en el directorio /usr/include).
Muchos programas leen slo un flujo de entrada y escriben slo un flujo de
salida; para tales programas la entrada y salida con getchar, putchar y printf,
puede ser totalmeme adecuada y en realidad es su ficiente para comenzar . EstO
es particularmente cierto si se emplea la redi reccin para conectar la salida de un

7.2

Salida con formato-prinlf

La funcin de salida printf traduce valores imernos a caraCleres. Ya hemos


empleado informalmente printf en los capitulos anteriores. La descripcin de
aqu cubre los usos ms tpicos, pero no est completa; para la definicin completa, vase el apndice B.
int printf(char '*format, arg j , arg2 ,

... )

printf convierte, da formato e imprime sus argumentos en la salida estndar bajo


el control de format. Regresa el nmero de caracteres impresos.
La cadena de formato contiene dos tipos de objetos : caracteres ordinarios,
que son copiados al flujo de salida, y especificaciones de conversin, cada uno
de los cuales causa la conversin e impresin e los siguientes argumentos sucesiVos de printf. Cada especificacin de conversin comienza con un 070 y termina
con Un carcter de conversin. Entre el 070 y el carcter de conversin pueden estar, en orden:

170

CAPITULO 7

ENTRADA Y SALIDA

Un signo menos, que especifica el ajuste a la izquierda del argumento conver-

tido.
Un nmero que especifica el ancho mnimo de campo. El argumento convertido ser impreso dentro de un campo de al menos este ancho. Si es necesario

ser llenado de blancos a la izquierda (o a la derecha, si se requiere ajuste a


la izquierda) para completar la amplitud del campo.

:%s:
:%105:
:%.10s:
%-105:
:%.15s:
:%-15s:
:%15.108:
:%-15.108:

cadena que sern impresos, o el nmero de dgitos despus del punto decimal
de un valor de punto flotante, o el nmero mnimo de dgitos para un entero.
Una h si el enLero ser impreso como un short, o una 1 (letra ele) si ser como
un long.

TABLA 7-1.

CONVERSIONES RASICAS DE PRINTF

CARACTER

TIPO DE ARGUMENTO: IMPRESO COMO

d, i
o

int; nmero decimal.


int; nmero octal sin signo (sin cero inicial) .
int; nmero hexadecimal sin signo (con un Ox o OX inicial, usando abcdef
o ABCDEF para 10, .. 15.
int; nmero decimal sin signo.
int; carcter sencillo.
char .; imprime caracteres de una cadena hasta un ,\0' o el nmero de caracteres dado por la precisin.
double; [-] m.dddddd, en donde el nmero de ds est dado por la precisin (predeterminado a 6).

x, X
u

e
8

e, E

double; [-]m.ddddddexx o [- ]m.ddddddE=, en donde el nmero de

g, G

ds est dado por la precisin (predeterminado a 6).


double; usa 11!0e o 11!0E si el exponente es menor que -4 o mayor o igual
a la precisin; de otra forma usa %f. Los ceros o el punto al final no se

OJo

imprimen.
void .. ; apuntador (representacin dependiente de la instalacin).
I no es convertido en ningn argumento; imprime un OJo .

Una amplitud o precisin se puede especificar por ., en cuyo caso el valor se

calcula convirtiendo el siguiente argumento (que debe ser int). Por ejemplo, para
imprimir al menos max caracteres de una cadena s,
printf("% ... s", max, 5);

171

La mayora de las conversiones de formato se han ilustrado en captulos anteriores. Una excepcin es la precisin relacionada con las cadenas. La siguiente
tabla muestra el efecto de una variedad de especificaciones al imprimir "hola,
mundo" (11 caracteres). Hemos colocado el car<lcter dos puntos alrededor de cada campo para que se pueda apreciar su extensin.

Un punto, que separa el ancho de campo de la precisin.


Un nmero, la precisin, que especifica el nmero mximo de caracteres de una

Los caracteres de conversin se muestran en la tabla 7-1 . Si el carcter despus del


OJo no es una especificacin de conversin, el comportamiento no est definido .

LISTAS DE ARGUMENTOS DE LONGITUD VARIABLE

SECCION 7.3

:hola,
:hola,
:hola,
:hola,
:hola,
:hola,

mundo:
mundo:
mund:
mundo:
mundo:
mundo
hola, mund:
:hola, mund

Una advertencia: printf emplea su primer argumento para decidir cuntos


argumentos le siguen y cules son sus tipos. printf se confundir y se obtendrn
resultados errneos si no hay suficientes argumentos o si tienen tipos incorrec-

tos. Tambin debe advertir la diferencia entre estas dos llamadas;


pntf(8);
printf("%8", 8);

/ .. FALLA si s contiene % .. /
/. SEGURO . /

La funcin sprintf realiza las mismas conversiones que printf, pero almacena
la salida en de una cadena :
int sprintf(char "cadena, char "format, arg], arg 2 ,

... )

sprintfda formato a los argumentos que estn en arg l , arg2 etc., ue acuerdo con
format como antes, pero coloca el resultado enlcadenalen vez de en la salida es-

tndar; Icaden~ debe ser suficientemente grande como para recibir el resultado.
Ejercicio 7-2. Escriba un programa que imprima una entrada arbitraria en forma
sensata. Como mnimo, deber imprimir caracteres no grficos en octal o hexade-

cimal de acuerdo con la costumbre local, y separar lneas largas de texto. O

7.3

Listas de argumentos de longitud variable

Esta seccin contiene la realizacin de una versin mnima de printf, para


fllOstrar cmo escribir una funcin que procese una lista de argumentos de longi-

tUd varia ble en una forma transportable. Puesto que estamos interesados princiPalmente en el procesamiento de argumentos, minprintf procesar la cadena de
formato y los argumentos, pero llamar al printf real para hacer las conversiones

de formato.

172

CAPITULO 7

ENTRADA Y SALIDA

SECCION 7.4

ENTRADA CON FORMATO-SCANF

printf("%d", ival);
break;

La declaracin correcta para printf es

int printf(char -Imt, ... )

case 'f':
dval

donde la declaracin ... significa que el nmero y tipo de esos argumentos puede
variar. La declaracin .. . slo puede aparecer al final de la lista de argumentos.

= vL-arg(ap, double);

printl("%I", dval);
break;
case 's':
for (sval

Nuestra minprintf se declara como


void minprintf(char *fmt, ... )

ya que no regresar la cuenta de caracteres que regresa printf.

= vL-arg(ap, char *); *sval; sval + +)

putchar( -sval);

El truco est en cmo minprintf recorre la lista de argumentos cuando la lista

break;
default:

ni siquiera tiene un nombre. El header estndar <stdarg.h> contiene un COnjunto de macrodefiniciones que definen cmo avanzar sobre una lista de argumentos. La realizacin de este header variar de una mquina a aira, pero la
interfaz que presenta es uniforme.
El tipo va.Jist se emplea para declarar una variable que se referir a cada
argumento en su momento; enminprintf,eSla variable se llama ap, por "argumenl pointer" (apuntador a argumento). La macro va....start inicializa ap para
apunlar al primer argumento sin nombre. Debe llamarse una vez antes de usar
ap. Al menos debe haber un argumento con nombre; el ltimo argumento con
nombre es empleado por v~tart para iniciar.

173

putchar( -p);
break;

va_end(ap);

/* limpia cuando todo est hecho */

Ejerl'irio 7-3. Aumente minlHinlf para que maneje otras de las

caractcr:-,tica~

de

print!. O

Cada llamada de Vd-arg regresa un argumento y avanza ap al siguiente;


Vd-arg emplea un nombre de tipo para determinar qu tipo regresar y cun gran-

de ser el avance. Finalmente, vd-end realiza las labores de limpieza y arreglo


que sea n necesarias. Debe invocarse ames que la funcian regrese.

Estas propiedades forman la base de nuestro print! simplificado:


#include <stdarg.h>

7.4

Entrada con formato-scanf

La funcin seanf es la entrada anloga de print!, y proporciona muchas de


las mismas facilidades de conversin en la direccin opuesta.
int scanf(char *format, ... )

1* minprintf:

prinU mnima con lista variable de argumentos *1


void minprintf(char *fmt, ... )

va~ist

ap;
1* apunta a cada arq sin nombre en orden */
char *p, *sval;
int ival;

sean! lee caracteres de la entrada estndar, los interpreta de acuerdo con las especificaciones que estn en !ormat, y almacena los resultados a travs de los argumentos restantes . El argumento de formato se describe abajo; los otros argumentos, cada uno de los cuales debe ser un apuntador, indican dnde deber
almacenarse la entrada correspondientemente convertida. Como con printf, esta
seccin es un resumen de las posibilidades ms tiles, no una lista exhaustiva.

sean! se detiene cuando termina con su cadena de formato, o cuando alguna

double dval;

entrada no coincide con la especificacin de control. Regresa como su valor el


va_start(ap, fmt);

/ * hace que ap apunte al ler. arg sin nombre . /

lor (p = Imt; _p; p+ +) {


if (_p!= '%') {

putchar( _p);
continue;

switch (_ + + p) {
case 'd':
ival

= va_arg(ap, int);

nmero de items de entrada que coinciden con xito. Esto se puede emplear para
decidir cuntos tems se encontraron. Al final del archivo, regresa EOF; ntese que esto es diferente de O, que significa que el siguiente carcter de entrada
no coincide con la primera especificacin en la cadena de formato. La siguiente
llamada a sean! contina la bsqueda inmediatamente despus del ltimo carcter que ya fue convertido.
Existe tambin una funcin sscanf que lee de una cadena y no de la entrada
estndar:
int sscanf(char *cadena, char *format, argl' arg 2 ,

... )

ENTRADA Y SALIDA

174

CAPITULO 7

Rastrea la cadena ele acuerdo con el formato en format, y almacena el valor


resultante a travs de argl' arg2 , etc. Estos argumentos deben ser apuntadores.
La cadena de formato generalmente contiene especificaciones de conversi n,
las cuales son empleadas para controlar la conversin de entrada. La cadena de
formato puede contener:

La especificacin de conversin dirige la conversin del siguiente campo de entrada. Normalmente el resultado se coloca en la variable apuntada por el argumento correspondiente. Si se indica la supresin de asignacin con el carcter .. ,
sin embargo, el campo de entrada es ignorado y no se realiza asignacin algu na. Un
campo de entrada est definido como una cadena de caracteres que no son espacio en blanco; se extiende hasta el siguiente espacio en blanco o hasta que el
ancho de campo se agote, si est especificado. Esto imp lica que scanf leer entre
varias lncas para encontrar su entrada, ya que las nuevas lneas son espacios en
blanco. (Los caracteres de espacio en blanco son tabulador, nueva lnea, retorno
de carro, tabulador vertical y avance de hoja.)
El carcter de conversin indica la interpretacin del campo de entrada. E l
argumento correspondiente debe ser un apuntador, como es requerido por la semntica de las llamadas por valor de C. Los caracteres de conversin se muestran
en la tabla 7-2.

175

cadena de caracteres (no entrecomillada); char *, apunta a un arreglo de


Garacleres suficientemente grande para la cadena y una terminacin ,\0'
que ser agregada.
e, f, 9
%

Blancos o tabuladores, los cuales son ignorados.


Caracteres ordinarios (no 070), que se espera coincidan con el siguiente carcter que no sea espacio en blanco del flujo de entrada.
Especificaciones de conversin, consistentes en el carcter OJo, un carcter optat ivo de supresin de asignacin .. , un nmero optativo que especifica el ancho
mximo de campo, una h, 1, o L optativa que indica la amp li tud del objetivo,
y un carcter de conversin.

ENTRADA CON FORMATO-SCANF

SECCION 7.4

nmero de punto flotante con signo, punto decimal y exponente optativos;


float *.
% literal; no se hace asignacin alguna.

Los caracteres de conversin d, i O, U x pueden ser precedidos por h para


indicar que en la lista de argumentos aparece un apuntador a short en lugar de
a in!, O por 1 (letra ele) para indicar que aparece un apuntador a long en la lista
de argumentos. En forma semejante, los caracteres de conversin e, f, g pueden
ser precedidos por 1 para indicar que hay un apuntador a double en lugar de a
!loa! en la lista de argumentos.
Como un primer ejemplo, la rudimentaria calculadora del captulo 4 se puede
escribir con scan! para hacer la conversin de entrada:
#include

<stdio.h>

main() / .. calculadora rudimentaria *1


{
double sum, v;
sum = O;
while (seanf("%lf", &v)

==

1)

printf("\!% .2f\n", 'um + = v);


return O;
Suponga que deseamos leer lneas de entrada que contienen fechas de la forma
25 Die 1988

TABLA 7-2 .

CARACTER

d,

o
u
x

CONVERSIONES BASleAS DE SCAN F

DATO DE ENTRADA: TIPO DE ARGUMENTO:

entero decimal; int *.


entero; int ... El entero puede estar en octal (iniciado con O) o hexadecimal
(iniciado con Ox o OX).
entero octal (con o sin cero inicial); int *.
entero decimal sin signo; unsigned ini *.
entero hexadecimal (iniciado o no con Ox O OX); int *.
caracteres; char *. Los siguientes caracteres de entrada (por omisin l) so n
colocados en el sitio indicado. El salto norma l sobre los espacios en blanca
es suprimido; para leer el siguiente espacio no blanco, use %1s.

La proposicin seanf es
int day, year;
char monthname{20];
scanf(\\%d %s %d", &day, monthname, &year);

No se emplea &. con monthname, ya que un nombre de arreglo es un apuntador.


Pueden aparecer caracteres literales en la cadena de formato de seanf, y deben coincidir con los mismos caracteres de la entrada. De modo que podemos leer
fechas de la forma rnrn/dd/yy con esta proposicin sean!:
int day, month, year;
scanf("%d/%d/%d", &month, &day, &year};

176

ENTRADA Y SALIDA

CAPITULO 7

scanf ignora los blancos y los tabuladores que estn en su cadena de formato
Adems, salta sobre los espacios en blanco (blancos, tabuladores, nuevas lnea;
etc.) mientras busca los valores de entrada. Para leer de entradas cuyo format~
no esta fijo, a menudo es mejor leer una lnea a la vez, y despus separarla con
sscanl. Por ejemplo, suponga que deseamos leer lineas que pueden contener fe.
chas en cualquiera de las formas anteriores. Entonces podemos escribir

while (getline(line, sizeol(line)) > O) {


if (sscanf(line, "%d %5 %d", &day, monthname, &year) = = 3)
printf("vlido: %s\n", Bne);
/ .. forma 25 Dic 1988 .. /
else il (sscanl(line, "%d/%d/%d", &rnonth, &day, &year) = = 3)
printf("vlido: %s\n", line);
1- lorma mrn/dd/yy -1
else
printf("invlido: %s\n", line);
/ .. forma invlida .. /
Las llamadas a scanl pueden estar mezcladas con llamadas a olras fun.
ciones de entrada. La siguiente llamada a cualquier funcin de entrada iniciar
leyendo el primer carcter no leido por sean/.
Una advertencia final: los argumentos de scanf y sscanf deben ser apuntadores.
El error ms comn es escribir
scanf("%d", n);

en lugar de
scanl("%d", &n);
Este error generalmente no se detecta en tiempo de compilacin.
Ejercicio 7-4. Escriba una versin privada de scanl anloga a minprint/ de la
secci n anterior. O
Ejercicio 7-5. Reescriba la calculadora post fija del captulo 4 usando scanl y/o
scanl para hacer la entrada y la conversin. O

7.5

Acceso a archivos

Hasta ahora todos los ejemplos han ledo de la entrada estndar y escrito en
la salida estndar, las cuales se definen automticamente para los programas
por el sistema operativo local.
El siguiente paso es escribir un programa que d acceso a un archivo que no
est ya coneclado al programa. Un programa que ilustra la necesidad de tales
operaciones es cat, el cual concatena en la salida estndar un conjunto de archi
vos nombrados. cat se emplea para escribir archivos en la pantalla, y como un

SECC ION 7.S

ACCESO A ARCHIVOS

177

colector de entradas de propsito general para programas que no disponen de la


capacidad de lener acceso a los archivos por nombre. Por ejemplo, la orden
cal x.e y.c

imprime el contenido de los archivos x.c y y.c (y nada ms) en la salida estndar.
La pregunta es cmo hacer que los archivos nombrados sean ledos -esto es,
cmo conectar las proposiciones que leen los datos, con los nombres eXlernos que
un usuario tiene en mente.
Las reglas son simples . Ames de que pueda ser ledo o escrito , un archivo tiene
Que ser abierto por la funcin de biblioteca lopen, la cual toma un nombre exter
no como x.e o y.c, hace algunos arreglos y negociaciones con el sistema operativo (cuyos detalles no deben importarnos), y regresa un apuntador que ser usado
en posteriores lecturas o escrituras del archivo.
Este apuntador, llamado apuntador de archivo, apunta a una estructura que
contiene informacin acerca del archivo, tal como la ubicacin de un buffer, la
posicin de carcter actual en el buffer, si el archivo est siendo ledo o escrito
y si han ocurrido errores o fin de archivo. Los usuarios no necesitan saber los
detalles, debido a que las definiciones obtenidas de <stdio.h> incluyen una
declaracin de estructura llamada FILE. La nica declaracin necesaria para un
apuntador de archivo se ejemplifica por
FILE -Ip;
FILE .. fopen(char "nombre, char "modo);

ESlO dice que Ip es un apuntador a un FILE , y lopen regresa un apunlador a


FILE. Ntese que FILE es un nombre de tipo, como inl, no un rtulo de estrue,"
ra; est definido con un Iypede/. (Los detalles de cmo realizar (open en el siste
ma UN IX se explican en la seccin 8.5.)
La llamada a lopen en un programa es
fp = fopen(nombre, modo};
El primer argumento de fopen es una cadena de caracteres que contiene el nom
bre del archivo. El segundo argumento es el modo, tambin una cadena de caracle
res, que indica cmo se intenta emplear el archivo. Los modos disponibles inclu
yen lectura ("r"), escritura C'w"), y aadido ("a"). Algunos sistemas distinguen
emre archivos de texto y binarios; para los ltims, debc escribirse una "b" lu ego
de la cade na de modo.
Si un archivo que no existe se abre para escribir o aadir, se crea, si es posi
ble. Abrir un archivo existente para escribir provoca que los contenidos anterio
res sean desechados, mientras que abrirlo para aadir los preserva. Es un error
lralar de leer un archivo que no existe, y tambin pueden haber otras causas de
error, como tratar de leer un archivo cuando no se tiene permiso. Si existe cualquier error, fopen regresa NULL. (El error puede ser identificado en forma ms
predsa; vase la discusin de funciones para manipulacin de errores al final de
la ":leccin I en el apndice B.)

178

ENTRADA Y SALIDA

CAPITULO 7

Lo siguiente que se requiere es una forma de leer o escribir el archivo una vez
que est abierto. Existen varias posibilidades, de las cuales getc y putc son las ms
simples. getc regresa el siguiente carcter de un archivo; necesita el apuntador
del archivo para decirle cul cs.
int gete(FlLE ,fp)

gete regresa el siguiente carcter del flujo al que se refiere Ip; regresa EOF si ocurre algn error.

pute es una funcin salida:


int pute(int e, FILE ,Ip)

pule escribe el carcter e en el archivo Ip y regresa el carcter escrito, o EOF si


ocurre un error. Tal como gelehar y putehar, gele y pule pueden ser macros en
lugar de funciones.
Cuando se arranca un programa en C, el medio ambiente del sistema operativo es responsable de abrir tres archivos y proporcionar apuntadores de archivo
para ellos. Estos archivos son la entrada estndar, la salida estndar y el error
es tndar; los apuntadores de archivo correspondientes se llaman stdin, stdoul y

slderr, y estn declarados en < stdio.h > . Normalmente stdin se conecta al teclado y stdout y stderr se conectan a la pantalla, pero stdin y stdout pueden ser redirigidos a archivos o a interconexiones (pipes) como se describe en la seccin 7.1.

getehar y putehar pueden estar definidos en trminos de gete, pute, stdin


y stdout, como sigue:
#define getehar()
#define putehar(e)

gete(stdin)
putee), stdout)

Para entrada o salida de archivos con formato se pueden emplear las funciones Iscanl y Iprint!. Estas son idnticas a seanl y print!, excepto en que el primer
argumento es un apuntador de archivo que especifica el archivo que ser ledo
o escrito; la cadena de formato es el segundo argumento.
inl fseanf(FILE 'fp, ehar 'formato, ... )
int fprintf(FlLE 'fp, ehar 'formato, ... )

SECCION 7.6

MANEJO DE ERRORES-STDERR y EXIT

179

if (argc = = 1) /* sin args; copia la entrada estndar


. filecopy(stdin, stdout);
else
while (- -arge > O)
if fp = fopen(,+ +argv, "r")) = = NULL) {
printf("cat: no se puede abrir %s\n", *argv);
return 1;
) else {
fileeopy(fp , sldout);
fclose(fp) ;
retum O;

/ . filecopy:
copia el archivo ifp al archivo ofp/
void fileeopy(FILE -ifp, FILE 'olp)
{
int c;

while e = gele(ifp)) ! = EOF)


pule(e, ofp);

Los apuntadores de archivo stdin y stdout son objetos de tipo FILE,. Sin embargo, son constantes, no variables, por lo que no es posible asignarles algo .
La funcin
inl fclose(FILE ,fp)

es lo inverso de lopen; interrumpe la conexin que fue establecida por lopen entre el apuntador de archivo y el nombre externo, liberando al apuntador de archi vo para otro archivo. Puesto que la mayora de los sistemas operativos tienen al ~
gunas limitantes sobre el nmero de archivos que un programa puede tener

abie rtos simultneamente, es una buena idea liberar los apuntadores de archivo

Habiendo hecho a un lado estos prerrequisitos, ya estamos ahora en posicin

cuando ya no son necesarios, como se hizo en cal. Tambin hay otra razn para

de escribir el programa cat, que concatena archivos. El diseo se ha encontrado


conveniente para muchos programas. Si existen argu mentos en la lnea de rdc~
ncs, se interpretan como nombres de archivos, y se procesan en orden. Si nO hay
arg umentos, se procesa la entrada est ndar.

USar lelose en un archivo de salida -vaca el buffer en el cual pute est colectando la salida. Cuando un programa termina normalmente, lelose es llamado automt icamente para cada archivo abierto. (Se puede cerrar stdin y stdout si no
Son necesarios. Tambin pueden ser reasignados por la funcin de biblioteca
Ireopen.)

#include <stdio.h>
/ * cat: concatena archivos, versin 1 ./
main(int argc, char *aIgv[ ])

{
FILE 'fp;
void fileeopy(FILE " FILE,);

7 _6 Manejo de errores-stderr y exit


El manejo de los errores en cat no es el ideal. El problema es que si no se puede te ner acceso a uno de los archivos por alguna razn, el diagnstico se imprime

]80

CAPITULO 7

ENTRADA Y SALIDA

al final de la salida concatenada. Eso podra ser aceptable si la salida va a la pantalla, pero no si va hacia un archivo o hacia Olro programa mediante una interco nexin.
Para manejar mejor esta situacin , se asigna un segu ndo flujo de sali da , llamado stderr, a un programa en la misma forma en que stdin y stdout. La salida
escrita hacia stderr normalmente aparece en la pantalla, aun si la sal ida estndar
es redirigida.
Corrijamos cat para escribir sus mensajes de error en el archivo de error
estndar.
#inc1ude <stdio.h>

SECC ION 7.7

ENTRA DA Y SALIDA DE LINEAS

181

probar el xito o fraca so del programa por Otro que lo use como subproceso.
convencionalmente, un valor de retorno O :..ei'lala que todo e~t bien; los \alore5
diferentes de cero generalmente sealan ~ ituaciones anormales. exit llama a
fclose por (ada archivo de sa lida abierto, para va(iar (ualquier ~alida gelll'rada
a travs d e un buffer.
.
Dentro de main, return expr es eqUIvalente a exit(expr). exit tiene la ventaja
de que puede ser llamada desde otras funciones, y que las llamadas a ella se pueden encontrar con un programa de bsqueda de patrones como el del ca pitulo 5.
La funcin ferror regresa un valor diferente de cero si ocurri un error en el
flujo fp.
int ferror(FILE .fp)

/ .. cat: concatena archivos, versin 2


main(int argc, char .. argv[ ])

~/

FILE fp;
void filecopy(FILE " FILE.);
char "prog = argv[OJ;

if (argc

= =

1)

Aunque los errores de salida so n raros, si ocurren (por ejemplo, s i un disco se llena), por lo que 105 programas ele proc!ucl'ill debe n revisar tambin e51O.
La funcin feof(FILE *) es anloga a ferror; regresa un valor diferente de cero
si ha ocurrido un fin de archivo en el archivo especificado.
int feof(FILE 'fp)

/ .. nombre del programa para errores .. /

/ .. sin args; copia la entrada estndar .. /

filecopy(stdin, stdout);

En ge neral, no nos hemos prl..'ocupad o por el estado de la salida de nu estros


pequeos programas ilustrativos, pero tocio programa serio debe tCllcr I..'uidado
de regresar valores de estado sensatos y tiles.

else

while (--argc > O)


if ((fp = fopen(. + +argv, "r")) = = NULL) {
fprintf(stderr, "%s: no se puede abrir %s\n",
prog, .. argv);

exit(l)
} else {
filecopy(fp, stdout);
fclose(fp);
if (ferror(stdout)) {
fprintf(stderr, "%5: error al escribir stdout\n", prog);

exit(2);
exit(O);
El programa seala errores en dos maneras. Primero, la sa lida de diagnsticOs
producida por fprintf va hacia stderr. de mod o que encuentra su camino hacia
la pantalla en lugar de desaparecer en una in te rco nexi n o dentro de un archivo
de salida. Incluimos el nombre del programa, tomndolo de argv[O), en el mensaje, para que si este programa se usa con otros, se identifique la fuellle del error.
Segundo, el programa utiliza la funcin de biblioteca est ndar exit, que termina la ejecucin de un programa cuando se le llama. El argumento de exit est
disponible para cualquier proceso que ha ya llamado a ste, para que se pueda

7,7

Entrada y salida de lneas

La biblioteca estndar proporciona una rutina de entrada fgets, es scmcjante a la funcin getline que hemos empleado en captulos anteriores:
char "fgets(char "lnea, int maxlnea, FILE .. Ip)

fgets Icc la siguiente lnea (incluyendo el (arctcr nueva lnea) del archi vo fp y la
deja en el arreglo de caracteres~; se Icen hasta maxline-l (aracteres. La lnea
resultante se termina con '\0'. Normalmente, fgets regresallneaJ; en caso de fin
de arc hivo o de error, regresa NULL. (Nuestra getline regresa la longitud de la
lnea, que es un valor ms til; cero significa fin de archivo.)
Para salida, la funcin fputs escribe una cadena (que no necesita contener una
nu eva lnea) a un archivo:
int fputs(char -Unea, FILE fp)
Esta funcin regresa EOF si ocurre un error y cero si no ocurre.
Las funciones de biblioteca gets y puts son semejantes a fgets y fputs, pero
operan sobre stdin y stdout. De modo desconcertante, get5 elimina el
termi
nal y puts lo agrega.
Para mostrar que no ha y nada cspedal sob re funciones como fgets y fputs,
a4u es tn , copiadas de la biblio teca estndar de nuestro sistema:

1SEt

/ .. fgets:
obtiene hasta n caracteres de iop .. /
char .. fgets(char .. s, int ni FILE *iop)

182

ENTRADA Y SALIDA

CAPITU LO 7

register int e;
register ehar *es;

SECC ION 7.8

7.8

es = s;
while (--n > 0&& (c = getc(iop)) ,= EOF)
il ((cs + + = c) = = '\n')
break;
*es = '\0';
return (e
EOF && es
s) ? NULL , S'

OTRA S fUNCIONES

183

Otras funciones

La biblioteca estndar proporciona una amplia variedad de funciones. Esta


seccin es una breve ~ inops ~ de las ms tiles . En el ap~llclil'(' B pueden encontrarse ms detalles y muchas otras funciones.

7.8.1 Operaciones sobre cadenas


Ya hemos mencionado las funciones sobre cadenas strlen, strcpy, strcat,
y st rcmp, que se encuentran en <Istring.hl>. En adelante, s y t son de tipo
char *, Y c Y n son ints.

/ * fputs:
coloea la eadena s en el archivo iop */
int fputs(ehar *s, FILE *iop)
strcat(s, t)
strncat(s,t,n)
strcmp(s,t)

{
jnt e;
while (c = .s+ +)
pute(c, iop);
return ferror(iop) ? EOF : O;

Por razones que no son obvias, el estndar especifica valores de retorno difere ntes para gets y puts.
Es fcil realizar nuestro getline a partir de fgets:
/* getline:
lee una linea, regresa su longitud */
int getline(ehar *line, int max)
jf (fgets(line, max, stdin)
return O;
else

NULL)

return strlen(line);

Ejercicio 7-6. Escriba un programa para comparar dos archivos, imprimiendo la


primera lnea en donde difieran. O
Ejercicio 7-7. Modifique el programa de bsqueda de un patrn del captulo 5
para que lOme su entrada de un conjulllO de archivos nombrados o, si no hay
archivos nombrados como argumentos, de la entrada estndar. Debe escribirse
el nombre del archivo cuando se encuentra una lnea que coincide? O

Ejercicio 7-8. Escriba un programa para imprimir un conjunto de archivos, inIciando cada nuevo archivo en una pgina nueva, con un ttulo y un contador de
pgina por cada archivo. O

strnemp(s,t,n)
strcpy(s, t)
strncpy(s, t, n)
strlen(s)
strehr(s,c)
strrchr(s,e)

concatena t al final de s
concatena n caracteres deITIal final de~
regresa negativo, cero, positivo para
s < t, s = = t, o s > t
igual que stremp pero slo en los primeros n caracteres
copia t en s
copia a lo ms n caracteres de t a s
regresa la longitud de s
regresa un apuntador al primer e que este en s, o NULL
si no est presente
regresa un apuntador al ltimo e que este en s, o NULL
si no est presente

7.8.2 Prueba y conversin de clases de caracteres


Varias funciones de <ctype.h> realizan pruebas y conversiones de caracteres. En lo que se muestra a continuacin, c es un int que se puede representar
como un unsigned char o EOF. Las funciones regresan~.
isalpha(c)
isupper(c)
islower(c}
isdigit(c)
isalnum(c)
isspaee(e)
toupper(c)
tolower(c)

diferente de cero si e es albaftica, O si no lo es


diferente de cero si c es mayscula, O si no lo es
diferente de cero si e es minscula, O si no lo es
diferente de cero si e es un dgito, O si no lo es
diferente de cero si isalpha(e} o isdigit(e), O si no lo es
diferente de cero si e es un blanco, tabulador, nueva lnea, retorno, avance de lnea tabulador vertical
regresa e convertida a mayscula
regresa e co nve rtida a min scula

7.8 .3 Ungetc
La biblioteca estndar proporciona una versin ms restringida de la funcin
Ungetch que escribimos en el captulo 4; se llama ungetc.

184

ENTRADA Y SALIDA

CAPITULO 7

int ungete(int e, FILE *fp)

SECCION 7.8

OTRA S FUNCIONES

La for ma correcta

coloca el carcter e de nuevo en el archivo fp y regresa e, o EOF en caso de errOr,


Slo se garantiza poner un carcter de regreso por archivo. Es posible utilizar
ungetc con cualquiera de las funciones como scanf, getc o getchar.

7.8.4 Ejecucin de rden es


La funcin system(char *s) ejecuta la orden contenida en la ;adena de carac~
!(.'res s, y despus contina la ejecucin del programa actual. Los contenidos de
s dependen fuertemente del sistema operativo local. Como un ejemplo trivial, en
sistemas UNIX, la proposicin
system("date");

provoca que se ejecute el programa date, el cua l imprime la fecha y hora del da en
la salida estndar. system regresa del comando ejecutado un estado entero depen.
diente del sistema. En el sistema UNIX, el eSlado de retorno es el valor regresado
por exit.

Las funciones manoe y eanoe obtienen bloques de memoria dinmicamente.


void

malloe(size_t n)

regresa un apuntador a n bytes de almacenamiento no inicializado, o NULL si la


peticin no se satisface.

g.uardar lo nel.:csario

for (p

~ head; p I ~ NULL; p
q = p->next;
free(p);

ante~

de liberar:

q) {

La ~cccill 8.7 muestra la realiLacion de un asignador de almacl'llamiL'l1!u


como malloc, en el cual los bloques asignados se pueden liberar en cualquier or~
den.

7.8.6 Funciones matemticas


Existen ms de veinte funciones matemticas declaradas en <math,h>; aqu
estn algunas de las empleadas con ms frecuencia. Cada una toma uno o dos
argume ntos double y regresa un double.
sin(x)
eos(X)

7.8.5 Administracin del almacenamiento

~~

185

atan2(y,x)
exp(x)
log(x)
logIO(x)
pow(x,y)
sqrt(x,)
fabs(x)

seno de x, x en radianes
coseno de x, x en radianes
arco tangente de y/x, en radianes
funcin exponencial el
logaritmo natural (base e) de x (x>O)
logaritmo comn (base 10) de x (x> O)
xy
raz cuadrada de x (x;?:O)
valor absol uto de x

void *ealloe(size_ t n, size_ t size)

regresa un apuntador a suficiente espacio para almacenar un arreglo de n objetos


del tamao especificado, o NULL si la peticin no se satisface. El espacio de a lmacenamiento es inicializado en cero,
El apuntador regresado por manoe o eanoe tiene la alineacin apropiada
para el objeto en cuestin, pero se le debe hacer una conversin forzada a l ti po
apropiado, corno en
int

ip

ip;
~

(int .) calloe(n, sizeof(int));

free(p) libera el espacio apuntado por p, donde p se obtuvo originalmente


por una llamada a manoe o eanoe . No existen restricciones sobre el orden en
el que se libera el espacio, pero es un grave error el liberar algo no obtenido por
una llamada a eanoe o manoe.
Tambin es un error usar algo despus de haber sido liberado. Un tpico pero
errneo fragmento de cdigo es este ciclo que libera elementos de una lista:
for (p ~ head; p ! ~ NULL; p
free(p);

p- > next)

/ . INCORRECTO /

7.8. 7 Generacin de nmeros aleatori os


La funcin rand( ) calcula una secuencia de enteros pseudoaleatorios en el
rango de cero a RAND..MAX, que est definido en <stdlib.h>. Una forma de
producir nmeros a leatorios de punto flotante mayores o iguales a ce ro pero menOres que uno es
#define frand( ) ((double) rand( ) / (RAND_MAX+ 1))

(Si su biblioteca ya proporciona una funcin para nmeros aleatorios de punto


flotante, es probable que tenga mejores propiedades estadsticas que sta .)
La funcin srand(unsgned) fija la semi ll a para rand. La implantacin porttil de rand y de srand sugerida por el estndar aparece en la seccin 2,7.
Ejen:icio 7-9. Se pueden escribir funcion es como isupper para ahorrar espacio o
tiempo. Explore ambas posibilidades . O

CAP ITU LO 8:

La interfaz con el sistema UNIX

El sistema operativo UN IX proporciona sus servicios a travs de un conjunto


de Ilall1(Jda~ a/ sistema, que consisten en funciones quc l'''iln dentro del ,>i'>tL'm:.t
operativo y que pucde n ser invo(udas por programas del usuario. Este capitulo
(k. . cribe cmo em plear al gu nas dc las ms imponalltcs llamadas al sistema desde
programas en C. Si e l lector lisa UN IX. eSln debe serie clircL'laJ1lCllle ltil, debido
a que algunas veces es necesario emp lear llamadas al sistema para teller mxima
eficiencia. o para tener acceso a algu na facilidad que no ('sil' en la biblioteca.
Indu . . o, si se empica en un sistema operativo difcrcnlL' el lecto r debera ser capa/. de adell trarse en la programacin estud iando ('\tos ejemp los: aunque lo,> detalle,> varan, 'ir I' Ih..:on trar un cd igo scmejante en cualquier 'ii'>lcma. PUI' . . to quc
la biblioteca de
ANS I est en Illucho . . casos modclada con ba-"c en la.', ra1..."i l id~
de . . dc UN I X, estc cdigo puede ayudar ta mbi1l a Stl clltcndilllic!lIn.

El captulo est di vidido en t res part es fund amemales : entrada/salida, sistema


de arc hivos y asignacin de a lmacena mi ento. Las primeras dos pa rt es su ponen una
modesta fami liaridad con las ca ractersticas externas de los sistemas UN IX.
El capitulo 7 luvo que ver (,;on una interfaz de e!l lrada/ salida u niforme: e 111 re
. . istemas operativos. En cua lq uier sist<.:ma las rut inas de la bib liotcca estndar;sc
tienen que esc ri bir cJl trmi nos de las fac ilidades proporcio nadas por el sistema
anfitri6n. En la;s ;secciones de este capit ulo describircmos las ll amadas al ;si'>tcllla
UNIX para e!ltrada y sal ida, y mostraremos cmo puede escribi rse parte (!t: la
biblioteca est ndar con ell as.

6.1

Descriptores de archivos

En el sistema operativo UNIX, todas las entradas y sa lidas se rea lizan po r la


lectura o escrilura d e arch ivos, debido a que los dispos itivos perifricos, aun el
(cclado y la panta ll a, son archivos que estn en el sistema. E..,tu signi fica que una
~enci!la interfaz homognea maneja toclas la~ comunicaciones cllIrc un programa
y loo;; di\flOsitivos perifricos.
En el caso ms general , antes de leer o escribir un archho, primero se debe informar al ~i~tema ace rca de la intenc in de hacerlo, mediante el proceso llamado
187

188

LA INTERFAZ DEL SISTEMA UNIX

CAPIT U LO 8

abrir un archivo. Si se va a csnibir en

UIl archivo tambi; puede ser necesario


crearlo o descartar el contenido previo. El sistema verifica los derechos de hacer
tal cosa (El archivo existe? tiene permiso de hacer acceso a l?) y, si todo est
correcto, regresa al programa un pequciio entero no n~gativo llamado descriptor
de archivo. Siempre que se van a efectuar acciones de entrada y salida sobre ese
archivo, se usa el desniptor de archivo para id entifica rl o en lugar del no mbre.
(Un descriptor de archivo es anlogo al apuntador de archivo usado por la
biblioteca estndar o al maniulador de archivo de MS-DOS .) Toda la informa_
cin acerca de un a rchi vo abierto es mantenida por el sistema; e l progra ma del
usuario se refiere al archivo slo por el descriptor.
Puesto que es tan comn que la entrada y la salida involucren al teclado y a
la pa ntalla, existen arr eglos especiales para hacer esto convenientemente. Cuan~
do e l intrprete de coma ndos (el "sh cll ") ejecuta un programa se abren tres
archivos, con descriptores 0, 1,2, llamados entrada est ndar . sa lida estndar y
error estndar. Si un programa lee de O y escribe a 1 ya 2, puede hacer ent rada y
sa lida sin preocuparse de abrir archivos.
El usuario de un programa puede redirigir la E/S hacia y desde archivos con

< y>:
prog < archent > archsal

En es te caso, ~ ca mbia las asignaciones predefinidas para los desc ripto res
O y 1 a los archivos nombrados. Normalmellle el descriptor de archivo 2 permanece asignado a la pantalla. para qu e los mensajes de error puedan ir hacia all.
Observaciones semejantes se apli can para la entrada y salida asociada con una interco nexin. En todo s los casos, la asignacin de archivos la cambia shelij. no el
programa. El programa no sa be de dnde proviene su entrada ni hacia dnde va
su salida, mientras use al archi vo O para elllrada y 1 y 2 para sa lida.

8.2

E/S de bajo nivel-read y write

La entrada y salida usa las llamadas al sistema read y write, a las que se tiene
acceso desde programas escritos en C a travs de dos funciones llamadas read Y
write. Para ambas, el primer argumento es un descriptor de archivo. E l segundo
argumento es un arreglo de caracteres perteneciente al programa hacia o de do nde
los detos van a ir o venir. El tercer argumento es el nmero de bytes que sern
transferidos.
int n.Jedos = read(int id, char, *buf, int n);
int n_escritos = write(int id, char *huf, int n ):

Cada llamada regresa una cuenta del nmero de bytes transferidos. En la lectu ra,
el nmero de bytes regresados puede ser menor que el nmero solicitado. U n valor de regreso de cero bytes implica fin de archivo y -1 indica un error de algn

SECC ION 8.2

E/S DE BAJO NIVEL-READ y WRITE

189

tipo. Para escritura, el valor de retorno es el nmero de bytes escritos: si ste no


es igual al nmero solicitado. ha ocurrido un error.
En una llamada pueden leerse cualquier nmero de bytes. Los valores ms comunes son 1, q ue significa un carcter a la vez (sin buffer), y un nmero como
1024 o 4098, que corresponde al tamao de un bloque fsico de un di spositivo perifrico. Los valores mayores sern ms eficientes debido a que sern realizadas
menos llamadas al sistema.
Para juntar estos temas, podemos escribir un sencillo programa que copie su
entrada a su salida, el equivalente del programa copiador de archivos escrito para
el captulo 1. Este programa copiar cualquier cosa a cualquier cosa, ya que la
entrada y la salida pueden ser redirigidas hacia cualquier archivo o dispositivo .
#include "syscalls_h"
main() / * copia la entrada a la salida
{
char buf(BUFSIZ);
int n;

*/

while ((n = read(O, buf, BUFSIZ) > O)


write(l, buf, n);
return O;

Hemos reunido prototipos de funciones para las llamadas al sis tema en un archivo llamado syscalls.h, de modo que podamos incluirlo en los programas de
este capt ulo. Sin embargo, este nombre no es estndar.
El parmetro BUFSIZ tambin est definido dentro de syscalls.h; su valor es
un tama o adecuado para el sistema local. Si el tamao del archivo no es un mltiplo de BUFSIZ, algn read regresar un nmero menor de bytes a ser esc rito s
por write; la sigui ente llamada a read despus de eS0 regresar cero.
Es instructivo ver cmo se pueden usa r read y write para construir rutinas de
alto nivel como getchar, pulchar, etc. Por ejemplo, aqu est una versin de gelchar que realiza entrada sin buffer, leyendo de la entrada estndar un carcter
a la vez.
#include "syscalls. h"
1* getchar: entrada de un carcter Simple sin buffer */
int getchar(void)
{
char c

return (read(O, &c, 1)

1) ? (unsigned char) c : EOF;

190

LA INTERFAZ DEL SISTEMA UNIX

CAPITULO 8

c debe ser un char, a que read necesita un apuntador a carcter. Forzar


c a ser unsigned char en la proposicin de regreso elimina cualquier problema
de extensin de signo.
La segunda versin de getchar hace la entrada en grandes fragmentos y saca
los caracteres uno a la vez .
#include "syscalls.h"
j. getchar:
versin con buffer simple . j
int getchar(void)

{
static char buf{BUFSIZ];
static char bufp = buI;
static int n = O;
if (n

~ ~ O) {
l. el buffer est vacio .1
n = read(O, buf, sizeof buf);
bufp ~ bufo

return (--n >

O) ? (unsigned char) .bufp + + : EOF;

Si esta versin de getchar fuese a ser compilada con < stdio.h > incluida, sera
necesario eliminar la definicin del nombre getchar con #undel en caso de que
est implantada como una macro.

8.3

Open . creat. clase. unlink

Adems de la entrada, la salida y el error estndar, se pueden abrir explcitamente archivos para leerlos o escribirlos. Existen dos llamadas al sistema para esto, open y crea!.
open es como el lopen expuesto en el capitulo 7, excepto que en lugar de regresar un apuntador de archivo, regresa un descriptor de archivo, que es tan slo
un int. open regresa -1 si ocurre algn error.
#include < fcntl.h >

int fd;
int open(char .nombre, int flags, int perms);
Id = open(nombre, flags, perms);

SECClON 8.3

OPEN,CREAT, CLaSE, UNLINK

191

O~DONLY

O
O

abrir slo para lectura


WRONLY abrir slo para escritura
RDWR
abrir para lectura y escritura

Estas cor.stantes estn definidas en < Icntl.h > en sistemas UNIX System V, Y
en <sys/lile.h> en versiones Berkeley (BSD).
Para abrir un archivo ya existente para lectura,
fd

open(nombre. O.-RDONLY. O);

El argumento perms es siempre cero para los usos de open que discutiremos.
Es un error tratar de abrir un archivo que no existe . Para crear nuevos archivos o reescribir anteriores, se proporciona la llamada al sistema creat.
int creat(char .nombre, int perms);
fd

creat(nombre, perms};

regresa un descriptor de archivo si fue capaz de crear el archivo, y -1 si no lo


fuco Si el archivo ya existe, creat lo truncar a longitud cero y por tanto descartar su contenido previo; no es un error crearjcreaqun archivo que ya existe.
Si el archivo no existe, creat lo crea con los permisos especificados por el ar-

gumento perms. En el sistema de archivos de UNIX hay nueve bits para informacin de permisos asociados con un archivo, que controlan el acceso a la leclura,
escritura y ejecucin para el propietario del archivo, para el grupo del propietario
y para todos los dems. As, un nmero octal de tres dgitos es conveniente para
especificar los permisos. Por ejemplo, 0755 especifica permisos para leer, escribir
y ejecu tar para el propietario, y leer y ejecutar para el grupo y para cualquier otro.

Para ilustrarlo, aqu est una versin simplificada del programa cp de UNIX,
que copia un archivo a otro. Nuestra versin copia slo un archivo, no permite
que el segundo argumento sea un directorio e inventa los permisos en lugar de
copiarlos.
#include <stdio.h>
#include < fcntl.h >
#include "syscalls.h"
#define PERMS 0666 ! * lectura y escritura para propietario, grupo y otros.!
void error(char *, ... };

l. cp:
copia fl a f2 1
main(int argc char argv [ ])
I

Como con lopen, el argumento nombre es una cadena de caracteres que contiene
el nombre del archivo. El segundo argumento, flags, es Un int que especifica
cmo ser abierto el archivo; los principales valores son

int 11, f2, n;


char buf[BUFSIZ];

A pesar de que en ingls la palabra correcta es "croata", el nombre de la funcin es slo


"croat". (N. de T.)

if (argc ! ~ 3)
error (ilUSO: cp de hacia");

192

LA INTERFAZ DEL SISTEMA UNIX

CA PITULO B

i! fl = open(argv[lJ, OJlDONLY,
= = - 1)
error("cp: no se puede abrir %s", argv[l]);
if !2 = creat(argv[2J, PERMS = = -1)
error("cp: no se puede crear %s, modo %030",
argv[2J, PERMS);
while n = read(fl, bu!, BUFSIZ > O)
if (write(!2, bu!, n) ! = n)
error ("cp: error de escritura en el archivo %s", argv[2J);
return O;

Este programa crea el archivo de sa lida con permisos fijos 0666. Con la llamada al sistema stat, descrita en la seccin 8.6, podemos determinar el modo de un
archivo existente y as dar el mismo modo a la copia.
Ntese que la funcinlerroij es invocada con una lista variable de argumentos
mu y semejante a la de printl. La realizacin de lerrorl ilustra cmo utilizar
otros miembros de la familia printf. La funcin de biblioteca estndar vprintf es
como printf, excepto que la lista variable de argumentos es reemplazada por un
solo argumento que ha sido iniciali zado llamando a la macro va....start . En for-

ma semejante, vlprintf y vsprintf coinciden con Iprintf y sprintf.


#include < stdio.h >
#include <stdarg.h>
/* error:
imprime un mensaje de error y muere */
void error(char *fmt, ... )
va.-list args;

v"-Starl(args, Iml);
fprintf(stderr, "error: ");
vfprintf{stderr, fmt, arqs);
Iprintf(stderr, "\n");
va_end(args);
exit(I);

Existe un limite (regularmente 20) en el nmero de archivos que un programa

ACCESO ALEATORIO- ISEE K

SECCION 8. 4

193

La funcin unlink(char nombre) remueve el archivo nombre del sistema de


archivos. Correspo nd e a la funcin de la biblioteca estndar remove.

Ejercicio 8-1. Reescriba el programa cat del captulo 7 usando read , write, open
y close, en lugar de sus equivalentes de la biblioteca estndar. Haga experimentos
para determinar la velocidad relativa de las dos versiones. o

8.4

Acceso a[eatorio-Iseek

La en trada y la salida so n normalmente secuencia les: cada read o write


ocurre en una posicin del archi vo justo despus de la anterior. Sin embargo,

cuando es necesario, un archivo se puede leer o escribir en cualqui er orden arbilrario. La llamada al sistema lseek proporciona un a forma de moverse en un
archivo sin leer o escribir ningn dato:
long lseek(int fd, long offset, int origen);

fijaen offset la posicin actual en el archivo cuyo descriptor es Id, que se toma
relativo a la localizacin especificada por~. Una lectura o escritura posterior principiar en esa posicin.lorigenl puede ser O, 1 o 2 para especificar que
el desplazamiento offset ser medido desde el principio, desde la posicin actual,
o desde el fin del archivo , respectivamente. Por ejemplo, para agregar a un archivo (la redireccin en ellshelil de UNIX, o '~" de lopen), hay que ir al final
anes de escribir:
Iseek(ld, aL, 2);

Para regresar al principio C'rebobinar " ),


Iseek(ld, aL, O);

Ntese el argumento OL; tambin podra ser escrito como (long) O O slo como
O si Iseek est declarado adecuadamente.
Con lseek, es posible tratar a los archivos ms o menos como a rreg los eXlen
sos, al precio de un acceso ms lento. Por ejemplo, la siguien te funcin lee cual

quier nmero de bytes en cualquier lugar arbitrario de un archivo. Regresa el nCmero leido, o - 1 en caso de error.

puede tener abiertos simultneamente. De acuerdo con esto, un programa que intente procesar muchos archivos debe ser preparado para reutilizar descriptores de

#include "syscalls.h"

archivo. La funcin close(int Id) suspende la conexin entre un descriptor de archivo y un archivo abierto, y libera al descriptor de archivo para ser utilizado con

/* get:
lee n bytes de la posicin pos */
int get{int fd, long pos , char *buf , iht n)

algn otro archivo; corresponde a fe lose de la biblioteca est ndar exceptO en


que no existe un buffer que vaciar. La terminacin de un programa va exit o re
turn desde el programa principal cierra todos los archivos abiertos.

{
il (Isee!(!d , pos, O) > = O) / - se sita en pos . /
return read(fd, bui, n);

194

LA INTERFAZ DEL SISTEMA UNI X

CAPITULO 8

EJEMPLO- UNA IMPLEMENTACION DE FOPEN y GETC

SECCION 8.S

(&..Job[Q])
(&..Job [1])
(&..Job [2])

#define stdin
#dehne.stdout
#define stderr

else
return -1;

El valor de regreso de lseek es un long que da la nueva posicin en el archivo,


o - 1 si ocurre un error. La funcin de biblioteca estndar fseek es semejante a
lseek, excepto en que el primer argumento es un FILE Y el valor de regreso es
di ferente de cero si ocurri un error.

195

enum -11ags {
_READ
= 01,
_WRITE
= 02,
_UNBUF = 04,
_EOF
= 010,
_ERR
= 020

l . archivo abierto para lectura ./


l . archivo abierto para escritura . 1

l. archivo sin buffer .1


l . ocurri fin de archivo (EOF) en este archivo 1
l. ocurri un error en este archivo .1

);

8,5

Ejemplo-una realizacin de fopen y getc

Ilustremos ahora cmo algunas de estas piezas quedan juntas, mostrando una
realizacin de las rutinas lopen y gete de la biblioteca estndar.
Recurdese que los archivos en la biblioteca estndar son descritos por apu ntadores de archivos en vez de co n descriptores de archivo. Un apuntador de
archivo es un apuntador a una estructura que contiene informacibn va ria acerca
del archivo: un apuntador a un buffer, para que el archivo pueda ser ledo
en grandes fragmentos; una cuenta del nmero de caracteres que quedan en el
buffer; un apuntador a la posicin del siguiente carcter en el buffer; el descriptor
de archivo, y banderas que describen el modo de lectura/esc ritura, estado de
error, etctera.
La estructura de datos que describe un archivo est contenida en < stdio.h > ,
que se debe incluir (con #include) en cualquier archivo fuente que uti lice rutinas
de la biblioteca de entrada/sa lida estndar. Tambin est incluido en las funciones de la biblioteca. En el siguien te fragmento de un <stdio.h> tpico, los
nombres que se intenta emplear slo en las funciones de la biblioteca estndar
principian con un subguin, por lo que son menos susceptibles de tener conOiclo
con nombres en los programas del usuario . Esta convencin la emplean todas las
rutinas de la biblioteca estndar.
#define
#define
#define
#define

NULL
EOF
BUFSIZ
OPEN_ MAX

o
(- 1)
1024
20 l . mximo numero de archivos abiertos a la vez .1

typedef struct ..Jobuf {


int cnt;
l . caracteres que quedan . 1
char .ptr;
l . posicin del siguiente carcter I
char .base;
l . localizacin del buffer 1
int flag;
l . modo de acceso al archivo . 1
l . descriptor de archivo 1
int fd;
} FILE;
extem FILE ..Job[OPEN-.MAXj;

int -.lillbuf(FILE .);


int -.llushbuf(int, FILE.);
((p)->flag & J:OF) ! = O)
(((p)->flag & J:RR) ! = O)
((p)->fd)

#define leol(p)
#define ferror(p)
#define fileno(p)
#define getc(p)

(__ (p)_> cnt > = 0 \

? (unsigned char) .(p)- >ptr+ + : -.lillbuf(p))


#define putc(x,p) (__ (p) _> ent > = 0 \
? (p)_ > ptr + + = (x) : -.llushbuf((x),p
#define getehar( )
#define putehar(x)

gete(stdin)
pute((x), stdout)

La macro getc normalmente decrementa la cuenta, avanza el apuntador y regresa el carcter. (Recuerde que un #define largo se contina con una diagonal in ve rtida.) Si la cuenta se hace negatva, sin embargo, gete llama a la funcin ..Jillbul para
llevar el buffer, reinicializa el contenido de la estructura, Y regresa un carcter.
Lo ~ caracteres son devueltos unsigned, lo que asegura que todos los caractcre~
ser n positivos.
Aunque no discutiremos ningn detalle, hemos incluido la definicin de pute
para mostrar que opera en forma muy semeja nte a getc. llamando a una
funcin ..Jlushbul cuando su buffer est lleno. Tambin hemos incluido macros
para tener acceso al estado de error, fin de arch ivo, y al descriptor del mi smo.
Ahora puede escribirse la funcin lopen. La mayor parte de lopen tiene que
\'er con tener el archivo abierto Ycolocado en el lugar correcto, y con fijar 10:-' bits
de la bandera flag para indicar el estado apropiado. lopen no a signa ningn espa<jo para el buffer; esto es realizado por ..Jillbul cuando el archivo se lee por
primera vez.
#include < fcntl.h >
#include "syscalls.h"
#define PERMS 0666

/ _ lectura y escritura para propietario, grupo, otros . 1

196

LA INTERFAZ DEL SISTEMA UNIX

CAPITULO 8

EJEMPlO

SECC ION 8.5

LNA 1f..!PL/:./I.!/:.NTACION DE FOPEN y GETC

/* lopen: abre un archivo, regresa un apuntador de archivo */


FILF * fopen(char *name, char *mode)

#include "syscalls.h"

1* -.lillbuf: asigna y llena un buffer de entrada */


int -.imbuf(FILE 'fp)

int id;
FILE ofp;

197

int bufsize;

if ((fp->fl.g&(.JlEADU:OF: _ERR ! = .JlEAD)


return EOF;
bufsize = (fp- > flag & _UNBUF) ? 1 : BUFSIZ;
if (fp->b.se = = NULL) l ' sin buffer an ' 1
if fp- > base = (ehar o) malloe(bufsize))
NULL)
/ * no puede obtener un buffer */
return EOF;
ip->ptr = ip->base;
Ip->ent = read(fp-> Id, Ip->ptr, bufsize);
il (--Ip->enl < O) {
if (fp- > enl = = -1)
Ip-> fl.g : = _ EOF;
else
fp-> fl.g
..ERR;
fp->enl = O;
return EOF;

if (*mode ! = 'r' && *mode ! = 'w' && *mode ! = 'a')


return NULL;
for (fp = ~ob; fp < _ iob + OPEN-.MAX; fp + +)
if fp->flag & (_READ : _ WRITE)) = = O)
break;
/ * se encontr una entrada libre */
if (fp > = -.Job + OPEN-.MAX)
lo no hay entradas lIbres 01
return NULL;

if (omode = = 'w')
fd = ereat(name, PERMS);
else if (omode = = 'a') {
-1)
if fd = open(name, O_ WRONLY, O))
fd = ere.t(n.me, PERMS);
lseek(fd, OL, 2);
} else
fd = open(n.me, O~DONLY, O);
if (id = = -1)
1* no hubo acceso al nombre */
return NULL;
fp-> fd = fd;
fp->ent = O;
Ip- > b.se = NULL;
'r') ? _ READ : _ WRITE;
fp-> flag = (omode
return fp;

:=

return (unsigned char) -fp- > ptr + +;

El nico cabo suelto es cmo arrancar todo. El arreglo -.Job debe ser definido e inicia lizado para stdin, stdout y stderr:
FILE _ iob[OPEN-.MAX} = {
l . stdin, stdoul, slderr: . 1
{ 0, (ehar .) 0, (ehar .) 0, JEAD, O},
{ 0, (ehar .) 0, (ehar o) 0, _ WRITE, 1 },
{ 0, (ehar .) 0, (ehar .) 0, _ WRITE : _ UNBUF, 2 }
};

Esta versin de lopen no maneja todas las posibilidades de modos de acceso del
estndar, aunque el agregarlas no se llevara mucho cdigo. En particular, nues-

'm"

tra fopen no reconoce la


que indica acceso binario, ya que eso no tiene significado en sistemas UNIX, ni el "[J" que permite tanto lectura como escritura.
La primera llamada a getc para un archivo en particular encuentra una cuenta

de cero, lo que obliga a una llamada a Jillbul. Si Jillbul encuentra que el archivo
no est abierto para lectura, regresa EOF de inmediato. De otra forma, trata de
asignar un buffer (si la lectura ser con buffer).
Una vez que el buffer ha sido establecido, Jillbuff llama a read para llenarlo,
fija la cuenta y los apuntadores, y regresa el carcter del principio del buffer. LaS
posteriores llamadas a Jillbul encontrarn un buffer asignado.

La inicializacin de la parte flag de la estructura muestra que stdin ser ledo ,


stdout ser escrito, y stderr ser escrito sin buffer.

Ejercicio 8-2. Reescriba lopen y .-iillbul con campos en vez de operaciones explicitas de bits . Compare el tamao del cdigo y la velocidad de ejecucin. O

Ejercicio 8-3, Disee y escriba Jlushbuf, fflush, y Iclose. o


Ejercicio 8-4. La funcin de biblioteca estndar
inl fseek(FILE fp, long offset, inl origen)

198

LA INTERFA Z DEL SISTEMA UNIX

CAPITULO 8

es idntica a lseek excepto que fp es un apuntado r de a rchivo en vez de u n des.


ni piar de archi\o, y el valor regresado es un eSlado int, no una posicin. Escri ba
fseek. Asegrese de que su fseek se coordina apropi ad amellte con el manejo
de buffers realizado por las Olras funciones de la biblioleca. O

8 .6

EJEMPLO- l.I STADO DE DIRECTORIOS

SECCIO N 8.6

dependiente del sistema. opendir regresa un apuntador a una estructura llamada


DIR , an loga a FILE, que es empleada por readdir y elosedir. La informacin
e S recolectada en un archivo llamado dirent.h.
#deline

NAME~AX

14

/ . componente de nombre de archivo ms grande; _/

/ . dependiente del sistema .. /

Ejemplo-listado de directorios

Algunas veces se requiere una forma diferellle de interaccin co n el sistema


de archivos, para determinar informacin acerca de un archivo, no lo que co ntiene. Un programa q ue liSIa un directorio tal corno la orden Is de UN IX es Un
ejemplo ~i mprime los no mbres de los archivos que est n en el directorio, y,
en forma optativa, ms info rmac i n , tal como ta maos, permisos y esas cosas.
La orden dir de MS-DOS es a nloga.
Corno un directorio de UN IX es simplemente un archivo, Is slo necesita leer
lo para oblener los nombres de archivos . Pero es necesario utilizar una llamada
al sistema para tener acceso a la otra informacin acerca del archi vo , tal como su
tamao. En Olros siste mas puede ser necesaria un a llamada al siste ma incluso pa
ra los nombres de los archivos; ste es el caso de MS-DOS, por ejemplo. Lo q ue
nosotros qu eremos es proporcionar acceso a la informacin en una forma relati
vamente independiente del sistema , a pesar incluso d e que la realizacin pueda
ser altame nte dependiente del sistema.
Ilustraremos algo de esto escribiendo un progra ma llamado fsize . fsize es una
forma es pecial de ls que imprime los tamao s de todos los archivos nombrados
en su liSia de argumentos. Si uno de los archivos es un directorio fsize se aplica
el1 forma recursiva para ese directorio. Si no hay ningn argumento, procesa el
directorio actual.
Princi piemos con una breve re visin de la estructura del sistema de archivos
de UNIX. Un directorio es un archivo qu e contiene una lista de nombres de archi
vo y algunas indicaciones de dnd e se localiza n. La "localizacin" es un ndice
en otra tabla llamada la "lisla de nodos-i". El nodo-i para un archivo es donde ~ e 1ll<1ll1ienc loda la informacin acerca ele un archivo, excepto su nom bre.
Una entrada en el direc tor io consiste generalme nte en slo dos it ems, el nombre
del archi\'o y el n mero de nodo-i.
Desafortunada mente, el formato y el contenido preciso de un directorio no es
el mismo cn todas las versiones del sistema. De modo que di vidi remos la larea en
dos partes para tratar de ai sla r las partes no tra nsportables. El nivel ms externo
dt.'rine una cstruc tur a llamada Dirent y tres rutinas, opendir, readdir,y closedir
para pro porcionar acceso independientc del sistema al nombre y nmero de
nodo- i en una entrada del directorio. Escri biremos fsize con eSta interfaz. Des~
pues mostraremos cmo hacer esto e n siste ma s que usan la misma estruct ura de
dircclOr io~ que UN IX Versin 7, y Systcm V; las variantes son dejadas como
cjcr c ici o~ .

La estructura Dirent contiene el nmero de nodo-i y el nombre. La longitud


maxima de un componente del nombre de archivo es NAME....MAX, que es un valor

199

typedef struct { / * entrada de directorio transportable: */


long ino;
/ * nmero de nodo-i /
char name[NAME...MAX+ 1];
/ . nombre + terminador '\0' ./
} Dirent;
/ * DIR mnima: sin buffer, etc. *1
/ . descripto r de archivo para el directorio . /
/ . la entrada del directorio . /

typedef struct {
int fd;
Dirent d;
} DIR;

DIR *opendir(char *dmame);


Direnl - readdir(DIR -dld );
void closedir(DIR - dld);

La llamada al sistema sIal torna un nombre de archivo y regresa lada la informacin que est en el nodo-i para ese archivo, o - 1 si exi ste Ull error. E.., lo e~ .
char *nombre ;
struct stat stbuf;
int stat(char ., struct sta! . );
stat(nombre, &strbuf);
llena la es tru ctura slbu! con la informacin del nodo-i para el nombre de archivo.
La est ructura que describe el valor regresado por sIal est en < sys/slat.h >, y tipicamcnlc se ve a s:
struct stat
dev_t
ino_ t
short
short
short
short
dev_ t
off~t

/. informacin de nodo-i regresada por stat -/


st_dev;
sl_ino;
st_ mode;
st_nlink;
st_ uid;
st_gid;
st_rdev;
st_size;

time_ t

st_atime ;

time_ t
time_ t

st_mtime;
st_ cUme;

};

/ . dispositivo de nodo -i . /
/ . nmero de nodo -i ./
/ . bits de modo . ,
/ . nmero de ligas al archivo . /
/ . id. de usuario del propietario */
/ . id. de grupo del propietario */
/ . para archivos especiales . /
/ _ tamao del archivo en caracteres . /
1* hora del ltimo acceso . 1
/ . hora de la ltima modificacin . /
/ . hora de creacin original . /

200

CAPITULO 8

LA INTERFA Z DEI. SISTEMA UN IX

La mayora de estos valores son explicados por los campos de comentario. Los

tipos como dev_t y ino_t estn definidos en < sys/types.h > ,que tambin debe
ser incluido.

{
struct stat atbuf;

chivo. La definicin de banderas est tambin incluida en < sys/stat.h >; slo requerimos de la parte que tiene que ver con el tipo de archivo
S.1FMT 0160000
S.1FDIR 0040000
S.1FCHR 0020000
S.1FBLK 0060000
S.1FREG 0100000
l ... 1

1/*
/.
/.
/*

il (stat(nama, &stbul) ~ ~ -1) {


fprin(stderr, "fsize: no se tiene acceso a %s\n", name);
return;
}
il stbuf.sLmode & S.1FMT) ~ ~ S.1FDIR)
dirwalk(name, fsize);
printf("%81d %s\n", stbuf.st_size, name);

tipo da archivo 1
directorio ./
especial de caracteres . /
especial de bloques /
reqular ./

La funcin dirwalk es una rutina de propsito general que aplica una funcin

Ahora estamos listos para escribir el programa fsize. Si el modo obtenido de

a cada archivo que est dentro de un directorio. Abre el directorio, itera con to-

stat indica que un archivo no es un directorio, entonces el tamao est a la mano

dos los an:hivos que hay en l, llamando en cada

y puede ser impreso directamente. Si el archivo es un directorio, sin embargo, en-

el directorio y regresa. Puesto que fsize llama a dirwalk en cada directorio, las

tonces tenemos que procesar ese direcrorio un archivo a la vez; puede a su vez
contener subdirectorios, de modo que el proceso es recursivo.
La rutina principal trata con lo s argumentos de la lnea de rdenes; pasa cada
argumento a la funcin fsize.

dos funciones se llaman recursivamente una a la otra.

#include
#include
#include
#include
#include
#include
#include

< stdio.h >


< slrinq.h >
"syscalls.h"
<Icntl.h>
< sys/types.h >
< sys/stat.h>
"direnLh"

UIlO

a la funcin; despus cierra

#deline MAlLl'ATH 1024


/ " diTWalk:
aplica fcn a todos los archivos de dir /
void dirwalk(char dir, void (Icn) (char -))

{
/. banderas para lectura y escritura ./
/. typedefs ./
/. estructura reqresada por stat ./

void fsize( char .);


/. imprime tamaos de archivos 1
main(int arqc, char arqv)

{
/. default: directorio actual ./
if (argc ~ ~ 1)
fsize(". ");
else
while (--argc > O)
fSize( '* + + argv)
return O;

La funcin size imprime el tamano del archivo. Sin embargo, si el archivo


es un directorio, fsiza llama primero a dirwalk para manejar todos los archivos
en l. Note como se usan los nombres de las banderas SJFMT y SJFDlR de
<sys/stat.h> para decidir si el archivo es un directorio. El uso de los parntesis
importa, debido a que la precedencia de & es inferior que la de
int stat(char ., struct stat .);
void dirwalk(char ., void (Icn) (char .));

201

/ .. fsize:
imprime el tamao del archivo "name" . /
void fsize(char _name)

La entrada sLmode comicne un conjunto de banderas que describen el ar-

#dafine
#define
#define
#define
#define

EJEMPLO-LISTADO DE DIRECTORIOS

SECCION 8.6

char name[MAlLl'ATH];
Dirent dp;
DIR -dld;
if dld ~ opendir(dir)) ~ ~ NULL) {
fprintf(stderr, "dirwalk: no se puede abrir %s\n", dir);
return;

while dp ~ readdir(dld)) ! ~ NULL) {


if (strcmp(dp->name, ".") = = O
:: strcmp(dp->name, ".") = = O)
continue;
/. se ignora a si mismo y a su padre . /
if (strlen(dir) + strlen(dp- > name) + 2 > sizeof(name
fprintf(stderr, "dirwalk:nombre %5/%5 demasiado larqo\n",
dir, dp-> name);

else {
sprintf(name, "%5/% S", dir, dp-> name);
(Icn) (name);

closedir(dld);

202

LA INTERFAZ DEL SISTEMA UNIX

CAPITULO 8

Cada llamada a readdir regresa un apuntador a informacin para el siguieme


archi\o, o NULL cuando ya no quedan archivos. Cada directorio siempre conI ene entradas para si mismo, llamada".", y para su padre " .. "; deben ser igno_
radas, o el programa iterar por siempre.
En eSle ni ve l, el cdigo es independiente de cmo est el formato de los direclorios. El sig uiente paso es presentar versiones mini mas de opendir, readdir, y
closedir para un sistema especfico. Las siguientes rutinas son para sistemas
UNIX Ver"in 7 y Sy~telll V; ulili/an la informacin que est en el Ileuder
<sys/dir.h>. que aparece as:

EJEMI'LO- L1STADO DE DIRECTORIOS

SECC ION 8.6

203

dp-> fd ~ Id;
return dp;

closedir L"icrra el archi\ O del din.'LlOrio y libera el espacio:


/ . closedir:
cierra un directorio abierto por opendir . /
void closedir(DI.n .dp)

if (dp) (
close(dp-> Id);
free(dp);

#ifndef DIRSIZ
#define DIRSIZ
14
#endif
struct direct /. entrada del directorio

Finalmcnle, readdir usa a read para leer cada elllrada uel dircclOrio. Si tina
ino_t d...ino
char Lname[DIRSIZ];

/. nmero de nodo-i ./
/. los nombres largos no tienen '\0' ./

};

Algunas versiones del sistema permiten nombres mucho ms largos y tienen una
estructura de directorios ms complicada.
El tipo ino_t es un typedef que describe al ndice a la lista de nodos-i. En
el sistema que usamos regularmente es un unsigned short, pero sta no es la clase
de informacin para incluir en un programa; puede ser distinta en un sistema diferente, de modo que typedef es mejor. Un juego completo de tipos "del sistema"
se encuentra en < sys/types.h > .

opendir abre el directorio, verifica que el archivo sea un directorio (esta vez
por medio ele la llamada al sistema fstat, que es como stat excepto en que se aplica
a un descriptor de archivo), asigna una estructura de directorio, y graba la infor~
macin.
int fstat(int fd, struct stat .);
/. opendir:
abre un directorio para llamadas de readdir /
DIR .opendir(char .dirname)
(
int fd;
struct sial stbuf;
DIR -dp;

if
::
::
::

((fd ~ open/dirname, O...RDONLY, O))


-1
Istat(fd, &stbuf) ~ ~ -1
(stbul.sLmode & S.1FMT) ! ~ S.1FDJR
(dp ~ (DIR -) malloc(sizeof(DIR)
NULL)
return NULL;

('!lIrada del dircclOrin no est actualmenle en uso (debido a que ha

~ido

removido

archivo), el nmero de nodo- i es cero, y esta posicin se salta. Oc otra for~


na, el nmero de nodo-i y el nombre son colocados en una estructura esttica
y \c regresa al usuario UIl apulltador a ella. Cada Ilall1ada sobreescribe la informacill de la alllerior.
UI1

#include <sys/dir.h>

/." estructura local de di reciario . /

lee en secuencia las enlradas de un directorio . /


/ readdir:
Dirent readdir(DIR -dp)
struct direct dirbuf;
sta tic Direnl d;

estructura local de directorio ./


regreso: estructura transportable ./

while (read(dp->fd, (char -) &dirbuf, sizeof(dirbuf))


~ ~ sizeof(dirbul (
if (dirbuf.d_ino = = O)
/ entrada que no est er, uso/
continue;
d.ino = dirbuLd_ ino;
strncpy(d.name, dirbuf.d~ame, DIRSIZ);
d.name[DIRSIZ] = '\0'; / . asegura la terminacin . /
return &d;
return NULL;

,Aunque el programa fsize es bastante especializado, ilustra un par de ideas


11l1ponalllcs. Primera, muchos ' programas no .)o n "programa~ del sistema";
'lill1plcmente usan informacin que es mantenida por el siste ma operativo. Para
tale"o programas es crucial que la representacin de la informacin aparezca s lo
en hellders estndar, y que los programas in~Juyan esos archivos en vez de tener

204

LA INTERFAZ DEL SISTEMA UN I X

CAPITULO B

la':l dedaraciones en ellos mismo.:... La segunda observacin es que con cuidado es


posible crear una intcrfaL hacia objetos dependientes del sistcma que a su vez sea
relativa mentc indepcnd iente del mismo. Las fu nciones de la bibl ioteca est ndar
\011 buenos ejemplos.
Ejercicio 8-5_ Modifique el programa fsize para que imprima el resto de la infor_
macin cOlllenida en la entrada del nodo-i . O

8_7

Ejemplo-asignador de memoria

En el captulo 5 present amos un asignador de memoria muy limitado que funcionaba en modo de pila. La versin qu e esc ribiremos a hora no tiene restr icciones. Las llamadas a malloe y free pueden oc urrir en cualquier orden; malloc
l/ama al sis tema operat ivo para obtener ms memoria cuand o es necesaria. Estas
rutillas ilustran a lg unas de las co nsideraciones implicadas en la creacin de cdigo dependiente de mquina en un a forma relativamente independiente, y tambin mues tran un a ap licacin de est ructura, uni o nes y typedef a la vida rea l.
En vez de as ignar un arreg lo precompilado de tamao fijo, malloe solicitar espacio a l sistema operativo cua nd o sea necesario. Dado que otras act ividades en el programa tambin pueden requerir espacio si n llamar a este asignadar. el espacio que malloe maneja puede no se r comiguo . As, el espacio libre de
almace namiento es mantenido co mo una lista de bloques libres. Cada bloq ue
contiene un tamao, un apuntador al siguiente bloque, y el espacio en s. Los bloques so n mantenidos en orden ascendente de d ireccin de almacenamiento, yel
ltimo bloque (direccin ms alta) apunta al primero.

'"",""

~ 0;~
1 luso" luso"1T I,: 1"

'lcr,

. . I :s: I

205

lista libre. Si no se encuenrra un bloque suficientement e grande, algn otro trOLa


urande se obticne -Qel sistema operativo y se liga a la li sta libre.
- La liberaci n tambin provoca una bsqueda en la lista libre, para encontrar
el lugar apropiado para insertar el bloque que est siendo liberado. Si el bloquc
que est siendo liberado es adyacente a un bloque libre en cualquiera de sus lados,
,>e une co n l en UIl bloque nico ms grande, por lo que el almacenamiento
na se fragmenta demasiado. Determinar la adyacencia es fc il puesto que la lista
libre es mantenida en orden asce ndente de direcciones.
Un problema, al que aludimos en el captulo 5, es asegurar que el almacenamiento regresado por malloe est a lineado apropiadamente para los objetos que
se almacenarn en l. Aunque las maquinas varan, para cada una existe un
!ipo que es el ms restrictivo: si el tipo ms res tri ctivo puede se r almacenado en
una direccin particu la r, todos los otros ti pos tambin lo se rn. En algu nas mquinas, el tipo ms reslrictivo es un double; en otras, basta int o long .
Un bloque libre conti ene un apuntador al siguiente bloq ue de la cadena, un
registro del tamao del bloque, y luego cl espacio disponible cn s; la informacin
de co ntrol que est al ini 'jo es llamada el encabezador. Para simplificar la a lineaci n, lodos los bloques son multiplos del I <ll11a o del cneabezador, y C.IIC se ali nca apropiadamente. Esto se log ra mediante tina unin que con ti ene la estruct ura deseada del encabezador y una ocurrencia del tipo de alineacin ms res tri cti vo, al que arbitrariamente hemos hecho long :
typedef long Align;

/* para alineamiento al lmite mayor */

union header {
/ * encabezado! del bloque .o /
struct {
union header *ptr; /* siguiente bloque si est en la list a Jbre */
unsigned size;
/* tamao de este bloque "/
) s;

Align x;

/ * obliga a la alineacin de bloques .o /

J;

libre, apropiado por malloc

Ien uso I en uso , apropiado por malloc

EJEMP LO-AS IGNADOR DE MEMORIA

SECClON 8.7

no apropiado por malloc

C uando se hace una solicit ud , se rastrea la lista libre hasta que se el1c uem ra un
bloque suficiememente grande. Este algoritrr:.o es llamado "de primer ajuste"
([irsl-fi!), en comrasle co n (besl fi!), que busca el bloque ms pcqueiio que sat isfar la sol icitud. Si el bloque es exactamente del tamao requerido, se d esl iga de
la li~ta y se entrega al usuario. Si el bloque es demasiado grande se divide, y la
(aTlliclad apropiada es entregada al usuario mientras que el resto permanece en la

typedef unjon header Header;

1:::1 ca mpo!Alignl nunca es utilizado; slo hace que cada encabczador est alinead o
al limite del peor caso.
En malloe, el tamao requerido en caracteres es redondeado a l numero
aprqpiado de unidades de tamao del encabezador; el bloque que "iera a,ignado
comiene una unidad ms, para el encabezador en s, y eo;;tc es el valor grabado en
el campo size. El apuntador es regresado por malloe apunta al espacio libre, no
cncabezador. El usuario puede hacer cualquie r cosa con el espacio requerido, pero si algo se escribe fuera del espacio a':lignado, la liSia ..,e puede de':lorgani/ar.

206

LA INTERFAZ DEL SISTEMA UNIX

CAPITU LO 8

EJEMPLO-ASIGNADOR DE MEMORIA

SECC ION 8.7

freep = prevp;
return (void *) (p+1);

apunta al siguiente bloque libre

J tamao
~

207

if (p = = freep) /. dlO la vuelta a ~a lisia libre .. /


il (p = morecore(nunits)) = = NULL)
return NULL;
/ * nada libre * /

direccin regresada al usuario

Un bloque regresado por malloc

El campo Isizel es necesario debido a que los bloques controlados por malloe no
requieren ser contiguos -no es posible calcular tamaos mediante aritmtica de
apuntadores.
La variab le basel se usa para comenzar. Si freep es NU LL , como lo es en la
primer llamada de malloc, entonces se crea una lista libre degenerada que contiene un bloque de tamao cero y apunta a s misma. En cualquier caso, luego se
busca en la lista libre. La bsqueda de un bloque libre de tamao adecuado principia en el punto (freep) donde se encontr el ltimo bloque; esta estrategia
ayuda a mantener la lista homognea. Si se encuentra un bloque demasiado gra nde, al usuario se le regresa la parte final; en esta forma el encabezador del original slo necesita tener ajustado su tamao. En todos los casos, el apuntado r
'regresado al usuario apUl1la al espacio libre dentro del bloque, que principia una
unidad ms all del encabezador.
siatic Header base;
static Header *freep

/ * lista vaca para inicar *j


NULL;
/ * inicio de una lista libre */

/ * malloc: aSignador de almacenamiento de propsito general .. /


void *malloc(unsigned nbytes)
{

a free.
La llamada sbrk(n) al sistema UNIX regresa un apuntador a n bytes ms de
almacenamiento. sbrk regresa - 1 si no hubo espacio, aunque NULL hubiera sido
un mejor diseo. El - 1 debe ser forzado a ehar * para que pueda ser comparado
con el valor de relOrno. NuevamelHe, las conversiones forzadas hacen a la funcin relativamente inmune a los detalles de representacin de apuntadorc ~ en mquinas diferentes. Hay, sin embargo, una suposicin ms; que los apuntadores a
bloques diferentes regresados por sbrk pueden ser comparados. Esto no e) garantizado por el eSlndar, que slo permite la comparacin de apuntadorc':.
dentro de un arreglo. As, esta versin de malloc es porttil slo entre mquina\
para las que la comparacin general de apuntadores es .I,ignifica tiva.
#define NALLOC

1024

/ * mnimo # de unidades por requerir */

/* morecore:
solicita ms memoria al sistema */
static Header *morecore{unsigned nu)

Header *P, * prevp;


Header "'morecore(unsigned);
unsigned nunits;

char "CPI *sbrk{int);


Header *up;

nunits = (nbyies + sizeof(Header)-1)/sizeof(Header) + 1;


f prevp = freep) = = NULL) { 1- no hay lista libre an -1
base.s.ptr = freep = prevp = &base;
base.s.size = O;
for (p = prevp- > s.pir; ; prevp
P, p = p- > s.ptr) {
if (p->s.size > = nunits) {
j * suficientemente grande
if (p- > s.size
nunits)
j * exacto */
prevp-> s.ptr = p -> s.ptr;
etse
/ * asigna la parle final */
p- > s.size - = nunits;
p + = p->s.size;
p- > s.size = nunits;

La funcin morecore obtiene espacio de almacenamiento del sistema operativo. Lo~ detalle~ de cmo lo hace varan de sistema a si~tcma. Debido a que pedir memoria al sistema es una operacin comparativamente costosa, no deseamos
hacerlo en cada llamada a malloe, as que morecore so licita al menos NALLOC
unidades; e~tc bloque grande ser seccionado de acuerdo con las necesidades.
Despus de fijar el campo size l morecore inserta la memoria adicional llamando

(nu < NALLOC)


nu = NALLOC;
cp = sbrk{nu .. sizeof(Header));
ii {cp = = (char .. ) - 1)
/ * no hay nada de espacio .. /
return NULL;
up = (Header .) cp;
up- > s.size = nu;
free(void .) (up + 1));
return freep;

*j

208

LA I NTER FAZ DEL SISTEMA UNIX

CAPITULO 8

free es la hima secci n. Recorre la lisLa libre, iniciando en freep, buscando


dnde insertar el bloque libre . Esto es ent re do s bloques existentes o en uno de los
extremos de la li sta . En cualquier caso , si el bloque que est siendo liberado es
adyace nte a alg n veci no , los bloques adyacentes se comb inan. Los nicos
problema.":. .')on mant ener los apuntadores sealando a las cm/as correclas y man_

tener los tamailos correctos.


/ * free: coloca el bloque ap en la lista vaca .. /
void free(void .. ap)

{
Header *bp, .p;
bp = (Header" )ap - 1;

1* apunta al enca bezador de un bloque .. /

for (p = freep; !(bp > p && bp < p- > s.plr); p = p- > s.plr)
if (p > = p- > s.plr && (bp > p :: bp < p->s.plr
break;

/ * libera bloque al inicio o al final . /

if (bp + bp->s.size = = p ->s. ptr) { / * une al nbr superior *


bp->s .size + = p->s. ptr->s. size;
bp-> s.ptr

p->s.ptr->s.ptr;

} else
bp->s .ptr
p->s.ptr;
if(p + p-> s.size == bp){
p->s.size + = bp->s.size;
p - > s.ptr
bp- > s.ptr;

* une al nbr inferior *

} else
p-

> s.ptr

bp;

freep = p;

Aunque la asignacin de memoria es intrnsecamente dependiente de la mquina, el cdigo anterior ilustra como pueden ser contro ladas las dependencias
de la mquina y confi nadas a una parte muy pequea del programa. El uso de
typedef y de funion] maneja la alineacin (suponiendo que sbrk proporciona un
apuntador apro piado). Las conversiones for zosas ha ce n qu e los apuntadores se
manejen adecuada y ex plcitamente, e incluso se acoplan a una interfaz para el
si~ tcma mal di seada. Aun cuando los detalles aqu est n relacionados con la
asignacin de almacenamiento, el acercamiento general es aplicable tambi n a
a iras situaci ones .

Ejercicio 8-6_ La funcin ealloe(n,size) de la biblioteca estndar regresa un


apuntador a n objetos de tamao size, con el almacenamiento inicializado en ce~
ro. Escriba ealloe, invocando a malloe o modificndola. O
Ejercicio 8-7_ malloe acepta un tamao solicitado sin verificar la posibilidad de
que sea vlido; free cree que el bloque que se pide liberar co ntiene un campo de la
ma o co rrecto. Mejore esas rutinas para que se tomen ms molestias en la revi
sin de errores. O

SECCION 8.7

EJEMPLO-ASIGNADOR DE MEMO RI A

209

Ejerd do 8~8. Es~ riba llna rut ina bfree(p,n)que libere un bloque arbitrario p
de n caracteres en la lista libre mantenida por malloe y free. Utilizando bfree,
un usuario puede agregar un arreg lo esttico o externo a la lista libre en cual
quier momento. O

APENDICE A:

A1.

Manual de referencia

Int roduccin

Este manual describe al lenguaje e tal como se especifica en Draft Proposed American
Nalional Standard jo,. Informarion Sysfems -Programming Lal/guoge e, documen to
nmero X3 J 11/88-001, con fecha 11 de enero de 1988. Este borrador no es el estndar final, y todava es posible que ocurran algunos cambios en el lenguaje. As pues, este manua l
no dcs\.Tibe la defi n icin f inal del lenguaje. M s an es una interpretac in de l borrador
propuesto del estndar, no el estndar en s, aunque se ha ten ido cuidado de hacerlo una
gua confiab le.
En su mayo r pane, este ma nual sigue la lnea amp lia del borrado r estndar, que a su vez
sig ue la de la primera edicin de este libro, au nque la organ izacin difiere en el deta lle. Excepto por renombrar algu n as prod ucciones y porque n o se for malizan las definici on es de
los componentes lxicos o de! preprm;csador, la g ra mt ica d ada a q u pa ra el lenguaje es
equivalente a la del borrador actua l.
En este manual, el material comel11ado se encuentra sangrado y escrito en un tipo
ms pequeo , corno estc. A mcnudo estos comentarios resa ltan las forma s en las
que el estndar r\NSI de e difiere del lenguaje definido por la primera edicin de

eSle libro, o de refinamientos introducidos posteriormente en varios

A2.

compi l adore~.

Convenciones lxicas

U n programa consiste en una o ms unidades de fradl/cen alma,;enadas en archivos.


Es traducido en varias fases, que se describen en AI2. Las primeras fases hacen transformacio nes lxicas de bajo nivel , ejecutan diret:t ivas introducidas con lneas que p rincipian
con el carcter #, Y realiLan macrodefinic iones y expansiones. Cuando el preprocesa mi enlo de ~A12 est complelO, el programa se ha redu\.:ido a una secuencia de compo nentes
lhicos.

A2.1 Componentes lxicos (tokens)


Exi ste n seis clases de componentes lxicos: identificadores, palabras reservadas. co ns
tall tes, cadenas lilerales , operadores y otros separadores. Los blancos, labuladores horilom ales y verticales, nueva lnea, avance de forma y comentarios, corno se describen
aUda llle (en su conjulllo . llamados "espacio en blanco") son ignorados, excepto lo s que

211

212

APENDICE A

MANUAL DE REFERENCIA

..,e paran l:ompOncnIC!:.. Se requiere de a lgn espacio en blanco para separar ide nr ificadores
de otra manera adyaL'Clllcs. palabr<15 rcscnadas y cons ta nt es.
Si el flujo de enlrada "e ha separado en co mponentes hasta un L'3ract er determinado, el
siguiente componente es la cadena m~ larga de caracteres que puede constit u ir uno.

SECC ION A2

CONVENC IONES LEXICAS

213

constante:
constante-entera
constan le-de-carcler
constante-flotante
constante-enumeracin

A2 .2 Comentarios
Los caracteres ; . inician un comentario, que termina con los caracteres ./. Los
mentarios no se anidan y no pueden estar dentro de cadenas o caracteres literales.

CO~

A2.3 Identificadores
Un identificador es una secuencia de letras y dgi tos. El primer carcter debe ser una
[cml; el subguin ~ c uenta COIllO una lelra. Las lelras minsculas y maysculas so n diferen-

tes. Lo~ ide ntificadore ~ puedc n Icner cual quier longitud y, para identifil:adores internos,
al !llenos los primeros 31 L'aracteres son signifi cativos; algunas im pla ntaci ones pueden
haL'er que m s L'aracteres sean signifil:ati vos. Los identificado res im ernos incluye n los
nombres de manos del preprocesador y todos los ot ros nombres que no tienen ligado
eXlc rn o (A 11.2) . Los identificadores co n ligado externo es tn ms restringidos: las implanta\.:ioncs pueden ha~. .'er que s lo sean sign ificativos se is caraL'tercs y pueden ig nora r la
dist in l.'in entre maysl,: ulas y minsculas.

Una co nstan te entera que consiste en una secue ncia de dgitos se toma como oc tal si
principia con O (dgito cero), de otra manera es decimal. Las constantes octales no contienen los dgitos a 9. Una secuencia de dgitos precedida por Ox OX (dgito cero) se toma
como un entero hexadecimal. Los dgitos hexadecimales incluyen de la a o A hasta la f
o F con valores ]0 al 15.
Una constante entera puede tener la letra u O U como sufijo, lo cual especifica que es
unsigned. Tambin puede tener como sufijo la letra 1 o L para estipular que es long.
El tipo de una constan te emera depende de su fo rma , valor y sufijo (vase A4 para una
discusin de tipos). Un decimal sin sufijo tiene el primero de estos tipos, en el que su valor
pucda ser represe ntado: int, long int, unsigned long int. Si es o;,;tal o hexadecimal s.i n sufijo, tiene el primer valor posible de estos tipos: int, unsigned int, long int, unsigned long
int. Si tiene el sufijo u o U , entonces es unsigned int unsigned long int. Si tiene el sufijo
1 o L, entonces es long int unsigned long int.
La elaboracin de 105 tipos de constantes enteras va considerablemente ms all
de la primera edicin, Que simplemente haca Que las grandes constantes enteras
fueran long. Los sufijos U son nuevos.

A2.4 Palabras reservadas


Los siguien tes id ent ifiL"adores so n palabras reservad as y no se pueden utilizar de otra
manera:

auto
break
case
char
const
continue
default
do

A2.5.1 Constantes enteras

double
else
enum
extern
fl oat
for
goto
if

int
long
register
return
short
signed
sizeof
static

struct
switch
typedef
union
unsigned
void
volatile
while

Algu nas implantaciones tambin reservan las palabras fortran y asm.


L~~ palabras const , signed y yolatile son nuevas en el estandar \'\'SI; enum )'
yoid \()1l ll11e\a~ desde la pri mera cdidn. pero en uso I.:omn: entry , ant es
rl.' . . l'l'\ ad~ pero Illll11'a \I~ada, ya no e.,t re!.l'l"\ ada. Depen diendo de las dedsiones
dl'll"omil(' '1.111, la ralabra noalias (:1l11bi0n puede estar re~t'f\ada.

A2.5 Constantes
Ha y varias clases de constantes. Cada una tiene un tipo de dato; en A4 .2 se disL' utell
los tipos bsicos.

A2.5.2 Constantes de carcter


Una co nstante de carcter es una secuencia de uno O ms caracteres encerrados en tre apstrofos, como 'x'. El valor de una co nstanle de ca rcter con un so lo carcter es el va lor numrico del ca rcter en el conjunto de caracteres de la mquina al tiempo de ejecucin. El
valor de una co nstan te mu lticareler est definido por la implantacin.
Las constant es de carcte r no cO lllienen el carc ter ' o nueva linea; para representarlos,
as como a algunos otros caracteres, se pueden utilizar las siguientes secuencias de escape.
nueva lnea
tab horizontal
tab vertical
retroceso
regreso de carro
avance de forma
seal audible

NL (LF)

\n

HT
VT

\t

BS
CR

FF
BEL

\v
\b

\r
\f

diagonal in versa
interrogacin
apstrofo
comillas
nmero octal
nmero hexadecimal

\\
\?

\'
\ "
000

\000

hh

\xhh

\a

El C'Icape \000 consiste en la diagonal inversa seguida por 1, 2 3 digitos octales, que est iPulan el Valor del carcter deseado. Un ejemplo eom n de esta construccin es \0 (no segUid o por un d git o) , que especifica el carcter NUL El escape \xhh consiste en la diagonal
n\er~a seg ui da por x, seg uida por dgitos hexadecimales, que estipulan el valor de carc ter
~e~eado. No hay lmite en el nmero de dgitos, pero el com portamiento qued a ind efi nid o
SI el valor de carc ter resultante excede al del carc ter ms grande. Para caracteres octa les

214

MANUAL DE REFERENCIA

APENDICE A

o hexadecimales, si la implantacin trala al tipo char como signed, el valor c.'> extendido
en .\ igno cumo..,i ~c for/ara a ser de tipo charo Si el carcter que sigue a \ no e:-, uno de los
cl,pccificadm, {'Icomportamient o no est definido.
En algunas implantaciones, existe un conjunto extendido de caracteres que no se puede
representar por el tipo charo Una constante en este conjunto extendido se escribe con una
L precedente, por ejernplo L'x', y se llama una constante de carcter amplio. Tal constante
I ene tipo wchar_ t, un lipa cnlero definido en el header < stddeLh >. Como con las COnstante" de carcter ordinarias, se rueden empicar escapes octales o hexadcdmales; el efeclo
est indefinido si el valor especificado excede al que se representa con wchar_t.
Algunas de estas secuencias de escape son nuevas, en particular la representacin
hexadecimal de caracteres. Los caracteres extendidos tambin son nuevos . Los
jUl"go:-, ue l"ar~ll'Il'1"C', l"l)!11nllh.:ntl' u<;ado,> en Amcri.,:a y Europa on:idC'lHal se PUeden codificar para quedar en el tipo char; la intencin principal de agregar
wchar_t rue adapJarsc a los lell.;uajrs asitlo.;os.

SECCION A4

SIGNIFICADO DE LOS IDENTIFICADORES

La especifil"acin de que ta ., <.:auenas titerale~ no tienen por lju 'ier di ~ lil1la,>. y I:J
prohibio.."in en <.:OInra de !l1odirio.."ar!a~. <;on I1mcdaue<; delltro del eqndar \" .... 1.
as corno la l'oIK<ltenal'i n de l"adenas literab. ad yal:l'lltcS. Las l:adcnas litnatc,
de cara<.:leres amplio<; son llUe\as .

A3.

Notacin sintctica

Dentro de la notacin sintctica que se emplea en este manual, las categoras sintcticas
se indican con estilo itlico, y las palabras textuales y caracteres en estilo mecanogrfico.
Las categoras alternativas usualmenIc se listan en lneas separadas; en algunos casos,
un conjullto amplio de alternativas corras se presenta en una lnea, marcada por la
frase "uno de". Un smbolo optativo terminal o no terminal lleva el subndice "Opl", de
modo que, por ejemplo,

{ expresin op /
A2.5.3 Constantes flotantes
Una constante flotante consta de una parte entera, un punto decimal, una parte fracI.."iunaria, una e o E, un exponente enlero signado optalivo y un tipo sufijo optativo entre
f o F, 101. La~ parles entera y fraccionaria constan de una secuencia de digitos. Cualquiera de las partes entera o fraccionaria (no ambas) puede omitirse; cualquiera de las partes del punto decimal o la e y el exponente (no ambas) pueden omitirse. El tipo est determinado por el sufijo; F o f la hacen float, L o Ila hacen long double; de otra manera es
double.
I.os sufijos C'n <.:onSlantes flotantes son nuevos.

215

significa una expresin optativa, encerrada entre llaves, La sintaxis se resume en A13.
A diferencia de la gramtica empleada en la primera edicin de este libro, la que
aqu se da hace explcita la precedencia y asociatividad de los operadores de expresin.

A4.

Significado de los identificadores

Los identificadores, o nombres, se refieren a una variedad de cosas: funciones; rtulos


y enumeraciones; miembrm de estructuras o de uniones; L'OIl'-;!a1l1CS de
enumcracin; nombrcs typedef y objetos. Un objeto, algunas veces llamado \ariable, es
una localidad en el espacio de almacenamiento y su interpretacin depende de dos atributos fundamentales: su categada de almacenamiento y su tipo, La categora de almacenamiento determina el tiempo de vida del almacenamiento asociado con el objeto identifica
do; el tipo determina el significado de los valores encontrados en el objeto identificado.
Un nombre tambin tiene un alcance, que es la regin del programa dentro de la que se
co noce, y una liga, que determina si el mismo nombre en otro alcance se refierc al mismo
objelO o funcin. El alcance y la liga se discuten en A 11.
de estructuras, uniones

A2.5.4 Constantes de enumeracin


Los identificadores declarados como enumeradores (vase A8.4) son constantes de
tipo int.

A2.6 Cadenas literales


Una I.."adcna literal, tambin llamada cadena con~lanle es una secuencia de caral"leres
delimitados por comillas, como en \\ .. ,". Una cadena tiene el tipo "arreglo de caraetl'("('S" y catt'goria de almacenamiento static (vase A4, abajo) y se inil'ializa con los
caracteres dados. El que cadenas idnticas sean distintas est definido por la implantacin,
y el comportamiento de un programa que intenta alterar una cadena literal est indefinido.
Catkna\ literales adyacentes se concatenan en una sola cadena. Despues de cualquier
COnCall'lla . . . in, se agrega un byte nulo \0 a la ~'adena, de macla que los programas que
rastrean la cadena puedan enconlrar el fin. La s cadenas literales no ~'onlienen caracteres
!luna lnea o comillas; para representarlos, se usan las mi smas secuerH.:ias de escape que
para la:'. constantes de carcter.
Como con la s comlanles de cara('!er, las cadena,> Iitcrales en un l'onjunto de caracteres
l'xtclH.lido se escriben ron una L precedente, rorno en L\\ ... ". Las cadenas literales
amplias de cararteres tienen tipo "arreglo de wchar_ t". La conl'alenacin de cadenas Jilcrak ~ ordinarias y amplia~ est indefinida.

A4.1 Categorias de almacenamiento


Existen dos categoras de almacenamiento: automtica y esttica. Varias palabras reservadas, junto con el contexto de la declaracin de un objeto, especifican su categora
de almacenamiento_ Los objetos automticos son locales a un bloque (A9.3), y son descartados al salir del bloque, Las declaraciones dentro de un bloque crean objetos automticos si no se menciona una especificacin de categora de almacenamiento, o si se emplea
el especificador auto. Los objetos declarados como register son automticos, y se almacenall' (si es posible) en registros rpidos de la mquina.
Los objetos estticos pueden ser locales a un bloque o externos a todos los bloques,
pe ro en cualquier caso mantienen su valor entre la s salidas y reentradas a fundoncs o
bloques. Dentro de un bloque, incluyendo lino que proporcione el cdigo de una funcin,
los objetos estticos se declaran con la palabra reservadalstatig, Los objetos que se decla~

216

MANUAL DE REFERENCIA

APENDICE A

fan fuera de todos los bloques, al mismo nivel que la definicin de las funciones, son siem_
pre cstillko~. Se pueden hacer locales a una unidad de Iradul:cion en panicular por el uso
dc la palabra reservada static; rsto le,l; otorga liga mema. Se hacen globales a un progral11a
l'Olllp[cto omitien do ulla calegora cxpJkia dc almacenamiento, o ulilit.ando la palabra
Il'~cnada exlern; ,,<:. tu les olorga lii!o externa.

SECCION A6

CONVERSIONES

217

Debido a que los objetos de estos tipos se pueden interpretar como nmeros, se har
referencia a ellos como tipos aritmticos. Los tipos char e int de todos los tamai'Jos, cada
uno con o sin signo , y tambin los tipos de enumeracin, se llamarn conjuntamente tipos
el/teros, Los tipos float, double y long double se llamarn tipos flotantes.
El tipo void especifica un conjunto vaco de valores. Se usa como el tipo regresado por
funciones que no generan un valor.

A4.2 Tipos bsicos


Existen varios tipos bsicos. El header estndar < hmits.h > que se describe en el apn_
dice B define los valores mayores y menores de cada tipo dentro de la implantacin local.
Los nmeros dados en el apndice B muestran las menores magnitudes aceptables.
Los objetos declarados como caracteres (char) son suficientemente grandes para alma.
cenar cualquier miembro del conjunto de caracteres en ejecucin. Si un carcter genuino
de ese conjunto se almacena en un objeto char, su valor es equivalente al cdigo entero
para ese carcter, y es no negativo. Se pueden almal.'enar otras .:antidades en va riables
char, pero el rango de valo res disponibles, yen espet.:ial si el va lor tielle signo, depende
de la implantacin.
Los caracteres sin signo declarados unsigned char consumen la misma cantidad de es
pacio que los caracteres sencillos, pero siempre aparecen como no negativos; los caracteres
explcitamente signados que se declaran signad char toman igualmente el mismo espacio
que los caracteres sencillos.
unsigned char no aparece en la primera edicin de este libro, pero es de uso co
mn. signed char es nuevo.

Adl'm;ls de 10\ tipos char, hay tres taman os de entero~, declarados COIllO shorl int, int,
y long int. Lo, objetos int sim ples tienen el tamao natural sugerido por la arquitet.'lUra
de la mquina donde se ejecuta; los Otros tamaiios se propon:ionan para cump lir
con necesidades especiales. Los enteros ms grandes proporcionan por lo menos tanto almacenamiento como los menores, pero la implantacin puede hacer a los enteros simples
equivalentes a los enteros cortos, o a los enteros largos. Todos los tipos int representan
\aJores con signo a menos que se e~pel'ifique lo contrario.
Los enteros sin signo, declarados mediante la palabra reservada unsigned, obedecen
a las leyes de la aritmtica mdulo 2" donde n es el nmero de bits en la representacin,
por lo que la aritmtica sobre cantidades signadas nunca puede desbordarse. El conjunto
de \'alores no negativos que se pueden alma;enar en objetos con sig no es un subconjunto
de los que ~e pueden alrna;enar en el correspondiente objeto sin signo, y la representaci n
para los valores en comn es la misma.
Cualquiera de los tipos punto flotante de precisin sencilla (float), punto flotante de
precisin doble (double) y punto flotante de precisin extra (long double) puede ser sin
nimo. pero los ltimos en la lista so n al menos tan precisos como los que los antc;eden.

M.3 Tipos derivados


Adems de los tipos bsicos, existe una categora conceptualmente infinita de tipos de
rivados, construidos a partir de los tipos fundamentales en las formas siguientes:

arreglos de objetos de un tipo dado;


funciones que regresan objetos de un tipo dado;
apuntadores a objetos de un tipo dado;
estructuras que contienen una secuencia de objetos de varios tipos;
uniones capaces de contener un objeto cualquiera de varios tipo s.
En general, estos mtodos de construccin de objetos se pueden aplicar en forma recursiva.

A4.4 Calificadores de tipo


Un tipo de objeto puede tener calificadores adicionales. El dedarar const a un objeto
anuncia que su valor no cambiar; declararlo volatile anuncia que tiene: propiedades e~
pedales de importancia para la optimizacin. Ningun ca lificador afecta el rango de \'alore\
o propiedades aritmticas del objeto. Los calificadores se discuten en A8 .2 .

A.S Objetos y valores-I


Un objeto es una regin de almacenamiento con nombre; un va/or-I es una expresin
que se refiere a un objeto. Un ejemplo obvio de una expresin valor-I es un identificador
con un tipo adecuado y una categora de almacenamiento. Existen operadores que produ
cen valores-I: por ejemplo, si E es una expresin de tipo apuntador, entonces *E es una
expresin valor-l que se refiere al objeto al cual apunta E. El nombre "valor-I" proviene
de la expresin de asignacin El = E2 en la que el operador izquierdo El debe ser una
expres in valor-1. La discusin de cada operador especifica si espera operandos valor-l
y si entrega un valor-1.

long double es nuevo, La primera edicin hizo a long float equivalente a


double; l'~!O ~C ha n:chalaclo.

Las l'IIUIII(!I'(I('iulI(!S .'.011 tipos unicos que tienen \'alores enteros; asociado con cada enU
11leral"n hay un (onjullto de l'on~lal1leS nombradas (A8.4). Las enumeraciones se comPl)! lan como I.'lltcro.." pero es (omun que un compilador d una advertencia cuando un
llbjL'IO dI.' UII tipo de t"llullleral'in en partl:ular !le a~igna a algo que no sea una de suS
constantes o una expresin de su tipo.

AS . Conversiones
Algunos operadores pueden, dependiendo de sus operandos, provocar la conversin del
valor de un operando de un tipo a otro. Esta seccin explica el resultado que se espera de tales
conversiones. A6.5 resume las conversiones demandadas por la mayora de los operadores
ordinarios; en donde se requiera. sera comple mentada con la discusin de cada operador.

218

MANUAL DE REFERENCIA

APENDICE A

A6.1 Promocin entera


Un carcH.:r, un cillero COrlO o un campo cntero de bits. todos con o si n signo, o Un

objeto de tipo enumeracin, se puede utilizar dentro de una expresin en cualquier lugar
en donde se pueda usar un entero. Si un int puede representar a todos los valores del tipo
origi nal , ento nces el valor es convertido a int; de otra manera el valor es convertido a unsigned int. E.,le proceso se llama prulllocin enlera.
A6.2 Conversiones enteras

Un enlero se conviene a un tipo sin signo dado encontrando el menor valor no negativo
que \C,I congrucllIc con ese entero, modulo UIIO ms que el mayor valor que se pueda repre-

sentar en el tipo sin sig no. En una representacin complemento a dos, esto es equivalente
al truncamiento por la izquierda si el patrn de bits del tipo sin signo es ms estrecho, y al
llenado co n ceros de valores sin signo y extensin de signo en valores con signo si el tipo
sin signo es ms amplio.
Cuando I,:ualquit:r CIl IlTO ~e com iene a un lipo COIl signo, el valor no se cambia si puede
'>l:1" repn.'scnlado en el lluevo lipo. yen Olro ca~o cstil definido por la illlplalllaci n.

SECCION A6

CONVERSIONES

219

De otra manera, si un operando es long int y el otro es unsigned int, el efecto depende
de si un long int puede representar a todos los valores de un unsigned int; si es as,
el operando unsiqned int es convertido a long int; si no lo es, ambos son convert idos
a unsigned long ini.
De otra manera, si un operando es long int, el otro es convertido a long int.
De otra manera, si cualquier operando es unsigned int, el otro es convertid o a unsigned ini.
De otra manera, am bos operandos tienen tipo int.
Aqu hay dos cambios. Primero, la aritmtica sobre operandos float se puede realizar en precisin sencilla, en lugar de doble; la primera edicin especificaba que
toda la aritmtica rIotante era de doble precisin. Segundo. los tipos sin signo
ms pequeos , U1ando ,>e I:ombinan wn un lipo mn signo ma)or, no propagan

la propiedad de no signado al tipo resu ltante; en la primera edicin, siempre dominaba lo no signado. L as nue\'a~ reglas son ligeramente Illa~ I:olllpljcada~. pl'ru
de alguna forma rcdul'cn la .. .,orprc.,a .. quc pUl'den o\:urrir ntando una l'anlidaa
sin signo enl:Uelllra a OIra ron sig ilO. An pucden onlrrir re.,ullado,> ine~p('radu\
l'uando una exprer.,ion ~in ~igllo c., l'ol11parada \.'\111 una e'\rre .. i'l l'OIl .,igno del
mismo lamao.

A6.6 Apuntadores y enteros


A6.3 Entero y flotante
Cuando un \alor de tipo flotanle se convierte a un lipo cntero. la parte fracciona ria
'>l' (k~carta: ~i d \'alm re<.,ultante no puede ser representado en el tipo entero, el comportamiento no est definido. En particular, el resultado de convertir va lores flotantes negativos
; tipO'> \.'lIH.:ro... :-. il1 :-.ig llll no esta especificado.
Cuamlo UIl \alm de tipo entero ~e \"oll\iertc a Ilotamc. y el \alor esta CI1 el rango representable pero no es exactamente representable, entonces el resultado puede ser el valor
siguiente ms alto o ms bajo_ Si el resultado est fuera de rango , el comportamiento est
indefinido.

A6.4 Tipos flotantes


Cuando un va lor flotante meno s preciso se convierte a un tipo flotante igualo ms preciso, el va lor no se modifica. Cuando un valor flotante ms preciso se convierte a un tipo
flotante menos preciso, y el valor est dentro del rango representable, el resultado puede
"l'l' 1.'1 .. i~l1il".'llIl' \ alm repre.'!clltabk rna.'! alto o el sig uiente ms bajo. Si el resultado est
fuera de rango, el comportamiento est indefinido.

A6.S Conversiones aritmticas


\ lucho" l)p~r<tdorl''> pro\ ocan una (011\ cp,in y I'rod 1I1".'cn I ipos resultantes en forma seIlIl'j:lllll'. 1:1 ('fcl'tu c. . pa . . ar los operandos a un tipo I".'ornn, que e!:. tambin el tipo del resulladl). \ l'''tl' patl'OIl "l' le llama' cO//I'C'I"SiollC'S an'Jlu?lIca~ IHlw/e<;.
Primero, si cualquier operando es un long double , el otro es conven ido a long double.
De otra manera, si cua lquier operando es double, el otro es co nvert ido a double.
De otra manera, si cualquier operando es float, el otro es co nvertido a floai.
Ik l)ll:l 111:1I1lI'1. 'l'lcali,a prlHllol'lll'nl~ra ('11 amho . . operal1do~: d( . . pus. si cualq uier
operando es unsigned long int , el otro es convertido a unsigned long ini.

Una expres in de tipo entero puede ser sumada o re~lada de un apuntador; en ta l ca . . o,


la ex presin cllIcra es convertida tal como se c~pccifica en la di~cus.in del operador de
,di,in (*A7.7).
Dos apuntadores a objetos del mismo tipo, dentro del mi smo arreglo, pueden ser restados; el resultado es convertido a un entero como se especifica en la discusin del operador
de sustraccin (A7.7).
Una ex pres i n ente ra constante ron valor 0, o esa expres in forzada al tipo void . ,
[1u(:de ser co nvertida, po r medio de un caSI, por asignacin, o por wmr>aral'in, a un
apuntador de cualquier tipo. Esto produce un apuntador nu lo que es igual a otro apuntador nulo del mismo tipo, pero diferente a cualquier apuntador a una funcin u objeto.
Se perm iten otras ciertas conversiones quc iJ1\olurran apuntadores, pero tienen a . . pel".to., dcpcndientes de la implantacion. Se deben cspc.:ifi('ar con un operador explcito de
conversin de tipo o cast (A7.5 y A8.8).
Un apuntador se puede convertir a un tipo entero su ficientemente grande para mantenerlo; el tarr:afio requerido depende de la implantacin. La funcin de mapeo tambin depende de la implantacin.
Un objeto de tipo entero se puede expl il'itamentc ronvcrt ir a un apuntador. El mapeo
r.,iempre lleva un entero suficientemente amplio convenido de un apuntador de rcgrc'lO al
mismo apuntador, pero de otra manera es dependiente de la implantacin.
Un apuntador de un tipo se puede convenir a un apuntador a olro lipa. El apuntador
rcslIltallle puede causar errores de direccionamiellto ~i no se rdiere a un objeto adecuadamente alineado en la memoria. Se garantiza que un apunwdor a un objeto se puede con\l'rur a un apuntador a un objeto cuyo tipo requiere de una menor o igualmente estricta
alineaci n en el almacenamiento y regresado de nue\ o sin cambio; la nodn de "alineacin" 1S dependiente de la implantacin, pero los objeto., de tipo char lienen los requisito.,
de alineacian menos estrictos. Como se describe en A6.8. un apuntador se puede com crtir
a tipo void y regresado de nue\o sin cambio.
Finalmente, un apuntador a una funcin se puede convertir a un apuntador a otro tipo
de funci n. La llamada a la funcin especificada por el apuntador convertido es depen -

220

MANUAL DE REFERENCIA

APENDICE A

diente de la implantacin; sin embargo, si el apuntador convenido es reconvertido a su


tipo original, el resultado es idntico al apuntador original.

SECCION A7

EXPRESIONES

221

El comit ANSI decidi, ultimamente en sus reportes, restringir la anterior libertad de reordenar las cxpre~ioncs que invotuc ran operadores matemticamente
conmutativos y a~otiativos, pero quc pueden 110 ser asociativos compuladonal mente. En la prctica, el cambio slo arecta a los clculos de punto flotante

cercanos a los lmites de su precisin y en situaciones en donde es posible el desbordamiento.

A6.7 Void
El (inexistente) valor de un objeto void no se puede utilizar en ninguna forma, ni se
puede aplicar la conversin explicita o implcita a ningn tipo no void. Debido a que la
c\:prc~in void dCIlOla un valor ine.'\istcllIe, slo se puede ulili zar donde no sea requerido el
\alor. por ejemplo. una proposicin de expresin (A9.2) o el operando izquierdo de un
operador coma (A7.18).

Una expres in se puede converti r a tipo void <,.'on un caSI. Por ejemplo, una conversin
rorL.ada a void deja dOl.:umenlado el rCl.:halO del valo r de una llamada a funcin utilizada
C0l110 proposk.'in de expresin ..
VoidlllO apareda en la primera edicin de CStc libro, pero se ha vuelto comn

desde entonces.

A6.8 Apuntadores a void


Cualquier apuntador se puede convertir a tipo void .. sin prdida de informacin. Si
el !'e\ul!aclo se regresa al tipo de apullIador original, ste es recuperado. A diferencia de
la I.on\ ~rsion apunlador-a-apumaclor disc utida en A6.6, que requiere un caSI explicito,
ID-' apulltadores [1UCdl'n ser asignados hacia y desde apuntadores de tipo void y pueden
-'l'r l'omlxlrado~ ~on ellos.
Esta interpretacin de apuntadores void .. es nueva; anteriormente, los apuntadores char jugaban el papel de apuntadores generi cos. El eSI<'mdar Aj\,SI especficamente consiente el encuentro de apuntadores void .. con apuntadores a objelOs en asignat:iones y relaciones, mientras que requiere caSI explicitas para otras
mezclas de apuntadores.

1:.1 manejo del desbordamiento, errores de divisin y otras condiciones de error dentro
de la evaluacin de expresiones no est dcfinido por el lenguaje. La mayora de las reali/aciones cxistcllIcs de e ignoran el desbordamiento en la evaluacin de cxpresionc\ y a\ignat.:io!les enteras con signo, pero este comportamiento no est garantizado. El trato de la divisin
el1tre O, Y todas las l:ondiciones de error de punlo flotante, vara entre las implanladones;
algunas veees es ajustable mediante el uso de funciones no estndar de bibliolcl:a.

A7.1 Generacin de apuntadores


Si el tipo de una expresin o sub expresin es un "arreglo de T", para algn tipo T,
entonces el valor de la expresin es un apuntador al primer objeto del arreglo, y el tipo
de la expresin es alterado a "apuntador a T". Esta conversin no sucede si ta expresin C"I
el opera ndo de un operador & unario, o de + +, --, sizeof, o el operando izquierdo dc
un operador de asignacin o "el operador . . " De modo semejante, una expresin de lipo
"funci n que regresa T", excepto cuando ~c ulilia como el operando del operador &, C!\
converti da a "apuntador a funcin que regresa T". Una expresin que ha \ufrido una de
e1llaS conve rsiones no es va lor-1.

A7.2 Expresiones primarias


Las expresiones primarias so n identifi cadores, constantes, cadenas, o exprc!\iones entre
parntesis.

expresin primaria:

identificador
A 7. Expresiones
La precedencia de los operadores en expresiones es la misma que el orden de las subsecciones principales de esta seccin, primero la ms alta precedencia. As, por ejemplo, las
('xpre.')iolles a las que se hace referencia corno operandos de + (A 7.7) son las definidas en
~~A 7.l-A 7 .6. Delltro de' cada subst'l'cin, los operadores tienen la misma precedencia . En
1..'d~1 "IUb.')l'I.:L' n se especi fi\:a la asociati\ idad por la izquierda o la derecha para los operadnrl.'!) di sl'\ltidos all. La gramtil'a illl'orpora la prel'edcncia y asociatividad de los opcradorl'" de la l'\pn.'sin y se resume en A 13.
La precedencia y asociatividad de los operadores est especificada completamente, pero
l'I orden d~ ('\alual'in de las npresiones est, ~on l'iertas ex~epciones, indefinido, aun si las
subexpresiones involucran efectos colaterales. Esto es, a menos que la definicin de un ope
rador gar'lIl1il'e que su!) operandos se t'\aluen en lJll orden particular, la implantacin
(' .. !tI en libcnad de e\ aluar los operandos en cualquier ordcn, o incluso illlercalar su evaluacin. Sin embargo, cada operador combina los valores producidos por sus operandos en
una forma compatible con el anlisis gramatical de la expresin en que aparece.

constante
cadena

( expresin)
Un identificador es una expre~in primaria, ~ iernpre que haya \ido dedarado adecuadamente tal como se discuti anteriormente. Su lipa eSla espet:ificadc por ~u dcclaracion. Un
identificador es un valor-I si se refiere a un objcto (A5) y <,i ,>u tipo e,> aritmtico, C\tructura, unin o apuntador.
Una constante es una expresin primaria. Su tipo depende de \u forma, tal como ,>e di-,cUli en A2.5.
Una cadena es una expresin primaria. Su tipo es originalmente "arreglo de chal" (para cadenas de caracteres amplios, "arreglo de wchar_t"), pero siguiendo la regla dada en
A7. 1, usualmente se modifica a "apuntador a chal" (wchaJ_t) y el rc,>ultado c"
Un Bpuntador al primer carcter de la cadena. La conversin tampoco ocurre en ciertos
inicializadores: vase A8. 7,
Una expresin entre parntesis es una expresin primaria cuyo tipo y valor son idntil'O~ a los de una expresin que no lo c\t. La pre,>encia de parntc\i .. no afl:cta el que la
C\p resion sea un \alor-1.

222

MANUAL DE REFERENCIA

APEND ICE A

A7.3 Expresiones posfijas


LO'. operadores de C'<prcsiones posfijas se agrupan de i/quierda a dcrcd13.

e.rpresiull-po.s.Iija:
expresin-primaria
expresin-posfijo (expresin J
expresill-posjija (lista-de-expresiones-argumento Op )
expresin-posfijo . identificador
expresilJll-posfija - > dent ificador
expresin-posfija + +
expresin-posfija

lista-expresiones-argumento:
expresi6n-de-asignacin
lista-expresiones-argumento

expresin-de-asignacin

A7.3.1 Referencias a arreglos


Una expresin pos fija segu ida por una expresin dentro de corchclcs es una expresion
posfija que denota una referencia indexada a un arreglo. Una de las dos expresiones debe
lener tipo "apuntador a T". donde T es algn tipo, y la otra debe ene!' tipo entero; el
tipo de la expres in subndice es T. La expresin El [E2] es idntica (por definicin) a

-((El) + (E2)). Vase A8.6.2 para una discusin adicional.

A7.3.2 Llamadas a funciones


Una llamada a funcin es una expresin pos fija, conocida (omo designador de funcin,
seguido de parcntcsis que contienen una lisIa posiblemente vaca de expresiones de asign adon separadas por comas (A7.17), que constituyen los argulllelllOs de la funcin. Si la
c\presin pos fija consiste en un idemifil.:ador para el que no exis((: una declaracin dentro
del akance a(lual, el identifi(ador es explcitamente declarado como si la declaracin

extem int identificador ( ) ;


hubiese sido dada en el bloque ms in terno que contenga la llamada a la funcin. La expresin pasfija (despues de una posible dedaracin implcita y generacibn de apunlad o r ,
A7.1) debe ser del tipo "apuntador a funcin que regresa T ", para algn tipo de r, y
el valo r de la llamada a la fun cin tiene el tipo T.
En la primera edicin, el tipo estaba reslringido a "funcin" y se requeda de

un operador. explcito para invocar a travs de apuntadores a Funciones. El es-

SECC ION A7

EXPRESIONES

223

mino.., "argumclllO (parllll:!tro real)" y "argulllell1o (parmetro) rormal" rc\pcctivamente, se usan algunas veces para hacer la misma distincin.
En prepara(in para la llamada a una fun(i6n, 'ie hace una copia de cada argumcnto;
todo el paso de argumentos es estrictamente por valor. Una funcin puede cambiar los valores de sus objetos parmetros, que son copias de la s expresiones argumentos, pero estos
cambios no pueden afectar los valores de los argumentos. Sin embargo, es posible pasar
un apuntador en el entendimiento de que la funcin puede cambiar el valor del objeto al
que apullIa el apul1\ador.
Existen dos esti los en los que se pueden declarar las funciones. En el nuevo e'itilo, los
tipos de los parmetros son explcitos y son parte del tipo de la funcin; (al declaracin
,>c llama tambin el protOlipo de la fUIl<..'in. En el estilo anterior, lo . . iro\ de 10\ pararnt.:ros no se espedfican. La declaracin de una run(in se Irata en **A8.6.3 y AIO.!.
Si la declaracin de funcin dentro del akance de una llamada e\t en el e ... tilo anterior,
ento nces la p romoci n de argumentos predefinida se aplica en cada argumento como si gue: la promo<:in t,:ntera (A6. 1) se realiLa en cada argumento de tipo entero, y (ada arguIllCIlto float es convertido a double. El efecto de la llamada queda indefin ido .\i el nmero
de argumentm no L:oincidc con el nmero de parmetros de la derinion de la funcin,
o si el tipo de un argumento despus de la promocin no coincide con el del parmetro
correspondiente. La coin cidencia de tipos depende de si la funcin de la funcin est en
,:l nuevo o el viejo estilo. Si esta en el anterior, enlonces la cornpara(j6n e.., entre el lipa
promovido del argumento de la llamada y el lipa promovido del parmclro; \i la definicin esta en el esti lo nuevo, el tipo promovido del argumento debe \er el del parmetro
en s, sin promocin.
Si la dec!arai..'in de la funcin en el alcance de una llamada e\ta el! e~tilo nuevo, entol1ces los argumentos se convierten, como por asignacin, a los tipos de fas parmetros correspo ndientes del prototipo de la funcin. El nmero de argumentos debe se r el mismo
que el nmero ele pararnetrm cxplicitametlle eleclarado~, a meno\ de que la !~ta de parametro) de la declaraci6n termine (on la nOlaci6n ele coma y tre\ punlO\ (, ... ). En e\e <:a<,o,
l'l nmero de argulllelllo", debe igualar o e,<ceder al nmero de parillllctro<,; 10\ argurnelllO\
ma\ all de los parametrO\ l'on tipo cxplkilarnellle declarado \urren la promon preddimcl. ele argurnelllOS de\crila en el prrafo preccdenle. Si la definicin de la f"ulll'in e<.,l en
c1l'stiJo anterior, entonces cli)1o de (ada parmetro dentro del prototipo ",\ibk a la llamada di.'bc <:oir1l'idir <..'on los paramelro<, corre,>pondiente\ de la dl'finicion, de ... pu\ de que al
tipo de paramelro de la definidn <,c le ha hedlO la promocin de argumento\.
bla\ regla.., \Un c~pccialrncllle wI;lnlicada~ debido a Cjuc deben ..,ali..,Jau:r una
me/da de funcione., en el nuevo y \iejo e,>lilo~. La., me!I.:la.,.,e Jcbl..'ll l\ilar.,1
es posible.

El orden de e\ aluan de lo~ argumento\ no e ... ta c<,pecificado; nc\c quc 1m l..'ulllpilauor(') difieren. Sin embargo, lo ... argumento) y 10\ de\ignadore ... de fun<:in \on l'OrTllllctam<:n1(' l\aluado\. incluyendo todo ... 10\ deltO\ 1.:0Iatcralc ... , ante ... de que ~C' entre a la lun<:in. Se
Pl'Tllliten la,> lIamada\ recur\i\a\ a cualquier funn.

landar .\,NSI esta de acuerdo con algunos (ompiladores e.xislell!es pcnniliendo

la misma sintaxis para llamadas a funciones y a funciones especificadas por


apunladores. La

~il1[axi<;

anlerior an se puede 11lilizar.

El trmino argumento se utiliza para una expresin pasada por una llamada a fu ncin;
el trmino parmeTro se emp lea para un objeto de entrada (o su identificador) recibido

"'7.3.3 Referencias a estructuras

por una definici n de funcin o descrito dentro de la declaracin de una funcin. Los tr-

~1 () 11 po\fija . 1:::1 primer operando de la C'xprc..,in dcbe \er una l'\truclura o

Lna e.\pre\in pmfija 'Ieguida por un puma ,>eguido de un Identificador t,:.,

cxpr(,'unin. y

ulla

ulla

224

MANUAL DE REFERENCIA

SECC ION A7

APENDICE A

EXPRESIONES

225

A7.4.2 Operador de drec;cin

el identifkaclor uebe nombrar a un miembro de la CSlrU<..'tura o unin. El valor es el miem_


bro nombrado de la estructura o unin y su tipo es el tipo del miembro. La expresin es
un valor-I si la primera expresin es un valor- l, y si el tipo de la segunda expresin no

El operador un ario &to01a la direccin de su operando. El operando debe ser el va


lor- I que no se refiera Jl a un campo de bil s ni a un objeto dedarado como register, o
debe ser de tipo funcin. !I resultado es un apuntador al objeto O funcin al que se refiere
el valor-1. Si el tipo del operando es T, el tipo del resultado es "apuntador a T'.

es un tipo arreglo.
Una expresin posfija seguida por ulla OCl:ha (w n struida con - y seguida pOr un
idcrllifiL'aclor es una expresin po sfija. El primer operando en la expresin debe ser

un apuntador a una estructura o una unin y el identificador dehe nombrar a un miembro


de la estructura o uni6n. El resultado se refiere al miembro nombrado de la estructura o
unin al cual apunta el apuntador de la expresin, y el tipo es el tipo del miembro; el resul.
lado ('s un \alor-I si el tipo no es arreglo.
As la expresin El->MOS es lo mismo que (.El).MOS. Las estructuras y uniones
se discuten en A8.3.

A7.4.3 Operador de inlireccin


El operador un ario * cenota indireccin y regresa el objeto o funcin a que apunta .. u
operando. Es un valor-lsi el operando es un apuntador a un objeto de tipo aritmtico,
estructura, unin O apunador. Si el tipo de la expresin es un "apuntador a T". el tiDo
del resultado es T.

En la primera edicin de este libro ya estaba la regla de que un nombre de miem.


bro en una expresin asi Icnia qu e pcncnc,;cr a la eSlfll,;tura o unin mencionada
en la e"presin posfija; si n embargo, una nO la admitia que esta regla no se segua
firmemente. Los wmpiladorcs recientes y el Ar-.~J la siguen.

A7.4.4 Operador ms maria


El operando del operalor unario + debe tener tipo aritmtico o apuntador y el resultado es el valor del opcrardo. Un operando entero sufre promocin entera. Et tipo del
resultado es el tipo del merando promovido.
El + umio es nuevo en el estndar ANSI. Se agreg por simetra con el- unario.

A7.3.4 Incrementos posfijos


Una expresin posfija seguida de un operador + + o -- es una expresin posfija.
El valor de la expresin es el valor del operando. Despus de usar el valor, se im:remeIHa el
opcrando ( + +) o se denemcnta (--) en l . El operando debe ser un valor-I; vase
las discusin de operadores aditivos (A.7.7) y de asignacin (A.7.17) para posteriores
restricciones en el operando y detalles de la operacin. El resultado no es un valor-l,

A7.4.5 Operador menes unario


El operador del - umrio debe tener tipo aritmtico y el resultado es el negativo de su
opera ndo. Un operandoenlero sufre promocin entera. El negativo de una ca ntidad
sin signo se calcula restaldo el valor promovida del mayor valor del tipo promovido y
agregndole uno; pero el ero negativo es cero. El tipo del resultado es el tipo del operando
promovido.

A 7.4 Operadores unarios


A7.4.6 Operador compemento a uno
Las expresiones con operadores unarias se agrupan de derecha a izquierda.
El operando del operalor lInario - debe tener tipo entero y el resultado e\ el complemento a uno de su opermdo. Se realiza promocin entera. Si el operando c\ .,in .. igno,
el res ultado se calcula reian do el valor del mayor valor del lipo promovido. Si cl operando es con signo, el reslltado se calcula co nvirtiendo el operando promovido al tipo .. in
~igno correspondiente, ap;cando - y regresa ndo al tipo con signo. El tipo del resultado e<:.
el tipo del operando pronovido.

expresin-un aria:
expresin-posfija
+ + expresin-unaria
-- expresin-unaria
operador-unario expresin-casf
sizeof expresin-unaria
sizeof (nombre-de(jpo)
operador-unario: uno de
&
+

A7.4.7 Operador de nlgacln lgica


El operando del operacor !debe tener lipa aritmctico o ser un apuntador, y el re.,ultado
es 1 si.el valor de su opennd o es compara igual a O , Y O en caso contrario. El tipo del
resu ltado es int.

A7.4.1 Operadores prefijos de Incremento


Una expresin unaria precedida por un operador + + o -- es una expresin unaria.
El operando se incrementa t + +) o decrementa (--) en l. El valor de la expresin es el
valor despus del incremento (decremento). El operando debe ser un valor-I; vase la dis
cusin de operadores aditivos (A 7.7) Yde asignacin (A 7.17) para posteriores restricciOnes en el operando y detalles de la operacin. El resultado no es un valor-l.

A7.4.8 Operador sizeol


El operador sizeof prcduc e el nmero de bytes requeridos para almacenar un objeto
del tipo de su operando. H operando es una expresin, que no es evaluada, o un nombre

.......

226

MANUAL DE REFERENCIA

APE N DICE A

de tipo entre parntesis. Cuando sizeof se aplica a char, el resultado es); cuando se aplica
a un arreglo, el resultado es el nmero total de bytes en el arreglo. Cuando se aplica a una
estructura o unin, el resultado es el nmero de bytes en el objeto, incluyendo cualquier
rell eno requ erido para completar a un arreg lo : el tarnai'l o de un arreglo de 1/ elemento s es 1/
\ e;c!:. el l ummi o de un c1CIllCIlIO. El operado r no ~c p uede aplicar a un ope rand o de tipo
f undn o de tipo i nr.:ompl elo. o a un campo de bits. E l resultado es un enl ero <.:o nstante sin
<igno; e l lipo pan i<.: ul ar se d e fin e por la implalllaci ll. El lIeuder es tndar <stddeLh:>
(\case el apend iL"e B) defin e este ti po comosize_ t.

A7.5 Cast
Una expresin unaria precedida por el nombre entre parntesis de un tipo provoca la
conversin del valor de la expresin al tipo nombrado.

expresin-cast:
expresin-un aria
( nombre-de-tipo ) expresin-casI
Es ta l.:onslrUl.:cin se llama casI (conversin for zada). Los nombres de tipo se describen en
A8.8. Lo s efectos de conversin son descritos en A6. Una cxpresin con un caSI no es un

\al or- 1.

A7.6 Operadores multiplicativos


Los operadores multiplicativos, 1, Y % se agrupan de izquierda a derecha.

expresin-multiplicativa:
expresin-cast
expresin-multiplicativa '* expresin-cast
expresin-multiplicativa I expresin-cast
expresin-multiplicativa % expresin-casI
Los operandos de y 1 deben tener tipo aritmtico; los operandos de % deben tener
tipo entero. Las conversiones aritmticas us uales se realizan sobre los operandos, y predicen el tipo del resultado.
El operador binario denota multiplicacin.
El operador binario 1 produce el cociente y el operador % el residuo de la divisin del
primer operando entre el segundo; si el segundo operando es O, el resultado est indefinido. De otra manera, siempre es cierto que (a/b) b + a%b es igual que a. Si ninguno de
los operandos es negati vo, entonces el residu o es no negativo y menor que el divi sor; si no
lo son, se garantiza slo que el valor absoluto del residuo es menor que el valor absoluto
del divisor.

SECC ION A7

EXPRE SIONES

227

expresin-aditiva:
expresin-multiplicativa
expresin-aditiva + expresin-multiplicativa
expresin-aditiva - expresin-multiplicativa
El res ult ad o del o perad o r + es la sum a de los o pera nd os. Un apun tador a un objclo
que c~t en un arreglo y un val o r de cualqu ier tipo enl ero se pu eden <,tunar. Lo lt imo
se conviene a una direccin de desplazamiento, multiplicndolo por el tamano del objeto
al que el apuntador apunta. La suma es un apuntador del mismo tipo que el apuntador
original y apunta a otro objeto dentro del mismo arreglo, desplazado apropiadamente del
objeto original. As, si P es un apuntador a un objeto en un arreglo, la expresin P + 1
en un apuntador al siguiente o bjeto en el arreglo. Si el apulltado r de la \ lllna apun ta fu era
de los lmites del arreglo, excepto a la primera localidad ms all del final, el resultado
es indefinido.
La po~i bil i d;t d dc apu llIadorc\ Illa.. alla del fi nal del arrcglo c~ nu cva. b l0 k gil illliza una expresin idiomtica comn para iterar sobre los elementos de un arreglo.

El resultado del operador - es la diferencia de los operandos. Un valor de cualquier


tipo ellt cro se puede restar de un apuntador , y se aplican las mi sma') l.:oIlVer.r.,ioncs y condiLio nes que para la adicin.
Si se restan dos apuntadores a objetos del mi smo tipo , el resultado es un valor ent ero
con signo que representa el desplazamiento entre los objetos apuntados; lo." apullIadore<., a
objetos sucesivos difieren en l. El tipo del resultad o depend ~ de la implantacin , pero e<., t
dcfi ni do co mo ptrdiff_t en el header es tndar < stddef.h>. El valor e.s t ind efinido a meno" de qu e los apuntadores apunten a o bjetos dentro del mi smo arregl o; "in embargo , si P
apunta al lt imo miembro de un arreglo, elll o nces (P+ l)-P tiene val or l.
A7.8 Operadores de corrimiento
Los operadores de corrimiento y se agrupan de izquierda a derecha. Para
ambos operadores, cada operand o debe se r en tc ro y c\ t "ujcto a la." promocion e .. enteras. El lipo del resultado es el del o perand o promov ido de la izquierda. El rc"tJltado
est indefinido si el operando de la derecha es negativo, mayor o igual al nmero de bits
del tipo de la expresin de la izquierda .

expresin-de-corrimiento:
expresin-aditiva
expresin-de-corrimiento < < expresin-aditiva
expresin -de-corrimiento > > expresin-aditiva
El valor de El < < E2 es El (interpretado como un patrn de bits) recorrido a la izquierda
E2 bits; en ausencia de desbordamiento, esto es equivalente a la multiplicacin por 2EI
El valor de ElE2 es El recorrido a la derecha E2 posiciones de bits. El corrimiento
a la derecha es equivalente a la divisin entre 2 si El es no tiene \ig no o "i tie ne un \a lor no negativo; de otra forma el resultado est definido por la implantacin .

A7.7 Operadores aditivos


Los operadores aditivos + y - se agrupan de izquierda a derecha. Si los operandos
tienen tipo aritmtico, se realizan las conversiones aritmticas usuales. Existen algunas posi bilidades ad ic ional es de tipos para cada ope rad or.

A7.9 Operadores de relacin


Los o peradore ~ de relaci n ~e ag rupan de izq uierd a a derecha, pero C\ to no C\ de ut ilidad, a<b<c se analiza como (a<b) < c, y a < b se evala como O o como 1.

228

MANUAL DE REFERENCIA

APENDICE A

expresin-relacional:
expresin-de-corrimiento
expresin-relacional < expresin-de-corrimiento
expresin-relacional > expresin-de-corrimiento
expresin-relacional
expresin-relacional

< = expresin-de-corrimiento
> = expresin-de-corrimienlO

Los operadores < (menor que), > (mayor que), < = (menor o igual a) y > = (mayor o
igual a) dan todos O si la relacin especificada es fa lsa, y 1 si es verdadera. Elipo del resul-

SEccrON A7

EXPRESIONES

229

A7.12 Operador OR exclusivo para bits

expresin-OR-exclusivo:
expresin-AND
expresin-OR-exclusivo

expresin-AND

Se realizan las conversiones aritmticas usuales; el resultado es la funcin OR exclusivo


de los operandos. El operador se aplica slo a operandos enteros.

tado es int. Las conversiones aritmticas usuales se realizan sobre los operandos
aritmticos. Pueden compararse los apuntadores a objetos del mismo tipo; el resultado depende de las localidades relativas en el espacio de direccionamiento de los objetos apuntados. La comparacin de apuntadores est definida slo para partes del mismo objeto: si
dos apuntadores apuntan al mismo objeto simple, se comparan como iguales; si los apunladores lo hacen a miembros de la misma estructura, los apuntadores a objetos declarados ms adelante en la estructura se comparan como mayores; si los apuntadores son a
miembros de la mi sma unin, se comparan como iguales; si los apullladores hacen referencia a miembros de un arreglo, la comparacin es equivalente a la comparacin de los correspondientes subndices. Si P apunta al ltimo miembro de un arreglo, entonces P + 1
se compara como mayor que P, incluso aunque P + 1 apunte fuera del arreglo. De otra
manera, la comparacin de apuntadores est indefinida.

A7.13 Operador OR inclusivo para bits

expresin-OR-inclusivo:
expresin-OR-exclusivo
expresin-OR-inclusivo : expresin-OR-exclusivo
Se realizan las conversiones aritmticas usuales; el resultado es la funcin OR inclusivo de
sus operandos. El operador se aplica s lo a operandos enteros.

A7.14 Operador lgico ANO

expresin-Igica-AND:
expresin-OR-inclusivo
expresin-Igica-AND && expresin-OR-inclusivo

Estas reglas liberan algo las restricciones establecidas en la primera edicin,


permitiendo la comparacin de apuntadores a diferentes miembros de una estructura o unin. Tambin legalizan la comparacin con un apuntador justo ms
alla del final de un arreglo.

A7.10 Operadores de igualdad

expresin-de-igualdad:
expresin-relacional
expresin~de~igualdad =

= expresin-relacional
expresin-de-igualdad ! = expresin-relacional

Los operadores = = (igual a) y ! = (no igual a) son anlogos a los operadores de relacin
excepo por su menor precede ncia. (As a< b == c< d es 1 cuando a < b y c<d tengan los
mismos valores de verdad).
Los operadores de igualdad siguen las mismas reglas que los operadores de relacin,
pero permiten posibilidades adicionales: un apuntador puede ser comparado con una expresin constante entera con valor O, o con un apuntador a void. Vase A.6.6.

A7.11 Operador ANO para bits

expresin-AND:
expresin-de-igualdad
expresi6nAND & expresi6nde-igualdad
Las conversiones aritmticas usuales se realizan; el resultado es la funcin AND de bits
de los operandos. El operador se aplica slo a operandos enteros.

El operador && se agrupa de izquierda a derecha. Regresa 1 si ambos operandos se comparan C0l110 diferentes de cero; de otra manera, regresa O. A diferencia ve &, && garantiza la evaluacin de izquierda a derecha: el primer operando es evaluado, incluyendo todos los efectos colaterales; si es igual a 0, el valor de la expresin es O. De otra manera,
el operando derecho es evaluado, y si es igual a 0 , el valor de la expresin es O; de aIra

manera es 1.
Los operandos no requieren de tener el mismo tipo, pero cada uno debe tener tipo
aritmtico o ser un apuntador. El resultado es int.

A7.15 Operador lgico OR

expresi6n-lgica-OR:
expresin-Igica-AND
expresin-fgica-OR : : expresin-Igica-AND
El operador 1 : se agrupa de izquiera a derecha. Regresa 1 si alguno de sus operandos
no se compara como cero, y O en caso contrario. A diferencia de :, I I garantiza la
c\aluacin de izquierda a derecha: el primer operando es eva.luado, incluyendo lo s efectos colaterales; si es diferente de 0, el valor de la expresin es 1. De otra manera, es evaluado el operando derecho y, s es diferente de 0, el valor de la expresin es 1; de otra manera
es cero.
Los operandos no requieren tener el mismo tipo, pero cada tino de ellos debe tener
numrico o ser apuntador. El resultado es int.

230

SECCION A7

APENDlCE A

MANUAL Dl:. REFERENCIA

EXPRESIONES

231

Dc a;uerdo o.;on las rCSlri;.:iotlcs ~lJHcriorcs, c;, ilegal a:>ignar apuntadore, L'uando
diado dereL'ho apunta a Ull objero de algll tipo y diado iLquierdo apunta a un
ubjcto (011 una \c-'lion laJifkada (on const de ese lipo. Una lectura cSlrit'ta de
(,~Ia regla ~ su anloga para casI L'<lu,a difiL'u)ades al irnplalll ar d('rla~ runL'ionL'~
de hibJiuleca: ,eria bueno que fucra rncllo') rc'.trL'ra.

A7.16 Operador condicional

expresin condicional:
expresin-Igica-OR

expresin-Igica-OR ? expresin

expresin-condicional
A7.18 Operador coma

La primera expn:::.ilI se ('vala, induyendo lodos 10.<, efectos colaterales; si se compara


como dil'crenle de O, (.,[ resultado es el \aJor de la segunda expresin; de olra manera

expresin:
expresin-de-asignacin
expresin expresin-de-asignaci6n

si

es el de la te rcera expresin. Slo se evala uno de los operadores segundo o tercero.


el segundo y el tercer operandos son aritmticos, se realizan las conversiones aritmticas
usuales para hacerlos de algn tipo comn y ese es el tipo del resultado. Si ambos son vOid,
estructuras o uniones del mismo tipo, o apuntadores a objetos del mismo tipo, el resultado
tiene el tipo comn. Si uno es un apunrador y el otro la 'constante 0, el es convertido
a tipo apumador y el resultado tiene ese tipo, Si lino es un apumador a void y el otro es

Un par de expresiones separadas por una com a se evala de izquierda a derecha y el va lor
de la expresin izquierda se descarta. El tipo y va lor del resultado son el tipo y valor del
operando de la derecha. Todos los efectos co laterales de la evaluacin del operando izquierdo se completan antes d e principiar la evalu acin del operando derecho. En contextos
donde a la coma se le da un significado especial, por ejemplo en listas de argumentos para
funcio nes (A 7 .3.2) Y listas de inicializadores (A8.7), la unidad sintctica requerida es
una expresin de asignacin, de modo que el operador coma slo aparece en una
agrupaci n limitada por parntesis; por ejemplo,

otro apulltador, el otro ap u ntador es convenido a apuntador a void y se es el tipo del


t'l'~ultado,

En la ,:ol11para.:in de tipos para apuntadores, los ca li ficadores ele tipo (A8,2) en el tipo al que punta el apulltador 110 imponan. pero el resultado hereda los calificadores de
a1llba~ ral11a~ de la condicional.

f(a, (t=3, t+2), e)


tiene tres argumentos, el segundo de los cuales tiene el valor 5.

A 7.17 Expresiones de asignacin


Existen varios operadores de asignacin; todos se agrupan de derecha a izquierda.

A7.19 Expresiones constantes

expresin-de-asignacin:
expresin-condicional
expresin-unaria operador-de-asignacin expresin-de-asignacin

Sin tcticamente, una expresin constan te es un a expresin restringida a un subconjunto de operadores:

expresin-constante:
expresin-condicional

operador-de-asignacin: uno d e
*=

/=

%=

+=

&=

:=

I(\~ exp resiones que se ~valan a una constan le se requieren en varios contextos: desru~ de
case, como lmites de un arreglo y longitudes de campos de bits, como valor de una constante de enumeracin, en inicia lizadores y dentro de ciertas expresiones del preprocesador.
Las expresiones constantes no pueden co ntener asignaciones, operadores de incremento o decremento, llamadas a funciones ni operadores coma, excepiO en un operando de
sizeof. Si se requiere que la expresin con~tante sea entera, ~lIS operandos deben comislir
en conStantes enteras, de enumeracin, de caracteres y notan[c~; lo~c ast deben estipular un
1ipo entero y cualquier constantc flotante debe ser CO]1\en ida a entero. Esto nece\ariarnctlle excluye operaciones sobre arreglos, indireccin, dircL'(,:in-de y miembros de eSlruClUra.
(Sin embargo, se permite cualquier operando para sizeoL)
Hay mas libertad para las expresiones CO Il <) lallle'i de inicializadores; los operandm
puede n ser de cualquier tipo de constante)' el operando unario & puede c;er aplicado a
objetos externos o estticos y a arreglos externoo; o e'ilticos, indexadO.':> con una e\prec;in
conc;la llle. El operador unaria & ambien se puede apli\:ar implcitamente por la aparicin
de arreglos no indexados y funciones. Los iniciali/adores deben e\'aluarse a una comtal11e
o a la direccin de un objelo externo o estatico previamenle declarado mas o mcno~ una
l'onSlante .

lodm n:quicrcll de un valor-I como operando i/quierdo y este ele be ser modificable: no debe ser un arreglo y no debe tener un tipo incompleto ni ser una funcin. Tampo':0 debe ,>er ca lifil..'ado I..'on const; \i e ... una cstrul'l ura o uni6n, no debe tener ningn miembro o, rel'ur"i\'allll' ntc ningn submiembro Gllifkaclo con consto El tipo de una expresin
dc <1'iignadn e,> ('1 de ~u operando iquierdo, y el valor es el almacenado en el operando
izquierdo despus de que ha tenido lugar la asignacin.
En la asignacin simple con =, el valor de la expresin reemplaza al del objeto al que
'l' hal,.'c rl'fcrelll..'ia .:un el \ alor- 1. Uno de los siguiell1e~ debe ser verdadero: ambos operandos tienen tipo aritmtico, en tal caso, el operando de la derecha es convertido al tipo
del operando izquierdo por la asignacin; o ambos operandos son estructuras o uniones del
mismo tipo; o un ope rando es un apuntador y el otro es un apuntador a void; o el operando izquierdo es un apuntador y el operando derecho es una expresin constante con valor
O; o ambos opera ndos son apuntadores a funciones u objetos cuyos tipos son los mismoS
excepto por la posible ausencia de const o volatile en el operando derecho.
Una expresin de la forma El op = E2 es equiva lente a El = El op (E2) excepto que
El es eva luado slo una vez.

......

232

MANUAL DE REFERENCI A

APENDCE A.

Scpcrrnitc menos libertad para [as expresiones enteras constantes despus de #if; n
se perniten expresiones sizeof, constantes de enumeracin ni cast. Vase AI2.5 .
o

AS. Declaraciones
L$ dec laraciones especifican la interpretacin dada a cada identificador; no necesaria~
mentereservan espacio de almacenamiento asociado con el identificador. Las declaraciones
que r6ervan almacenamiento se llaman definiciones. Las declaraciones tienen la forma

declaracin:
especificadores-de-declaracin lista-de-declaradores-initoPI ;
Los drdaradores que estn en la lista-de-dedaradores-init contie nen la lista de identifica_
dorcsquc cSln sie ndo declarados; los especificadores-de-declaracin co nsiste n en una se.
cuenca de especificadores de tipo y categora de almacenamiento.

especificadores-declaracin:
especificador- categon-almacenamienlo especificadores-de-declaracinoPI
especificador-de-tipo especificadores-de-declaracinOpl
cualificador-de-tipo especificadores-de-declpracinoPI

SECCION AS

DECLARAC IONES

233

register es equivalenlc a una declaracin aUIO, pero sugiere que se har aCl'e~o frecuente
a los objetos declarados. Pocos objeLos son realmente 10l'aliados en registros y slo l:icrtoS tipos son elegibles; las restrit'cio ncs so n dependientes de la implantacin. Sin embargo,
si un objeto es declarado regisler, el operador unario & no se le puede aplil'ar, explkita o
irnplicitarnente.
La regla de Que es ilegal calcular la direccin de un objeto declarado register,
pero que realmente se tomar como auto, es nueva.

El especificador sta tic da a los objetos declarados categora de almacenamiento esttica, y se puede emplear dentro o fuera de las funciones. Dentro de una funcin, este especificador provoca que se as igne almacenamiento y sirve corno definicin; para sus efectos
fuera de una funcin, vase A 11.2.
Una declaracin con extern, utilizada dentro de una funcin, especifica que el almacenamiento para los objetos declarados est definido en algn otro lugar; para sus efectos
fuera de una funcin, vase Al 1.2.
El especificador typedef no reserva almacenamiento y se llama especificador de categora de almacenamiento slo por conveniencia sintctica; se discute en A8.9.
Cuando ms se puede dar un especificado r de categora de almacenamiento dentro
de una declaracin. Si no se da ninguno, se utilizan estas reglas: los objetos declarados
dentro de una funcin se toman como auto; las funciones declaradas dentro de una funcin se toman como extern; los objetos y funciones declarado s fuera de una funcin se
toman co mo estaticos, co n liga externa. Vase A IO-AII.

lista-declaradores-in;t:
declarador-init
lista-declaradores-init , declarador-init
declarador-init:
declarador
declarador = inicializador
Los dtd aradores se disl'uti ran posteriormente (A8.5), y cOiHienell los nombres que estan

siend, declarados. Una declaracin debe tener al menos un declarador o su especificador


de I ipl debe dcdarar el rtu lo de una estructura, un rtulo de unin o los miembros de una
Cllltlllll"81..'in; no se pcrmilcn dedaradones vacas.

A.8.2 Especificadores de tipo


Los especificadores de tipo son

especificador-de-tipo:
void

char
short
int
long

float
double

A8.1 Especificadores de categorla de almacenamiento


Lcs especificadores de categora de almacenamiento son:
espe('I}tcador-cafegoro-aIIIUlcelloll1Ienlo:

auto
register
static

extern
typedef
Los sgnificados de las categoras de almacenamiento se discutieron en A4.
Les especificadores auto y reqisler dan a los objetos declarados categora de almacenamienv automtico y slo se pueden usar dentro de funciones. Tales declaraciones tambin
sirven como definiciones y provocan que se reserve almacenamiento. Una declaracin

signed
unsigned
especificador-es f ruc tu ra-o- u n in

especificador-enum
nombre-Iypedef
Cuando ms se puede especificar una de las palabras long O shorl junto con int; el sig nificado es el mismo si no se menciona int. La palabra long se puede especificar junto con
double. Cuando ms se puede espec ificar una de las palabras signed o unsigned junto
COn int o cualquiera de sus variantes. short, long o con charo Cualquiera de las dos puede
aparecer sola ; en tal caso se entiende COrnO int. El especificador signed es (lIil para forzar a
los objelOs char a lener signo; es permisible pero redundante dentro de Olros lipos enteros.
De otra manera, cuando mas se puede dar un especificador de tipo en un dedaracin. Si el especificador de tipo se omite de una declaracin, se toma corno ini.

234

MANUAL DE REFERENCIA

APENO ICE A

Los ipos tambien se pueden ralificar. para indicar propiedades cspedales de los objc~

tos que estn siendo declarados.


coIUinlr!or-de- {po

const
volatile
Lm \""alifkadorcs d e lipo p uedcll aparecer con cualq uier espct:ifil:ador d e tipo. Un objeto
const ~e puede init.:iali/ar, pero despus no se le rued e asignar nada. No hay semnti ca
i ndependi ente de la il11 plamacion para objetos volatile.
Las propiedades canst y volatile son nuevas dentro de! estndar ANSI. El prop_
sito de canst es anunciar objetos que pueden ser localizados en memoria de slo
lectura y tal vez incrementar las oportunidades para optimizacin. El propsito
de volatile es rou:ar a la implalH 3l:in a suprimir la oplimil.adn que. de Ofra
manera, podra ocurrir. Por ejelllplo. para una maquina con elurada/ sa lida
a<,i,gnado a memoria, el apuntador a un registro de dispo~j\vo se podra decla-

rar como un apumador a volatile para prevenir que el compilador remueva las
referencias aparentemente redundantes a travs del apuntador. Excepto que debe
diagnostil:ar explkitamentc los intensos de cambiar objetos const, el compilador
puede ignorar eSIOs l:al fl:ad ore~ .
Un Icrcer c<llifl:ador, noalias, permanece bajo wnsidcracin por el comit de

l'~l all darizal:in .

A8.3 Declaraciones de estructura y unin


Una cstrW':lura es UIl objt.,\o que ro nsta de ulla ser uen da de miembros nombrados de
\arim lipos. Una unin en un objeto que comiene, en momentos dislimos, cualquiera
dc algullo!'! miembro .. de var ios tipos. Lo~ esp' ifiladores de estnu.:tura y unin tienen la
l11i'irna forma.
esp('cU/ca(/o/"-esl ru C!lIra-()-1I1/ iI/:

estructura-o-unin idenrijicadorOjJ/ { Iista-declorociones-struct }


estructura-o-unin identificador
estructura-o-unin:
struct

union
Una lista-de-declaraciones-struct es una secuencia de declaraciones para los miembros de
la estructura o unin:
I is(a-de('ltlraeo'les~sl I"ll("!:

dedaracin-struct
lislll-declaraciolles-sl rilO declaracin- SI rIle!

declaracin-sr rucr:
lis t a- calificador-espec ficado r lis t a-de-declaradores-s t ru ct .
Iista-cualljicador-especificador:
especlficador-de-tipo lisra-calificador-especificadoroPI
calificador-de- tipo lis t a- calificador-especificadorOpl

SECCION A8

DECLARACIONES

235

lis t a-deda radores-srruc r:


dedarador-struct
lista-declaradores-struct , dec/arador-struct
por lo general, un declarador-struct es slo un declarador para un miembro de estructura
o unin: Un miembro de estructura tambin puede constar de un nmero especificado de
bits. Tal miembro tambin se llama campo de bits, o simplemente campo; un carcter dos
puntos marca el inicio de su longitud despus del nombre del campo.

dedo rador-struct:
declarador
declaradorQP1 : expresi6n-constante
Un especificador de tipo de la forma

estructura-o-unin identificador { lista-dedaraciones-struct }


declara que el identificador ser el rtulo de la estructura o unin especificado por la lista.
Una declaracin posterior en el mismo alcance o ms interno se puede referir al mismo
tipo utilizando el rtulo dentro de un especificador sin la lista:

estructura-o-unin identificador
Si aparece un especificador con un rtulo pero sin lista cuando el rtulo no est declarado,
se especifica un tipo incompleto. Los objetos con tipo incompleto de estructura o unin se
pueden mencionar en contextos donde no sea necesario su tamao, por ejemplo, en declaraciones (no definiciones), para estipular un apuntador o para crear un typedef , pero no
de a Ira manera. El tipo se co mpl eta al prescntarse un c~pecificador subsecuente con ese
rtulo qu e co nt enga una lista de declaraciones. In cl uso en especificadores co n una liSIa, el
tipo de la cs tru ct ura o unin que est sie ndo declarado es in complelO dentro de la lista. y Se
l"Olllp lcla s lo en el que termina el especificador.
Una estructura no puede contener un miembro de tipo incompleto. Por lo tanto, es imposible declarar una estructura o unin que contenga una instancia de ella misma. Sin embargo, adems de dar un nombre al tipo de estructura o unin, los rtulos permiten la definicin de estructuras autorreferenciadas; una estructura o unin puede contener un
apuntador a una instancia de ella misma, debido a que pueden ser declarados apuntadores
a ti pos incompletos.
Una regla muy especial se aplica a declaraciones de la forma

estructura-o-unin identificador;
que declara una estructura o unin, pero no tiene lista de declaraciones ni declaradores.
Aun si el identificador es un rtulo de estructura o unin ya declarado en un alcance ms
externo (All.l), esta declaracin hace al identificador el rtulo de una nueva estructura
o unin. de tipo incompleto, en el alcance actual.
Esta regla cs nueva bajo ''''SI. SU funcin es tralar con cstructuras InUIUamelHC
recursivas declaradas en un alcance ms interno, pero cuyos rtulos podran haber sido ya declarados en el alcance ms externo.
Un especificador de estruc!ura o unin con una liSIa ~in rlulo crea un tipo nico; se le
puede hacer referencia directamenle slo en la declaradn de la que es ["Janc.
Los nombres de miembros y rtulos no entran en confliclO entre ellos o con variables
ordinarias. Un nombre de miembro no puede aparecer dos veces en la misma estructu-

236

MANUAL DE REFERENCIA

APENDICE A

fa o unin, pero el mismo nombre de miembro se puede emplear en diferentes estructuras

o uniones.

DECLARACIONES

SECCION A8

237

sp->count
se refiere al campo count de la estructura a la que apunta sp;

En la primera edicin de este libro, Jos nombres de miembros de estructuras y


uniones no estaban asociados con su padre. Sin embargo, esta asociacin se hizo
comn en compiladores mucho antes del estndar ANSI.
Un miembro que no sea campo de una estructura o unin puede tener cualquier tipo de
objclO. Un miembro campo (que no requiere tCller un declarador y, por anta, puede no
tener nombre) tiene tipo int, unsiqned int, o signed int, y es interpretado como un objeto

de tipo cillero de la longitud en bits especificada; e l que un campo int se trate como COn
~igno depende de la implantacin. Los campos adyacelllcs que son miembros de estruclU.
ras se empaquetan en unidades de almacenamiento dependientes de la implantacin en una
direccion lambin dependiente, Cuando hay la posibilidad de que un campo que sig ue a
otro no entre en una unidad de almacenamiento parcialmente llena, se puede separar en
unidades, o la unidad Sto puede rellenar. Un campo sin nombre con amplitud O fuerza a este
rellenado, de modo que el siguiellle campo principiara en la orilla de la siguiente unidad de
asignal'in,
El eSlandar \ . . SI hace que los campos sean an ms dependientes de la implantacin que la primera edicin, Es H'l'oJllendable leer las reglas del lenguaje para almacenar campos de bits ..:omo "dcTn:ndientes de la implantacin" sin limita
ciones, Las eSlrucluras l'OI1 campos de bils se pueden emplear (omo una forma
t ramporTable de itUenlar reducir el alma..:enamieruo requerido para una eSl ruetura (l'on el casIO probable de in..:rcmcm3r el espacio de instrucciones y tiempo para
tener an'c!.o a los campo,), o ..:omo una forma no transportable de describir una
plantilla de almacenamiento conocida al nivel de bits, En el segundo caso, es necesario entender las reglas de la implarlladn local.
Los miembros de una estructura tienen direcciones ascendentes en el orden de sus de
daraciones, De una estrm:tura un miembro que no sa campo se alinea con un Iirn,he de
direccionamiento depcndiendo dc su tipo; por tamo, pu ede haber huecos sin nombre
delllro de una estructura, Si un apuntador a una estructura es convertido al tipo de un
apuntador a su primer miembro, el resullado se refiere al primer miembro,
Se puede pensar en una unin como una estructura donde todos sus miembros principian en el desplazamiento O y cuyo tamao es suficiente para contener a cualquiera de sus
miembros. Cuando ms, uno de los miembros puede ser almacenado denlro de una union a
la vez. Si un apuntador a uni on es conven ido al tipo de un apuntador a un miembro, el
resultado se refiere a ese miembro.
Un ejemplo simple de declaracin de estructura es

struct tnode {
char tword(20J
int count;
struct tnode *left
struct tnode *right
};
que contiene un arreglo de 20 caracteres, un entero y dos apuntadores a estructuras semejamcs. Una \CZ que se ha dado esta declaracin, la declaracin

struct tnode s, *sP


de\.:lara s como una estrUl.'lura de la \ ariedad dada y sp corno apuntador a una eSlructura de
es(' tipo, Con estas declaraciones, la expresin

s.left
se refiere al apuntador al subrbol izquierdo de la estructura s; y

s.right->tword{O)
se refiere al primer carcter del miembro tword del subrbol derecho de s.
En general, un miembro de una union no puede ser inspeccionado a menos de que e\ valor
de la union haya sido asignado ulilizando ese miembro, Sin embargo, ulla l:0llsidcraci6n
especial simplifica el uso de uniones: si una unin contiene varias estructuras que comparten una secuencia inicial comn, Y si la unin actualmente contiene una de esas estructuras,
se permite hacer referencia a la parte inicial eomn de cualesquiera de las estructuras con
tenidas. Por ejemplo, el siguiente fragmento es legtimo:

union
struct
int type
} n
struct {
int typej
int intnode;
ni;
struct {
int type;
float floatnode;
nf;

U;
u.nf.type = FLOAT;
u.nf.floatnode = 3.14

if (u.n.type
oO.

==

FLOAT)
sin(u.nf.floatnodel

A8.4 Enumeraciones
Las enumeraciones son tipos con valores que fluctan entre un conjunto de constantes
nombradas que se llaman enumeradores, La forma de un especificador de enumeracin
se toma de la de las estructuras y uniones.

especificadorenum:
enum identificadoropr { /istadeenumerador }
enum identificador
!istadeenumerador:
enumerador
/ista.de.enumerador enumerador
enumerador:
identificador
expresinconstan te
identificador

238

MANUAL DE REFERENCIA

APENDICE A

Los identificadores dentro de una lista de enumerador se declaran como constantes de lipo
int y pueden aparecer en cualquier lugar donde se requiera una constante. Si no aparecen
enumerado res con = I entonces los valores de las correspondientes constantes principian
en O y se inl:remCrU3n en I al leer la declaracin de izquierda a derecha. Un enumerador
con = da al identificador asociado el valor especificado; los identificadores subsecuentes
continan la progresin del valor asignado.
Los nombres de cn ulllcradores en el l1li~mo alcance deben ser dislintos clllre s y de los
nombres de variables ordinarias, pero no es necesario que los \alores sea n dislintos.
El papel del identificador en el especificador-enum es anlogo al del rtulo de las es~
IrUl'!UraS en un especifil.:ador-de-estrtlctura; nombra una enumeracin particular. Las
reglas para especificadores-enum con y sin rtulos y listas son las mismas que para especificadores de estructuras y uniones, excepto que los tipos de enumeracin incompleta no
existen; el rtulo de un especificador-enum sin una lista de enumeradores debe referirse
a un especificador en el alcance dentro de la lista.
Las enumeraciones son nuevas desde la primera edicin de este libro, pero han
sido parte del lenguaje por algunos aos.
AS.5 Declaradores
Los declaradores tienen la sintaxis :
declarador
apuntadorOpt declarador-directo
declarador-directo:
identificador

( declarador)
declarador-directo [ expresin-constanteoPI )
declarador-directo ( Iista-tipos-de-parmetro )
declarador-directo ( lista-de-identi/icadoresoP' )
apuntador:
* lisla-calijicadores-de-lipoO,)/
* Iisra-ca/iJicadores-de-lipo "1'1 apuntador
lis Ia-clIliji('(ldores-de-I ipo,'
calificador-de-, ipo
IisfQ-caIiJicadores-de-1 ipo calificador-de- (ipo

La estructura de los declaradores es semejante a la de las expresiones de indireccin, funI.:iones y arreglo s; el agrupamiento es el mismo.
A8.6 Significado de los declaradores
La lista de declaradores aparece despus de una sec uencia de especificadores de catCgora de tipo y almacenamiento. Cada declarador declara un ident ifi cador principal nico,
que aparece como la primera ahernativa de la produccin para declarador-direclo. Los
especi ficadores de categora de almacenamiento se aplican directamente a este identificador, pero su tipo depende de la forma de su declarador. Un declarador es ledo comO la
afirmacin de que cuando su identificador aparece en una expresin de la misma forma
que el declarador, produce un objelO del tipo especificado.

Considerando slo las partes de tipo de los especificadores de declaracin (A8.2) Y


Un declarador panicular, una declaracin tiene la forma "T D", donde T es un tipo y D
es un declarador. El tipo atribuido al identificador en las varias formas del declarador se
describe inductivamente empleando esta notacin.

DECLARACIONES

SECCION AS

239

En una declaracin T D donde D es un idcntifil.:ador !-'olo. el (ipo del idcntificador es T.


En una declaracin T D donde D tiene la forma

( Dl )
el tipo del identificador en D1 es el mir.,mo quc d de D. Lo<., parnter.,i~ no a[teran el tipo, pero pueden cambiar [a a~ociaL'ioll de declaradores cO[l1plejo~.

AS.6.l Declaradores de apuntadores


Es una declaracin T D en donde D tiene la forma
*

Iis({[-calificudoresde-lipo,,!JI D1

y cltipo del identificador que est en la declaracin T D1 es "l1Iodificudur-de"ipo T", el


lipa del id entificador de D CIj "modificador-de-lipo lista-calificadores-de-lipo apuntador a
T". Los calificadores que ~iglleJl al * se aplican al apuntador en .. . no a[ ohjelO al que
apunta.
Por ejemplo, considere la declaracin,

int *ap[]j
Aqu ap[ 1juega el papel de D1; una declaracin "int ap[ 1" (ms abajo) dar a ap el tipo
"arreglo de int", la lista calificador-de-tipo er.,t val.:a, yel modificadorde-tipo c\ "arreglo de". Por com iguientc la declaracin da a ap el tipo "arreglo de apurlladore" a
int" .
Como otros ejemplos, las declaraciones

int i, *pi, *eonst epi


const int ci = 3, *pci

&i

declaran un entero i y un apuntador a un entero pi. El valor del apuntador constante epi
no puede ser cambiado; siempre apunta a la misma localidad, aunque el valor al que se
refiere puede ser alterado. El entero ci es constante, y no se puede cambiar (aunque s
se puede inicializar, como aqu). El tipo de pei es "apuntador a const int", Y pci en si puede
se r modificada para apuntar a otro lugar. pero el valor al que apunta no se puede alterar
por asignacin a travs de pci.
AS.6.2 Declaradores de arreglos
En una declaracin T D donde D tiene la forma
DI [expresin-constanteoPfl
y el tipo del identificador en la declaracin T DI es "modificador-de-tipo T", el tipo del
identificador Des "modificador-de-tipo arreglo de T". Si la expresin-constante est pre"eme, debe ~er de tipo entero, y con valor mayor que O. Si 'le omite la cxpre"in COn'otanle
que especifica el lmite, el arreglo tiene tipo incompleto.
Un arreglo se puede construir a partir de un tipo aritmtico, de un apuntador, de una
estructura O unin o de otro arreglo (para generar un arreglo de varias dimensiones). Cualquier tipo del que se construya un arreglo debe ser completo; no debe ser un arreglo o estructura de tipo incompleto. Esto implica que para un arreglo multidimensional, slo se
puede omitir la primera dimensin. El tipo de un objeto de tipo arreglo incompleto se com-

240

MANUAL DE REFERENCIA

APENDICE A

plela ~on otra declaracin para el objclO (A10.2), completa o por su inicializacin
(AB.7). Por ejemplo,
float fa[17], *afp[17];

dedara un arreglo de nllmero floal y un arreglo de apuntadores a nmero float. Por otro lado,
static int x3d[3][S][7];

dedara un arreglo tridimensional esttico de enteros, con rango 3 x 5 X 7. Con todo


detalle, x 3d es un arreglo de Ires elementos; cada elemento es un arreglo de cinco arreglos;
cada uno de los ltimos arreglos es un arreglo de siete enteros . Cualquiera de las expresiones x 3d, x 3d(i], x 3d (i](i], x 3d(i](i](k] pueden aparecer razonablemente dentro de una
expresin. Los primeros tres tienen tipo "arreglo" yel ltimo tiene tipo int. Ms especficamente, x 3d(i](i] es un arreglo de 7 enteros, y x 3d(i] es un arreglo de 5 arreglos de 7 enteros.
La operacin de indexado de un arreglo est definida de modo que E1[E2) es idntica
a *(E1 + E2). Por lo tanto, fuera de su apariencia asimtrica, la indexacin es una operacin conmutativa. Debido a las reglas de conversin que se aplican a + ya lo s arreglos
(A6.6, A7.I, A 7.7), si El es un arreglo y E2 un entero, entonces El(E2] se refiere al E2simo miembro de El.
En el ejemplo, x 3d(i]li](k] es equivalente a .( x 3d (i](il + k). La primera subexpresin X 3dli) (j) se convierte por A7.1 al tipo "apuntador a arreglo de enteros"; por A7 .7,
la adicin involucra multiplicacian por el tamao de un entero. De las reglas se sig ue que
los arreglos se almacena n por renglones (el ltimo subnd ice vara ms rpido), y que el
primer sub ndice dentro de la declaracin ayuda a determinar la cant idad de almacenamielllo consumido por un arreglo, pero no tiene ms uti lidad en el clculo de subndices.

AS.6.3 Declaracin de funciones


En una declaracin del nuevo esti lo T D, donde D tiene la forma

Dl(/ista-tipos-de-parmetro)
y el tipo del identificador deI1lfO de la declaracin T Dl es "modificador-de-tipo T", el
lipa del identificador de D es " modificador-de-po funcin con argume nto lista-ripos-deparmerros que regresa T".
La sintaxis de los parmetros es

lisla-ripos-de-parmerro:
lista-de-parmetros
lisra-de-parmetros ,
lista-de-parmetros:
declaracin-parmetro
lista-de-parmetros , declaracin-parmetro
declaracin-parmetro:
especificadores-de-declaracin declarador
especificadores-de-declaracin declarador-abstracto opr

DECLARACIONES

SECCION AS

241

En la declaracin del nuevo es[ilo, la lista de parmetros estipula los tipos de los parmeIroS. Como un caso especial. el declarador para una funcin del nuevo estilo .,in parmetro.,
tiene una lista de tipos de parmetros consistente nicamente en la palabra reservada void.
Si la lista de tipos de parmetros finaliza con puntos suspensivos", ... ", entonces la funcin puede aceptar ms argumentos que el nmero de parmetros descritos explcilamente;
vcr A 7.3.2.
Los tipos de parmetros que son arreglos o funciones se alteran y quedan como apuntadores, de acuerdo co n las reglas para conversio nes de parmetros; ver A 10.1. El nko
especificador de categora de almacenamiento permitido dentro de un especificador de declaracin de parmetros es register, y este especificador es ignorado a menos que el declarador de funcin encabece una definicin de funcin. De modo semejante, si lo,> declaradores que estn en las declaraciones de parmelros contienen identificadores, y el declarador de funcin no encabeza un a definici n de func in, los idcntificadore., .,alen
inmediatamente del alcance. Los declaradores abst ractos, que no mencionan a los identificadores, se discuten en A8.8 .
En una declaracin de runcin del estilo anterior T D, donde D tiene la forma
DI (lis ta-de-iden tificadoresop,)
y el tipo del identificador dentro de la declaracin T Di es "modificador-de-tipo T", el
tipo del identificador de D es "modificador-de-tipo funcin de argumentos no especificados que regresa T" . Los parmetros (si estn presentes) tienen la forma

lista-de-identificadores:
identificador
lista-de-identificadores , identificador
En el declarador del estilo anterior, la lista de identiricadores debe estar au.,cnle a meno., de
que el declarador se utilice en el encabezador de una definicin de funcin (AJO.I). La
declaraci n no proporciona ninguna informacin acerca de los lipos de 10'-10 parmetro.,.
Por ejem plo, la declaracin

int f( )t *fpi(), (* pfi) ()

declara una funcin f que regresa un entero, una runcin fpi que rcgre.,a un apuntador a un
entero, y un apuntador pfi a una funcin que regresa un entero. En ninguna de stas.,e especifica la lista de parmetros; estn en estilo anterior.
En una declaracin del nuevo esti lo
int strcpy (char *dest t const char *source)t rand (void);

strcpyes una funcn que regresa un int, con dos argumentm, el primero e\ un apuntador
a carcter y el segundo un apuntador a caracteres constantes. Los nombres de los parmetros son realmente comentarios. La segunda funcin rand no emplea argumento\ y regre.,a
int.
Los declaradores de funciones con prototipos de parmetros son, con mucho,
el cambio ms importante introducido al lenguaje por el estndar ANSI. Qfre<:en
una ventaja sobre lo, declaradorc,> "del e,>tilo anterior" de la primera elh~ln.
proporcionando deteccion de errare., y ~o",,"er,on de argumento,> cnl re Ilamada\
a funcione~, pero a un co~to; de<,ordcn y cnfu,in durantc \u illlroduct:in, ~ la
necesidad de permitir amba<, forma'. Se requirieron alguna\ abcrraciOlle .. ,intt:tieas para eompalibilidad, como void, u\ado t:omo una marca explicita de la\
funcione., ,>in parmetro,> del nUC\Q c\tilo.

242

MANUAL DE REFERENCIA

APENDICE A

La nota:in d..: pUlltl1\ \lI~I)(, l l..,i\os .' ... para fUIH:ione'i (on nrncro "'ariahlc tI<: argulIlento ... 1:1lllbin ('~ Ilue\a y, junIO l'on I;J~ manm etl el /euder el;.
I andar < stdarg.h >, fonnaliLa un 1ll<':l.:ani~ll1o qUe l'\1 U\ () ofIcialmente prhib',
do pero c,traofi!.:ialrnt:llli..' pl'rJnlido en la primera cdk-ioll,
I
Esas nOlaciones fueron adaptadas del lenguaje + + .

A8.7 Inicializacin

Cuando se declara un objeto. su declarador-init puede especificar un valor inicial para


el identificador que ,"sl siendo declarado. El iniciatiLador e::. precedido por =, y es una
cxpre"ill o una lista de inicializadores anidados entre llaves. Una li&la puede terminar COn
coma, un buen dClallc para un formato daro .

inicializador:
expresin-asignacin
{ lista-de-inicializadores }
{ /ista-de-inicia/izadores ,}
lista-de-inicia/izadores:
inicializador
/isra-de-inicia/izadores , inicializador
Todas la.., <-""presiones del inkializador para un objeto o arreglo estatieo deben ser exprc.':Iiones l:onstantes lal l:omo se describe en A 7 .19. Las expresiones en el inicializador
para un objeto o arreglo auto O register debe; igualmente ser expresiones constantes si el
inicialiLador es una li SIa elll.:errada entre llaves. Sin embargo, si el inicialiLador para un objeto 3utomlil:o es una expresin simple, no requiere ser una expresin constanle. sino que
debe tener si mplernel11e el lipa apropiado para la asignal:in al objeto.
La primera edicin no aprobaba la inicializacin de estructuras automticas,
uniones o arreglos. El estndar ANSI lo permite. pero s610 por construcciones
~'oIlS!anles o mcnos. de qUe el iniciali7ador se pueda expresar con una cxpresin
,implc.
Un objeto estatico no inicialilado explcitamellte se inicializa I.:'omo si a l (o a sus
miembros) se le asigna la conStante o. El valor inicial de un objeto auloma[iL'O que no est
c\plit:i[alllen{c ini,:ialitado es indefinido.
El iniL'ialiLador para un cstru,:lura es ulla expresin del mismo [ipo o UBa liSia entre
lIa\es de inkialiLadore<;, para !lUS miembros en orden. Si hay rneno ~ inicialiLadores que
El inicializador para una estructura es o una expresin del mismo tipo, o una lista entre
llaves de inicializado res para sus miembros en orden. Si hay menos inicializadores que los
miembros de la estructura, los ltimos miembros son inicializados con O. No puede haber
ms inicializadores que miembros.
El inkial izado r para un arreglo es una lista de inicialiLadores elltre Ila\'es para sus
miembros. Si el arreglo tiene tamao desconocido, el nmero de inicializadores determina
el tamao del arreglo y su tipo se completa. Si el arreglo tiene tamao fijo, el nmero de
inidalizadores no puede exceder al nmero de miembros del arreglo; si hay menos, los ltimos miembro s so n inicializados co n O.
Como caso especial, un arreglo de caract~rcs se puede iniciali7ar con una cadena
li[eral; los ,:aractercs sucesivos de la cadena inidalilan miembros su,:esivos del arreglo.
De modo ~emeja nle , un earaclcr literal amplio (A2.6) puede inicializar un arreglo de tipo
wchar_t. Si el arreglo liene tamai'to desconocido, el nmero de caracteres en la cadena, in~

DECLARAC IONES

SECCION AR

243

c1uyendo el carcter nl110 de terminacin, determina su tamao; si su tamao es fijo, el


nmero de caracteres de la cadena, sin contar el carcter nulo de terminacin. no debe exceder al tamao del arreglo.
El inicial iLad or para una unin es una cxpre~in ..,implc del mi.'>mo [ipo o un ini,:ializador entre llaves para el primer miembro de la unin.
La primera edicin no permitia la inicializacin para uniones. La regla del "primer miembro" es confusa, pero es difcil generalizar sin la nueva sintaxis. Adems de permitir que las uniones sean inicializadas explcitamente. al menos en
una forma primili\a, eSla regla ''' . . 1 \uehe definiti\a la 'l:nlinuic.I dt' llllionc\
cstalkas no inidali7ada,> cxplkilamclllc.
Un agregado es una estructura o un arreglo. Si un agregado contiene miembros de tipo
a!!regado, la s reglas de inicializacin se aplican en forma recur siva. La \ Ilavc\ pueden dc!\ap;recer de la inicializacin como sig ue: si el inicializador para un mi e mbro de agregado
que en ~i es un agregatlo principia con una lIa\e izquierda, elllonces la Ii \ta de inicial indore", \eparados por coma que ~igue inicialia los miembro), del subagregado; es errneo
que haya ms inicializadores que miembros. Sin embargo, ),i el init:ialiador para un 'iubagregado no principia con una llave izquierda, enlonces slo se toman de la lista los elementoS suficientes para los miembros del subagregado; cualesquiera miembros restantes se dejan para inicializar el sigu iente miembro del agregado del cual el sl1bagregado C'i panco
Por ejemplo,

int xl]

={"

3, 5 };

declara e inicializa x como un arreglo de una dimensin con tres miembros, puesto que
no se ha especificado tamao y existen tres inicializadores.

float y[4][31 = {
{ 1 , 3, 5 },
{2,4,6},
{3,S,7},

1;
e) una inicialiacion complelamcllle entre llave,>: 1,3, Y 5 inidaliLan al prilller rcngln d<.:
arreglo y[ O1, e, decir y[ 01 [O], y[ O1 [1], Y y[ 0][ 21. EI1 igual forma, la, ,iguicI1Lc,
do,> lineas inicialian y[ 1] Y y[2]. El inidaliLador termina ante,> y, pUl' lo lantO, lo~ elemelllOS de y[ 3] son inicializado,> con O. Pre,>arncllle el mi~rno efecto ..,e puede obtener
con

float y[4][31 =
1, 3, S, 2, 4, 6, 3,
1;
El inicializador para y principia con una
se utilizan tres elementos de la lista. De
la lista se toman sucesivamente para y(

5, 7
llave izquierda, pero el de y [O) no. por lo que
la misma forma los siguientes tres elementos de
1] y despus y[2). Tambin,

float y[41[31 = {

{ 1 l, { 2 l, { 3 l, { 4 )

1;
inicializa la primera columna de y (considerado como un arreglo bidimensional) y deja al
resto en O.
Finalmente.

char msg[)

" Error de sintaxis en lnea %s\.n" i

PROPOSICIONES

244

MANUAL DE REFERENCIA

APENDICE A

muestra un arreglo de caracteres cuyos miembros son inicializados con una cadena; su la
mafio incluye el carcter nulo de terminacin.

A8.8 Nombres de tipos


Dentro de muchos contextos (para especificar las conversiones de tipo explcitamente
con un caSI, para declarar ipos de parmetros en declaradores de funcion, y como un
argumento de sizeof) es necesario proporcionar el nombre de un tipo de dato. Esto se logra
utilizando un nombre de tipo, que sintcticamente es una declaracin para un objeto de
ese lipo. omitiendo el nombre del objeto.

nombre-de-tipo:
I isra-ml ificadar-especificador declarador-absr me( o Opl

declarador-abstracto:
apuntador
apuntadorQpl declarador-abstracto-directo
declarador-abstracto-directo:
( declarador-abstracto )
declarador-abstracto-directo oP1 [ expresin-constanteoP1 ]
declarador-abstraclo-directo oP1 (Iista-tipos-de-parmetro op ,)
Es posible identificar unvocamcllIe el lugar dentro del declarador abstracto en donde
podra aparecer el identificador si la co nstruccin fuera un declarador en una declaracin.
El tipo nombrado es entonces cl mismo que el tipo del identificador hipottico. Por
ejemplo,
int
int *
int 0[3J
int (o) [ J
int *()
int (o [ J ) (void)
nombra respectivamente los tipos "entero", "apuntador a entero", "arreglo de 3 apuntadores a enteros", "apuntador a un arreglo de un nmero no especificado de enteros",
"funcin de parmetros no especificados que regresa un apuntador a entero" y "arreglo,
de tamai'lo no especificado, de apuntadores a funciones sin parmetros que regresa cada
una un entero".

Por ejemplo, despus de


typedef long Blockno, *Blockptr;
typedef struct { double r, theta; } Complex;
las construcciones
Blockno b;
extern Blockptr bp;
Complex z, *ZPi

son declaraciones legtimas. El tipo de b es long, el de bp es "apuntador a long", y el


de z es la estructura especificada; zp es un apuntador a tal estructura.
typedef no introduce nuevos tipos, slo formas sinnimas para tipos que se podran
mencionar en otra forma. En el ejemplo, b tiene el mismo tipo que cualquier otro objeto
long.
Los nombres typedef se pueden redeclarar dentro de un alcance ms interno, pero se
debe dar un conjunto no vaco de especificadores de tipo. Por ejemplo,
extern Blockno;

no redeclara a Blockno, pero


extern int Blockno;

si 10 hace.
A8.10 Equivalencia de tipo
Dos listas de especificadores de tipo son equivalentes si contienen el mismo conjunto
de especificadores de tipo, lOmando en cuenta que algunos especificadores pueden implicar
ot ros (por ejemplo, long so lo implica long int). Estructuras, uniones, y enumeraciones con
r6tulos diferellles son distintas, y una uni6n, estructura, o enumeracin sin rtulo estipula
un tipo nico.

Dos tipos son el mismo si sus declaradores abstractos (A8.8l, despus de expandir

cualesquiera tipos typedef y de eliminar cualesquiera identificadores de parmetros de


funcin, son las mismas o equivalentes listas de especificadores de tipo. Los tamai'l.os de
los arreglos y los parmetros de las funciones son significativos.

A9.
A8.9 Typedef
Las declaraciones cuyo especificador de categora de almacenamiento es typedef no declaran objetos; en lugar de ello definen identificadores que nombran tipos. Esos identificadores se llaman nombres typedef.

nombre-typedef"
identificador
Una declaracin typedef atribuye un tipo a cada nombre entre sus declaradores en la forma usual (ver 8.6). Luego de eso, cada nombre typedef es sintcticamente equivalente
a una palabra reservada para especificador de tipo para el tipo asociado.

245

SECC ION A9

Proposiciones

Excepto en donde as se describe, las proposiciones se ejecutan en secuencia. Las propo~


siciones se ejecutan por sus efectos y no tienen valores. Entran en varios grupos.

proposici6n:
proposici6n-etiquetada
proposicin-expresi6n
proposicin-compuesta
proposicin-de-selecci6n
proposicj6n-de-iteracjn
proposici6n-de-salto

246

MANUAL DE REfER ENCIA

APENDICE A.

A9.1 Proposiciones etiquetadas


Las proposiciones pueden tener etiquetas co mo prefijos.

proposicin-etiquetada:
identificador: proposicin
case expresin-constante: proposicin
default : proposicin
Una etiqueta consistente en un identificador declara al identificador. El nico uso de una
etiqueta identificadora es como destino de un gato. El alcance del identificador est dentro
de la funcin actual. Debido a que las etiquetas tienen su propio espacio de nombre, no
interfieren con otros identificadores y no se pueden redeclarar. Vase All.l.
Las etiquetas de case y default se aplican con la proposicin switch (A9.4). La expresin l.:onSlante del case debe tener tipo entero.
Las etiquetas por s misma s no altcran el flujo de control.

A9.2 Proposicin de expresin

La mayora de las proposic iones son proposicicJnes de expresin, que tienen la forma
proposicin-expresin:
expresin oP' ;
La mayora de las proposiciones expresiones son asignaciones o llamadas a funcin. Todos
los efectos I.:olaterales de la expresin son completados antes de ejecutar la siguiente proposicin. Si la expresin se omite, la construccin se llama proposicibn nula; a menudo se
cmplea para proporcionar un cuerpo vaco a una proposicin de iteracin O para situar una
eliqueta.

A9.3 Proposicin compuesta


Para que se puedan utilizar varias proposiciones donde se espera una, se proporciona
la proposicin compuesta (tambin llamada "bloque"). El cuerpo de una definicin de
funcin
es una proposicin compuesta.
,

proposicin-compuesta:
{ Iista-dec/aracin Op lista-de-proposicionesop }
lista-de-dec/araciones:
declaracin
lista-de-dec/araciones declaracin
lista-de-proposiciones:
proposicin
lisla-de-proposiciones proposicin
Si un identificador dentro de la lista-de-declaraciones estuvo en un alcance exterior al bloque. la del'laradn ms externa se sus pende dentro del bloque (vase A 11.1), despus
de lo ( ual I:o ntinua. Un identificador se puede declarar slo una vez dentro del mismo bloque. ESla!> reglas se apli(an a idcnlifkadorcs dentro del mi smo espacio de nombre (A 11);
los idemifkadores dcmro de espacios de nombre diferentes son tratados co mo distintos.

PROPOSI CIONES

SECCION A9

247

La inicializacin de objetos automticos se realiza cada vez que se entra al bloque por
la parte superior, y procede en el orden de los dcdaradorcs. Si se ejecuta un salto dentro
del bloque, estas inicializaciones no se realizan. Las inicializaciones de objetos static se realizan slo una vez, antes de que el programa comience su ejecucin.

A9.4 Proposiciones de seleccin


Las proposiciones de seleccin eligen uno de varios flujos de control.

proposicin-de-seleccin:
if ( expresin) proposicin
if ( expresin) proposicin else proposicin
switch ( expresin) proposicin
En ambas formas de la proposicin if, la expresin, que debe ser aritmtica o de tipo
apuntador, es evaluada, incluyendo lodos los efectos colaterales, y si se compara como diferent e de O, se ejecuta la primera subproposicin. En la segunda forma, la segunda sub proposicin se ejecuta si la expresin es O. La ambigedad del else se resuelve conectando un
else con el ltimo if sin else encontrado en el mismo nivel de anidamjento del bloque.
La proposicin switch provoca que el control sea transferido a una de varias proposiI.:io nes, dependiendo del valor de una expresin, que debe tener tipo entero. La subproposici n controlada por un switch usualmente es compuesta. Cualquier proposicin dentro
de la subproposicin se puede etiquetar con una o ms eliquctas case(A9.1). La expresin de control tiene promocin integral (A6.1), y las constantes de los case so n convertidas allipo promovido. No se permite que dos de las expresiones case asociadas con el mi smo switch puedan tener el mismo valor despus de la convcrsin. Cuando mas puede
haber una etiqueta defauIt asociada con un switch. Las proposiciones switch pueden estar
anidadas; una etiqueta case o defauIt est asociada con el switch ms anidado que la contiene.
Cuando se ejecuta la proposicin switch, su expresin se evala incluyendo todos lo s
efec tos colaterales y es comparada con cada constante del case. Si una de las constantes
case es igual al valor de la expresin, el control pasa a la proposicin de la etiqueta case
coincidente. Si ninguna. constante case coincide con la expresin y si existe una etiqueta
defauIt, el control pasa a la proposicion etiquetada. Si ningn caso co incide y no hay
defauIt, entonces no se ejectua ninguna de las sub proposiciones del switch.
En la primera edicin de este libro, se requera Que la expresin de control del
switch y las constantes case tuvieran tipo int.

A9.5 Proposiciones de iteracin


Las proposiciones de iteracin especifican la ejecucin de un ciclo.

proposicin-de-iteracin:
while ( expresin) proposicin
do proposici6n while ( expresi6n ) ;
far ( expresin op ; expresinopj expresinoPt

proposicin

En las proposiciones while y do, la sub proposicin se ejecula en forma repetida


mielllras que el valor de la expresin permanezca diferente de O; la expresin debe teIler tipo
aritmtico o apuntador. Con while, la prueba, incluyendo todos los efectos colaterales de

248

MANUAL DE REFERENCIA

APENDlCE A

la expresin, ocurre antes de cada ejecucin de la proposicin; con do, la prueba sigue de
cada iteracin.
En la proposicin for, la primera expresin se evala una vez y especifica la iniciali~
acin para el cit:lo. No hay resric.:cin en c uanto a su tipo. La segunda expresin debe
(C!ler tipo arilllletico o apuntador; se evala antes de cada iteracin y, si es igual a 0, el
far cl"mina . La crecra c;,<presin se evala antes de Lada iteracin y especifica una reinicializacion para el ('iclo. No hay restriccin en cuanto a su tipo. Los efectos colaterales

de cada expresin se completan inmediatamente despus de su evaluacin. Si la subpropo_


sicin no contiene continue. una proposicin
for ( expresin] ; expresin2 i expresin3 ) proposicin

es equivalente a
expresin]
while ( expresin2 ) {
proposicin
expresin3 ;

Cualquiera de las tres expresiones se puede descartar. La falta de una segunda expresin
hace que la prueba implicada sea equivalente a probar una constante diferente de cero.

SECC ION AJO

DECLARACIONES EXTERNAS

249

Que el flujo llegue hasta el final de la funcin es equivalente a un lfeturn! sin expresin.
En cualquier caso, el valor regresado est indefinido.

Al0. Declaraciones externas


La unidad de entrada proporcionada al compilador de e se llama unidad de traduccin; consiste en una secucncia de declaraciones externas, que son declaraciorH.''> o defini ciones de funcin.
unidad-de-traduccin:
declaracin-externa
unidad-de-traduccin declaracin-externa
declaracin-externa:
definicin-de-funcin
declaracin
El alcance de las declaraciones externas persiste hasta el final de la unidad de traducci n en la que son declaradas, precisamente corno el efeno de las declaracione.'" den! ro de
los bloques persiste hasla el final de! bloque. La .')intaxis de las declaracione, externa,> e~ la
misma que para todas las declaraciones, cxceplO que el cdigo dc las rUllcione~ ~lo '>c
puede dar en este nivel.

A9.6 Proposiciones de sallo


Las proposiciones de salto transfieren el control incondicionalmente.

Las definiciones de funciones tienen la forma

proposicin-de-salto:
goto identificador;
continue i
break ;
return expresin OPI

definicin-de-funcin:
especificadores-de-declaracin oP' declarador

En la proposicin goto, el identificador debe ser una etiqueta (A9.1) localizada en la funcin actual. El control se transfiere a la proposicin etiquetada.
Una proposkin continue slo puede aparecer dentro de una proposicin de iteracin,
y oCJ.siona que el I.;on[rol pase a la pon:in de continuacin del ciclo ms anidado que
~ncierra a tal proposicin. En forma ms precisa, dentro de cada una de las proposiciones
while ( ... )

do (

for ( ... )

contin:

contin:
} while ( ... ) i

contin:

Al0.l Definicin de funciones

un continue que no est contenido dentro de una proposicin de iteracin ms anidada


es lo mismo que goto contin.
Una proposicin break puede aparecer slo dentro de una proposicin de iteracin o
de LJ na proposil.:in switch, y termina la ejecllL'in de lo ms anidado que encierre tal proposicin; el control pasa a la proposicin que sigue a la proposicin terminada.
Una funcin regresa a quien la im'oc con la proposicin return. Cuando returo es seguido por una expresin, el valor se regresa al invocador de la funcin. La expresin se
(011\ ('rte. como si se asignara, al tipo regresado por la funcin en la que aparece.

lista-de~declaracionesoPI

proposicin-compuesta

Los nicos especificadores de categora de almacenamiento permitidos entre los especificadores de declaracin son extem o static; vase All.2 para la distincin entre ellos.
Una funcin puede regresar un tipo aritmtico, una estructura, una unin, un apuntador o void, pero no una funcin o un arreglo. El declarador en la declaracin de una funcin debe especificar explcitamente que el identificador declarado tiene tipo funcin; esto
es, debe contener una de las formas (vase A8.6.3)
declarador-directo ( lista-tipos-de-parmetros )
declarador-directo ( Iista-de-identificadoresoPI )

donde el declarador-directo es un identificador o un identificador con parntesis . En particu lar, no dcbc adquirir el tipo ele funcin por medio de un typedef.
En la primera forma, la definicin es una funcin en el c,,"tilo Ilue\'o, y ,>u~ parlllc[rm,
junto con sus tipos, son declarados dentro de su lista de tipos de parmetros; la lista-dedeclaraciones que sigue al declarador de la funcin debe estar ausente. Salvo que la lista
de lipos de parmclros consista solamente en void, moslrando que la fum:in no tiene pararnelros, cada declarador que este en la lista de tipos de parmctro.., debe contener un
identificador. Si la lista de tipos de parmetros termina con", ... " entonces la funcin
se puede llamar con ms argumentos que parmetros; para hacer referencia a los argumenros extra se debe ulilizar el mecanismo de la mano va_ argdcfinid o ('n el h(!({t/(!re'>lndar

250

MANUAL DE REFERENCIA

SECCION

APENDICE A

tener al menos un parmetro nombrado.


En la seg unda forma, la derinicin esta en el estilo anterior: la lista de identificadores
nombra los parmetros, en tanto que la lista de declaraciones les atribuye tipos. Si no

se da ninguna declaracin para un parmetro, su tipo se toma como int. La lista de declaraciones dehe declarar so lamente parmetros nombrados en la li sta; no est permitida la

inicializacin y el nico especificador de categora de almacenamiento posible es reqisler.


En ambos es(i1os de definicin de funciones, se entiende que los parmetros sern declarados precisamente despus del principio de la proposicin compuesta que constituye
el ;uerpo de la funcin y no pueden ser redeclarados all los mismos identificadores (aun~
que podran, como otros identificadores, ser redeclarados en bloques ms internos). Si se
declara que un parmetro tendr tipo "arreglo de tipo", la declaracin se ajusta para ser
"apullIador a 'ipo"; de manera semejante, si un parmetro es declarado con tipo "funcin
que rcgrc:,a 'ipo", la declaracin se ajusta como "apuntador a funcin que regresa lipo".
Durallle la llamada a una funcin, los ar~lImentos se convierten de acuerdo con las necesidades y son asignados a los parmetros; vase A 7.3.2.
La definicin de funciones en el nuevo estilo es nueva en el estndar ANSI. Tambien hay un pequeo cambio en los detalles de la promocin; la primera edicin
estipula que las declaraciones de parmetros float se ajustaron para leerse como
double. La diferencia es notoria cuando dentro de una funcin se genera un
apuntador a un parmetro.
Un cjcmplo t.:omplclO de una funcin en el estilo nuevo es

int max(int a, int b, int e)


{
int mi

(a > b) ? a

return (m

>

: b
el ? m : e

Aqu int es el especificador de la declaracin; max(int a, int b, int e) es el declarador de


la funcin, y I ... J es el bloque con el cdigo para la funcin. La correspondienle definidn cn el estilo anterior seria

int max(a, b, e)
int a, b, Ci
{

.......

ALCANCE Y LIGADURA

251

Pueden existir muchas declaraciones externas para el mismo identificador dentro de la


misma unidad de traduccin si corresponden con su tipo y liga, y si hay cuando ms una
defi nicin para el identificador.
Dos declaraciones para un objeto o funcin se hacen coincidir en tipo bajo las reglas
discutidas en A8.1O. Adems, si las declaraciones difieren debido a que un tipo es una
estructura, unin o enumeracin incompleta (A8.3) y el otro es el tipo completo correspondiente con el mismo rtulo, se considera que los tipos coinciden. Ms an, si uno es
un tipo de arreglo incompleto (A8.6.2) y el otro es un tipo de arreglo completo, los tipos,
si por lo dems son idnticos, tambin se consideran coincidentes. Finalmente, si un tipo
estipula una funcin en el estilo anterior y el otro una funcin en el nuevo y por lo dems
idntica, con declaracin de parmetros, los tipos se toman como coineidcmes.
Si la primera declaracin externa para una funcin u objeto incluye el especificador
static, ste tiene liga interna; de OIra manera tiene liga exrema. La liga se discute en A 11.2.
Una declaracin externa para un objeto es una definicin si tiene inicializador. La
declaracin de un objeto externo que no tiene inidalizador y no contiene el especificador
declaracin de objeto externo que no tiene un inicializador y no contiene al especificador
extern, es una definicin tentativa. Si en una unidad de traduccin aparece una definicin
_ ...(a un objeto, cualesquiera declaraciones tentativas se tratan simplemente como declaraciones redundantes. Si no aparece ninguna definicin para el objeto dentro de la unidad
de traduccin, todas sus definiciones tentativas se ccnvicnen en una sola definicin con
inicia lizador O.
Cada objeto debe tener exactamente una definicin. Para objetos con liga interna,
esta regla se aplica en forma separada para cada unidad de traduccin, debido a que los
objetos ligados internamente son nicos para una unidad de traduccin. Para objetos con
liga externa se aplica al programa completo.
Aunque la regla de una sola definicin est formulada algo diferente en la primera
edicin dt este libro, es en efecto igual a la que se establece aqu. AJgunas
implantaciones la aligeran, generalizando la idea de definicin tentativa. En ia
formulacin alterna, que es comn en sistemas UNtX y reconocida como una extensin comn por el estndar, loda.s las definiciones lelualivas para un objelO
ligado externamente, a travs de todas las unidades de traduccin de un programa, se consideran juntas en lugar de separadamente en cada unidad de traduc
~in. Si en algn lugar del programa Ol.:urre una definicin. emom:cs la\
dcfinjciones temativas ~on ~imple) dcdaral.:ioni:s, pcro ~i no aparcl.:c ninguna
definicin. enlOnces todas sus definiciones tentativas se hacen definicionc~ con
inicializador O.

<stdarq.h> y descrito en el apndice B. Las funciones con nmero variable de argumentO S deben

All

A 11. Alcance y liga

donde ahora int max(a, b, e) es el declarador, e int a, b, e; es la lista de declaraciones


para los parmetros.

No es necesario que un programa se co mpile todo a la vez: el texto fuente se puede


mantener en varios archivos con unidades de traduccin, y se pueden cargar rutinas
preco mpiladas de bibliotecas. La comunicacin entre las funciones de un programa puede
llevarse a cabo tanto a Iravs de llamadas co mo a traves de la manipulacin de datos externos.
Por tanto, existen dos clases de alcance a considerar: primera, el alcance lxico de un
identificador, que es la regin del texto del programa dentro de la que sc entienden las
caractersticas del identificador; y seg unda , el alcance asociado con objetos y funciones con
liga externa, que determina la conexin entre identificadores en unidades de traduccin
l:o mpiladas por separado .

Al0.2 Declaraciones externas


Las declaraciones externas estipulan las caractersticas de objetos, funciones y otrOS
idcntitii.'adores. El termino "cxterna" se refiere a su localizacin fuera de las funciones, y nO
est directamente conectado con la palabra reservada extern; la categora de almacenamiento para un objeto declarado externamente puede dejarse vaca, o se puede especificar
como extern o statie.

......

252

MANUAL DE REFERENCIA

APENDlCE A

A 11.1 Alcance lxico


Los identificadores se dividen en varios espacios de nombre que no interfieren entre s;
el mismo identificador se pued e utilizar para difcrcnIcs propsitos, incluso dentro del mismo alcance, si los usos son en diferentes espacios de nombre. Estas calegoras son: objetos,
funciones, nombres ypcdcf y conSlanleS enum; etiquetas; rlUlos de estructura, uniones y
enumeraciones; y miembros de cada estructu ra o unin individualmente.
Estas reglas difieren en varias formas de las descritas en la primera edicin de
este manual. Las etiquetas no tenan anteriormente su propio espacio de nombre;
los rtulos de estructuras y de uniones tenan cada uno su espacio separado, y
en algunas implantaciones tambin lo tenan los rtulos de enumeraciones; colocar las diferentes clases de rtulos en el mismo espacio es una nueva restriccin.
El ms imponame alejamiento de la primera edicin es que cada estructura o
unin crea un espacio de nombre separado para sus miembros, de modo que en
varias estructuras puede aparecer el mismo nombre. Esta regla ha sido una prctica comn por muchos aos.

PREPROCESAMIENTO

SECCION Al2

253

A 12. Preprocesamiento
Un preprocesador realiza macrosubstituciones, compilacin condicional e inclusin de
archivos nombrados. Las lneas que principian con #, aunque estn precedidas por espacios en blanco, se comunican con este preprocesador. La sintaxis de estas lneas es independiente del resto del lenguaje; pueden aparecer en cualquier lugar y su efeclo termina (independiente mente del alcance) hasta el final de la unidad de traduccin. Los lmites de las
lneas son significativos; cada lnea se analiza individualmente (pero vase A 12.2 cmo
unir lneas). Para el preprocesador, un token es un token del lenguaje o una secuencia de
caracteres que da un nombre de archivo co mo en la directiva #include (A 12.4); adcms,
cualquier carcter que no est definido de aira mallera se toma como token. Sin embargo,
el efecto de los caracteres espacio en blanco que no sean espacio y tabulador horizontal eSl
definirlo dentro de las lneas del preprocesador.
El preprocesamiento sucede en varias fases lgicamente sucesivas que, en una im plantacin en particular, se pueden condensar.
I. Primero, las secuencias trigrficas que se describen en A12 .1 son reemplazadas por

El alcance lxico del identificador de un objeto O funcin dentro de una declaracin


externa principia al final de su declarador y persiste hasta el final de la unidad de traduccin en la que aparece. El alcance del parmetro de una definicin de funcin principia
31 inicio del bloque que define la funcin, y persiste a travs de la funcin; el alcance
de un pafJmetro en la declarcin de la funcin termina al final del declarador. El alcance de un identificador declarado a la cabeza de un bloque principia al final de su declarador y persiste hasta el final del bloque. El alcance de una etiqueta es la totalidad de
la funcin en la cual aparece. El alcance del rtulo de una estructura, unin o enumeracin, o de una constante de enumeracin, principia al aparecer en un especificador de tipo
y persiste hasta el final de la unidad de traduccin (para declaraciones en el nivel externo)
o hasta el final del bloque (para declaraciones dentro de una funcin).
Si un identificador se declara explcitamente a la cabeza de un bloque, incluyendo el
que co nst ituye a la funcin, cualquier declaracin del identificador fuera del mi smo bloque
se suspe nde hasl a el final.

2,
3.

4.

5.

sus equivalentes. De requerirlo el medio ambiente del sistema operativo, se introdu cen
caracteres nueva lnea enlfe las lneas del archivo fuente.
Cada ocurrencia de un carcter diagonal inversa / seguido por una nueva lnea se elimina, uniendo as lneas (AI2.2).
El programa se divide en tokens separados por caracteres espacio en blanco; los comentarios se reemplazan por un solo espacio. Despus se obedecen las directivas de preprocesamiento, y as macros (AI2.3AI2.1O) se expanden.
Las secuencias de escape que estn dentro de caracteres y cadenas literales constantes
(A2.5.2, A2.6) se reemplazan por sus equivalentes; despus se concatenan las cadenas
literales adyacentes .
El resultado se traduce, despus se liga junto con otros programas y bibliotecas, recolectando los programas y datos necesarios y conectando las funciones y objetos externos con sus definiciones.

A12.1 Secuencias trigrficas

A11.2 Liga
Dentro de una unidad de traduccin, todas las declaraciones del mismo identificador
de objelo o fUllcin con liga inlerna se refieren a la misma cosa, y el objeto o funcin es
ni("o para esa ullidad de traduccin. Todas las declaraciones para el mismo identificador de objeto o funcin con liga externa se refieren a la misma cosa, yel objeto o funcin
es compartido por todo el programa.
Como se discul en A 10. 2, la primera declaracin externa para un identificador da
al idcmificador liga interna si se usa el especifil"ador static, de olfa manera, le da liga externa. Si la del"laracin para un identificador dentro de un bloque no incluye el es pecificJ.do r
extern, el identificador no tiene liga y es unico para la funcin . Si incluye extern y hay una
del"laral'in externa acti,a para el identificador dentro del alcance que rodea al bloque, enlon("es el idenlificador liene la misma liga que la declaral"in externa y se refiere al mismo
objclo o fu rKi n; pero si no hay ninguna declaracin externa visible, su liga es externa.

El conjunto de caracteres de los programas fuente de e est contenido dentro del cdigo ASC II de siete bits, pero es un superconjunto dell so646-l983/nvariant Code Seto Para
poder representar a los programas en el conjunto reducido, todas las ocurrencias de las
siguientes secuencias trigrficas se reemplazan por el carcter simple correspondiente. Este
reemplazo ocurre antes de cualquier otro procesamiento .

77=
77/
77'

??(

771
771

77<
77>
77-

No ocurre ningn otro reemplazo.


Las secuencias trigrficas son nuevas en el estndar

ANSI.

A12.2 Unin de lineas


Las lneas que terminan con el carcler diagonal invenida / se empalman eliminando la
diagonal inversa y el siguiente carcter nueva lnea. Esto ocurre anles de hacer la di visin
en tokens.

PREPROCESAMIENTO

254

MANUAL

DE REFERENCIA

AIENDICE A

A 12.3 Definicin y expansin de macros

Una lnea de control de la forma


# define identificador secuencia-de-tokens

255

SECCtQ N A12

En ambas clases de macro, la sec uencia de reemplazo de tokens se rastrea nuevamente


en forma repetida, buscando ms definiciones de identificadores. Sin embargo, una vez
que un identificador dado ha sido reemplazo en una expansin dada, no se reemplaza si
se encuentra nuevamente durante la bsqueda, sino que permanece sin cam bio.
Aun cuando el valor final de una macroexpansin principie con #, no se toma como
una directiva de preprocesamiento.

hace que el preprocesador reemplace las instancias subsecuentes del identificador con la
sec uencia de lokens dada; se descartan los espacios en blanco que se encuentran alrededor
de la secuencia de tokens. Un segundo # define para el mismo identificador es erroneo a
menos que la segunda secuencia de tokens sea idntica a la primera, en donde todos los
espacios en blanco se consideran equivalentes.
Una lnea de la forma

Los detalles del proceso de la macroexpansin se describen en forma ms precisa


en el est,ndar ANSI que en la primera edicin. El cambio ms importante es la adici6n de los operadores # y # # , que hacen admisible el entrecomillado y la concatenacin. Algunas de las nuevas reglas, especialmente las que involucran concatenacin, son muy extrai'las. (Yanse los ejemplOS siguientes.)

# define identificador ( /ista-de-identificadores ) secuencia-de-Iokens

donde no hay espacio en blanco entre el primer identificador y el (. es una maerodefinicin


con parmetros dados por la lista de identificadores. Tal como la primera forma, los espacios en blanco alrededor de la secuencia de tokens se descaTlan, y la macro puede redefinirse
slo con una definicin en la que el nmero y descripcin de parmetros y la secuencia
de tokens sea idntica.
Una lnea de control de la forma
# undef identificador

hace que el preprocesador olvide la definicin del identificador. No es errneo aplicar #undef a un identificador desconocido.
Cuando una macro se ha definido en la segunda forma, las instancias textuales posteriores del identificador de la macro seguidas por espacio en blanco optativo, y despus por
(, una secuencia de rokells separados por comas y un), constituyen una llamada a la macro.
Los argumentos de la llamada son las secuencias de tokens sepa rados por comas; las comas
que se encuentran entre comillas o protegidas por parntesis anidados no separan argumentos. Durante la recoleccin los argumentos no son macroexpandidos. El nmero de
argumentos en la llamada debe coincidir con el nmero de parmetros en la definicin.
Despus de que los argumentos se aslan, se remueven los espacios en blanco Que los principian y finalizan. Despus se subslituye la secuencia de tokens resultante de cada argumento
por cada ocurrencia no entrecomillada del identificador del parmetro correspondiente en la
secuencia de reemplazo de rokells de la macro. A menos de que el parmetro en la secuencia de
reemplazo est precedido por #, o precedido o seguido por ##, los tokens argumentos se
examinan para macrollamadas y se expanden como sea necesario, justo antes de la insercin.
Dos operadores especiales influyen sobre el proceso de reemplazo. Primero, si la ocurrencia de un parmetro en la secue ncia de tokens de reemplazo est precedida inmediatamente por 11, se colocan comillas (") alrededor del parmetro correspondiente y despus,
tanto el 11 como el identificador del parmetro, se reemplazan por el argumento entrecomillado. Antes de cada carcter" o \ que aparezca alrededor o dentro de una cadena literal
o cons tante de carcter en el argumento, se inserta un carcter l.
Segundo, si la secuencia de definicin de tokens para cualquier clase de macro contiene
un operador ##, entonces justo despus del reemplazo de los parmetros, se elimina cada
## , junto con cualquier espacio en blanco de ambos lados, para co ncatenar los tokens adyacentes y formar uno nuevo. El efecto est indefinido si se producen tokens invlidos, o si
el resultado depende del orden de procesamiento de los operadores ##. Adems, ## no
puede aparecer al principiO o fin de una secuencia de tokens de reemplazo.

Por ejemplo , esto se puede utilizar para "constantes manific~tas", como en


Idefine TABSIZE 100
int table[TABSIZE1;

La definicin
#define ABSDIFF(a, b)

(a).(b) 7 (a)-(b)

(b)-(a))

define una macro que regresa el valor absoluto de la diferencia entre sus argumentos.
A diferencia de la funcin que hace la misma actividad, los argumentos y el tipo regresado
pueden tener cualquier tipo aritmtico o incluso ser apuntadores. Los argumentos que pueden tener efectos colaterales so n evaluados dos veces, una para la prueba y otra para produdr el valor.
Dada la definicin
Idefine tempfile(dir)

#dir "/%s"

la maerollamada tempfile(/usr/tmp) entrega


"/usr/tmp"

11

/%s"

que posteriormente se concatenar como una cadena sencilla. Despus de

x 1# Y

Idefine cat(x, y)

la lIamadacat(var, 123) produce varl23. Sin embargo. la llamada cot(cat(I,2). 3))estil indefinida: la presencia de ## impide que los argumenlOS de la llamada ms externa sea n
expa ndido s. As se produce la cadena de tokens.
cat

13

Y )3 (la unin del ltimo token del primer argumento con el primer token del segundo)
no es un tOken legal. Si se introduce un segundo nivel de macrodefinicin,
Idefine xcat(x,y)

cat(X,y)

las cosas trabajan ms suavemente; xcat(xcat(l , 2), 3) produce 123, debido a que la ex
pansin de xcat en s no involucra al operador ##.
En la misma forma, ABSDlFF(ABSDlFF(a,b) ,c) produce lo esperado, un resultado
completamente expandido.

256

MANUAL DE REFERENC IA

APENDICE A

A 12.4 Inclusin de archivos

Una lnea de control de la forma


# inelude

< nombre-de-archivo >

ocasiona el reemplazo de esa linea por el contenido completo del archivo nombre-de_
archivo. Los caracteres del nombre nombre-de-archivo no deben incluir> o nueva lnea
y est indefinido el efecto si contiene ", " \, o / *. El archivo nombrado se busca en
una secuena de lugares dependiendo de la implantacin.

De modo semejante, una lnea de control de la forma


# inelude "nombre-de-archivo "
busca primero en asociacin con el archivo fuente original (fase deliberadamente dependiente de la implantacin), y si tal bsqueda falla. entonces lo hace como en la primera
forma. El efecto de usar " \. o /* en el nombre del archivo permanece indefinido, pero
est permitido >.
Finalmente, una directiva de la forma

# include secuencia-de-lOkens

que no l"oincida con una de las formas previas se interpreta expandiendo la secuencia de
tokens como texto normal; debe resullar una de las dos formas con <... > o " ... ", y entonccs se lrata como se describi anleriormente.
Los archivos #include pueden estar anidados.

A 12.5 Compilacin condicional


Parte de un programa se pueden compilar condicionalmente, de acuerdo con la siguiente sintaxis esquemtica.

preprocesador-condicional:
/(nea-lf texto partes-e/if parte-e!seopt #endif
Iinea-iI
# if expresin-constante
# ifdef identificador
# ifndef identificador

PREPROCESA MIENTO

SECCIQN A12

orden hasta que se encuentra una expresin con valor diferente de cero; el texto que sigue
a una lnea con valor cero se descarta. El texto que sigue a una lnea de directiva con xito
se trata normalmente. Aqu "texto" se refiere a cualquier material, incluyendo lneas del
preprocesador , que no es parte de la estructura co ndicional; pucde ser vaco . Una ve/. que
se ha encontrado una lnea #if o #elif con xito y se ha procesado ...,u texto, las linea:-.
#elif y #else que le siguen, junto con su texto, se descartan. Si todas las expresiones son
cero y hay un #else, el texto que sigue al #else se trata normalmente. El texto controlado
por ramificaciones inactivas de la condicional se ignora, excepto para verificar el anidamiento de condicionales.
La expresin constante en #if y #elif est sujeta a macroreemplazo ordinario. Adems, cualesquier expresiones de la forma
defined identificador
o
defined ( identificador)
se reemplazan, antcs de buscar macros, por lL si el identificador est definido en el
preprocesador y por OL si no lo est. Cualesquiera identificadores restantes despus de la
macro expansin se reemplazan por OL. Finalmente, cada constantc entera se considera como con sufijo L, de modo que toda la aritmtica se forma corno long o unsigned long.
La expresin constante q Ut' resulta (A 7.19) est. restri ngida: debe ser emera y no debe
conte ner sizeof, o una constante de enumeracin.
Las lneas de control
#ifdef identificador
#ifndef identificador
son equivalentes a
# if defined ;dentificador
# if ! defined identificador

respectivamente.
#alif es nueva desde la primera edicin aunque ha estado disponible en algunos
preprocesadores. El operador dafinad del preprocesador lambin es nuevo.

partes-elif:
!lnea-e/if texto
partes-e/ifoPI

A12 .6 Control de linea

!{nea-e/if:
# ehf expresin-constante

Para beneficio de otros preprocesadores que generan programas en


de las formas

parte-e/se:
Ilnea-else texto
'{nea-else:
#else
Cada una de las directivas (Inea-if, Inea-elif, Inea-else, y #endif) aparece sola en una lnea. Las expresiones constantes que estn en #if y posteriores lneas #elif se evalan en

257

e,

una lnea en una

# line constante " nombre-de-archivo"


# line constante
ocasiona que el compilador suponga, para propsitos de diagnstico de errores, que el nmero de lnea de la siguiente linea fuente est dado por la consta nt e cntera decimal, y que el
archivo actual de emrada esta nombrado por el identificador. Si el nombre de archivo
entrecomillado est ausente, el nombre recordado no camb ia. La.., macro.., de la linea ..,on
expandidas ame<; de ser interpretadas.

258

MANUAL DE REFERENClA

APENO ICE A

SECCION Al3

A 12. 7 Generacin de errores

# error secuellcia-de-tokens opl

ocasiona que el preprocesador escriba un mensaje de diagnstico que incluye la secuencia


de lokens.

A12.8 Pragmas
Una lnea de control de la forma
# pragma secuencia-de-tokens oP1

unidad-de-traduccin:
declaracin-externa
unidad-de-/raduccin declaracin-externa
declaracin-externa:
definicin-de-funcin
declaracin
definicin-de-funcin:
especificadores-de-declaracin oP' declarador lista-de-declaracionesoP'
proposicin-compuesta
declaracin:
espenficadores-de-declaracin lis! a-declaradores-in il "PI;
lista-de-declaraciones:
declaracin
lista-de-declaraciones declaracin
especificadores-de-declaracin:
especificador-cQtegora-almacenamienlo especificadores-dedeclaracin oP'
especificador-de-tipo especijicadores-de-declaracin oP'
calificador-de-tipo especificadores-de-declaracin oP'
especificador-calegoda-almacenamiento: uno de
auto register static extern typedef
especijicador-de-tipa: uno de
void char short int long float double signed
unsigned especificador-estruclura-unin
especificador-enum nombre-typedej
calificador-de-lipo: uno de
const volatile
especificador-eSI ruCI ura-o-unin:
estructura-a-unin identificadorOpl { listadeclaraciones-struc/ }
estructura-o-unin iden/lficador
estructura-a-unin: uno de
struct union
lis! a-declaraciones-sI ruCI:
dec/aracin-Slruct
lisla-declo raciones-strucl declaracin-sI rucl
lista-decforadores-inil:
dec/arador-init

ocasiona que el preprocesador realice una accin que depende de la implantacin. Un

!pragmal no reconocido es ignorado.


A12.9 Directiva nula

Una lnea del preprocesador de la forma


#

no tiene efecto.
A12.10 Nombres predefinidos
Varios identificadores estn predefinidos, y se expanden para producir informacin especial, ni ellos ni el operador de expresin del preprocesador defined, pueden estar sin
definicin o redefinidos.

DATE

Constante decimal que contiene el nmero de lnea actual.


Cadena literal que contiene el nombre del archivo que se est compilando.
Cadena literal que contiene la fecha de la compilacin, en la forma
"Mmm dd aaaa".

TIME
STDC

A 13.

259

la gramlica ha retirado la definicin a los smbolos terminales constante-entera,


cons/ante-de-carc/er, constante-flolame, den/iJicador, cadena, y constante-enumeracin;
las palabras en estilo mecanogrfico y los smbolos son terminales dadas literalmente. La
gramtica se puede transformar mecnicamente en una entrada aceptable por un generador de parsers automtico, Adems de agregar alguna marca sintctica para indicar allernativas en las producciones, es necesario expandir las construcciones "uno de" y
(depe ndiendo de las reglas del generador de parsers) auplicar cada produccin con simbo lo Opl, una vez con el smbolo y Olra sin l. Con un cambio adicional, borrar la produccin nombre-lypedef'identificador y hacer a nombre-Iypedef smbolo terminal, esta gramtica es aceptable para el generador de parsers YACe. Slo tiene un conflicto, generado
por la ambigedad del if-else.

Una lnea del preprocesador de la forma

LINE
FILE

GRAMATICA

Cadena literal que contiene la hora de la compilacin, en la forma


"hh:mm:ss".
la constante 1. Este identificador ser definido como 1 slo en implantaciones que conforman el estndar.
#error y #praqma son nuevos con el estndar ANSI; las macros predefinidas del
preprocesador son nuevas, pero algunas de ellas han estado disponibles en algunas implantaciones.

Gramtica

A continuacin se encuentra una recapitulacin de la gramtica expuesta a lo largo de


la primera parte de este apndice. Tiene exactamente el mismo contenido, pero se encuentra en diferente orden.

......,

260

MANUAL DE REFERENCIA

SECCION AI3

APENDlCE A

lista-de-identificadores identificador
inicializador:
expresin-asignacin
{ lisfa-de-inicializadores }
{ lista-de-inicializadores , }
lisla-de-inicializadores:
inicializador
lista-de-inicializadores , inicializador
nombre-de-tipo:
lista-calificador-especificador declarador-abstracto oP1
declarador-abstracto:
apuntador
apuntadoropl declarador-abstracta-directo
declarador-abstracto-directo:
( declarador-abstraclO )
declarador-abstracto-directo op expresin-constanteoP' ]
declarador-abstracto-direClOaP1 ( listo-tipos-deparmetroopt )

lista-declaradores-init , declarador-init
declarador-init:
declarador
declarador = inicializador
declaracin-struct:
lista-calificador-especificador listo-declaradores-struct ;
fis ro-calJicado r-especJicador.'

especificador-de-l po lisIa-calificador-especificadorUpl
calificador-de-, po lisIa-calificador-especificadorVpl
Iisl a-declaradores-s'fuet :

declarador-strucl
lisla-dec/aradoreS-SfrUCI

declarador-struct

declarador-Slruct:
declarador
declaradoroP1

GRAMATICA

expresin-constante

especificador-enum:
enum identificador,p, { lista-de-enumerador )
anum identificador

nombre-typedef;

lista-de-enumerador:

identificador
proposicin:
proposicin-etiquetada
proposicin-expresin
proposicin-compuesta
proposicin-de-seleccin
proposicin-de-iteracin
proposicin-de-salto
proposicin-etiquetada:
identificador: proposicin
case expresin-constante: proposicin
default : proposicin
proposicin-expresin:
expresinop / ;
proposicin-compuesta:
{ lista-declaracin oP / Iista-de-proposiciones oP1 }
lista-de-proposiciones:
proposicin
lista-de-proposiciones proposicin
proposicin-de-seleccin:
if ( expresin) proposicin
if ( expresin) proposici6n else proposicin
switch ( expresin) proposicin
proposicin-de-iteracin:
while ( expresin) proposicin
do proposicin while ( expresin)
( expresinoP1 ; expresin op , ; expresinopc ) proposicin
proposicin-de-salto:
goto identificador;

enumerador
lista-de-enumerador , enumerador

enumerador:
identificador
identificador
expresin-constante
declarador:
apuntadoropt declarador-directo
declarador-directo:
identificador

( declarador)
declarador-directo [ expresin-constanteoP1 ]

declarador-directo ( lista-tipos-de-parmetro )
declarador-directo ( Iista-de-identificadores oP' )
apuntador:
* Iista-calificadores-de-tipoOPI
* Iista-calificadores-de-lipol'P1 apuntador
I SI a-calificadores-de- t po:
calificador-de-tipo
lisla-calificadores-de-t ipo calificador-de-t ipo
lisla-, ipos-de-parmet ro:
lista-de-parmetros
lista-de-parmetros
lista-de-parmetros:
declaracin-parmetro
lista-de-parmetros declaracin-parmetro
declaracin-parmetro:
especificadores-de-declaracin declarador
especificadores-de-declaracin declarador-abstracto oP /
lista-de-identijicadores:
identificador

continue ;

.....

261

262

APENDICE A

MANUAL DE REFERENCIA

break;
retum expresinQP1 , ;

expresin:
expresin-de-asignacin
expresin expresin-de-asignacin
expresin-de-asignacin:
expresin-condicional
expresin-unaria operador-de-asignacin expresin-de-asignacin
operador-de-asignacin: uno de

*=

/=

%=

+=

&=

:=

expresin-condicional:
expresin-Igica-OR
expresin-Igica-OR ? expresin: expresin -condicional
expresin-constante:
expresin-condicional
expresin-lgicQ-OR:

expresin-Igica-AND
expresin-Igica-OR : : expresin-Igico-AND
expresin-Igica-AND:
expresin-OR-inclusivo
expresin-lgicQ-AND && expresin-OR-inclusivo
expresin-OR-inclusivo:
expresin-OR-exclusivo
expresin-OR-inclusivo : expresin-OR-exclusivo
expresin-OR-exclusivo:
expresin-AND
expresin-OR-exclusivo " expresin-AND
expresin-AND:
expresin-de-igualdad
expresin-AND & expresin-de-iguaJdad
expresin-de-igualdad:
expresin-relacional
expresin-de-igualdad
expresin -relacion a1
expresin-de-igualdad 1= expresin-relacional
expresin-relacional:
expresin-de-corrimiento
expresin-relacional < expresin-de-corrimiento
expresin-relacional > expresin-de-corrimiento
expresin-relacional < = expresin-de-corrimiento
expresin-relacional > = expresin-de-corrimiento
expresin-de-corrimiento:
expresin-aditiva
expresin-de-corrimienro expresin-aditiva
expresin-de-corrimienro > > expresin-aditiva
expresin-adi' iva:
expresin-multiplicativa
expresin-aditiva + expresin-multiplicativa
expresin-aditiva - expresin-multiplicativa
expresin-m ultiplicat va:

GRAMATICA

SECClON AI3

263

expresin-cast
expresin-multiplicativa expresin-cast
expresin-multiplicativa / expresin-casI
expresin multiplicativa % expresin-cast
expresin-cast:
expresin-unaria
( nombre-de-lipo ) expresin-casI
expresin-unaria:
expresilJ- posfija
+ + expresin-unaria
- - expresin-unaria
operador-unario expresin-casI
sizeof expresin-unaria
sizeof ( nombre-de-tipo )
operador-unario: uno de

& +
expresill-posjija:
expresin-primaria
expresin-posfija { expresin 1
expresin- posfija (lista-de-expresiones-argumenro opr)
expresin-posfija . identificador
expresilI-posfijll -> identificador
expresill-poj!i)a ++
expresilI-posfijll -expresin-primaria:
identificador
constante
cadena
( expresin )
lis/(l-expresiones-arg limen ro:
expresin-de-asignacin
/isra-expresiones-argumento expresin-de-asignacin
constante:constante-entera
consta" fe-de-carc/ er
constan te-flotan te
constante-enumeracin

La siguiente gramtica para el preprocesador resume la estructura de las lneas de conlrol , pero no es adecuada para un parser mecanizado. Inclu ye el ~l11bo l o feX!, que es texto
ordinario de programa, lneas de control no condicionales del preprocesador. o construcciones condicionales completas del preprocesador.
1
lnea de control:
# define idenlificador secuencia-de-/okens
# define identificador ( identificador, .. , identificador) secuencia-eJe-tokens
# undef identificador
# inelude < nombre-de-archivo >
# inelude "nombre-de-archiYo"
# inelude secllencia-de- fo kells

264

MANUAL DE REFERENCIA

APENDlCE A

# Hna constante "nombre-de-archivo"


# Une constante

# error secuencia-de-okensol'l
# pragma secuencia-de- rokens"'!I
#
preprocesador-condicional
preprocesador-condicional:
Ilnea-if texto partes-eli! parte-elseOP1 # endif
linea-if:
# if expresin-constante
# ifdef identificador
# ifndef identificador
partes-elif:
Ilnea-elif texto
partes-ellfopr
lnea-elif:
# eHf expresin-constante
parte-else:
lnea-e/se texlO
lnea-else:
# else

APENDICE B:

Biblioteca estndar

Este apndice es un resumen de la biblioteca definida por el estndar ANSI. La bibliotcca estnd ar no es propiamente parte del lenguaje e, pero un entorno que opere con e
c~la ndar proporcionar las declaraciones y tipos de funciones y las macroderinkiones
de esta biblioteca. Hemos omitido algunas funciones que son de utilidad limitada o fcilmente sintetizadas a partir de otras; tambin hemos omitido caracteres de bytes mltiples
as como la disc usin de 'cuest iones locales, esto cs, propiedades que dependen del lenguaje local, la nacionalidad o la cultura.
Las funciones, tipos y macros de la biblioteca estndar estn declarados en headers estndar:
<assert.h>
<ctype.h>
<errnO.h>

<float.h>
<limits.h>
<locale.h>

<math.h>
<setjmp.h>
<signal.h>

<stdarg.h>
<stddef.h>
<stdio.h>

<stdlib.h>
<string.h>
<time.h>

Se puede tener acceso a un header por medio de


#include <header>

Los headers se pueden incluir en cualquier orden y nmero de veces. un header se debe
incluir fuera de cualquier declaracin o definicin externa y antes de cualquier uso de cualquier cosa que declare. No es necesario que un Ileader sea un archivo fuente.
Los identificadores externos que principian con subguin estn reservados para uso de
la biblioteca, como lo estn todos los otros identificadores que principian con un subguin
y una letra mayscula u otro subguin.

81.

Entrada y salida: <stdio.h>

Las funciones, tipos y macros de entrada y salida, definidos en < stdio.h >, representan cerca de la tercera parte de la biblioteca.
Un flujo (slream) es una fuente o destino de datos que puede estar asociada con un
disco u otro perifrico. La biblioteca maneja flujo s de texto y flujos binarios, aunque en
algunos sistemas, notablemente UNIX, son idnticos. Un flujo de texto es una secuencia de lneas; cada lnea tiene cero o ms caracteres y est terminada por ,\n'. Un entorno
puede necesitar convenir un flujo de texto a alguna representacin, o de alguna Otra representacin (tal como la asociacin de '\n' a retorno de carro y avance de lnea). Un flujo
binario es una secuencia de bytes no procesados que representan datos imernos, con la pro265

266

APENDICE B

BIBLIOTECA ESTANDAR

SECCION 81

piedad d e que ,, e,> eSnilQ y despus ledo de nuevo en el mi .. rno sistema, ser comparado
':OIllO iglml.
Un nujo se conecta a un archivo o dispositivo al abrirlo; Id. conexin se rompe cerrando
el flujo. El abrir un archivo regresa un apuntador a un objeto de tipo FILE, que registra
cualquier informacin necesaria para controlar el flujo. Usaremos "apuntador de archivo" y "flujo" indistintamente cuando no haya ambigedad.
CU31ldo un programa rlfincipia su cjcc ucion, 10'1 flujos stdin, stdout, y stderr ya estn
abierto ....

(iposize~t

int rename(const char *oldname, const char *newname)


renamec3mbia el nombre de un archivo; regresa un \alor diferente de cero...,i el intento

ralla.
FILE +tmpfile : void)
tmpfile \.Tea un an.:hi\o temporal con modo "wb+" que sera removido autornlic3mcn.
te cuando se cierre o cuando el programa termine normalmente. tmpfile regresa un flujo, o NULL si no puede crear el archivo.
char *tmpnam(char s[L_tmpnam])
tmpnam(NULL) crea una cadena que no es el nombre de un archivo existente y regresa
un apuntador a un arreglo esttico interno. tmpname(s) almacena la cadena en s y tam.
bin la regresa como el valor de la funcin; s debe tener espacio suficiente para al menos LJmpnam caracteres. tmpnam genera un nombre diferente cada vez que se invoca;
cuando mas estan garantizados TMP _MAX difcrellles nombres durante la ejecucioll
del programa. Ntese que tmpnam crea un nombre, no un archivo.

es el tipo

FILE *fopen(const char *filename, const char +mode)

Iepen abre el archivo nombrado, y regresa


valores legtimos de mode incluyen
"r"
"w"
"a"
"r+"
"w+"
I'a+"

un~flujo,

267

int remove(const char *filename)


removeremueve el archivo nombrado, de modo que un intento posterior de abrirlo
faltar. Regresa un valor diferente de l'cro si el intento falla.

81.1 Operaciones sobre archivos


La'> 'iigui('ntc~ runl'iones tralan con operacioncc; sobre archivos. El
Cillero sin signo produ<.:ido por el operador sizeof.

ENT RADA Y SALIDA <STDlO. H >

o NULL si falla el intento. Los

int setvbuf(FILE *stream, char *buf, int mode, size_t size)


setvbuf controla el uso de buffer para el flujo; se debe invocar antes de leer o escribir.
Un modo _IOFBF ocasiona uso completo dl' buffers, _IOLBF uso de buffer. . por linea
de archivo de texlO, e _IONBF ningn uso de buffer. Si buI no es NULL, se empicara
como el buffer, de aira mancra sera asignado un buffer. size delcrmina su amao.
setvbuf regresa un valor diferente de cero en ca~o dc c ualquier error.

abre archivo de texto para lectura


crea archivo de texto para escritura; descarta el contenido previo si
existe
agrega; abre o crea un archivo para escribir al final
abre archivo para actualizacin (esto es, lectura o escritura)
crea archivo de texto para actualizacin; desearla cualquier conteni
do previo si existe
agrega; abre o crea archivo de texlO para actualizacin, escribiendo
al final

void setbuf(FILE *stream, char *buf)


Si huI es NULL, se suspende el uso de buffer para el flujo. De otra manera, sethuf es
equivalente a (void) setvbuf (stream, buf, _IOFBF, BUFSIZ).

El modo de actualizacin permite la lectura y escritura del mismo archivo; entre una
lectura y una esnitura debe llamarse a fflush O a una funcin de posicin o viceversa.
Si el modo incluye b despus de la letra inicial, como en "rb" or "w+b", indica que
el archivo es binario. Los nombres de archivo estn limitados a FILENAME.MAX caral'tL'l'l''''. Cuando ms pucden ser abiertos FOPEN.MAX archivos a la vez.

81.2 Salida con formato


Las funciones printf proporcionan conversiones de salida con formato.
int fprintf(FILE *stream, const char *format, ... )
fprintf toma una salida y la convierte y escribe hacia el stream bajo el control de
formato El valor regresado es el nmero de caracteres escritos, o un valor negativo si ocu
rri algn error.
La cadena de formato contiene dos tipos de objetos: caracteres ordinarios, que son copiados al flujo de salida, y especificaciones de conversin, cada uno de los cuales provoca
la conversin e impresin de los sigu ient es argumelllos sucesi\os de Iprintf. Cada especificacin de conversin principia con el carcter % y termina con un carcter de conversin.
Entre el 010 y el carc ter de comersin puede haber, en orden:

FILE *freopen(const char *filename, const char -mode, FILE *strearn)


freopen abre el archivo con el modo estipulado y asocia al flujo con l. Regresa
stream, o NULL si ocurre un error. freopen norrnalmete se usa para cambiar los archi
vos asociados con stdin, stdout o stderr.
int fflush(FILE *stream)
Para un !lujo de .;,alida. fflush ocasiona que sea C'lCr ilO cualquier dalO con uso de bufrer que h;I,ta ese momelllO no hay sido eSl'rito: para un flujo dc ('m rada, el efecto est
ludl'finido. Rl'gn: ... a EOF en l'aso dt' un error de c~l'ritura. y l'ero en caso contrario.

Banderas (en cualquier orden), que modifican la especificacin:


-. que especifica ajuste del argumento convenido hacia la izquierda dentro de su campo.
+, que estipula que el nmero siempre ser impreso con signo.
espacio: si el primer carcter no es un signo, se prefijar un espacio.
O: para conversin numrica, estipula rellenado con ceros iniciales de la totalidad del campo.
#, que estipula una forma alterna de salida. Para!9, el primer dgito ser cero. Para x o X, cual-

int fclose(FILE *stream)


fclese descarga cualquier dalO no escrilO de stream, descarta cualquier buffer de entrada no ledo, libera cualquier buffer asignado automticamente, desIJus cierra el flujo. Regresa EOF si ocurre cualquier error y cero en caso contrario.

........L

APENDICE B

BIBLlUTI:.CA ES I AN DAR

268

q uier resultado diferente de cero ser prefijado con Ox o OX. Para e, E, f. q, Y G,la salida siempre tendr un punto decimal ; para 9 y G, los ceros acarreados no sern removidos.
U n n mero que c\l ipula un ancho m ni mo de cam po. El argument o cO rn cnid o sera impreso en un
l'J m pO de por lo mella' (.'\13 arn ptilUd. ) en una m ayor ~i C'o Il Cl.'C'i<'lrio. Si el argumelH o m nq:rt ido liene

menos caracteres que el ancho de campo, ser rellenado a la izquierda (o derecha, si se ha requerido
ju ... t ifi:a n a la itqui(,'rda) para com pletar el ancho de ca mpo . El carcter de rel leno normalmenlc

es es pado, pero es O ~i c~ [ prc. . cntc la ban dera de relleno 1.:011 (ero .....
Un pumo, que separa el ancho de campo de la precisin.
Un nmero, la precisin, que estipula el nmero mximo de caracteres de una cadena que sern
impresos, o el nmero de dgitos que sern impresm despus del punto decimal para conversiones
e, E, O f, O el nmero de dgitos signifi c ~. !i ~' os para conversiones g o G , o el nmero mnimo de
dgit os que sern impresos para un entero (sern agregados ceros al principio para completar el an
cho de campo necesario).
Un modificador de longitud h, 1 (letra ele), o L. "h" indica que el argumento correspondiente va
a ser impreso como short o unsigned short; "}" indica que el argumento es long o unsigned
long; "L" indica que el argumento es long double.

Con se puede especificar la amplitud o precisin, o ambas, en tal caso el valor se calcula
convirtiendo el (los) siguiente(s) argumento(s), que debe(n) ser ini.
Los caracteres de conversin y sus significados se~muestran en la tabla B1. Si el carcter
que est despus del % no es un carcter de conversin, el comportamiento est indefinido.

TABLA
CARACTER 1

d, i
o

x,X
u
e

e,E

g,G

p
n
%

BI.

CONVERSIONES DE PRINTF

T-IPo DE ARGUMENTO; CONVERTIDO A

int; notaci6n decimal con signo.


int; notacin octal sin signo (sin ceros al principio)
int; notacin hexadecimal sin signo (sin Ox o OX al principio), utilizando
.bedel para Ox o ABCDEF para OX.
int; notacin decimal sin signo.
int; carcter sencillo, despus de la conversin a unsigned chal.
char .; los caracteres de la cadena son impresos hasta que se alcanza un
'\0' O hasta que ha sido impreso el nmero de caracteres indicados por la
precisin.
double; notacin decimal de la forma [-}mmm.ddd, en donde el numero
de d es especificado por la preci ~ in. La precisin po r omisi6n es 6; una
prcl:isin O suprime el punto decimal.
double; notacin decimal de la forma [-}m.ddddddexx O
[-)m.ddddddExx, en donde el numero de d est especificado por la precisin. La precisin por omisin es 6; una precisin O suprime el punto
decimal.
double; es usa %e o %E si el exponente es menor que -4 o mayor o
igual que la precisin; de otra forma es usado %1. Los ceros y el punto decimal al final no son impresos.
void .; imprime como un apuntador (representacin dependiente de la implantacin).
int .; el nmero dc l'a ral.ere s esni tos hasta el momen tO por eSla llamada a
printI c.\, escrilU en el argumento. No es convertido ningn argurnelllo.
No es convertido ningn argumento; se imprime como %.

SECCJON 81

ENTRADA Y SALIDA <$TDIO .H >

269

int printf(const char *format, ... )


printf ( .. . ) es equivalente a fprintf (stdout, .. ).
int sprintf(char *S, const char *format, . .. )
sprintf es lo mismo que printf excepto que la salida es escrita en la cadena s, terminada
con '\ 0' . s debe ser suficientemente grande para contener el resultado. La cuenta regresada no incluye el ,\0'.
v printf(const char *format, va_list arg)
vfprintf(FILE *stream, const char .format, va_list arg)
vsprintf(char *s, const char _format, va_list arg)
Las funciones vprintf, vfprintf, y vsprintf son equivalentes a las correspondientes fun
ciones printf, excepto que la lista variable de argumentos es remplazada por arg, que
ha sido inicializado por la macro va.starl y tal vez llamadas a va.arg. Vase la exposi
cin de < stdarg.h > en la seccin B7.

81.3 Entrada con formato


Las funciones scanf tratan con la conversin de entrada con formato.
int fscanf(FILE *strearn, const char *format, . .. )
fscanf lee del stream bajo el control de format, y asigna los valores convertidos a travs
de argumentos subsecuentes, cada uno de fos cuales debe ser un apuntador. Regresa cuando format se ha agOlado. fscanf regresa EOF si se encuentra fin de archivo o un error antes
de la conversin; de otra forma regresa el nmero de artculos de entrada convertidos y
asignados.
La cadena de formato generalmente contiene especificaciones de conversin, que son
utilizadas para dirigir la illlerpretacin de la entrada. La cadena de format o puede contener:
Blancos o tabuladores, que son ignorados.
Caract cres ordinar io ... (no 010 ) , qu e <;e e<; pcra coi ncida n con lo" ..,iguicnI c<; caraClcrc" qu e no ~o n c<;
paci o en blanco del fluj o de enIrada.
Especificacioncs de convcr..,i n, con ... istenIe" en 070, un carct er o ptati vo de "up rc<,in de a~i gnaci n
., un nmero optativo que especifica una amplitud mxima de campo, una h, lo L optativa que
indica la amplitud del objetivo, y un carcter de conversin .

Una especificacin de conversin determina la conversin del siguiente campo de en


trada. Normalmente el resultado es situado en la variable apuntada por el argumento ca
rrespondiente. Sin embargo, si se indica supresin de asignacin con "'. como en %s, el
campo de entrada simplemente se salta; no se hace asignacin. Un campo de entrada est
definido por una cadena de caracteres diferentes de espacio en blanco; se extiende hasta
el siguiente carcter de espacio en blanco o ha\ ta que el an cho de campo, \i e"t e . . pet:ificado, se ha agOlado. Est o implica que scanf leer ms al1a de los lmile!-. de la linea para en
contrar su entrada, ya que las nuevas lineas so n espacio . . en blanco. (La... caracterc . . de e\pacio en blan co son blan co, tabulador, nueva lnea, retorno de carro, tabulador verli cal y
avan ce de linea.)
El carcter de conversin indica la interpretacin del campo de entrada. El argumento
correspondiente debe ser un apuntador. Los caracteres de conversin legales se muestran

en la tabla B-2.
Los caracteres de conversin d, i, n. o, u, y x pueden estar precedidos por h si el argu mento es un apuntador a short en vez de int, o por 1 (letra ele) si el argumento es un apun
tador a long. Los caracteres de conversin e, f, y 9 pueden estar precedidos por 1 si en
w

270

APENDICE B

BIBLIOTECA ESTANDAR

la lisIa de argull1enlO~ hay un apuluador a double y no a float, y por L si hay un apuntador

a long double.
TABLA 82.
CARACTER

o
u

x
e

e,f,q

p
n

l .J

1 ... J
%

CONVERSIONES DE SCANF

DATO DE ENTRADA; TIPO DE ARGUMENTO

entero decimal; int ...


entero; int *. El entero puede estar en octal (iniciado con O) o hexadecimal
(iniciado con Ox o OX).
entero octal (con o si n cero al principio); int *.
entero decimal sin signo; unsigned int .
entero hexadecimal (con O sin Ox o OX al principio); int
caracteres; char ... Los siguientes caracteres de entrada se pondrn en el
arreglo indicado, hasta el nmero dado por el ancho de campo; el valor
por omision es l. No se agrega '\0'. En eSle ,:aso se suprime el salto normal
<;obrc los l'aracte res de espado en blanco; para leer el ~iguiente carcter que
no sea blanco, use %15.
.:adena de caraL:tercs que no es espacio en blanco (no entrecomillados);
ehar ., apunta a un arreglo de caracteres suficientemente grande para co nlener la cadena y un '\0' terminal que se le agregar.
nmero de punto Ootante; Boat . El formato de entrada para los float es
un signo oplativo, una cadena de nmeros posiblemente con un punto decimal, y un campo optativo de exponemecon una E o t' seg uida posiblemen te
de un entero con signo.
valor apuntador como se imprime por printJ ("%p"); void .
escribe en el argumento el nmero de caracteres escritos hasta el momento
por esta llamada; int . No se lee entrada alguna. La cuenta de elementos
convert idos no se incrementa.
coincide con la mayor cadena no vaca de caracteres de entrada del conjunto
entre corchetes; ehar . Se agrega un ,\0'. [ l .. .] incluye] en el conjunto.
coincide con la mayor cadena no vaca de caracteres de ent rada que 110 sean
del conjunto entre corchetes; char . Se agrega un ,\0'. n1 inclu ye 1
en el conjunto.
literal; no se hace ninguna asignacin.

ENTRADA Y SALIDA <$TDIO.H >

SECCION 131

271

ehar *fgets(ehar *s, int n, FILE *stream)


fgets lee hasta los siguientes n-l caracteres en el arreglo s, detenindose si en.:ue ntra
nueva linea; el nueva lnea es incluido en el arreglo, quc cs terminado por ,\0'. fgets
regresa s, o NULL si se encuentra fin de archivo u ocurre un error.
int fpute(int e, FILE *stream )
(pute escribe el carcter e (convertido a unsigned ehar) en stream. Regresa el carcter escrito, o EOF en caso de error.
int fputs(eonst ehar *s, FILE *stream)
fputs escribe la cadena s (que no necesita contener ,\n') en stream; regresa un valor no
negativo, o EOF si hay error.
int gete(FILE *stream)
gete es equivalente a fgete excepto que si es una macro, puede evaluar a stream ms
de una vez.
int getehar(void)
getehar es equivalente

a getc (stdin).

char *gets(ehar *s)


gets lec la siguicnte linea de entrada y la deja en el arreglo s; reemplaza el ,;ar<..:tcr nueva
linea final con ,\0'. Regresa s, o NULL si ocurre fin de archivo o error.
int pute(int e, FILE *stream)
pute es equivalente a fpute excepto que, Iji es una mano, pucde evaluar a stream ms
de una vez.
int putehar(int el
putehar (c) es equivalente a pute (e, stdout).
int puts(eonst ehar *s)
puts escribe la cadena s y un nueva lnea a stdout. Regresa EOF si ocurre un error, de
otra forma, un valor no negativo.
int ungete(int e, FILE *stream)
ungete regresa e (convertido en unsigned ehar) de nuevo al stream, de donde ser
regresado en la prxima lectura. Slo se garantiza un carcter de regreso por flujo.
EOF no puede ser regresado. ungete devuelve el carcter regresado, o EOF en caso
de error.

int scanf(const char *format, ... )


seanf( ... ) es idntica a fscanf(stdin, .. ).
int sseanf (char *s I const char *format, ... )
sscanf(s, ... ) es equivalente a seanf( ... ) excepto que los caracteres de entrada son tomados de la cadena s.

81.4 Funciones de entrada y salida de caracteres

int fgete(FILE *stream)


fqete regresa el siguiente carcter de stream corno unsigned ehar (convertido a un
int), o EOF si se encontr el fin de archivo o un error.

81 .5 Funciones de entrada y salida directa

size_t fread(void *ptr, size_t size, size_t nobj, FILE *stream)


fread lee de stream en el arreglo ptr hasta nobj objetos de tamao size. fread regresa
el nmero de objetos ledos; esto puede ser menor que el nmero solicitado. Para determinar el st atu s deben utilizar.;;e feot y ferror.
size_t fwrite(eonst void *ptr, size_t size, size_t nobj,
FILE *stream)
fwrite escribe, del arreglo ptr, nobj objetos de tamaos size en stream. Devuelve el
nmero de objclOs es<..:ritolj, que elj menor que nobj en caso de error.

272

BIBLIOTECA ESTANDAR

APENDlCE B

SECClON B3

isalnum(c)
isalpha{c)

81.6 Funciones de colocacin en archivos

int fseek{FILE *strearn, long offset, int originl


fseek fija la posicin en el archivo para el stream; una lectura o escritura posterior tendr acceso a datos que principian en la nueva posicin. Para un archivo binario, la po-

iscntrl(c)
iSdigit(c)
isgraph(c)
islower(c)
isprint(c)
ispunct(c)
isspace(c)

sicion se fija a offset caracteres de origin, el cual puede ser SEEK_SET (principio),

SEEK_CUR (posicin actual), o SEEK_END (fin del archivo). Para un stream de texto,
offset debe ser cero, o un valor regresado por ftell (en tal caso origin debe ser SEEK_
SET.) fseek regresa un valor diferente de cero en caso de error.
long ftell(FILE *stream)
ftell regresa la posicin actual de stream en el archivo, o -lL en caso de error.

void rewind(FILE *stream)


rewind(lp) es equivalente a lseek(lp,OL,SEEK_SET); c1earerr(lp).
int fgetpos(FILE *stream, fpos_t *ptr)
fgetpas graba en ptr la posicin actual de stream, para uso posterior de fsetpas .
El tipo fposJ es adecuado para grabar tales valores. fgetpos regresa un valor diferente
de cero en caso de error.
int fsetpos(FILE *stream, const fpos_t *ptr)
fsetpos coloca stream en la posicin grabada en "'ptr por fgetpas. En caso de error,
fsatpas regresa un valor diferente de cero.

isupper(c)
isxdigit(c)

273

FUNCIONES PARA CADENAS: <STRING.H>

isalpha(c) o isdigit{c) es verdadera


isupper{c) o islower(c) es verdadera
carcter de control
dgito decimal
carcter de impresin excepto espacio
letra minscula
carcter de impresin incluyendo espacio
carcter dc impresin excepto espacio, letra o dgito.
espacio, avance de lnea, nueva lnea, retorno de carro, tabuladar, tabulador vertical
letra mayscula
dgito hexadecimal

En el conjunto de caracteres ASCII de siete bits, los caracteres de impresin son de Ox20
(' ') a Ox7E ('-'); os caracteres de control son de O (NuLl a Ox1F (US) y Ox7F (DEL).
Adems, hay dos funciones que convierten letras:
int tolower(int e)
int toupper(int el

convierte c a minscula
convierte c a mayscula

Si c es una letra mayscula, tolawer(c) regresa la correspondiellte letra minscula; en airo


caso regresa c. Si c es una letra minscula, taupper(e) regresa la correspondiente letra
mayscula; en otro caso regresa c.

B1.7 Funciones de error


Muchas funciones de [a biblioteca activan indicadores de estado cuando ocurre un erIOr
o fin de archivo. Estos indicadores se pueden fijar y probar explcitamente. Adems, la
expresin entera enna (declarada en <errno.h> puede contener un nmero de error que
da informacin adicional acerca del error ms reciente.
void clearerr(FILE *stream)
c1earrer limpia los indicadores de fin de archivo y error para el stream.
int feof(FILE *stream)
feof regresa un valor diferente de cero si est encendido el indicador de fin de archivo
para ~.
int ferror(FILE *stream)
ferrar regresa un valor diferente de cero si est encendido el indicador de error de
stream.
void perror(const char *s)
perror(s) imprime s y un mensaje de error definido por la implantacin, co rrespondiente al entero que est en erma, como si fuera
fprintf(stderr, "%s: %s\n", s, "mensaje de error")
Ver sirerrar en la seccin 83 .

82.

Pruebas de clasificacin de caracteres:

< ctype.h >

El header <ctype.h> declara funciones para la prueba de caracteres. Para cada fun
cin, el argumento es un int cuyo valor debe ser EOF o representable por un unsigned
char, y el valor de retorno es un int. Las funciones regresan diferente de cero (verdadero)
si el argumento e satisface la condicin descrita, y cero si no lo hace.

83.

Funciones para cadenas;

< string.h >

Hay dos grupos de funciones para cadenas definidas en el header <string.h>. Las
primeras tienen nombres que comienzan con str; las segundas tien en nombre.s que comicn
Lall con memo
Excepto para memmave, el camponamiento est indefinido .si la copia
ocurre entre objetos que se traslapan.
En la siguiente tabla, las variables s y t son de tipo char "'; cs y ct son de tipo eanst
ehar "'; n es de tipo sizeJ; y c es un int convertido a char
ehar *strepy(s,ct)

copia la cadena et a la cadena s, incluyendo '\0'; regresa


s.

char *strncpy(s,et,n)
char *strcat(s,ct)
char *strncat{s,ct,n)
int strcmp(cs,etl
int strnemp(cs,et,n)
char *strchr(cs,c)
char *strrchr(es,c)

"

copia hasta n caracteres de la cadena ct a s; regresa Rellena con '\0' si ct tiene menos de n caracteres.
concatena la cadena ct al final de la cadena s; regresa s;
concate na hasta n caracteres de la cadena et a la cadena
s, terminando con ,\0'; regresa S.
compara la cadena es con la cadena ct; regresa < O si
cs<ct, O si es==ct, o >0 if cs>ct.
compara hasta n caracteres de la cadena cs con la cadena
et; regresa <O if es<ct, O si cs==ct, o >0 si es>ct.
regresa un apumador a la primera ocurrencia de e en es,
o NULL si no est presente.
regresa un apuntador a la ltima ocurrencia de e en es, o
NULL si no est presente.

274

size t strspn(cs,et)
size t strcspn(es,et)
ehar *strpbrk(cs,et)

char *strstr(es,ct)
size_t strlen(cs)
ehar *strerror(n)
char *strtok(s,et)

SECClON 85

APENDICE B

BIBLIOTECA ESTANDAR

int merncrnp{es,ct,n)
void *mernchr(cs,c,n)

void *memset(s,e,n)

84 .

275

un double. Si el resultado se desbo rda, la funcin regresa HUGE_ VAL COn el ~ i g n()
corre;to, yermo se hace ERANGE. Si el resultado e::; tan pequei'io que no ."e puede representar (underflow), la funcin regresa cero; el que errno sea fijado a ERRANGE depende
de la implanta;in.
En la tabla siguiente, x y y son de tipo double, n es un int, y todas las funciones regresan double. Los ngulos para las runciones trigonomtricas estn representados en
radianes.

regresa la longitud del prefijo de es que consiste en los ca.


racteTes en et.
regresa la longitud del prefijo de es que consiste en los caracteres que no estn en et.
reg resa un apuntador a la primera ocurrencia en la cadena
es de cualquier ca rcter de la cadena el, o NULL si ningu_

no est presente.
regresa un apuntador a la primera oc urrencia de la cadena
et en cs, o NULL si no est presente.
regresa la longitud d e cs.

sin{ x )
eos(x)
tan(x)
asin( x )
acos(x)
atan(x)
atan2(y, x )
sinh(x)
cosh( x)
tanh ( x )
exp(x)
log(x)
log10(x)
pow( x,y)

regresa un apuntador a una cadena definida por la implantacin, corres pondiente al erro r n .
strlok busca en s tokens delimitados por ca racteres de el;
ve r abajo.

Una 'ic;ucncia de llamadas a strtok(s,ct) divide s en tokens. cada uno delimitado por un
caraclcr de et. La primer ll amada en la secuencia do tiene una s no NULL. Encuentra el
primer token en s que consiste en ca racteres que no estn en ct; termina al sob reescribir el
:-iguicntc carcter de s con ,\0' y regresa un apuntado r all oken. Cada llamada subsigu iente, indicada por un va lor NULL de s, regresa elloken sig ui ente, buscando jus{O a partir del
rinal del anterior. strtok regresa NULL cuando ya no encue ntra tokens. La cadena et
plJede ser direrclltc en cada llamada.
La intencin de las funciones mem ... es manipular objetos co mo arreglos de caracteres; la intencin es lograr una interfaz para rutinas eficientes. En la sigu ient e tabla, s y t
son de tipo void *; es y ct son de tipo eonst void *; n es de tipo sizeJ; y e es un int co nvertido a unsigned ehar.

void *rnerncpy(s,et,n}
void *rnernmove(s,ct,n)

FUNCIONES DE UTI LER IA: <STDLl 8 .H >

seno de x
coseno de x
tangente de x
sin-' (x) en el rango [-,,12 . ,,/21. x E [-l . 11.
cos-' (xl en el rango [o. ,,1. x E [-1 , 11.
tan -' (x) en el rango [-,,12, ,,121.
tan -' (y/ x) en el rango [-". ,,1.
seno hiperblico de x
coseno hiperblico de x
tangente hiperblica de x
funcin exponencial eX
logaritmo natural In(x), x>O.
logaritmo base 10 loglO (x), x> O.
xY Ocurre un error de dominio si x-O y y :s;: O, o si x< O y Y no es un entero .
x ;;'0.
menor entero no menor que x, co mo double.
mayor entero no mayor que x, como double .
valor absoluto Ix I

E.

sq rt(x)
ee il ( x)
floo r(x )
fabs( x )
lde xp(x,n )
x'21'1
fre xp(x, int *exp)
divide x en una rraccin norma li zada dentro del intervalo [1 /2, 1], que
se regresa, y una potencia de 2, que se almacena en exp. Si x es cero,
ambas partes del resultado son ce ro.
mod f ( x, double *ip)
divide x en parte entera y fraccionaria, cada una con el mi\mo \igno
que x. Almacena la part e en tera en *ip, y regresa la parte fraccionaria.
residuo de punto flotante de x/y, con el mi smo signo que x. Si y es cero ,
fmod (x, y)
el resultado est definido por la implantacin .

copia n caracteres de et a s, Y regresa s.


lo mismo que memcpy excepto que funciona aun si los
objetos se traslapan.
compara los primeros n ca racteres de es con et; regresa
lo mi smo que stremp.
regresa un apuntador a la primera ocurrencia del carcter
e en es, o NULL si no est presente entre los primeros n
caracteres.
co loca el caracter e en los pr imeros n caracteres de s, regresa s.

Funciones matemticas: <m ath.h >

El header < math.h> declara funciones y macros matemticas.


Las macros EDOM y ERANGE (que se encuCnlran en < ermo .h son constantes enteras co n valor direrente de cero que se usan para senalar errores de dominio y de rango pa~
ra las funciones; HUGE_ VAL es un valor positivo double. Un error de dominio oc urre SI
un argumento est ruera del dominio sobre el que est definida la runcin. En caso de un
error de dominio, armo lOma el valor de EDOM; el valor regresado depende de la implantaci n. Un error de rango ocurre si el resultad o de la funcin no se puede representar como

85 .

Funciones de utilera: < stdlib.h >

El f:eader <stdlib .h > declara funciones para conversi n numrica, asignacin de memoria y tareas se mejantes.
doub le atof (eonst char *s )
atof conviene s a double; es equivalente a strtod(s, (char ** ) NULL ).

-"""'"

276

BIBLIOTECA ESTANDAR

SECCION

APENDlCE B

ss

FUNCIONES PARA UTILERIA: <STDLlS.H>

277

int atoi(const char *s)


convierte s a int; es equivalente a (int)strtol(s, (char**)NULL, 10).

void free(void *p)


free desasigna el espacio apuntado por~; si p es NULL. no hace nada. pdebe ser un
apuntador a un espacio previamente asignado por callee, malloc, O realloe.

long atol(const char *s)


convierte s a long; es equivalente a strtol(s, (char**)NULL, 10).

void abort(void)
abort llea . . ioll<l q lit.: l'l plllglalTl.l tt.:III1IIH.' lIllrlllall1lellle. l't111l0 cun raise( SIGABRT).

double strtod(const char *5, char **endp)

void exit(int status)


exit ocasiona la terminacin normal del programa. Las funciones atexit se llaman en
orden inverso del registrado, los archivos abiertos se vacan, los flujos abiertos se
liL'l'Ian. ~ d l'lllllrol "'c rC!!I'" ... a al ("Illorno. COIllO "'l" It.:gl'l.....t.: status al entorne dqll'lldc dl'
la Illlplanlalin. pero ccro im.lila cuando la ICIIIlllJaeln ticllt.: c\ilo. St' put.:dclI lIliii/:u
tallll,il"n lo . . \alore ... EXIT_SUCCESS y EXIT_FAILURE

strtod ":011\ CriC el prefijo de s a doubIe. ignorando el espado en blanco inial; almacena en -endp un apuntador a l' ualquier !) ufij o no cubierto salvo cua nd o endp es NULL.
Si la respuesta se desborda, regresa HUGE_ VAL con el signo apropiado; si el resullado
ruera tan pequeo que no se pueda representar (ulldelflow), se regresa I..'ero. En cualquier ca.,o errno 10m3 el va lor ERANGE.

int atexit(void (*fcn)(void


atexit registra a la funcin fen para que sea llamada cuando el programa termina norIllalmclltc; rt.:grl' . . a UI1 \alor diferenle c.!t.: ccro cuando no I,i..' pucdt.: haccr el rc!!i . . no.

long strtol(const char *s, char .*endp, int base)


strtol convierte el prefijo de s a long, ignorando los espacios en blanco iniciales; almacena Ull apuntador en *endp a (ualquier sufijo no (ubierlo a menos de que endp sea
NULL. Si base est entre 2 y 36, la (onversin se realiza suponiendo que la entrada es
esnila en esa base. Si [basel es cero, la base es 8, 10, o 16; los O init:iales irnpli(an o([al,
mienl ra ~ que Ox y OX, he,xadcmal. Las lel ras, ya sean maysculas o minsculas, representa n dgitos desde 10 hasta basc-I; cn base 16 se permite init:iar (on Oxo OX . Si el re~1Ilt3do se desborda, se regresa LONG_MAX O LONG_MIN, dependiendo del signo
del resultado, y errno se hace ERANGE.

int system(const char *s)


system pasa la cadena s al entorno para que se ejecute. Si s es NULL, system regresa
un \alo!' difcrclll(' dc cero si hay un procc . . ador de I'!cIlC .... Sis 110 e . . NULL, el valor
IL'gl'C . . adn dClL'lldc de la implantaL'in,
char *getenv(const char *name)
getenv rq:rL' . . a 1,1 eadi.'11<1 dclL'l1ll..lrllO a~i.K'lada
tI!t:1, dependen dc la impl alllulill.

unsigned long strtoul(eonst ehar *5, char **endp, int base)


strteul es lo mismo que strtol excepto que el resultado es unsigned long y el valor de
error es ULONG_MAX.

1,:011

name, O NULL "i

110

ni . . II... Ll'''' dc-

void *bseareh(eonst void .key, eonst void *base,


size_t n, size_t size,
int (*cmp) (eonst void *keyval, const void *daturo))
bsearch busca en base[O] ... base[n-l] un elemento que coincida con *key. La funcin
emp debe regresar un valor negativo si su primer argumento (la llave de bsqueda) es
menor que su segundo (una entrada en la tabla), cero si es igual, y positivo para mayor.
Los elementos del arreglo base deben estar en orden ascendente. bsearch regresa un
apuntador al elemento coincidente, o NULL si no existe.

int rand(void)
ra1.d devuelve un entero pseudoaleatorio en el rango de O a RAND_MAX, que es al menos 32767.
void srand(unsigned int seed)
srand utiliza seed como semilla para una nueva secuencia de nmeros pseudoaleatorios. La semilla inicial es 1.

void qsort(void *base. size_t n, size_t size,


int (*cmp) (const void ., const void *
qsort l'l,hifica CI1 orden a~I.:ClldcnIL' un 3ITc!!lobase[O] ... base[n-l] de objetos de tamao size. La funcin de comparacin emp es como en bseareh.

void *calloc(size_t nObj, size t size)


callee devuelve un apuntador al espacio para un arreglo de nebj objetos, cada uno de
tamao size, o NULL si la solicitud no puede satisfacerse. El espacio se inicializa a
bytes con cero.

int abs(int n)
abs regresa el valor absoluto de su argumenlO int.
long labs(long n)
labs regresa el valor absoluto de su argumento long.

void *malloc(size t size)


mallee regresa un apuntador al espacio para un objeto de tamao size, o NULL si la
solicitud no se puede satisfacer. El espacio no se inicializa.

div_t div(int nuro, int denom)


div calcula el cociente y el residuo de num/denom. Los resultados se almacenan en los
miembros de tipo int quet y rem de una estructura de tipo di v_t.

void *realloc(void .p, size_t size)


realioe cambia el tamao del objeto apuntado por p a size. El contenido no cambiar
si no hasla el mnimo de los tamaos viej o y nu evo. Si el nue vo tamailo es mayor, el espacio nuevo no se inicializa. realloeregresa un apumador al nuevo espacio, o NULL si
la so li citud no se puede satisfacer; en tal caso *P no cambia,

ldiv_t ldiv(long num, long denom)


ldiv calcula el cociente y el residuo de num/denem . Los resultados son almacenados
en los miembros de tipo long quot y rem de una estructura de tipo ldi v_t.

......,j

278

BIBLIOTECA ESTANDAR

B6.

Diagnsticos: <assert.h>

/ * llega aqu por una llamada de longjmp */


void longjmp(jmp_buf env. int val)
longjmp re~lablecl' I'll'slado guardado por la llamada lll . . rCCil'lllL" <.k setjmp, lllili/ando 1..1 inforrna\.in almacL'nada en env; la ejcnH,:ion cOlltina C0Il10., la 1"ul,cln setjmp
.,610 hubil'ra ~ido llamada y hubiera regrc.,ado un \alor dL" \ al di fen::nlL' dl' cl.'ro. La
fUllL"in que I..'olllienc setjmp llO debe habcr terminado. 1.0" objeto\ accc.,ibk'., tlL'111.'11 1m
valores quc cnan en el mOlllcnto en quc longjmp fue llamada: 10'1 \ alo["(:'I 110 'Io n guardado,> por setjmp.

B9.

void (*signal(int sig, void (*handler)(int)) )(int)


signal determina cmo se manejarn las sea les subsiguientes. Si handler es SIG_DFL,
se usa el comportamiento predefinido por la implanlacin; si es SIG_IGN, a seal se ignora; de Otra manera, se llamar a la fUIIL"ion apuntada por handler,con lo,> argumento., dcl
tipo de la seal. Las seales vlidas incluyen
SIGABRT
SIGFPE
SIGILL
SIGINT
SIGSEGV
SIGTERM

tl'fminaL"in anormal, p. ej .. dc::'<.k


error aritmtico, p. ej., di"i.,in entre cero o '1obrcflujo
imagen de fundn ilega l, p. ej., in\lrm:dn ilegal
atcndn ilegal al almacenamienlo; p. cj., aL"Cc,>o fuera de 10'1 limitc,>
acceso ilegal al almacenamiento, vgr., acceso fuera de los lmites
de memoria
::,ol1citud de tcrminaL"in emiada a t..'\te programa

signal regresa el valor previo de handler para la sea l especfica, o SIG..:::ERR si ocurre un
error.
Cuando ocurre ...ubs('(uentementc una .,ci''1al sig, la senal <,c regresa a '1U l:Olllport<.llllict\to
predct<:rminado; luego se llama a la funcin manejadora de la '1eal, corno si se ('jecutara
(*handler)(sig). Si el manejador regre.,a. la L'j~cucin ,:ontilluar donde '>L" l'ncontraba
("llando ocurri la seal.
El estado inicial de las sea les est definido por la implantacin.

Saltos no locales: <setjmp.h>

Las declaraciones que estn en < setjmp.h > proporcionan una forma de evitar la secuencia norma l de llamada y regreso de funciones, tpicamente para permitir un regreso inmediato de una llamada a una fundn profundamente anidada.

int raise(int sig)


raise enva la 'ieal sig al programa; r~gre.,a un \'alor diferente de cero L"uamlo no tiene
(',ito.

int setjmp(jmp_buf env)


La maL"ro setjmp guarda la informacin del eSlado que se tiene en env para ser usada
por longjmp. El retorno es L"ero desde una llamada direcIade setjmp y diferente de cero
de'lde una llamada !lubsiguiellte a lonqjmp. Slo puede ocurrir una llamada a setjmp
dentro de ciertos contextos, bsicamente la prueba de if, switch, y ciclos, y slo en expresiones de relacin simples.
if

Seales: <signal.h>

El /mr!er <signal. h> de facilidades para manejar condiciones excepcionales que


aparecen durante la ejecucin, tal como una se nal de interrupcin de una fuente externa
o un error en la ejecucin.

Listas de argumentos variables: <stdarg.h>

El Ileader < stdarg.h > proporciona recursos para recorrer una lista de argumentos
de funcin de, tamao y tipo desconocido.
Supngase que lastarg es el ltimo parmetro nombrado de una funcin f con un nmero variable de argumentos. EntO'!1ces se declara dentro de f una variable ap de tipo
va_list que apuntar a L"ada argumeIllo en orden:
va_list ap;
ap se debe inicializar una vez con la macro va)ist antes de tener acceso a cualquier argumento no nombrado:
va_start(va_list ap, lastarg);
Despus de eso, cada ejecucin de la macro va_arg producir un valor que tiene el tipo
y valor del siguiente argumento no nombrado, y modificar tambin ap de modo que
el prximo uso de va_arg devuelva el argumento siguiente:
type va_arg(va_list ap, type);
La macro
void va_end(va_list ap);
se debe llamar una vez despues de que han sido procesados los argumentos, pero ames de
haber salido de f.

B8.

279

else

La macro assert es usada para agregar diagnsticos a los programas :


void assert( int expresin)
Si expresin es cero cuando
assert (expresin)
se ejecul3, la macro assert imprimir en stderr un mensaje, como
Assertion failed: expresin, file filename, line nnn
Despus llama a abor! para terminar la ejecucin. El archivo fuente filename y el nmero
de lnea viene de las macros --,FILE -- y -- LINE -- del preprocesador.
Si NDEBUG eSla definido cuando se induy <assert. h> se ignora la macro assert.

B7.

SEA LES: <SIGNAL. H >

SECC10N 89

APENDICE B

B10.

Funciones de fecha y hora <time.h>

l:l /eader <time, h> declara los tipos y funciones para manipulacin de fecha y
hora. Algunas funciones procesa n la hora (oca(, que puede diferir de la del calendario,
por ejemplo. debido a la zona horaria. clockJ y timeJ son tipos aritmticos que representan tiempos. y struct tm mantiene las cOlT'ponentes de un calendario:

(setjmp(env) == O)
/* llega aqu en una llamada directa * /

..J

280

APENDICE B

BIBLIOTECA ESTANDAR

int
int
int
int
int
int
int
int

tm_sec;
tm_min;
tm_hour;
tm_mday;
tm_mon;
tm_year;
tm_wdaYi
tm_yday;

int trn_isdst ;

LIMITES DEFI NIDOS EN LA IMPLA NTACION

SECClON 811

segundos despus del minuto (0,59)


minutos despus de la hora (0,59)

piados del ento rno. En s no se colocan ms de smax caracteres. strltime regresa


el nmero de caracteres, excluyendo el '\0', o cero si fuer on producidos ms de smax

horas desde la medianoche (0,23)

caracteres.

dia del mes (1,31)


meses desde enero (0,11)
aos desde 1900
das desde el domingo (0,6)
dias desde enero I (0,365)
bandera de Day/igJ( Savin;i Tillle

tm)sdst C~ posiliva \i Dayligllf S(fI'ing Time esta en efecto,


si la inrormacin no est disponible.

l'Cro

%a
%A

%b
%B

%c
%d

si no lo est y negativa

%R

%1
%j

clock_t clock(void)
clock regresa el tiempo de procesador empleado por el programa desde el inicio de su
ejecucin. o -1 si no est disponible. clock( ) /CLK_ TCK es el tiempo en segundos.

%ro

%M

%p
%S

time_t time(time_t *tp)

%U

time regresa la fecha y hora actual del calendario. o -1 si no est disponible. Si tpno
es NULL, el valor de retorno tambin es asignado a *tp.

%w

double difftime(time_t time2, time_t time1)


difftime regresa time2time1 expresado en segundos.

%W

time_t mktime(struct tm *tp)


mktime convierte la fecha y hora local de la estructura *tp a fecha y hora del calen da
rio en la misma representacin utilizada por time. Los componentes tendrn valores
dentro de los rangos mostrados. mktime regresa la fecha y hora del calendari o, 0- 1
si no puede ser representada.
Las siguientes cuatro fu nciones regresan apuntadores a objetos estticos que pueden
ser sob rescr itos por otras llamadas.

%x

char *asctime(const struct tm *tp)


asctime convierte la hora de la estructura *tp a una <.:adena de la forma
Sun Jan

281

3 15:14: 13 1988\n\0

char *ctime(const time_t *tp)


ctime ;,.'ominle la hora del \"alcndario *tp a hora local; es equi\alenlC' a
asctime(localtime(tp))
struct tm *gmtime(const time_t *tp)
gmtime <.:omicne la hora del <.:alendario *tp a Hora Coordinada Uni\ersal (Coordi
l/Oled Unirersa/ Time --UTC). Regresa NULL si UTC no est disponible. El nomb re
gmtime tiene signifi<.:ado hislori<.:o.
struct tm *localtime(const time_t *tp )
localhme l'oll\ienc la hora del ;.:alcndario *tp a hora lo<.:al.
size_t strftime(char *s, size_t smax, const char *fmt,
const struct tm *tp )
strftime da formaw a la hora y fecha de *tp en s de acuerdo con fmt, que es anlogo
al formato printL Los caracteres ordinarios (incluyendo la terminacin '\0') se copian
dentro de 5. Cada %c se reemplaza como se describe abajo, utilizando los valores apro

nombre abreviado del da de la semana.


nombre completo de la semana.
nombre abreviado del mes.
nombre completo del mes.
representacin local de fecha y hora.
dia del mes (O 1-31).
hora (reloj de 24 horas) (00-23L
hora (reloj de 12 horas) (01-12).
dia del ao (00 1-366L
mes (01-12).
minuto (00-59).
equivalencia local de AM o PM.
segundos (00-59).
nmerO de semana del ao (domingo es el primer da de la semana)
(00-53).
da de la semana (0-6, domingo es O).
nmero de semana del afio (lunes es el primer da de la semana)

%x
%y
%y

%Z
%%

811_

(00-53).
representacin local de la fecha.
representacin local de la hora.
ao sin el siglo (00-99).
ao con el siglo.
nombre de la zona horaria, si existe.
%.

Lmites definidos en la implantacin: <limlts_h> Y <float.h>

El header < limits.h> defi ne cons tantes para el tamao de los tipos ent eros. Los va
lores most rados so n magnitudes mnimas aceptables; se pueden emplear va lores mayores.
CHAR_BIT
CRAR_MAX
CHAR_MIN
INT_MAX
INT_MIN
LONG_MAX
LONG_MIN
SCHAR_MAX
SCHAR_MIN
SHRT_MAX
SRRT_MIN
UCHAR_MAX
UINT_MAX

8
UCHAR _ MAX or
SCHAR_MAX
O or SCHAR_MIN
+32767
-32767
+2147483647L
-2147483647L
+127
-127
+32767
-32767
255U
65535U

bits en un char
valor
valor
valor
valor
valor
valor
valor
valor
valor
valor
valor
valor

mximo de char
mnimo de char
mximo de int
mnimo de int
mximo de long
mnimo d~ long
mximo de signed char
mnimo de signed char
mximo de short
mnimo de short
mximo de unsigned char
mximo de uDsigned int

282

BIBLIOTECA EST ANDAR

ULONG_MAX
USHRT_MAX

APENDICE B

4294967295UL
65535U

valor mximo de unsigned long


valor mximo de unsigned short

Lo ... Jlolllbrc~ de la labia siguielllc, ,>ubl'onjullIo de <floaLh> , son ('onslanlCS relacionada ... L'()Jl la arilml:il'a de pUllIO flotante. Cuando SC' da un valor, representa la lllagnillld
mnillla parn la l'anlidad corrc.,pondielllc. Cada implantacin define 1m valores apropiado ....

FLT_RADIX
FLT_ROUNDS
FLT_DIG
FLT_EPSlLON
FLT_MANT_DIG
FLT_MAX
FLT_MAX_EXP
FLT_MIN
FLT_MIN_EXP
DBL_DIG
DBL_EPSILON
DBL_MANT_DIG
DBL_MAX
DBL_MAX_EXP
DBL_MIN
DBL_MIN_EXP

6
1E-S
1E+37

1E-37

10
1E-9
1E+37
1E- 3 7

radical de la representa,:in exponencial, p. ej., 2, 16

APENDICE C:

modo de redondeo de punto flotante para adicin


dgitos decimales de precisin
menor nmero x tal que 1.0 + x ~ 1.0
nmero de dgitos de base FLT _RADIX en la mantisa
mximo nmero de punto flotante
mximo n tal que FLT _RADIXn-1 es representable
mnimo nmero normalizado de punto flotante
mnimo n tal que Ion es un nmero normalizado
dgitos decimales de precisin
menor nmero x tal que 1.0 + x ~ 1.0
nmero de dgitos de base FLT _RADIX en la mantisa
mximo nmero double de punto flotante
mximo n tal que FLT _RADIXIl-l cs representable
mnimo nmero double normalizado de punto flotante
mnimo n tal que IOn es un nmero normalizado

Resumen de modificaciones

Desde la publicacin de la primera edicin de este libro, la definicin del lenguaje e


ha sufrido modificaciones. Casi todas fueron extensiones al lenguaje original, y fueron diseadas cuidadosamente para permanecer compatibles con la prctica existente; algunas
repararon ambigedades de la descripcin original, y otras representan modificaciones de
la prctica existente. Muchas de las nuevas caractersticas se anunciaron en los documentos
que acompaan a los compiladores disponibles de AT&T, y posteriormente se han adoptado por otros proveedores de compiladores del lenguaje C. Recientemente, el comit ANSI
incorpor ms de esos cambios estandarizando el lenguaje, y tambin introdujo otras modificaciones significativas. Su reporte fue en parte anticipado por algunos compiladores
comerciales an antes de la publicacin del estndar formal.
Este apndice resume las diferencias entre el lenguaje definido por la primera edicin
de este libro, y lo esperado como la definicin del estndar final. Trata solamente al lenguaje en si, no a su entorno ni a su biblioteca; aunque esas son partes importantes del
estndar, hay poco con qu compararlas, puesto que en la primera edicin no se intent
definirlas.
El preprocesamiento est definido ms cuidadosamente en el Estndar que en la primera
edicin, y est extendido: est explcitamete basado en IOkens (smbolos); existen nuevos
operadores para la concatenacin de tokens (##) y creacin de cadenas (#); hay nuvas
lneas de control como #elif y #pragma; est explcitamente permitida la redec1aracin
,de macros por la misma secuencia de tokens; ya no se reemplazan los parmetros que
estn dentro de cadenas. La separacin de lneas por \ est permitida en cualquier lugar,
no slo en definiciones de cadenas y macros. Vase AI2.
El significado mnimo el ms pequeo de todos los identificadores internos sc incremento a 31 caracteres; permitido para identificadores con liga externo permanece en 6
letras, sin importar sin son maysculas o m1sculas (muchas implantaciones proporcionan ms).
Las secuencias trigrficas introducidas por ?? permiten la representacin de caracteres
que no se encuentran en algunos conjuntos. Estn definidos los escapes para #\ A[ ]{ };".
Vase A12.1. Obsrvese que la introduccin 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 carctcr y cadenas literales. El efecto de seguir \ con un carcter que no sea parte de una secuencia
de escape aprobada est indefinido. Vase A2.5.2.
283
~

284

RESUMEN DE MODIFICACIONES

APENDICE

RESU MEN DE MODIFICACIONES

El trivial cambio favorito de todos: 8 y 9 no son dgitos octales.


El estndar introduce un conjunto ms grande de sufijos para hacer explcito el tipo de

APEND ICE C

285

El tipo de una expresin de corrimiento es el del operando de la izquierda; el operando


de la derecha no puede promover el resultado. Vase A7.8.
El e!:.tillldar legali7a la creacin de un apuntador justo mas all del fin de un arreglo y
permite aritmti\.~a y relaciones sob rc l; Vase A7.7.
El estndar introduce (tomndolo de C++) la noc in de declaracin de una funt.:in
prototipo que incorpora los tipos de los parmetro!:., e incluye un reconot.:imiento
explcito de funciones con listas variables de argumentos, junto con una forma aproba
da de tratarlas. Vase A7.3.2. A8.6.3 Y 87 . El estilo antiguo todava se acepta, con
rest ricciones.
Las declaraciones vacas, que no tienen declaradores y no declaran al menos a una
estructura, unin o enumeracin, estn prohibidas por el estndar. Por otro lado, una
declaracin con slo un rtulo de estructura o de unin, redeclara ese rtulo aun si
fue declarado en un alcance ms exte rno .
Estn prohibidas las declaraciones externas sin ningn especificador o calificador (con
s lo el declarador).
Algunas implantaciones, cuando examinan una declaracin cxlern en un bloque l11~
interno, podan exportar la declaracin al resto del archivo. El eSlndar hace claro que
el alcance de tal declaracin es slo el bloque.
El alcance de los parmetros se introduce en la proposicin com puesta de una funcin,
as que las declaraciones de variables en el nivel superior de la funcin no pueden ocultar los parmetros.
Los espacios de nombre de los identificadores son algo diferentes. El e'\tnda!" pone todos los rtulos en un espacio ~encillo de nombre, y tambin introduce un e~pacio ~epa
rada de nombres para etiquetas; vase A 11.1. Los nombres de miembro\ tambin c\t<in asoc iados con la estructura o unin de la que so n parte (e'oto ha sido prctica comn
por algn tie mpo).
Las uniones se pueden inicializar; el inicializador se refiere al primer miembro.
Las estructuras, uniones y arreglos automticos se puede inicializar, aunque en una forma restringida.
Los a rreglos de carac teres COIl tamao explicito se pueden inicializar con una cadena lileral con exactamente la misma cantidad dc caracteres (el \0 ,'l e excluye calladamcntc,.
La expresin dc control y las etiquetas de las alternativas de un swith, pueden tener
cualquier tipo entero.

constantes: U o L para enteros, F o L para flotantes. Tambin afina las reglas para el
uso de constantes Sill su fijo (A2.5).
Las cadenas literales adyacentes se concatenan.
Existe una notacin para cadenas literales amplias de ca racteres y constan tes de carcler. Vase A2.6.

Los caracteres, as como otros tipos, pueden ser explcitamente declarados para tener
o no signo, utilizando las palabras reservadas signed o unsiqned. Se retir la locucin
long JIoat como un si nnimo para double, pero long dauble puede ser milizada para
declarar una cantidad r!otame de precisin extra.
Por algn tiempo, el tipo unsigned char ha estado disponible. El estndar introduce
la palabra reservada signed para hacer explcito el signo para char y otros objetos
en tcros.
Por algunos aos, el tipo void ha estado disponible en algunas implantaciones. El estndar introduce el uso del tipo void * como un tipo de apuntador genrico; anteriormente
char * dese mpe este papel. Al mismo tiempo, se crearon reglas explcitas contra la
me7cla de apuntadores y enteros, y de apuntadores de aiferente tipo, sin el uso de operadores cast.
El estnda r fija mnimos explcitos en los rangos de tipos aritmticos y delega a los archivos lIeader limits.h> y <float.h el dar las caractersticas de cada implantacin
particular.
Las enumeraciones son algo nuevo desde la primera edicin de este libro.
El estndar adopta de C++ la nocin de cal ifi cador de tipo, por ejemplo, const
(A8.2).
Las cadenas ya no son modificables. por lo que pueden situarse en memoria de slo
lectura.
Se cambiaron las "convenciones aritmticas usuales". esencialmente de "para enteros, unsigned siempre gana; para punto flotante . siempre use double" a "promueva
al tipo ms pequeo de suficiente capacidad". Vase A6.5.
Los antiguos operadores de asignacin como = + realmente desaparecieron. Tambin, los operadores de asignacin son ahora tokens se ncillos; en la primera edicin
fueron parejas y se podan separar por espacio en blanco.
Se cance l la licencia del compilador para tratar a los operadores matemticamente
asociativos como computacional mente asociativos.
Se introdujo un operador unario + por simetra con el - unario.
Un apu ntador a una funcin se puede utilizar como un designador de funcin sin un
operador * explcito. Vase A7.3.2.
Las C'strw,:turas se pueden asignar, pasar a funciones, y regresa r por funciones.
Est permitido aplicar el operador "direccin de" a arreglos, y el resultado es un
apuntador al arreglo.
El operador sizeof, en la primera edicin, daba el tipo int; posteriormente, muchas
implantadones lo hicieron unsigned. El estndar hace el tipo explcitamente depen
dicJ1[e de la implantacin pero requiere que el tipo sizeJ sea definido en un lIeader
estndar stddeLh . Un cambio semejante ocurre en el tipo (ptrdiffJ) de la diferencia entre apuntadores. Vase A7.4.8 y A7.7.
El operador & ("direccin de") no se puede aplicar a un objeto declarado register.
aun si la implantacin decide no mantener al objeto en un registro.

Indice
<assert.h> 278
, carcter apstrofo 21, 41-43, 213
\ f carcter avance de hoja (forrnfeed) 42, 213
" carcter comillas 8, 21, 43, 214
\a carcter de alarma 42, 213
\b carcter de retroceso 8, 42, 213
\1 carcter de tabulacin 8,11,42,213
\v caracter de tabulacin vertical 42, 213
\ \carcter diagonal invertida (antidiagonal) 8, 42
\n carcter nueva lnea 7, 16,21,41.42,213,265
\0 carcter nulo 32, 42, 213
\r carcter retorno de carro 42, 213
,carcter subguin 39, 212, 265
Ox .. conStante hexadecimal 41, 213
O .. constante oClal41, 213
#define 15, 98-99, 254
#define con argumentos 99
#cleline de varias lneas 98
#deline V5. enum 43 -44, 165
#eI56, # eHf 101, 256
#endif 101
#error 258
': expresin condicional 57, 230
#iI 101, 151, 256
#ifdel 101, 256

#ifndef 101, 256


#include 35, 98, 168, 256
)OFBF, )OLBF, JONBF 267
#line 257
{j llaves 7, JO, 61, 93
& operador AND para bits 53, 2282:29
&& operador AND lgico 22, 46, 54, 228229
-> operador apuntador a estructura 146, 222
, operador coma 69, 231
?: operador condicional 57, 230
+ operador de adicin 4546, 226227
= operador de asignacin 18,46, 230
+ = operador de asignacin 55
~ operador de complemento a uno 54, 224225
> > operador de corrimiento a la derecha 54. 227-2211
< < operador de corrimiento a la izquierda 54,
227228
-- operador de decremento IlJ, 5 1, J 18, 224
! = operador de desigualdad 17,45,228
& operador de direccin 104, 225

I operador de divisin 11, 4546, 226


= = operador de igualdad 20-21,4546, 228
+ + operador de incremento 19, 51, 118, 224
.. operador de indireccin 104, 225
.. operador de multiplicacin 454(. 22h227
! operador de negacin lgica 224225
- operador de substraccin 4546, 226
# operador del preprocesador 100, 254
## operador del preprocesador 100, 254
+ operador ms unario 224-225
> = operador mayor o igual que 46, 228
> operador mayor que 46. 22722x
< = operador menor o igual que 46, 228
< operador menor que -1.6, 227 22X
- operador menos unario 224225
. operador miembro de estructl"lra [42, 222
% operador mdulo 45, 226
" operador OR exclusivo para bit, 53, 229
: operador OR inclusivo para bits 53, 229
: : operador OR lgico 22, 46, 54, 229
#pragma 258
\x secuencia de escape hexadecimal 42, 213
#undef 99100, 190, 254
+ + y -- post fijos 51, 116
+ +y -- prefijos 51, lIS
a.out 6, 79
acceso a un archivo 176, IX7 IXX, IYA, 2M,
agregar a un archivo 177, 193, 266
alcance (seope) 215, 251252
alcance de automlicas 89, 252
alcance de externas 89, 252
.. , declaracin 172 , 223
alcance de una etiqueta 72, 246, 252253
alcance lxico 251
alineacin de campos de bits 165166, 236
alinr;acin por unin 205
ambigedad en ifelse 62, 2-1.7, 2~Y
American National Standard, Imitute (AN')I) ix,
2, 211
apertura de un archivo 176177, 187188, 190
apuntador a funcin 1JI)-I J 1, 162, 222
apuntador a una estructura 1S1
apuntador a una funcin 130131, 162,222
a[)Unl<1uor gCIlCTI":O l'hl\{' <J[hJ!llau\lr

287
~

vOld-

288

EL LENGUAJ [: DEL PROGRAMADOR

apuntador nulo 113,219


apuntador para archivo 177, 194,226
apuntador void 103, 114, 132-133,219-220
apuntador vs. arreglo 108- 110. 115-116, 124-125
apuntadores y subndices 108, 109, 240
rbol binario 154
rbol de descripcin gramatical 135
archivo encabezado 35, 90-91
archivo para inclusin dir.h 202
archivo para inclusin fcntl.h 191
archivo para inclusin stat.h 199-200
archivo para inclusin syscalls.h 189
nrdli\Ll para in..:!u,in Iypes.h 200, 202
arg\l\m:nlo apuntador 110-1\1
argumento de funcin 27, 223
arclI111C'llln ('fe~'li\'o I'("ase dcfinidn de argumento
ar~lIl11('n!O~ de un programa ('hu' linca de
l'olllanuo, como argulnrnlos

argumentos lomados de una linea de comandos


126-130
aritmtica de apuntadores 104- 105, 108-109, 111-114,
129, 152-153,226-227
artill1l'lica de dirl'cciolles \'ase ritmllca de
apul1tador
aritmtica ilegal de apuntadores 113- 114, 152-153,227
aritmtica usual de conversiones 47, 218
arreglo bidimensional 122, 123, 243
arreglo de apuntadores 118-119
arreglo de caracteres 21. 30. 115- 116
arreglo de estructuras 147, 162-163
arrl'glo l11ull idimensional 122, 239-240
al'reglo \s _ apunlador 108-111
asignacin mltiple 22
asignador de almacenamiento 157, 204-208
asociati vdad de los operadores 57, 220

bloque rase proposkin L'ompuesla

BUFSIZ 267
cailella de caracteres Fase cOllstante l'adena
cadcHa literal I'/!ase COIlSlal\le cadena
cadena nula 43
cadena vacia 42
clculo de aos bisiestos 45, 122
campo vase Carcter de afarma
carcter de control 273
carcter de impresin 273
carcter signado 15,216
carcter sin signo 49, 216
caracteres espacio en blanco 174, 183-184,269,273
categora de almacenamiento 215
categora de almacenamiento automtica 34, 215
categora de almacenamiento esttica 34, 92, 215
categora de almacenamiento automtica 33, 215
cido rase While, for
ciclo infinito for(;;) 66-67, 99
CLLTCK 280
comando ce 6, 79

EL LENGUAJE DEL PROGRAMADOR C

INDICE

cua[ificador volalile 217, 234


cuenta de argumentos argc 126

comando ls 198
comentario 9, 2[ 1-212, 254
com)aracin de apuntadores 113, 152-153, 207.
228-229
compilacin condicional 100-J01, 256
compilacin de varios archivos 79
compilacin separada 75, 88-89, 251
compilando un programa en C 6, 27
concatenacin de cadenas 43, 100,214-215
concatenacin de smbolos 100, 254
condicin de lmite 20, 71-72
conducto (pipe) 168, 188
conjunto de caracteres 253
conjunto de caracteres ASCII 21, 42, 48, 253, 273
conjunto de caracteres EBCDIC 48
conjunto de caracteres ISO 253
constante cadena 7, 21, 32, 42-43, 110, 115,214-215
constante cadena amplia (wide) 214
constante carcter 21, 41, 213-214
constante carcter amplio (wide) 213-215
constante carcter en octal 41-42
constante de enumeracin 43-44, 101,213-214,238
constante double 41,214
constante entera 13,41,213
constante Ootante 13,41,214
comtante long 41,213
comtante long douhle 41, 214
constante unsigned 41, 213
constante unsigned long 41, 213
constantes 41, 212-213
contenido de <sldio .h> 194
continuacin de lneas 253
convenciones lxica 211
con\ersin 217-220
cOll\crsin %ld 19-20
conversin apuntador-elllero 219-220, 226
comersin carcter-entero 24-25, 47, 218
conversin de apuntadores 157, 219, 227
conversion de argumentos de funcian vease
promocin de un argumento
conversin de fecha 122
conversin de nombre de un arreglo 109-110, 221
conversin de tipo por relum 81, 249
conversin de una funcin 221
conversin de nombre de arreglo 109-110, 221
conversin douhle-float 50--51, 218
conversin entero-apuntador 219, 227
conversin entero-carcter 50
conversin entero-flotante 13, 218
conversin floal-douhle 49, 218
conversin flotante-entero 50, 218
conversin por casi 50--51, 219-220, 226
conversin por asignacin 49, 230
conversin por relum 81, 249
l"onv~rsioncs aritmticas usuale_
s 47,218
creacin de un archivo 177-178, 187-188
CRLF 167, 265
cualificador Consl 45, 217, 234
cualificador de tipo 229-230, 234

decisin con varias a]crnllivas 226-227


declaracin 9, 44-45, 232-242
... declaraci n 172, 223
declaracin de apuntadores 104, 110-111, 239
declaracin de campos de bits 165-166, 235
declaracin de categoras de almacenamiento 232-233
declaracin de funcin 240-241
declaracin de funcin slalic 92
declaracin de tipo 238-239
declaracin de tipos inconsistentes 80
declaracin de un arreglo 24, 122, 239
declaracin de una estructura 142, 234-235
declaracin de unin 162-163, 234-235
declaracin de variable externa 33, 249
dcdaracin externa 249-251
dcdaracin implcita de una funi.:in 29, 81, 222
declaracin inconsistente de tipo 80
declaracin slalic de una funcin 92
declaracin typedef 161, 232, 244
declaracin vs. definicin 35-36, 89
declarador 238-242
declarador abstracto 244
declarador de arreglo 239
declarador de funcin 240
definicin de almacenamiento 232-233
definicin de argumentos 27, 222
definicin de un argumento 27, 222
definicin de un parmetro 27, 222
definicin de una funcin 27, 77, 249
definicin de una macro .254
defini~-in de una variable externa 35 -36, 251
definicin tentativa 251
desbordamiento 46, 221, 274, 279
descriptor de archivo 188
descriplOr gramatlcal recursivo-descendente 136
designativo de funcin 222
direccin de un registro 232
direccin de una variable 30, 104, 224
divisin entera 11, 45
EDOM 274
efeclOS colaterales 59. 99. 220, 223
eficiencia 56, 92, 97-98, 157, 207
ele \'ase propo~icin- U-alsa
eIsa-U 25, 63
encabezado <:ctype,h> 48, 272
encabezado <:ermo.h> 272
encabezado <:floal.h> 41, 281-282
encabezado dimils.h> 41, 281-282
encabezado <: locale.h > 265
encabezado <: malh.h > 49, 79, 274
encabezado <::setjmp.h> 278
encabezado <: signal.h > 279
encabezado <sldarg .h> 172, 192,278
encabezado <:stddef.h> 114,150,265

I NDICE

289

encabezado <sldio.h> 6, 17,98-99,113,


167-168, 265
encabezado <stdlib.h> 157, 275
encabezado <slring.h> 43, 117,273
encabezado <lime.h> 279
entrada del teclado 16, 167, 188-189
entrada estndar 167, 177-178, 188-189
entrada sin uso de buffer 189
entrada usando huffer 189
entrada 'i salida por terminal 16
emrada/salida de caracteres 16, 167
enum vs. #define 44, 165
enumerador 214, 237-238
EOF 17, 168,266
equivalencia de tipos 245
ERANGE 274
armo 272, 274
error estndar 177- 178, 188-189
errores de entrada/salida 181, 272
ecalamiento en aritmtica de apuntadores 113-[14,
218-219
espacio de nombres 252
espacio en blanco 211
e\pecificador de ~'a(egoria dc almaccnamielllo 232-233
e\pecifieador de calegoria de almal'enamierHo auto
232-233
e~pecificador de calegoria dc almacenamiClllO extern
33, 35-36, 88-89, 232, 233
e\pecifil'<ldor de categoria de almaccnamiento regisler
92, 233
e., pecificador de catcgor'la de almacenamiento slatic92, 233
especificador de categora exlern 33, 35-36, 88-89,
232-233
especificador de tipo 233-234
especificador amum 43-44, 237
especificador slmc! 234-235
especificador unin 234-235
estructura anidada 143-144
e,tructura aUlorreferida 155, 235
estructura de un bloque 61,93,246-247
estruclUra DlR 199
estructura Dirent 199
estructura sial 199
estructuras anidadas 143 144
estructuras mutuamente recursiva 155, 235
etapas de traduccin 211, 252
etiqueta 72, 245-246
etiqueta por omisin 64, 246
etiquetas del casa 64, 246
evitando el gola 72-73
excepciones (condiciones excepcionales) 220-221, 279
EXITJAILURE, EXIT---..SUCCESS 277
expansin de macros 254-255
exponenciacin 26, 275
expresin 220--232
expresin constante 42, 64, 101,230-231
expresin de asignacin 18, 22, 56, 230
expresin entre parntesis 22 1

290

EL L ENGUAJ I- DEL PROGRAMADOR

expresin primaria 221


expresin proposicin 61, 63, 245-246
extensin del signo 49-50, 195,214

talla (k ... 'p'~lti..:auor dl' calegoria dI'


;11111;1,'l'I1;lllllo.'llllI2JJ

falta de especificador de tipo 233-234


FlLENAMEJ1AX 266
1'111 lk ~u..:hi\\l '-"/1\"(' EOF

FOPENj1AX 266
lor 'o'S. while 1415, 66-67
forma de un inicializador 94, 231
formato de un programa 11,20-21, 24-25. 44-45,
152.153,212
fundn addpoint 144

funcin addlree 156


funcin afree 113
fundn
funcin
funcin
funcin
funcin
funcin
funcin
funcin

al nuevo esti lo 223


al viejo estilo 28, 36, 80-81, 223
alloe 111-112
ato! 79-80
aloi 48, 68, 81
bisearch 64, 148-149, 151-152
bitcoun! 56
canonrect 145

funcin closedir 203


funcin copy 3 1, 35
funcin day_of_year 122-12]
funcin del 135-1]6
funcin de biblioteca 7, 75, 88
funcin de biblioteca abor! 277
fundn de biblioteca ahs 277
funcin de biblioteca aeos 275
fundn de biblioteca aselime 280
funcin de biblioteca asin ".75
funcin de biblioteca atof 275
funcin de bibliotcca atol 4S, 68, 81
funcin de biblioteca alol 276
funcin de biblioteca bseareh 277
funcin de biblioteca ealloe 184, 276
funcin de biblioteca eeil 275
funcin de biblioteca clearerr 272
funcin de biblioteca cloek 2S0
funcin de biblioteca eos 275
funcin de biblioteca eosh 275
funcin de biblioteca etime 280
funcin de bibliOleca difltime 280
funcin de biblioteca div 277
funcin de bibliOleca ext 179-180, 277
funcin de biblioteca exp 275
funcin de biblioteca fabs 275
funcin de biblioteca fclose 179, 266
funcin de bibliOleca leol 181, 272
funcin de biblioteca lerror 195
funcin de biblioteca fIlush 266
funcin de biblioteca Igete 270
funcin de biblioteca fgetpos 272
funcin de biblioteca floo r 275

EL LENGUAJE DE L PROGRAMALJOR

I NDICE

funcin de biblioteca mod 275


funcin de biblioteca lopen 177, 266
funcin de bibJioteea fprinlf 17S, 267
funcin de biblioteca fpulc 27 J
funcin de biblioteca Ipuls ISI, 271
funcin de bibliot~a hee 184- 185, 277
funcin de biblioteca freopen 179, 266
funcin de biblioteca Iraxp 275
funcin de biblioteca sean! 178, 269
funcin de biblioteca fseek 272
funcin de biblioteca fsatpas 272
funcin de bibliot~a Itall 272
funcin de biblioteca Iwrite 271
func in de biblioteca getbits 54
func.n de biblioteca gete 178,271
func.n de biblioteca Q'etchar 16, 167, 178, 271
func in de biblioteca gelenv 277
funcin de biblioteca gets 181,271
funcin de biblioteca gmlime 280
funcin de biblioteca isalnum 151,273
funcin de biblioteca isalpha 150, 183, 273
funcin de biblioteca iscnlrl 27]
funcin de bib lioteca isdigit 183, 27]
funcin de biblioteca isgraph 273
funcin de biblioteca islower 183, 273
funcin de biblioteca isprint 273
funcin de biblioteca ispunet 273
funcin de biblioteca isspace 150, 183, 273
funcin de biblioteca isupper 183,273
funcin de biblioteca isxdigit 273
funcin de biblioteca labs 277
funcin de biblioteca Idexp 275
funcin de biblioteca Idiv 277
funcin de biblioteca localtime 280
funcin de bibliOleca longimp 279
funcin de bibliOlcca malloc 158, 184,276
funcin de bib l iot~a rnernehr 274
funcin de biblioteca merncmp 274
funcin de biblioteca memcpy 274
funcin de biblioteca memmove 274
funcin de biblioteca memsel 274
funcin de biblioteca mktime 280
funcin de biblioteca modl 275
funcin de biblioteca perror 272
fu ncin de biblioteca pow 26, 275
funcin de biblioteca printf 7-S, 11- 12 , 19, 169,269
funcin de biblioteca pute 178, 271
funcin de biblioteca putehar 16, 168, 178,271
funcin de biblioteca puls 181, 271
funcin de biblioteca qsor! 277
funcin de bibliot~a raise 279
funcin de biblioteca rand 276
funcin de biblioteca realloe 276
funcin de biblioteca remove 267
funcin de biblioteca rename 267
funcin de biblioteca rewind 272
funcin de biblioteca sean! 107, 173, 270
funcin de biblioteca setbuf 267
funcin de biblioteca setjmp 278

funcin
funcin
funcin
funcin
funcin
func in
funcin
funcin
funcin
funcin
funcin
funcin
funcin
funcin
funcin
funcin
funcin
funcin
funcin
funcin
funcin
funcin
fu ncin
funcin
funci n
funcin
func i n
funci n
funcin
funci n
funcin
funcin
funcin
funcin
funcin
funcin
funcin
funcin
funcin
funcin
funcin
funcin
funcin
funcin
funcin
funcin
funcin
funcin
funcin
funcin
funcin
funcin
funcin
funcin
funcin
funcin
funcin
funcin
funcin

de biblioteca setvbul 267


de biblioteca signal 279
de biblioteca sin 275
de bibliOleca sinh 275
de biblioteca sprintf 171, 269
de biblioteca sqrt 275
de biblioteca srand 276
de biblioteca sscanf 270
de biblioteca sheat 273
de biblioteca strchr 273
de biblioteca stremp 273
de biblioteca strcpy 273
de biblioteca strcspn 274
de biblioteca strenor 274
de biblioteca strftime 280
de bibliOleca slrlen 274
de bibliot~a strncdt 273
de biblioteea slmcmp 273
de biblioteca stmepy 273
de biblioteca strpbrk 274
de biblioteca slrrehar 273
de bihlioteca strs pn 274
de biblioteca slrstr 274
de bib lioteca slrtod 275
de biblioteca slrlok 274
de biblioteca system 184, 277
de biblioteca Idn 275
de biblioteca ta nh 275
de bibJioteea time 280
de bibliOleca Implile 267
de biblioteca tmpnam 267
de biblioteca lolower 169, 183, 273
de biblioteca touppe r 183, 273
de biblioteca ungetc 183-184 , 271
dirdcl 136-137
dirwalk 200-201
e rro r 192
Igets 181
fileeopy 178-179
lopen 195-196
puls ISI
hee 207-208

!site 201
qeteh S7
getin! 107
qelli ne 31, 34, 77, 182
getop 86
qettoken 137
gelwo rd 150-151
hash 159-160
lnstall 160
loa 70
lookup 160
lower 48
malO 6
makepolDt 144
maUee 206-207
month_day 122-123
monlh--Dame 124

I ND I CE
funcin mor&Core 207
funcin numemp 133
funcin opendir 202
funcin pop 86
funcin power 26-27, 28-29
funcin printd 96
funcin ptinrect 145
funcin push 85-86
funcin qsorl 96-97, 121, 132
funcin que no hace nada 78
funcin rand 51
funcin readdir 203
funcin readlines 120
funcin reverse 69
funcin shellsorl 68
funcin squeeze 52
funcin srand 51
funcin strcat 53
funcin slremp 117
funcin strcpy 116-117
funci n slrindex 77
funcin strlen 43, 110, 114
funcin swap 106, 121, 133
funcin laUoe 157
funcin Ireeprint 156- 157
funcin Irim 71
funcin ungete 87
funcin vaca 78
funcin writelines 120-121
funcin --flllbul 196-197
funciones de biblioteca ahm, a tan2 275
funciones de biblioteca atexil 277
funciones de biblioteca log, log10 275
funciones de biblioteca slrtol , st rtoul 276
funciones de biblioteca vpnntf, vfpnnll, vspnntf
192, 269
funcio nes de prueba sobre caracteres 183, 272
generacin de un apuntador 221
qetchar sin uso de buffer 189
gelehar usando buff er 190
Hoare, C. A. R. 96

HUGEyAL 274
ident ificador 212
inclusin de archivos 98, 256
ndices negativos 1II
i' Jlcializacin 44-45, 94 , 242
inicializacin de apuntadores 113, 152
inicializacin de arreglos bidimensionales 12], 24]
inicializacin de arreglos de estructura~ 147148
inicializacin de automticas 33, 45, 94, 242
inicializacin de esuilicas 45, 94, 242
inicializacin de externas 45, 90, 94, 242
inicializacin de un arreglo 95, 124,243
inicializacin de un arreglo bidimensional I !J, 2~ 1
inicializacin de una constante cadena 95, 242
inicializacin de una estructura 142, 242
inicializacin de una unin 243

29 1

292

EL LENGUAJE DEL PROGRAMADOR

inicializacin dentro de un bloque 93, 246-247


inicializacin por cadena constante 95, 2.. 2
inicializacin por omisin 95, 242
inicializador 251

legibilidad de un programa 11, 56, 70-71, 95,162


ligado 215, 251-252
ligado externo 82, 212, 216, 233, 252
ligado interno 216, 252
#line 216,252
lnea de comandos como argumentos 126, 13 t
lnea de control 98, 254-258
lista de argumentos de longitud variable 171, 192,
223,241,278,279
lisia de argumentos void 35, 81, 241, 249
liSia de palabras clave 212
longitud de constantes simblicas 39
longitud de nombres 39-40, 212
longitud de nombres de funcin 40, 212
longillld de nombres de variable 212
longitud de nombres de variables 212
longitud de nombres externos 40, 212
longitud de nombres internos 40, 212
longitud de una cadena 32, 43, lIS
LONG_MAX, LONG _MIN 276

llamada al sistema close 192


llamada al sistema creal 190
llamada al sistema Istal 202
llamada al sistema Iseek 193
llamada al sistema open 190
llamada al sistema read 188
llamada al sistema sbrk 207
llamada al sistema sial 199
llamada al sistema unlink 193
llamada al sistema wrile 188
llamada por referencia 29
llamada por valor 29, 106, 223
llamadas al sistema 187
macro leol 195
macro lerror 195
macro gele 195
macro pule 195
macros con argumentos 98-99
manifestacin de constantes 254
!1lodi,mo, <;'0 manipulao.:ion de bits 54, 164-165
modo de acceso a un archivo 176-177, 196-197,266
modularizacin 26, 30, 36, 75, 82-84, 119
nodo-i (inode) 198
nombre 212
nombre de tipo clock_t 279
nombre de tipo FILE 176
nombre de tipo Ipos) 272
nombre de tipo plrdifl_t 114, 162, 227
nombre de tipo size) 114, 150, 162,226,266
nombre de tipo lime) 279
nombre de tipo wchar_1 214

1NDICE

nombre de un arreglo como argumento 30, 111, 123


nombre de un miembro de estructura 142-143,
235-236
nombre __ FILE __ para el preprocesador 278
nombre __ FILE __ para el preprocesador 278
nombre __ LINE __ para el preprocesador 278
nombres de tipo div) y div_1 277
nombre, de tipos 244
nombres predefinidos para el preprocesador 258
notacin cientfica 41, 81-82
notacin E 41, 214
notacin Polaca 82
notacin Polaca inversa 82
notacin sintctica 215
nueva linea 211, 253-254
NULL 113
nmeros mgicos 15
objeto 215, 218
ocultamiento de informacin 75-76, 84, 86
ocultamiento de un nombre 103
omisin dc la prueba contra cero 62, 117
operaciones permitidas sobre apuntadores 114
opual'iollc~ ... obre unionc, 163
operador casi 50, 157, 184,218,226,244
operador de direccin 103, 225
operador defined para el preprocesador lO!, 257
operador OH lgico 22, 45, 54, 229
operador sizeol 101,114,150,225-226,271
operadores aditivos 226
operadores aritmticos 45
operadores de adicin 227
operadores de asignacin 46, 55, 229
operadores de bits 53, 228
operadores de corrimiento 53, 227
operadores de igualdad 45, 228
operadores de relacin 17, -15, 227
operadores multiplicativos 227
operadores para bits 53, 229
orden de almacenamiento de un arreglo 124, 240
orden de evaluacin 22, 54, 59, 69, 86, 100, 105, 221
orden de evaluacin de una expresin 58, 221
orden de traduccin 253
ordenamiento lexicogrfico 130
ordenamiento numrico 130
ordenando lneas de texto 118, 131
O_RDINDLY, O_RDWR, O_WRONLY 190
palabra clave asm 212
palabra clave lorlran 212
palabras reservadas 38, 212
parmetro 93, 110, 222
pariullclro formal reus!" parametro
permisos sobre un archivo 19!
portabilidad 3, 39, 47, 54, 162, 167, 169,204
posicin de llaves II
precedencia de los operadores 18, 58, 105,
147-148, 210

EL LENGUAJE DEL PROGRAMADOR C

preprocesador de macros 97, 252-258


problemas regionales 265
programa cat 176, 178-179
programa del 146
programa de bsqueda en una tabla 158
programa de conversin a minsculas .169
programa de conversin de temperaturas 8-10,
13- 15, 16
programa de la !.:akuladora 81. R3, 85, 174
programa de ordenamiento 119,242
programa de terminacin 178, 180
programa echo 127, 128
programa fsi>;e 200
programa de concatena archivos 176
programa que copia archivos 17, 18, 189, 191
programa que cuenta caracteres 19
progtama que cuenta espacios en blanco 23, 65
programa que cuenta lneas 20
programa que cuenta palabras 21, 134
programa que cuenta palabras clave 148
programa que encuentra la linea ms larga 31, 34
programa que encuentra un patrn 75, 77, 128-129
programa que escribe la lista de un directorio 198
programa undel 149
programa a la defensiva 63, 65
promocin de un argumento 49, 224
promocin integral 48, 218
proposicin break 65, 69, 249
proposicin compuesta 61, 93, 247, 250-251
proposicin continue 70, 249
proposicin de asignacin anidada 18, 22, 57
proposicin de seleccin 248
proposicin do 68, 249
proposicin for 14, 19, 66, 249
proposicin goto 70, 249
proposicin if_else 20, 22, 61, 248
proposicin nula 19, 247
proposicin return 27, 33, 76, 79, 230
proposicin swilch 64, 82, 248
proposicin vaca .'ase proposicin nula
proposicin while 10, 66, 249
proposil;iones 2.:17-250
proposiciones de iteracin 249
proposiciones de salto 249
proposiciones etiquetadas 70, 247
prototipo de una funcin 28, 33, 49, 78, 132, 222
punto y coma lO, 16, 19,61,63
quieksorl 96, 121

RANO _MAX 276


recursividad 95,154, 156,201, 24, 293
redircecin vuse redireccion dC' entrada/salida
redireccin de enlrada/salida 168, 177, 186
redireccin de salida 168
referencia de un arreglo 223
reglas de alcance 89, 251
reglas de conversin de tipo 46, 48, 218
regreso de lo que ha entrado 84
remocion de una definlcion vase #undef

INDICE

remplazo de smbolos 253


reservacin de almacenamiento 231
restricciones de alineacin 153, 157, 164, 179,
204, 219
relum de main 27. 176
Richards, M. I
Ritchie, D. M, xi
rotulo de enumeracin 236
rtulo de estructura 152, 233
rtulo de unin 233
salida en pantalla 16, 168, 179, 186
salida estndar 168, 177, 186
sangrado 10, 20, 24, 62
secuencia de escape 8, 20, 39-41, 213, 254
secuencia de proposiciones 247
secuencia trigrfica 254

SEEK_CUR, SEEK_ENO, SEEK_SET 272


semntiq de la llamada a una funcin 223
semntica de la referencia a una estructura 224
Shell, D. L. 67
SIG_OFL, SIG_ERR, SIGJGN 279
smbolo (Ioken) 212, 254
sin desbordamiento 46, 274, 279
sintaxis de la llamada a una funcin 221
sintaxis de la referencia a una estructura 222
sintaxis de nombres de variable 37, 212
sistema de archivos de UNIX 187, 198
slderr 177, 179, 266
sldin 177, 266
stdoul 177, 266
stream binario 176, 265-266
stream de texto 16, 167, 265
subarreglos corno argumentos 11I
subndices negativos II1
subndices y apuntadores 107, 110,241
subl11di/ado de arreglo\ 23. 107,221, 241
substraccin de apuntadores, 114, 153, 218
sulijo.h para nombre de archivo 35
\urijo, en l'on~tante.~ 39. 213
supresin de asignacin, seanl 173, 269
tabla de conversiones de printf 170, 268
tabla de conversiones de scanf 174, 270
tabra de ejemplos de printf 14, 170
labIa de enl'abc/ado\ e\tndar 266
tabla de hash 159
labIa de operadores 59
tabla de secuencias de escape 42,213
tamai'lo de nmeros 9, 19, 38, 281
tamao de una estructura 153, 225
tamai'lo por omisin de un arreglo 95, 125, 149
terminador de proposicin 10, 61
Thompson, K. L I
tipo ehar 10, 38, 216, 234
tipO de cadena 221
tipo de constante 39, 214
tipo double 10, 19,38,217,234
tipo enumeraci: 217

293

294

EL LENGUAJE DEL PROGRAMADOR

tipo
tipo
tipo
tipo

lIoal 39, 215


incompleto 235
inl 9, 38, 235
long 10, 19, 38, 217, 2]4
111'0 long double 38, 217
tipo por omisin de una runcin 33, 222
lipo short 10, 38, 234, 235
tipo signed 38, 234
l ipo unsigned 38,56,217,234
tipo unsigned char 38, 189
tipo void 33, 2 16, 219, 234
tipos aritmticos 216
liplh rJcri\'auo,> 1, 10,216
tipos flotantes 216
tipos fundamentales 9. 38, 215
tipos integrales 216

lNDICE
ULONG.MAX. 276
unidad de traducin 211, 249, 251
uso de buffer vase Setbuf Setvbuf

valor numrico de expresiones

TMP.MAX, 267
truncamiento de punto flotante 49, 217
truncamiento por divisin JI, 45, 226

16gica~

48

valor numrico de una expresin de relacin 46, 48


valor numrico de una expresin lgica 48
valor-I 217
variable 215
variable automtica 34, 82, 216
variable externa 34, 81, 216
variables externas static, 91
variables internas stalic 91
va.lisl, vaoslart, vaoarg, vII.end 171, 192,269,278
vector de argumentos argv 125, 190

whUe

'ISo

Jor 15,66