Capítulo
1. INTRODUCCIÓN GENERAL (CONTENIDO)
A continuación se muestra un
programa para imprimir
hello, world
#include <stdio.h>
main(){
Algunos caracteres escape
Proposición while
La sintaxis de la proposición
while es la siguiente:
while (fahr <= upper)
{
...
}
Algunos Ejemplos de programas simples
en C
main() /* copia la entrada a la salida,
versión 2 */
{
main() /* Table Farenheit-Celsius */
{
int fahr;
for (fahr=0; fah<=300; fahr=fahr+20)
printf("4d %6.1f\n",fahr,(5.0/9.0)*(fahr-32));
}
main() /* cuenta los caracteres leídos
*/
{
double nc;
for (nc=0; getchar()!=EOF; ++nc)
;
printf("%.0f\n",nc);
}
Asignación multiple.
Se propaga de derecha a izquierda,
por ejemplo:
nl=nw=nc=0;
se entiende como:
nc=(nl=(nw=0)));
OR
if (c==' ' || c=='\n' || c=='\t')
if
if (expresión)
proposición 1
else
proposición 2
Declaración de un vector de
10 enteros. El primero es ndigit[0];
int ndigit[10];
Funciones
Formato de una función:
[tipo] nombre (lista de argumentos
si los hay)
{
declaraciones;
proposiciones;
}
solo se usa "tipo" si se regresa un
valor.
Ej. Función "power"
power(int x, int n)
{
int i, p;
p=1;
for (i=1; i<=n; ++i)
p=p*x;
return (p);
}
Ej. Uso de la función "power"
#include <stdio.h>
main()
{
for (i=0; i<10; ++i)
printf("%d %d %d\n", i, power(2,i),
power (-3,i));
}
Las llamadas en C son por valor.
Lo que permite modificar las copias
privadas y temporales, es decir, las variables automáticas. Cuando
se usa un arreglo como argumento, se pasa la dirección del mismo.
Cadenas
de caracteres.
Tienen un caracter nulo al final ('\0').
Ej. "hello\n" -> h e l l o \n \0
Tipos
de variables
Entre los tipos de variables se cuentan:
Automáticas
Externas
Las variables automáticas...
- aparecen y desaparecen con la llamada
de la función.
Las variables externas...
- se declaran globalmente: extern
int max;
- se definen fuera de las funciones:
int max;
- se declaran dentro de las funciones:
extern int max;
Capítulo 2. TIPOS OPERADORES Y EXPRESIONES (CONTENIDO)
Nombres de
las variables
Letra (Letra o Dígito)* ->
31 caracteres
Las mayúsculas son diferentes
de las minúsculas
Es práctica común de
C, denotar con
- MAYUSCULAS las constantes
- minúsculas las variables
Tipos
char
int -> (CALIFICADORES: short, long,
unsigned)
float
double
Constantes
Simbólicas (Preprocesamiento)
Ej. #define PI 3.14159265359
#define MAXLINE 1000
char line [MAXLINE+1]
#define FORMFEED '\014'
Variables constantes:
Ej. const float pi=3.14159265359;
Alfanuméricas:
Se evalúan en tiempo de compilación,
no de ejecución.
Ejs. 3.14159265359
123.456e-7
0.12E3
'0' -> ASCII 48, EBCDIC 240
Declaraciones:
Se pueden declarar varias variables
en pocos renglones:
int lower,upper,step;
char c, line[1000];
También se pueden escribir
más explicitamente, para agregar comentarios:
int lower; /* algunos comentarios
*/
int upper;
int step;
char c;
char line[1000];
Inicialización
de variables:
char backslash='\\';
int i=0;
Operadores
aritméticos:
+ - * / %(módulo)
Ej. (uso del módulo)
if (year%4==0 && year%100!=0
|| year%400==0)
es un año bisiesto;
else
no lo es;
Operadores
relacionales y lógicos:
> >= <= < == !=
El operador que convierte en 0 algo
distinto de 0, y en 1, el cero, es !. Ej.
if (!inword)
if (inword==0)
Conversiones
de Tipos
Se hacen conversiones automáticas
cuando tiene sentido. Ej.
float + int -> float
"char" e "int" se manejan indiscrimidamente.
Ej.
int atoi(char s[]) /* convierte s
a int */
{
int i,n;
n=0;
for (i=0; s[i]>='0' && s[i]<='9';++i)
n=10*n+s[i]-'0';
return (n);
}
Conversión coaccionada "cast"
Se puede forzar la conversión
de un tipo en otro. El formato es:
(nombre-de-tipo) expresión
por ejemplo:
sqrt( (double) n)
Operadores de incremento
y decremento
si n=5, entonces x=n++; -> x=5 n=6
x=++n; -> x=6 n=6
Operadores lógicos para manejo
de bits:
& AND lógico
| OR lógico
^ XOR lógico
<< desplazamiento a la izquierda
>> desplazamiento a la derecha
~ complemento a uno (unario)
Ejs:
c = n & '\077';
x = x | MASK;
x = x & ~'\077';
Ej. Función para tomar n bits
a partir de la posición p
getbits(unsigned int x, p, n)
{
return ((x>>(p+1-n)) & ~(~0 <<
n));
}
Nota: (~0 << n) es 11..100..0
con n ceros a la derecha
~(~0 << n) es 00..011..1 con
n unos a la derecha
Operadores y expresiones de asignación
La expresión
e1 op= e2
es idéntico a
e1 = e1 op e2
donde "op" es un operador como {],-,*,/,%,<<,>>,&,^,|}
Por ejemplo: i=i+2; es idÚntico
a i+=2;
Expresiones
condicionales
Tienen el siguiente formato
e1 ? e2 : e3;
si e1 es verdadero se evalúa
e2, si e1 es falso, se evalúa e3.
z=(a>b) ? a : b; /* z=max(a,b) */
Precedencia y orden de evaluación
OPERADOR ASOCIATIVIDAD
() [] -> . ->
! ~ ++ -- - (tipo) * & sizeof
<-
* / % ->
+ - ->
<< >> ->
< <= > >= ->
== != ->
& ->
^ ->
| ->
&& ->
|| ->
? : <-
= += -= etc <-
0 ->
ASOCIATIVIDAD
-> izquierda a derecha
<- derecha a izquierda
i=i+2
i+=2
Capítulo 3. CONTROL DE FLUJO (CONTENIDO)
Proposiciones
Se terminan con ; (";" es terminador,
no separador)
Nula: ;
Simple: x=0;
Compuesta: { proposiciones; }
if else
if (expresión)
proposición 1
else
proposición 2
Las siguientes proposiciones son equivalentes:
if (expresión)
if (expresión != 0)
El "else" se asocia a la condicional
más interna:
if (n>0) if(a>b) z=a; else z=b;
Se entendería como:
if (n>0)
if(a>b)
z=a;
else
z=b;
Si se quiere que el "else" se asocie
a la condicional externa, es necesario usar llaves, como se muestra a continuación:
if (n>0) { if(a>b) z=a;} else z=b;
que se entiende como:
if (n>0) {
if(a>b)
z=a;
}
else
z=b;
else if
Permite escribir condicionales de
casos EXCLUYENTES
if (expresión)
proposición
else if (expresión)
proposición
else
proposición
switch
Sintetiza una secuencia larga else
if, incluyendo || (or).
Ej.
main() /* cuenta dígitos, espacios
en blanco y otros */
{
case ' ':
case '\n':
case '\t':
default:
}
printf("digits= ");
for (i=0; i<10; i++)
printf("%d",ndigit[i]);
printf("\n white space = %d, ",
"other = %d\n",nwhite,nother);
return 0;
}
while y for
while (expresión)
proposición
Las siguientes proposiciones "for"
y "while" son equivalentes:
for (expr1; expr2; expr3)
proposicion
expr1;
while (expr2)
{
proposición
expr3;
}
Uso de la coma
","
La coma "," permite el cálculo
secuencial de expresiones.
Por ejemplo:
reverse(s) /* invierte cadena s */
char s[];
{
do while
do
proposición
while (expresión);
break
La proposición "break" permite
una salida forzada de:
continue
Obliga a ejecutar la siguiente iteración
del ciclo, en las proposiciones:
/* procesa los valores positivos de
un arreglo */
for (i=0; i
if (a[i]<0) /* salta los elementos
negativos */
continue;
/* aquí procesa los elementos
positivos */
}
goto y etiquetas
Generalmente es usada en el manejo
de errores. Se aconseja ser parcos en su uso.
Por ejemplo
/* muestra el primer valor negativo
de un */
/* arreglo multidimensional */
for (i=0; i
goto found;
/* no se encontró */
found: /* se encontró en la
posición i,j */
..
Capítulo 4. FUNCIONES Y LA ESTRUCTURA DEL PROGRAMA (CONTENIDO)
En C no se pueden escribir funciones
dentro de funciones.
Por ejemplo:
/* Imprime las líneas que contengan
cierto patrón */
#define MAXLINE 100
void index(char [], char []);
extern void getline(char [], int);
main()
{
char line[MAXLINE];
while(getline(line,MAXLINE)>0)
if (index(line,"the")>=0
printf("%s",line);
}
void index(char s[], char t[])
/* devuelve posición t en s;
o -1 si no está */
{
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 (t[k]=='\0')
return (i);
}
}
Compilación y encadenamiento
En UNIX:
$ cc main.c getline.c index.c
produce:
main.o getline.o index.o <- programas
objeto
a.out <- programa ejecutable
Funciones que devuelven valores
no enteros
Se tienen las siguientes 2 puntos:
- Por default el tipo de la función
es "int" ("char" se convierte a "int").
- En caso de no ser "int", la función
se debe declarar:
- en la función:
double atof(s) /* convierte la cadena
s a double */
- en la llamada:
double sum,atof();
...
(... sum+=atof(line));
Reglas sobre el alcance
de validez de una variable:
Variable externa (declaración)
Se dan a conocer las propiedades de
una variable, por ejemplo tipo, tamaño, etc.
Variable externa (definición)
Causa una asignación de memoria.
Solo se debe hacer una definición.
Por ejemplo, las siguientes líneas
Sin embargo, las líneas
Variables
estáticas
Las variables estáticas son
accesibles solo dentro del archivo donde son definidas. Y pueden ser definidas
dentro o fuera de una función.
Ej. Si las 2 variables y las 2 funciones
siguientes se compilan
en un archivo con:
static char buf[BUFSIZE]; /* buffer
para "ungetch" */
static int bufp=0; /* sig. pos. libre
en "buf" */
getch() {...}
ungetch() {...}
el resto de las funciones de los otros archivos no tendrá acceso ni a "buf" ni a "bufp". Por lo anterior, no habrá conflictos si los mismos nombres son usados en otros programas.
Una función estática
es inaccesible fuera del archivo donde se declara (y define, por supuesto).
Variables
registro.
Las variables registro sólo
pueden ser las variables automáticas y los parámetros formales
de la función.
Las variables registro son usadas para variables muy usadas. El compilar coloca las variables registro en los registros de la CPU de la máquina, siempre y cuando sea posible.
NO ES POSIBLE obtener la dirección de una variable estática.
Se declaran de la siguiente manera:
register int x;
register char c;
Y la declaración para parámetros
formales de función, es:
f(register int c,n)
{
...
}
Estructura de bloques
Se tienen las siguientes características:
- No se pueden definir funciones dentro
de funciones. Si embargo si se pueden declarar funciones dentro de funciones.
Esta declaración hace referencia a funciones que se encuentran en
otro parte.
- Las declaraciones o definiciones
(incluyendo inicializaciones), se colocan dentro de las llaves que introduce
cualquier sentencia compuesta.
Por ejemplo:
if (n>0)
{
/* se declara una variable nueva "i"
*/
int i;
for (i=0; i
}
Inicialización
La inicialización tiene las
siguientes consideraciones:
- Si no se inicializan explicitamente
las variables, se tiene:
- Las variables externas y estáticas
tendrán cero.
- Las variables automáticas
y registro tendrán basura.
- Las variables simples, que no son
arreglos ni estructuras, se inicializan así:
int x;
char squoute = '\'';
long day=60*24; /* minutos del día
*/
- Las variables externas estáticas se inicializan una vez en tiempo de compilación; las variables automáticas y registro, cada vez que se entra a la función.
- Las variables automáticas
y registro se pueden inicializar con valores definidos previamente, incluso
llamadas a función. Por ejemplo:
{
int low=0;
int high=n-1; /* n definida anteriormente
*/
int mid;
..
}
- Los arreglos automáticos no pueden ser incializados.
- Los arreglos externos y estáticos
se inicializan con valores separados por ',' y entre llaves. Por ejemplo,
el programa siguiente para contar dígitos, espacios y otros:
/* cuenta dígitos, espacios
y otros */
main()
{
int c,i,nwhite,nother;
int ndigit[10];
nwhite=nother=0;
for (i=0; i<10; i++)
ndigit[i]=0;
...
}
puede ser escrito como:
/* cuenta dígitos, espacios
y otros */
int nwhite=0;
int nother=0;
/* inicialización con 10 0's
*/
int digit[10]={0,0,0,0,0,0,0,0,0,0,0};
main()
{
int c,i;
...
}
- Se pueden inicializar arreglos de
caracteres de 2 maneras:
- Como cadena de caracteres
char pattern[]="the";
- Como arreglo de caracteres
char pattern[]={'t','h','e','\0'};
Recursividad
- Las funciones en C pueden llamarse
a si mismas, es decir pueden ser recursivas.
- Una función puede llamarse
a si misma, directa o indirectamente. Por ejemplo:
/* imprime n en decimal (recursivamente)
*/
void printd(int n)
{
int i;
if (n<0)
{
putchar('-');
n=-n;
}
if ((i=n/10) != 0)
printd(i);
putchar(n%10+'0');
}
El preprocesador de C
Inclusión de archivos (include):
Una línea de la forma
#include "nombre"
indica al preprocesador de C, substituir
la línea con el contenido del archivo "nombre", de trayectoria absoluta.
Una línea de la forma
#include <nombre>
indica al preprocesador de C, substituir
la línea con el contenido del archivo "nombre", de la trayectoria
PREDEFENIDA para los archivos "include".
Los "include"s pueden ser anidados.
Los "include"s son recomendables para tener las mismas definiciones en varios programas fuentes.
Sustitución de Macros
Para sustituir SI por 1, usamos la
línea siguiente.
#define SI 1
aquí, las cadenas "SI=" no
serán sustituidas.
Para usar una sintaxis similar al PASCAL,
usamos las líneas
#define then
#define begin {
#define end }
y podríamos escribir después
if (i>0) then
begin
a=1;
b=2
end
Podemos usar parámetros en la
sustitución de macros. Por ejemplo:
#define max(A,B) ( (A)>(B) ? (A) :
(B) )
y usarla como
x=max(p+q,r+s);
que sería sustituida por
x=((p+q)>(r+s) ? (p+q) : (r+s));
NOTAS:
- Es necesaria utilizar paréntesis
en la definición de macros, para evitar resultados erróneos
debido a la prioridad de las operaciones aritmÚticas.
- Con una MACRO se puede crear una
sola función para varios tipos de argumentos.
- Las MACROS con parámetros
son más eficientes que las llamadas a funciones, ya que son una
expansión en línea. Por ejemplo, las funciones "getchar()"
y "putchar()" están definidas como MACROS de las funciones "putc()"
y "getc()", usando la salida estándard (stdout) y entrada estándard
(stdin) respectivamente.
Capítulo 5. Apuntadores y arreglos. (CONTENIDO)
Apuntadores
y direcciones.
Los símbolos * y & pueden
ser usados para el contenido y la dirección de una variable, respectivamente.
Por ejemplo,
si "x" es una variable de tipo "int"
y "y" es una variable de tipo "int"
y "px" es una variable de tipo "apuntador
a int"
entonces, se podría hacer lo
siguiente:
Asignar la dirección de "x"
al apuntador "px"
px = &x;
Aumentar en 1, el valor apuntado por
"px" (es decir, el valor de "x") y asignarlo a la variable "y".
y = *px+1;
Aumentar en 1, el valor del apuntador "px" (es decir, el valor de una variable desconocida) y asignarlo a la variable "y".
Aumentar en 1 el valor apuntado por
"px" (es decir, el valor de "x").
*px+=1;
o bien
(*px)++;
notar que en este último ejemplo
es necesario usar parÚntesis, debido a que, aunque la prioridad
de "*" y "++" es igual, la asociatividad es de DERECHA a IZQUIERDA. Así,
sin los parÚntesis, la expresión "*px++;" se entendería
como: "*(px++);".
Se definimos el arreglo de 10 enteros
de la siguiente manera:
int a[10];
podemos referenciar a[0] a[1] .. a[9].
Si además definimos el apuntador
a entero "pa"
int *pa;
y le asignamos la dirección
del primer elemento del arreglo "a",
pa=&pa[0]; o bien pa=a;
entonces, el contenido de "a[i]" puede
ser visto con
*(pa+i)
En apuntadores a arreglos de caracteres,
las 2 siguientes declaraciones son equivalentes:
char s[];
char *s;
AritmÚtica de direcciones.
Son posibles las siguientes operaciones
con apuntadores:
- Sumar y restar un entero a un apuntador.
- Restar y comparar 2 apuntadores.
- Comparar un apuntador con NULL.
Apuntadores a caracteres y a funciones.
En el siguiente ejemplo, la variable
"mensaje" es SOLO UN APUNTADOR.
char *message="now is the time";
No se debe comfundir el envío
de un APUNTADOR a un arreglo de caracteres con el envío del ARREGLO
de caracteres. Por ejemplo, en la siguiente función se envían
los apuntadores a 2 arreglos de caracteres (cuya memoria fue asignada en
otra parte del programa).
strcpy(s,t) /* copia t a s, con apuntadores
*/
{
while (*s++=*t++)
;
}
Arreglos Multidimensionales
En C, los arreglos de 2 dimensiones
se implementan como arreglos unidimensionales. Donde cada elemento es,
a su vez, un arreglo unidimensional. Por lo anterior, la sintaxis en C
es
day_tab[i][j]; /* [renglón]
[columna] */
en lugar de:
day_tab[i,j]; /* INCORRECTO */
Los arreglos de 2 dimensiones pueden
ser inicializados como sigue:
static int day_tab[2][13] =
{
{0,31,28,..,31},
{0,31,29,..,31},
};
Arreglo de apuntadores. Apuntadores
a apuntadores.
Inicialización de arreglos
de apuntadores:
static char *name[]=
{
"mes ilegal",
"enero",
...
"diciembre",
};
Diferencia entre apuntadores y arreglos
multidimensionales.
No se debe confundir las declaraciones:
int a[10][20]; /* matriz de 10*20=200
enteros */
int *b[10]; /* vector de 10 apuntadores
a enteros */
/* o 10 apuntadores a arreglos a enteros
*/
Si consideramos que en los 2 arreglos
anteriores, usamos
[renglón] [columna]
Entonces tenemos 2 diferencias básicas:
1. En el arreglo "a" el número
de columnas (elementos) x renglón es 20 para todos los renglones.
En el arreglo "b" podemos tener un número diferente de columnas
x renglón para cada renglón. Por ejemplo 2 elementos en el
renglón 0; 5 en el renglón 1; e incluso una matriz de 4x4
en el renglón 3, etc.
2. La memoria ocupada por los 2 arreglos
es diferente. Inclusive en el caso de que los apuntadores del arreglo "b"
apuntaran a un arreglo de 20 elementos por renglón, la memoria ocupada
por los 2 arreglos sería diferente.
Para el arreglo "a", 200 enteros.
Para el arreglo "b", 200 enteros +
10 apuntadores.
Argumentos
de la línea de comandos.
Al correr el programa ejecutable producido
por un programa fuente escrito en C, podemos agregar argumentos en la línea
de comandos. Estos argumentos pueden ser usados para producir efectos diferentes
al correr el mismo programa. Así, podríamos usar opciones
como las usadas en los sistemas operativos:
C:> copy
C:> copy a:\arch1 b:
C:> copy a:\arch1 b:\arch2
El procesamiento de los argumentos de la línea de comandos se puede ejemplificar con el siguiente programa en C, que simplemente escribe los argumentos en la salida estándard.
/* escribe los argumentos en la salida
estándard */
main(int argc, char *argv[])
{
while(--argc>0)
printf("%s%c",*++argv,(argc>1)?' ':'\n');
}
"argc" y "argv" son palabras predefinidas. "argc" indica el número de argumentos (incluyendo el nombre del programa ejecutable). "argv" es un arreglo de apuntadores a arreglos de caracteres. "argv[i]" es un apuntador al arreglo de caracteres del argumento i.
Por ejemplo, si "echo.exe" es un programa
ejecutable producido por un programa escrito en C, entonces al ser corrido
con:
echo hello world
los valores que tomarían "argc"
y "argv" en caso de haber sido usados son:
argc=3 *argv[0]=echo *argv[1]=hello
*argv[2]=world
Apuntadores
a funciones
En la definición:
/* compara 2 cadenas */
int strcmp();
En la declaración:
/* apuntador a función que
devuelve un entero */
int (* comp)();
/* función que devuelve apuntador
a entero */
int *comp();
Capítulo 6. ESTRUCTURAS (CONTENIDO)
Por ejemplo, si queremos crear el tipo
"date" y crear 2 variables de ese tipo, "birthdate" y "hiredate". Podemos
usar:
struct date
{
int day;
int month;
int year;
};
date birthdate, hiredate;
o bien, también podemos escribir:
struct date
{
int day;
int month;
int year;
} birthdate, hiredate;
Si solo queremos crear las variables,
y no llamar a su tipo con algún nombre, podemos simplemente teclear:
struct
{
int day;
int month;
int year;
} birthdate, hiredate;
Una estructura puede contener a su
vez estructuras, como en:
struct person
{
char name[NAMESIZE];
char address[ADRSIZE];
long zipcode;
double salary;
struct date birthdate;
struct date hiredate;
}emp;
En el caso de haber declarado la variable
"emp" de la forma anterior, podemos hacer referencia al mes de su fecha
de nacimiento con:
emp.birthdate.month
También podemos inicializar
estructuras estáticas y externas de la siguiente manera:
struct date d={4,7,1776}
Si declaramos apuntadores a estructuras,
como en
struct date *pd;
podemos hacer referencias con la notación:
pd->year
o bien
(*pd).year
NOTAS:
- punto(.) -> () [] tienen la máxima
precedencia
Por lo tanto,
++p->x incrementa x
- La asociatividad de punto(.) y ->
es de izq. a der.
Por lo tanto,
p->q->memb
se entiende como (p->q)->memb
emp.birthdate.month
se entiende como (emp.birthdate).month
Arreglos de estructuras
En lugar de usar arreglos paralelos
en C, como en el ejemplo siguiente para contar palabras reservadas de C,
char *keyword[NKEYS];
int keycount[NKEYS];
se pueden usar estructuras, de la siguiente
manera:
struct key
{
char *keyword;
int keycount;
} keytab[NKEYS];
pero tendremos que asignar valores
despuÚs a "keytab".
También podemos usar declaración
explicita como en:
struct key
{
char *keyword;
int keycount;
} keytab[]=
{
"break",0,
"case",0,
...
"while",0
};
#define NKEYS (sizeof(keytab)/sizeof(struct
key))
donde se define NKEYS en base a los
tamaños de la estructura y la declaración explícita,
para no tener que calcular su valor y recalcularlo cada vez que el contenido
de "keytab" cambie.
Apuntadores a estructuras
Se pueden declarar apuntadores a estructuras,
como por ejemplo, si usamos la estructura "key" anteriormente definida:
struct key *binary(),*pk;
y podemos referenciar a los campos
de la estructura con:
pk->keyword
pk->keycount
Estructuras autoreferenciadas
Las estructuras pueden contener apuntadores
a si mismas como se muestra en el siguiente ejemplo:
struct tnode /* el nodo */
{
char *word; /* apuntador a los caracteres
*/
int count; /* número de ocurrencias
*/
struct tnode *left; /* hijo izquierdo
*/
struct tnode *right; /* hijo derecho
*/
};
Se puede convertir un apuntador a caracter
en un apuntador de estructura mediante el uso de un 'cast' (conversión
forzada).
char *p;
... (struct tnode *) p ...
La anterior conversión es usada
principalmente en el uso de memoria dinámica.
Uniones
Las uniones son usadas cuando se necesita
que una variable tome valores de tipos diferentes. En el ejemplo siguiente:
union u_tag
{
int ival;
float fval;
char *pval;
} uval;
la variable "uval" de tipo "u_tag"
puede tomar valores
"int" si se usa "uval.ival"
"float" si se usa "uval.fval"
"char *" si se usa "uval.pval"
Es necesario el uso consistente de
uniones. Es decir, el tipo utilizado debe ser el tipo más recientemente
almacenado.
Las uniones pueden ser anidadas en
una estructura, como se muestra:
struct
{
char *name;
int flags;
int utype;
union
{
int ival;
float fval;
char *pval;
} uval;
} symtab[NSYM];
y se hace referencia de la siguiente
manera:
symtab[i].uval.ival para ival
symtab[i].uval.pval para el primer
caracter de pval
typedef
El "typedef" permite declarar sinónimos
de tipos. Por ejemplo
typedef int LENGTH;
declara el nombre de LENGTH como sinónimo
de int.
Este "nuevo tipo" podría ser
usado en
LENGTH len, maxlen;
LENGTH *lengths[]; /* arreglo de apuntadores
a tipo LENGTH=int */
Algunas observaciones del uso de "typedef"
- Sintácticamente "typedef"
se parece a las clases de almacenamiento "extern", "static", etc.
- No crea un nuevo tipo, sino añade
un nuevo nombre.
- Es parecido a "#define" pero con
mayor alcance. Por ejemplo, el siguiente sinónimo es para un apuntador
a una función que devuelve un int.
typedef int (*PFI) ();
..
PFI strcmp, numcmp, swap;
Capítulo 7. ENTRADA Y SALIDA (CONTENIDO)
Acceso a la biblioteca estándard
La línea siguiente se coloca
al principio de un programa fuente. Define ciertas macros y variables empleadas
por la biblioteca estándard de E/S.
#include
Se usa < >, en vez de "", para
que el archivo sea buscado en el directorio de información estándard,
por ejemplo:
/usr/include ---> en UNIX
\TURBOC2.0\INCLUDE ---> en MS-DOS
Entrada y salida estándard:
putchar(), getchar()
El uso de la entrada y salida estándard
permite hacer redireccionamiento de y hacia archivos.
prog < arch_ent
prog > arch_sal
o bien procesamiento entubado, como
en:
prog | otr_prog
otr_prog | prog
Por ejemplo, el siguiente programa
copia la entrada estándard (teclado) a la salida estándard
(monitor).
#include <stdio.h>
main()
{
int c;
while ((c=getchar())!=EOF)
putchar(isupper(c) ? tolower(c) :
c);
}
y podría correrse, en el casO
de llamarse "lower", de la siguiente manera:
C:> type file1 | lower > salida
Salida con formato, printf()
La sintaxis es la siguiente:
printf(control,arg1,arg2,...,argn)
donde:
control::="{char}%[-][long_max]convers.[char_o_decim]{char}"
char ::=cualquier caracter
- justificación a la izquierda
long_max::=longitud máxima.
Si no cabe el valor, se viola
convers::=d decimal
o octal
x hexadecimal
u unsigned
c caracter
s cadena(string)
e float o double => [-]m.nnnnnnE[+-]xx
f float o double => [-]mmm.nnnnnn
g más corto que %e o %f. Los
ceros no significativos no se imprimen
char_o_dec número de caracteres
de la cadena o número de decimales
Por ejemplo, si "s" es un apuntador
a "hello, world", se tiene:
0 1 2
1234567890123456789012
printf(":%10s:",s) => :hello world:
printf(":%-10s:",s) => :hello world:
printf(":%20s:",s) => : hello world:
printf(":%-20s:",s) => :hello world
:
Entrada con formato, scanf()
La sintaxis es la siguiente:
scanf(control,arg1,arg2,...,argn)
donde:
control::="{char}%[*]convers[long_max]convers"
char::=cualquier caracter
convers::= d decimal
o octal
x hexadecimal
h entero corto (short)
c caracter
s cadena
f,e punto flotante. Puede usar E+
o E-
l long (double)
Por ejemplo, si declaramos:
int i;
float x;
char name[50];
entonces, la llamada de la función,
líneas leídas y valores asignados, se muestran a continuació:
scanf("%d %f %s",&i,&x,name);
25 54.32E-1 Torres
=> i=25 x=5.432 name="Torres\0"
scanf("%2d %f %*d %25",&i,&x,name);
56789 0123 45182
=> i=56 x=789.0 name="45\0"
Conversión de formato en
memoria, sprintf(), sscanf().
sprintf() y sscanf() hacen la misma
función que printf() y sscanf(), pero en lugar de enviar el resultado
a la salida estándar o leerlo de la entrada estándar, usan
variables de tipo de arreglo de caracteres.
Por ejemplo, si n=123, la siguiente
llamada a sprintf() almacena "temp123\0" en el arreglo de caracteres name.
sprintf(name,"temp%d",n);
Y si name contiene "temp123", el valor
123 se asignará a n al ejecutarse la siguiente llamada
sscanf(name,"temp%d",n);
Acceso a archivos (funciones de
stdio.h)
En la librería <stdio.h>
está definido el tipo FILE. Este tipo permite
guardar información de posición y descripción del
buffer asociado a un archivo.
El tipo FILE se usa de la siguiente
forma:
FILE *fopen(),
*fp;
donde "fp" puede ser usado como apuntador
del archivo que se abre:
fp=fopen(nombre,modo);
donde:
nombre es el nombre del archivo que
se va a abrir
modo puede ser: lectura("r"), escritura("w"),
y añadido("a").
NOTA: si se produce un error, fopen
regresa NULL.
Para escribir y leer información
de un archivo, usamos: putc() y getc(), de la siguiente manera:
c=getc(fp);
/* lee un caracter del archivo apuntado fp */
putc(c,fp);
/* escribe un caracter al arch. apunt. fp */
Existen en la librería <stdio.h>
las definiciones para 3 apuntadores estándard a archivos:
stdin archivo de la entrada estándard
stdout archivo de la salida estándard
stderr archivo de la salida del error
estándard
En base a los apuntadores estándard
anteriores, se pueden definir las funciones putchar() y getchar().
#define getchar() getc(stdin)
#define putchar(c) putc(c,stdout)
También podemos usar entrada
y salida formateada a archivo, con las funciones:
fscanf(fp,formato,lista
de variables);
fprintf(fp,formato,lista
de variables);
con la misma sintaxis que scanf(),printf().
NOTA: Cuando se detecta un error de entrada/salida en archivos, es conveniente escribir el mensaje de error en stderr.
Entrada y salida de líneas
Para almacenar en el arreglo de caracteres
line, una línea de un máximo de MAXLINES caracteres leída
del archivo apuntado por fp, usamos:
fgets(line,MAXLINE,fp)
Para escribir en el archivo apuntado
por fp el arreglo de caracteres line, usamos
fputs(line,fp);
Operaciones con cadenas de caracteres, string.h
En el directorio include de la versión
de C que se estÚ manejando existen encabezados de otras librerías,
entre las que se cuentan string.h. En este encabezado están las
declaraciones para las funciones de manejo de cadenas de caracteres que
se listan a continuación. En adelante, s y t son de tipo apuntador
a caracter, y c y n son enteros.
strcat(s,t);
concatena s al final de t
strncat(s,t,n);
concat. n caracts. de t al final de s.
strcmp(s,t);
regresa negativo, cero, o positivo para: s<t, s==t, s > t
strncmp(s,t,n);
i gual que strcmp pero solo con n caract.
strcpy(s,t);
copia t en s
strncpy(s,t,n);
copia a lo mas n caract. de s en t
strlen(s);
regresa la longitud de s
strchr(s,c);
regresa un apuntador al primer c que estÚ en s, o NULL si no está
presente.
strrchr(s,c);
regresa un apuntador al último c que estÚ en s, o NULL si
no está presente.
Prueba y conversión de cadenas
de caracteres, ctype.h
Varias de las funciones declaradas
en <ctype> realizan pruebas y conversiones de caracteres. En lo que
se muestra a continuación, c es un int que se puede representar
como un unsigned char o EOF. Las funciones regresan int
isalpha(c);
!= 0 si c es alfabÚtica, 0 otro caso.
isupper(c);
mayúscula
islower(c);
minúscula
isdigit(c);
dígito
isalnum(c);
isalpha(c) || isdigit(c)
isspace(c);
blanco, tabulador, nueva línea, retorno, avance de línea,
tabulador vertical
toupper(c);
regresa c convertida a mayúscula
tolower(c);
regresa c convertida a minúscula
ungetc(c,fp)
Esta función se usa para devolver el caracter c al archivo apuntado
por fp.
Funcion system()
La función system() permite
hacer una llamada al sistema operativo. La sintáxis es la siguiente:
system("date");
system("dir");
Memoria
Dinámica
Al declarar (o definir) una variable
en C, normalmente se establece una memoria limitada. Por ejemplo:
int i;
int a[10];
int *c;
Sin embargo si nosotros queremos que
ciertos datos se almacenen en memoria no limitada (salvo la limitación
del "heap" del sistema), podemos usar memoria dinámica.
El siguiente ejemplo muestra como almacenar una cadena arbitrariamente larga de caracteres.
/* programa para almacenar cadena de
longitud ilimitada */
#include <stdio.h>
#include <malloc.c>
struct scar
{
char car; /* caracter */
struct caract *psig; /* apuntador
al siguiente */
} pini, pfin;
main()
{
int c;
struct caract pc;
/* lectura y almacenamiento */
pini=pfin=NULL
if (c=getchar()!=EOF)
do
c=getchar();
for (pini=pfin=NULL; (c=getchar())!=EOF;
)
pfin=(struct scar *) malloc(sizeof(struct
scar))
if (pfin=NULL)
puts("No hay memoria",stderr);
else
pini->car=c, pini->psig=NULL)
while (c!=EOF);
}
INDICE ALFABÉTICO (CONTENIDO)
alcance
de una variable, apuntadores (a
funciones), argumentos
en la línea de comandos, arreglos (multidimensionales),
asociatividad de operadores,
break, char, cadenas
de caracteres, cast, coma,
compilación, constantes
simbólicas, continue, conversiones
de tipos, direcciones, declaraciones,
decremento, double, do
while, encadenamiento, expresiones
condicionales, else if, FILE,
for, funciones, fscanf,
fprintf, getc, goto,
if, if else, include,
incremento, inicialización
de variables, int, isalpha
isupper, islower, isdigit,
isalnum, isspace ,float,
fopen, long, macros,
nombres de las variables, operadores
(aritméticos, relacionales
y lógicos, manejo de bits,
asignación),
or, prioridad
de operadores, putc, recursividad,
sscanf, short, sprintf,
string.h, strcat, strncat,
strcmp, strncmp, strcpy,
strncpy, strlen, strchr,
strnchr, switch, system,
tipos de variables, toupper,
tolower, ungetc, variables
(automáticas, externas, estáticas,
registro), while.
Consultas y sugerencias a: pvale002@pinhue.ufro.cl