Capitulo 4: Funciones y la estructura del programa
Las funciones dividen tareas grandes de computacion en varias mas pequenas, y permiten la posibilidad de construir sobre lo que otros ya han hecho, en lugar de comenzar desde cero. Las funciones apropiadas ocultan los detalles de operacion de las partes del programa que no necesitan saber acerca de ellos, asi que dan claridad a la totalidad y facilitan la penosa tarea de hacer cambios.
El lenguaje C se diseno para hacer que las funciones fueran eficientes y faciles de usar; los programas escritos en C se componen de muchas funciones pequenas en lugar de solo algunas grandes. Un programa puede residir en uno o mas archivos fuente, los cuales pueden compilarse por separado y cargarse junto con funciones de biblioteca previamente compiladas. No trataremos aqui tales procesos, puesto que los detalles varian de un sistema a otro.
La declaracion y definicion de funciones es el area donde el estandar ANSI ha hecho los cambios mas visibles a C. Tal como mencionam os en el capitulo 1, ahora es posible declarar los tipos de los argumentos cuando se declara una funcion. La sintaxis de la definicion de funciones tambien cambia, de modo que las declaraciones y las definiciones coincidan. Esto hace posible que el compilador pueda detectar muchos mas errores de lo que podia anteriormente. Ademas, cuando los argumentos se declaran con propiedad, se realizan automaticamente las conversiones convenientes.
El estandar clarifica las reglas sobre el alcance de los nombres; en particular, requiere que solo haya una definicion de cada objeto externo. La inicializacion es mas general: los arreglos y las estructuras automaticas ahora se pueden inicializar.
El preprocesador de C tambien se ha mejorado. Las nuevas facilidades del Procesador incluyen un conjunto mas completo de directivas para la compilacion condicional, una forma de crear cadenas entrecomilladas a partir de argumentos de macros y un mejor control sobre el proceso de expansion de macros.
4.1 Conceptos basicos de funciones
Para comenzar, disenemos y escribamos un programa que imprim a cada linea de su entrada que contenga un “patro n” o cadena de caracteres en particular.
(Este es un caso especial del programa grep de UNIX.) Por ejem plo, al buscar el patron de letras "ould." en el conjunto de lineas
Ah Love! could you and I with Fate conspire
To grasp this sorry Scheme of Things entire,
Would not we shatter it to bits -- and then
Re-mould it nearer to the Heart's Desire!
producira la salida
Ah Love! could you and I with Fate conspire
Would not we shatter it to bits -- and then
Re-mould it nearer to the Heart's Desire!
El trabajo se ajusta ordenadamente en tres partes:
while {hay otra linea)
if {la linea contiene el patron)
imprimela
Aunque ciertamente es posible poner el codigo de todo esto en
main
, una mejor forma es aprovechar la estructura haciendo
de cada parte una funcion separada. Es mas facil trabajar con tres
piezas pequenas que con una grande, debido a que los detalles
irrelevantes se pueden ocultar dentro de las funciones, y minimizar asi
el riesgo de interacciones no deseadas. Los fragmentos incluso se pueden
emplear en otros programas.
“Mientras hay otra linea” es getline
, funcion que ya
escribimos en el capitulo 1, e
“imprimela” es printf
, que alguien ya nos proporciono. Esto
significa que solo necesitamos escribir una rutina para decidir si la
linea contiene una ocurrencia del patron.
Podemos resolver ese problema escribiendo una funcion
strindex(s,t)
, que regresa la posicion o indice en la
cadena s
en donde comienza la cadena t
, o
-1
si s
no contiene t
. Debido a
que los arreglos en C principian en la posicion cero, los indices seran
cero o positivos, y asi un valor negativo como -1
es
conveniente para senalar una falla. Cuando posteriormente se necesite
una coincidencia de patrones mas elaborada, solo se debe reemplazar
strindex
; el resto del codigo puede permanecer igual. (La
biblioteca estandar provee una funcion strstr
que es
semejante a strindex
, excepto en que regresa un apuntador
en lugar de un indice.)
Una vez definido todo este diseno, llenar los detalles del programa es simple. Aqui esta en su totalidad, de modo que se puede ver como las piezas quedan juntas. Por ahora, el patron que se buscara es una cadena literal, lo cual no es el mecanismo mas general. Regresaremos en breve a una discusion sobre como inicializar arreglos de caracteres, y en el capitulo 5 mostraremos como hacer que el patron de caracteres sea un parametro fijado cuando se ejecuta el programa.
Tambien hay una version ligeramente diferente de
getline
, que se podra comparar con la del capitulo 1.
# include <stdio.h>
#define MAXILINE 1000 /* longitud maxima por linea de entrada */
int getline(char line[], int max)
int strindex(char source[], char searchfor[]);
char pattern[] = "ould"; /* patron a buscar */
/* encontrar todas las lineas que coincidan con el patron */
main()
{
char line[MAXLINE];
int found = 0;
while (getline(line, MAXLINE) > 0)
if (strindex(line, pattern) >= 0) {
printf("%s", line);
found++;
}
return found;
}
/* getline: pone linea en s, retorna longitud */
int getline(char s[], int lim)
{
int c, i;
i = 0;
while (--lim > 0 && (c=getchar()) != EOF && c != '\n')
s[i++] = c;
if (c == '\n')
s[i++] = c;
s[i] = '\0';
return i;
}
/* strindex: retorna el indice de t en s, -1 si no hay */
int strindex(char s[], char t[])
{
int i, j, k;
for (i = 0; s[i] != '\0'; i++) {
for (j=i, k=0; t[k]!='\0' && s[j]==t[k]; j++, k++)
;
if (k > 0 && t[k] == '\0')
return i;
}
return -1;
}
Cada definicion de funcion tiene la forma
tipo-regresado nombre-de-funcion(declaraciones de argumentos)
{
declaraciones y proposiciones
}
Varias partes pueden omitirse; una funcion minima "dummy" es
nada() {}
que no hace ni regresa nada. Una funcion hacer-nada
,
como esta, es algunas veces util para reservar lugar al desarrollar un
programa. Si el tipo que regresa se omite, se supone
int
.
Un programa es solo un conjunto de definiciones de variables y funciones. La comunicacion entre funciones es por argumentos y valores regresados por las funciones, y a traves 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 proposicion return
es el mecanismo para que la
funcion que se llama regrese un valor a su invocador. Al
return
le puede seguir cualquier expresion:
return expresion
La expresion se convertira al tipo de retorno de la funcion
si es necesario. Con frecuencia se utilizan parentesis para encerrar la
(
expresion)
, pero son optativos.
La funcion que llama tiene la libertad de ignorar el valor regresado.
Incluso, no hay necesidad de un a expresion despues de
return
; en tal caso, ningun valor regresa al invocador.
Tambien el control regresa, sin valor, cuando la ejecucion “cae al
final” de la funcion al alcanzar la llave cerrada derecha
}
. No es ilegal, aunque probablemente un signo de
problemas, el que una funcion regrese un valor desde un lugar y ninguno
desde otro. En cualquier caso, si una funcion no regresa explicitamente
un valor, su “valor” es ciertamente basura.
El programa de busqueda del patron regresa un estado desde
main
, el numero de coincidencias encontradas. Este valor
esta disponible para ser empleado por el medio ambiente que llamo al
programa.
El mecanismo de como compilar y cargar un programa en C que reside en
varios archivos fuente varia de un sistema a otro. En el sistema UNIX,
por ejemplo, la orden cc mencionada en el capitulo 1 hace el trabajo. Suponiendo que las
tres funciones se almacenan en tres archivos llamados
main.c
, getline.c
, y strindex.c
.
Entonces la orden
cc main.c getline.c strindex.c
compila los tres archivos, situa el codigo objeto resultante en los
archivos main.o
, getline.o
, y
strindex.o
, y despues los carga todos dentro de un archivo
ejecutable llamado a.out
. Si existe un error - digamos en
main.c
- dicho archivo puede volverse a compilar por si
mismo y el resultado cargado con los archivos objeto previos, con la
orden.
cc main.c getline.o strindex.o
cc emplea la convencion ".c
" contra
".o
" para distinguir los archivos fuente de los archivos
objeto.
- Ejercicio 4-1. Escriba la funcion
strrindex(s,t)
, que regresa la posicion de la ocurrencia de mas a la derecha det
ens
, o-1
si no hay alguna.
4.2 Funciones que regresan valores no enteros
Basta ahora los ejemplos de funciones han regresado o ningun valor
(void) o un int
. ¿Que pasa si una funcion debe regresar
algo de otro tipo? Muchas funciones numericas como sqrt
,
sin
y cos
regresan double
; otras
funciones especializadas regresan tipos diferentes. Para ilustrar como
tratar con esto, escribamos y usemos la funcion atof(s)
,
que convierte la cadena s
a su valor equivalente de punto
flotante de doble precision. La funcion atoi
es una
extension 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 version no es una
rutina de conversion de alta calidad; tomaria mas espacio del que
podemos dedicarle. La biblioteca estandar incluye un atof
;
el header <math.h>
la declara.
Primero, atof
por si misma debe declarar el tipo del
valor que regresa, puesto que no es int
. El nombre del tipo
precede al nombre de la funcion:
#include <ctype.h>
/* atof: convierte la cadena s a double */
double atof(char s[])
{
double val, power;
int i, sign;
for (i = 0; isspace(s[i]); i++) /* saltea espacio en blanco */
;
sign = (s[i] == '-') ? -1 : 1;
if (s[i] == '+' || s[i] == '-')
i++;
for (val = 0.0; isdigit(s[i]); i++)
val = 10.0 * val + (s[i] - '0');
if (s[i] == '.')
i++;
for (power = 1.0; isdigit(s[i]); i++) {
val = 10.0 * val + (s[i] - '0');
power *= 10;
}
return sign * val / power;
}
Segundo, e igualmente importante, la rutina que llama debe indicar
que atof
regresa un valor que no es int
. Una
forma de asegurar esto es declarar atof
explicitamente en
la rutina que la llama. La declaracion se muestra en esta primitiva
calculadora (apenas adecuada para un balance de chequera), que lee un
numero por linea, precedido en forma optativa por un signo, y lo
acumula, imprimiendo la suma actual despues de cada entrada:
#include <stdio.h>
#define MAXLINE 100 /* calculadora rudimentaria * /
main()
{
double sum, atof(char []);
char line[MAXLINE];
int getline(char line[], int max);
sum = 0;
while (getline(line, MAXLINE) > 0)
printf("\t%g\n", sum += atof(line));
return 0;
}
La declaracion
double sum, atof(char []);
senala que sum
es una variable double
, y
que atof
es una funcion que toma un argumento
char[]
y regresa un double
.
La funcion atof
se debe declarar y definir
consistentemente. Si atof
en si misma y la llamada a ella
en main
tienen tipos inconsistentes dentro del mismo
archivo fuente, el error sera detectado por el compilador. Pero si (como
es probable) atof
fuera compilada separadamente, la falta
de consistencia no se detectaria, atof
regresaria un valor
double
que main
trataria como
int
, y se producirian resultados incongruentes.
A la luz de lo que hemos mencionado acerca de como deben coincidir las declaraciones con las definiciones, esto podria ser sorprendente. La razon de que ocurra una falta de coincidencia es que, si no existe el prototipo de una funcion, esta es declarada implicitam ente la primera vez que aparece en una expresion, como
sum += atof(line)
Si en una expresion se encuentra un nombre que no ha sido declarado
previamente y esta seguido por parentesis izquierdo, se declara por
contexto, de modo que se supone que es el nombre de una funcion que
regresa un int
, y nada se supone acerca de sus argumentos.
Aun mas, si la declaracion de una funcion no incluye argumentos como
en
double atof();
tambien es tomada de modo que no se supone nada acerca de los
argumentos de atof
; se desactiva toda revision de
parametros. Este significado especial de la lista de argumentos vacia se
hace para permitir que los programas en C viejos se compilen con los
nuevos compiladores. Pero es una mala tactica usar esto con programas
nuevos. Si la funcion toma argumentos, declarelos; si no los toma, use
void
.
Dado atof
, propiamente declarado, podemos escribir
atoi
(convierte una cade na a int
) en terminos
de el:
/* atoi: convierte la cadena s a entero usando atof */
int atoi(char s[])
{
double atof(char s[]);
return (int) atof(s);
}
Notese la estructura de las declaraciones y la proposicion
return
. El valor de la expresion en
return expresion;
se convierte al tipo de la funcion antes de que se tome el
return
. Por lo tanto, el valor de atof
, un
double
, se convierte automaticamente a int
cuando aparece en este return
, puesto que la funcion
atoi
regresa un int
. Sin embargo, esta
operacion potencialmente descarta informacion, de manera que algunos
compiladores lo previenen. El cast establece explicitamente lo
que la operacion intenta y suprime las advertencias.
- Ejercicio 4-2. Extienda
atof
para que maneje notacion cientifica de la forma123.45e-6
donde un numero de punto flotante puede ir seguido pore
oE
y opcionalmente un exponente con signo. □
4.3 Variables Externas
Un programa en C consta de un conjunto de objetos externos, que son
variables o funciones. El adjetivo “externo” se emplea en contraste con
“interno”, el cual describe los argumentos y las variables definidas
dentro de las funciones. Las variables externas se definen fuera de
cualquier funcion, y por lo tanto , estan potencialmente disponibles
para muchas funciones. Las funciones en si mismas son siempre externas,
puesto que C no permite definir funciones dentro de otras funciones. Por
omision, 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
estandar llama a esta propiedad ligado externo). En este
sentido, las variables externas son analogas a los bloques
COMMON
de Fortran o a las variables del bloque mas externo
de Pascal. Mas adelante veremos como definir variables y funciones
externas que sean visibles solo 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. Cualquier funcion puede tener acceso a variables externas haciendo referencia a ellas solamente por su nombre, si este ha sido declarado de alguna manera.
Si un gran numero de variables se debe compartir entre funciones, las variables externas son mas convenientes y eficientes que las largas listas de argumentos. Sin embargo, como se senalo en el capitulo 1, este razonamiento se debera aplicar con precaucion, pues puede tener un efecto negativo sobre la estructura del programa y dar lugar a programas con demasiadas conexiones de datos entre funciones.
Las variables externas son tambien utiles debido a su mayor alcance y tiempo de vida. Las variables automaticas son internas a una funcion y su existencia se inicia cuando se entra a la funcion y desaparecen cuando esta se abandona. Por otro lado, las variables externas son permanentes, de modo que retienen sus valores de la invocacion de una funcion a la siguiente. Asi, si dos funciones deben compartir algunos datos, aun si ninguna llama a la otra, con frecuencia es mas conveniente que los datos compartidos se mantengan en variables externas, en lugar de que sean pasados como argumentos de entrada y salida.
Examinemos mas a fondo este tema con un ejemplo mas amplio. El
problema es escribir el programa de una calculadora que provea los
operadores +
-
*
y
/
. Por ser mas facil su implantacion, la calculadora
utilizara notacion polaca inversa en lugar de infija. (La polaca inversa
es utilizada por algunas calculadoras de bolsillo, y en lenguajes como
Forth y PostScript.)
En notacion polaca inversa, cada operador sigue a sus operandos; por lo tanto, lo que en una expresion infija se exoresa como
(1 - 2) * (4 + 5)
en notacion polaca inversa se introduce como
1 2 - 4 5 + *
Las parentesis son innecesarias; toda vez que sepamos cuantos operandos espera cada operador, la notacion no resulta ambigua.
La implantacion es simple. Cada operando se introduce en una pila o
"stack", cuando se arriba a un operador, se extrae el numero correcto de
operandos (dos en el caso de los operadores binarios), se aplica el
operador y el resultado se regresa a la pila. En el ejemplo anterior, se
introducen 1
y 2
, luego se reemplazan por su
diferencia, -1
. A continuacion se introducen 4
y 5
y luego se reemplazan por su adicion, 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 linea de
entrada.
De esta forma, La estructura del programa que opere de esta manera sera un ciclo que realice las operaciones adecuadas sobre cada operador y operando en la medida que aparecen;
while (siguiente operador u operando que no es fin de archivo)
if (numero)
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 operaciones de introducir en pila {push, o "apilar") y extraer de una pila (pop o "desapilar") resultan triviales, pero cuando al intentar agregar la deteccion y recuperacion de errores, resultan suficientemente largas como para que sea conveniente colocarlas en funciones separadas en lugar del codigo repartido a lo largo de todo el programa. Ademas, deberia existir una funcion separada para buscar el siguiente operador u operando.
La principal decision de diseno que aun no se ha explicado es donde
esta la pila, esto es, cuales rutinas tienen acceso a ella directamente.
Una posibilidad es mantenerla en main
, y pasar la pila y la
posicion actual a las rutinas que introducen y extraen elementos. Pero
main
no necesita saber acerca de las variables que
controlan a la pila; solo efectua las operaciones de introducir y
extraer. Asi, hemos decidido almacenar la pila y su informacion asociada
en variables externas accesibles a las funciones push
y
pop
, pero no a main.
Traducir este bosquejo a codigo es sumamente facil. Si por ahora pensamos que el programa existe en un archivo fuente, se vera asi
#includeS
#defineS
declaracion de funciones para main
main() { ... }
variables externas para push y pop
void push(double f) { ... }
double pop(void) { ... }
int getop(char s[ ]) { ... }
rutinas llamadas por getop
Mas adelante se vera como se puede dividir todo esto entre dos o mas archivos de codigo fuente.
La funcion main
es un ciclo que contiene un
switch
gigante sobre el tipo de operador y operando; este
es un uso del switch
mas tipico que el mostrado en la seccion 3.4.
#include <stdio.h>
#include <math.h> /*para atof() */
#define MAXOP 100 /* max tamano de operando u operador */
#define NUMBER '0' /* senal de que un numero se encontro */
int getop(char []);
void push(double);
double pop(void);
/* Calculadora polaca inversa */
main()
{
int type;
double op2;
char s[MAXOP];
while ((type = getop(s)) != EOF) {
switch (type) {
case NUMBER:
push(atof(s));
break;
case '+':
push(pop() + pop());
break;
case '*':
push(pop() * pop());
break;
case '-':
op2 = pop();
push(pop() - op2);
break;
case '/':
op2 = pop();
if (op2 != 0.0)
push(pop() / op2);
else
printf("error: divisor cero\n");
break;
case '\n':
printf("\t%.8g\n", pop());
break;
default:
printf("error: comando desconocido %s\n", s);
break;
}
}
return 0;
}
Puesto que +
y *
son operadores
conmutativos, el orden en el que se combinan los operandos extraidos es
irrelevante ("el orden de los factores no altera el producto"), pero en
el caso de -
y /
, se hace necesario distinguir
su orden, en este caso especifico, cuales son los operandos izquierdo y
derecho. En
push(pop() - pop()); /* INCORRECTO */
no se ha definido el orden en el que se evaluan las dos llamadas de
pop
. Para garantizar que el orden sea correcto, se hace
necesario extraer el primer valor en una variable temporal, como hicimos
en main
.
#define MAXVAL 100 /* maxima profundidad de la pila val •/
int sp = 0 ; /* siguiente posicion libre en la pila */
double val[MAXVAL]; /* valores de la pila */
/* push: introduce f a la pila */
void push(double f)
{
if (sp < MAXVAL)
val[sp++] = f;
else
printf("error: pula llena, no puede hacer push de %g\n", f);
}
/* pop: extrae y regresa el valor superior de la pila */
double pop(void)
{
if (sp > 0)
return val[--sp];
else {
printf("error: pila vacia\n");
return 0.0;
}
}
Una variable es externa si se encuentra definida fuera de cualquier
funcion. Por tanto, la pila y el indice de la pila que deben ser
compartidos por push
y por pop
va definida por
fuera de estas funciones. Pero main
en si misma no hace
referencia a la pila o a la posicion de la pila — la representacion
puede estar oculta.
Pasemos ahora a la implantacion de getop
, la funcion que
toma el siguiente operador u operando. La tarea es facil: "Ignorar
blancos y tabuladores. Si el siguiente caracter no es un digito o punto
decimal, regresarlo; De otra manera, reunir una cadena de digitos (que
pueda incluir un punto decimal), y regresar NUMBER
(la
senal de que ha sido reunido un numero)".
#include <ctype.h>
int getch(void);
void ungetch(int);
/* getop: obtiene siguiente caracter u operando numerico */
int getop(char s[])
{
int i, c;
while ((s[0] = c = getch()) == ' ' || c == '\t')
;
s[1] = '\0';
if (!isdigit(c) && c != '.')
return c; /* no-numero */
i = 0;
if (isdigit(c)) /* recoge parte de entero */
while (isdigit(s[++i] = c = getch()))
;
if (c == '.') /* recoge parte fraccional */
while (isdigit(s[++i] = c = getch()))
;
s[i] = '\0';
if (c != EOF)
ungetch(c);
return NUMBER;
}
¿Que son getch
y ungetch
? A menudo se da el
caso de que un programa no puede determinar si ha leido lo suficiente de
la entrada hasta que ya ha leido demasiado de ella. Reunir los
caracteres que forman un numero es un ejemplo de ello: el numero no esta
completo hasta que se ve el primer caracter no-digito... pero para
cuando ello sucede, el programa ya ha leido un caracter de mas, para lo
cual no esta preparado.
El problema podria ser resuelto si fuera posible “desleer” el
caracter indeseado. De esta manera, toda vez que el programa lea un
caracter de mas, podria devolverlo a la entrada, provision con la cual
el resto del codigo se comportara como la lectura de mas jamas hubiese
sucedido. Afortunadamente, es facil simular el regreso de un caracter,
escribiendo un par de funciones cooperativas, getch
entrega
el siguiente caracter de la entrada que va a ser considerado;
ungetch
reintegra el caracter devuelto a la entrada, de
modo que llamadas posteriores a getch
lo regresaran antes
de leer algo nuevo de la entrada.
Como trabajan juntas es sencillo, ungetch
coloca el
caracter devuelto en un buffer compartido: un arreglo de caracteres.
getch
lee del buffer si hay algo alli, y si el buffer esta
vacio llama a getchar
. Tambien debe existir una variable
indice que registra la posicion del caracter actual en el buffer
temporal. Puesto que el buffer y el indice son compartidos por
getch
y ungetch
- y deben retener sus valores
entre llamadas - deben ser externos a ambas rutinas. Asi, podemos
escribir getch
, ungetch
y sus variables
compartidas como:
#define BUFSIZE 100
char buf[BUFSIZE]; /* buffer para ungetch */
int bufp = 0; /* siguiente posicion libre en el buffer */
int getch(void) /* obtiene un (posiblemente regresado) caracter */
{
return (bufp > 0) ? buf[--bufp] : getchar();
}
void ungetch(int c) /* devuelve caracter a la entrada */
{
if (bufp >= BUFSIZE)
printf("ungetch: demasiados caracteres\n");
else
buf[bufp++] = c;
}
La biblioteca estandar incluye una funcion ungetc
que
proporciona el regreso de un caracter; esto se vera en el capitulo 7. Se ha utilizado un arreglo para
lo que se regresa a la entrada, en lugar de un caracter sencillo, para
dar una idea mas general.
- Ejercicio 4-3. Dada la estructura basica, es facil
extender la calculadora. Agregue el operador modulo (
%
) y consideraciones para numeros negativos. □ - Ejercicio 4-4. Agregue ordenes para imprimir el elemento al tope de la pila sin sacarlo de ella, para duplicarlo y para intercambiar los dos elementos del tope. Agregue una orden para limpiar la pila. □
- Ejercicio 4-5. Agregue acceso a funciones de
biblioteca como
sin
,exp
ypow
.
Consulte <math.h>
en el apendice B, seccion 4. □
- Ejercicio 4-6. Agregue ordenes para manipular variables. (Es facil proporcionar veintiseis variables con nombres de una letra.) Anada una variable para el valor impreso mas reciente. □
- Ejercicio 4-7. Escriba un a rutina
ungets(s)
que regresa a la entrada una cadena completa. ¿Debeungets
conocer acerca debuf
ybufp
, o solo debe usarungetch
? □ - Ejercicio 4-8. Suponga que nunca existira mas de un
caracter de regreso. Modifique
getch
yungetch
de acuerdo con eso. □ - Ejercicio 4-9, Nuestros
getch
yungetch
no manejan correctamente un EOF que se regresa. Decida cuales deben ser sus propiedades si se regresa un EOF, y despues realice su diseno. □ - Ejercicio 4-10. Una organizacion alternativa emplea
getline
para leer una linea completa de entrada; esto hace innecesarios agetch
y aungetch
. Corrija la calculadora para que use este planteamiento. □
4.4 Reglas y Alcance
4.5 Archivo de encabezamiento header
4.6 Variables estaticas
4.7 Variables tipo registro
4.8 Estructura de bloques
4.9 Inicializacion
4.10 Recursividad
4.11 El preprocesador de C
4.1.1 Inclusion de archivos
4.1.2 Substitucion de macros
4.1.3 Inclusion condicional
Continuar: Capitulo 5