Documentos de Académico
Documentos de Profesional
Documentos de Cultura
GuiaCAnsi PDF
GuiaCAnsi PDF
<ldc@ldc.usb.ve>
Enero 2007
ndice
1. Antecedentes
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
2. Introduccin al lenguaje C
Tipos y tamaos de datos
Conversin de tipos . . . .
Expresiones y operadores .
Control de ujo . . . . . .
3.4.1. Sentencia if . . . .
3.4.2. Setencia switch . .
3.4.3. Setencia while . . .
3.4.4. Setencia for . . . .
3.4.5. Break y Continue .
3.4.6. Goto y etiquetas .
4. Estructuras de Datos
4.1.
4.2.
4.3.
4.4.
4.5.
Ejemplo Bsico . . . . . .
Estructuras y Funciones .
Arreglos de Estructuras . .
Apuntadores a Estructuras
Typedef . . . . . . . . . .
5
5
5
6
6
6
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
9
10
12
14
14
14
15
15
16
17
18
18
19
21
24
26
5. Entrada/Salida
27
6. Apuntadores
37
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
37
40
40
44
7. Funciones
8. Estructura de un programa
8.1.
8.2.
8.3.
8.4.
8.5.
8.6.
48
Variables externas . . . . . .
Archivos de Encabezado . .
Estructura de Bloques . . .
Inicializacin . . . . . . . .
Recursin . . . . . . . . . .
El preprocesador de C . . .
8.6.1. Inclusin de Archivos
8.6.2. Substituciones Macro
8.6.3. Inclusin Condicional
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
53
53
58
59
60
60
62
62
62
63
9. Herramientas
65
10.Ejercicios
70
9.1. Makele . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65
9.2. Debugging con gdb . . . . . . . . . . . . . . . . . . . . . . . . 68
9.3. Core dumps . . . . . . . . . . . . . . . . . . . . . . . . . . . . 69
1.
Introduccin
2.
Caracter
Entero corto
Entero
Entero largo
Entero sin signo
Nmero con coma otante
Nmero con coma otante de doble precisin
main ( ) {
p r i n t f ( "Tamano
p r i n t f ( "Tamano
p r i n t f ( "Tamano
p r i n t f ( "Tamano
p r i n t f ( "Tamano
p r i n t f ( "Tamano
de
de
de
de
de
de
#include
<s t d i o . h>
main ( ) {
float
cels , farh ;
farh = 35.0;
c e l s = 5.0 ( farh 32.0 ) / 9 . 0 ;
p r i n t f ( "> %f F son %f C\n" , f a r h , c e l s ) ;
#include
#include
<s t d i o . h>
<math . h>
main ( )
{
int
int
i = 256;
root
r o o t = s q r t ( ( double ) i ) ;
p r i n t f ( " R e s u l t a d o : %d\n" , r o o t ) ;
Cdigo 3: Raz cuadrada de un numero entero
Este programa calcula la raz cuadrada de un nmero entero, previa conversin a tipo double. La conversion es realizada colocando entre parntesis
8
el nombre del tipo requerido justo antes del valor. En este caso, (double), el
resultado de sqrt( (double) i); es tambin un doble, pero es convertido
automticamente a entero en la asignacin a la variable root.
Otro ejemplo se presenta a continuacin:
#include
#include
int
<s t d i o . h>
<math . h>
main ( ) {
float a ;
int b ;
b = 10;
a = 0.5;
i f ( a <=( float ) b ) {
b = ( float ) a ;
}
Cdigo 4: Ejemplo de conversin
complemento a 1
!
complemento a 2, NOT lgico
==, !=
igualdad, desigualdad
&&, ||
AND, OR lgico
<, <=
menor, menor o igual
>, >=
mayor, mayor o igual
Al usarlos debe tenerse en cuenta su precendencia y las reglas de asociatividad, que son las normales en la mayora de lenguajes y en matemticas.
Se debe consultar el manual de referencia para obtener una explicacin detallada. Adems hay toda una serie de operadores que realizan una operacin
9
<e> ?
<x> : <y>
#include
int
<math . h>
main ( )
int a = 1 , b = 3 ;
float x , y = 0 . 0 ;
int PI = 3 . 1 4 1 5 9 ;
x = (a > b) ? a : b ;
( b a > 0 . 0 ) ? ( y=s i n ( PI / 8 ) ) : ( y=c o s ( PI / 4 ) ) ;
Cdigo 5: Expresiones condicionales
10
2.4.1. Sentencia if
La sentencia de control de decisin bsica es if (<e>) then <s> else <t>.
Evala una expresin booleana y, si se cumple, ejecuta la sentencia s. En caso
contrario, ejecuta la sentencia t. La segunda parte de sentencia else <t>,
es opcional.
void
c e r o ( double a ) {
i f ( a == 0 . 0 )
p r i n t f ( " a e s c e r o \n" ) ;
else
p r i n t f ( " a e s d i f e r e n t e de c e r o \n" ) ;
Cdigo 6: Control de ujo con
if
switch
Cuando se encuentra una sentencia case que concuerda con el valor del
switch se ejecutan las sentencias que le siguen y todas las dems a partir
11
char
c ) {
switch ( c ) {
case 'a ' :
p r i n t f ( "Op
break ;
case 'b ' :
p r i n t f ( "Op
break ;
case ' c ' :
case 'd ' :
p r i n t f ( "Op
break ;
default :
p r i n t f ( "Op
}
A\n" ) ;
B\n" ) ;
C o D\n" ) ;
?\n" ) ;
Cdigo 8: Ejemplo de
switch
long r a i z ( long
long r = 1 ;
valor ) {
w h i l e ( r r <= v a l o r )
r++;
return r ;
while
Una variacin ligera de la sentencia while es do ... while, que se escribe
Cdigo 9: Control de ujo con
<i>;
while ( <e> ) {
<s>;
<p>;
}
El ejemplo anterior se podra escribir como:
long r a i z ( long
long r ;
}
valor ) {
f o r ( r = 1 ; r r <= v a l o r ; r++) ;
r;
return
for
13
start_engines () ;
if (
s t a t u s ( ) == WARNING )
break ;
if (
}
count == 0 ) {
launch ( ) ;
p r i n t f ( " S h u t t l e l a u n c h e d \n" ) ;
else
p r i n t f ( "WARNING c o n d i t i o n r e c e i v e d . \ n" ) ;
p r i n t f ( "Count h e l d a t T %d\n" , count ) ;
14
break
3.
Estructuras de Datos
struct point {
int x;
int y;
}
La palabra clave struct introduce la declaracin de una estructura, que
es una lista de declaraciones entre llaves. Este tipo de declaracin dene un
tipo. La llave que cierra la estructura puede estar seguida de una lista de
variables, como cualquier otro tipo.
struct{...} x, y, x;
Esto es sintcticamente anlogo a:
int x, y, z;
En el sentido que cada declaracin establece a estas variables como del
tipo especicado, struct e int y el espacio correspondiente es asginado para
ellas.
Una declaracin que no es seguida de una lista de variables no reserva espacio en memoria, ya solo describe una plantilla de una estuctura: struct x;.
Sin embargo, en el caso struct point pt; se dene una variable pt que es
una estructura de tipo struct point.
Una estructura puede ser inicializada especicando una lista de expresiones constantes para cada miembro, struct maxpt = {320,200};. Igualmente, una estructura puede inicializarse por asignacin o por la llamada de
una funcin que devuelve el tipo correcto.
15
struct rect {
struct point pt1;
struct point pt2;
};
Si se declara screen como un tipo rect, para referirse a la coordenada x
del primer punto es screen.pt1.x.
16
17
char *keyword[NKEYS];
int keycount[NKEYS];
Sin embargo, existe una mejor forma de organizar:
char *word;
int cout;
struct key {
char *word;
int count;
} keytab[NKEYS];
O tambin:
struct key {
char *word;
int count;
};
struct key keytab[NKEYS];
Esto declara una estructura tipo key, dene un arreglo keytab de estructuras de este tipo, y establece el espacio en memoria necesario. Dado que
keytab contiene un conjunto de nombres, la inicializacin es trivial:
struct key {
char *word;
int count;
} keytab[] = {
"auto", 0,
"break", 0,
"case", 0,
"char", 0,
"const", 0,
"continue", 0,
18
};
"default", 0,
/* ... */
"unsigned", 0,
"void", 0,
"volatile", 0,
"while", 0
Es equivalente encerrar entre llaves cada elemento del arreglo cuando son
tipos bsicos:
{ "auto", 0 },
{ "break", 0 },
{ "case", 0 },
...
El programa comienza con la denicin de keytab. La rutina main lee de
la entrada repetidamente cada palabra a travs de la funcin getword. Cada
palabra es chequeada en el arreglo con una funcin que realiza una bsqueda
binaria, por esto las claves deben estar ordenadas de forma creciente.
keywords
int ) ;
main ( )
{
int n ;
char word [MAXWORD] ;
while ( getword ( word , MAXWORD) !=
i f ( i s a l p h a ( word [ 0 ] ) )
i f ( ( n = b i n s e a r c h ( word , keytab ,
keytab [ n ] . count++;
19
EOF)
NKEYS) ) >= 0 )
return
int
f i n d
word
in
b i n s e a r c h ( char word ,
tab [ 0 ] . . . tab [ n
struct
1]
key tab [ ] ,
int
n)
int
int
cond ;
low , high , mid ;
low = 0 ;
high = n 1 ;
while ( low <= h i g h )
{
mid = ( low+h i g h ) / 2 ;
i f ( ( cond = strcmp ( word , tab [ mid ] . word ) ) < 0 )
h i g h = mid 1 ;
else i f ( cond > 0 )
low = mid + 1 ;
else
return
mid ;
return 1;
int
keytab [ n ] . word ) ;
b i n s e a r c h :
}
}
0;
getword :
get
next
word
or
int
c h a r a c t e r
lim )
int c , g e t c h ( void ) ;
void ungetch ( int ) ;
char w = word ;
while ( i s s p a c e ( c = g e t c h ( ) ) )
if
( c != EOF)
w++ = c ;
if (! isalpha (c) )
{
w = ' \0 ' ;
return c ;
}
for ( ; l i m > 0 ; w++)
i f ( ! i s a l n u m ( w = g e t c h ( ) ) )
20
from
input
ungetch ( w) ;
break ;
}
w = ' \0 ' ;
return word [ 0 ] ;
key ,
count
keywords ;
p o i n t e r
v e r s i o n
main ( )
{
MAXWORD) != EOF)
21
int ) ;
if
( i s a l p h a ( word [ 0 ] ) )
( ( p=b i n s e a r c h ( word , keytab , NKEYS) ) != NULL)
p>count++;
for ( p = keytab ; p < keytab + NKEYS; p++)
i f ( p>count > 0 )
p r i n t f ( " %4d %s \n" , p>count , p>word ) ;
return 0 ;
b i n s e a r c h :
struct
{
n)
f i n d
word
in
tab [ 0 ] . . . tab [ n
1]
int
int cond ;
struct key low = &tab [ 0 ] ;
struct key h i g h = &tab [ n ] ;
struct key mid ;
while ( low < h i g h )
}
}
if
else
return
return
mid ;
NULL;
Cdigo 13: Keyword Count V2
3.5. Typedef
C provee una facilidad llamada typedef para crear nuevos nombres para
tipos de datos. Por ejemplo, la declaracin:
23
4.
Entrada/Salida
#include
#include
main ( )
{
<s t d i o . h>
<c t y p e . h>
lower :
c o n v e r t
input
to
lower
case
int c
while
( ( c = g e t c h a r ( ) ) != EOF)
putchar ( tolower ( c ) ) ;
return 0 ;
Cdigo 14: Lower
La funcin tolower est denida en <ctype.h>; convierte un string de
24
mayscula a minscula.
#include
/
m i n p r i n t f :
l i s t
void
<s t d a r g . h>
minimal
p r i n t f
v a r i a b l e
argument
m i n p r i n t f ( char fmt , . . . )
v a _ l i s t ap ; / p o i n t s
char p , s v a l ;
int i v a l ;
double d v a l ;
v a _ s t a r t ( ap , fmt ) ; /
for
with
arg
if
{
}
to
each
make
ap
( p = fmt ; p ; p++)
( p != ' %' )
putchar (p) ;
continue ;
switch (++p )
case
'd ' :
i v a l = va_arg ( ap ,
int ) ;
26
unnamed
p oi n t
to
arg
1 st
in
turn
unnamed
p r i n t f ( " %d" , i v a l ) ;
break ;
case ' f ' :
d v a l = va_arg ( ap , double ) ;
p r i n t f ( " %f " , d v a l ) ;
break ;
case ' s ' :
for ( s v a l = va_arg ( ap , char ) ; s v a l ; s v a l ++)
putchar ( s v a l ) ;
break ;
default :
putchar (p) ;
break ;
}
}
va_end ( ap ) ;
c l e a n
up
when
done
27
fp = fopen(name, mode);
El primer argumento de fopen es un string que contiene el nombre del
archiv, el segundo es el modo de apertura del archivo: lectura (r), escritura
(w) y agregar (a). Si un archivo que no existe es abierto para escritura o
para aadir, es creado si es posible. Abrir archivos existente para escritura
causa que el viejo contenido sea desechado, mientras que se indica el modo
append preserva el contenido.
El abrir un archivo que no existe para lectura indicar un error. Otras
causas de error son tratar de leer un archivo cuando no tiene los permisos
apropiados. Si existe algn error, fopen retornar NULL.
Existen un par de funciones tiles que permitirn la escritura y lectura de
un archivo, una vez abierto. getc retorna el prximo caracter de un archivo
a ser ledo, EOF si encuentra nal de archivo o error: int getc(FILE *fp)
Por otro lado, putc es una funcin para escribir en un archivo:
int putc(int c, FILE *fp)
putc escribe el caracter c al archivi fp y retorna el caracter escrito, o EOF
si un error ocurri.
Cuando un programa en C se ejecuta, el sistema operativo es responsable
de abrir tres archivos y provee apuntadores para stos. Estos archivos son
la entrada estndar, la salida estndar y el error estndar; los apuntadores
correspondientes son denominados stdin, stdout, y stderr, y son declarados
en <stdio.h>. Normalmente, stdin esta asociado al teclado, y stdout y stderr estn asociados a la pantalla. Sin embargo, stdin y stdout pueden ser
redirigidos a otros archivos.
Adems, esta libreria ofrece las funciones fscanf y fprintf para escritura
28
#include
/
cat :
<s t d i o . h>
c o n c a t e n a
a r c h i v o s
else
while( a r g c > 0 )
i f ( ( f p = f o p e n(++argv ,
{
de
s t d i n
}
else
{
f i l e c o p y ( fp , s t d o u t ) ;
f c l o s e ( fp ) ;
}
return 0;
/ f i l e c o p y : c o p i a a r c h i v o i f p a o f p /
v o i d f i l e c o p y ( FILE i f p , FILE o f p )
{
int c ;
w h i l e ( ( c = g e t c ( i f p ) ) != EOF)
putc ( c , o f p ) ;
}
Cdigo 16: Versin propia de cat
29
Los apuntadores stdin y stdout son objetos de tipo FILE *. stos son
constantes, por esto no es posible asignarles algo.
Finalmente, la funcin fclose realiza lo inverso de fopen, cierra la conexin
entre el apuntador al archivo y el propio archivo. Por algunos parmetros que
se establecen en el sistema operativo, es buena idea ejecutar esta operacin
siempre que se abre un archivo.
#include
/
cat :
<s t d i o . h>
c o n c a t e n a
archivos
v e r s i o n
if
e r r o r e s
else
while ( a r g c > 0 )
i f ( ( f p = f o p e n(++argv ,
del
programa
para
( a r g c == 1 ) / n o a r g s
f i l e c o p y ( stdin , stdout ) ;
c o p i a
de
s t d i n
else
{
}
if
f i l e c o p y ( fp , s t d o u t ) ;
f c l o s e ( fp ) ;
( f e r r o r ( stdout ) )
f p r i n t f ( s t d e r r , " %s : e r r o r w r i t i n g s t d o u t \n" ,
prog ) ;
exit (2) ;
}
exit (0) ;
30
/* CORRECTO */
q = p->next;
free(p);
Algunas funciones matemticas se encuentran disponibles en <math.h>
sin(x) sine of x, x in radians
cos(x) cosine of x, x in radians
atan2(y,x) arctangent of y/x, in radians
exp(x) exponential function ex
log(x) natural (base e) logarithm of x (x>0)
log10(x) common (base 10) logarithm of x (x>0)
pow(x,y) xy
sqrt(x) square root of x (x>0)
fabs(x) absolute value of x
33
5.
Funciones
int
int
D e c l a r a c i o n
l o n g i t u d
f u n c i o n e s
maxima
de
la
char
/
de
Funcin
p r i n c i p a l
patron
34
buscar
l i n e s
main ( ) {
char l i n e [MAXLINE ] ;
int found = 0 ;
while ( g e t l i n e ( l i n e , MAXLINE) > 0 )
i f ( s t r i n d e x ( l i n e , p a t t e r n ) >= 0 )
{
p r i n t f ( " %s " , l i n e ) ;
found++;
}
return found ;
}
/
y
g e t l i n e :
r e t o r n a
la
guarda
la
l o n g i t u d
int g e t l i n e ( char
int c , i ;
s [] ,
l i n e a
en
la
v a r i a b l e
int
lim ) {
i = 0;
while ( l i m
> 0
&& ( c=g e t c h a r ( ) ) != EOF && c != ' \n ' )
s [ i ++] = c ;
i f ( c == ' \n ' )
s [ i ++] = c ;
s [ i ] = ' \0 ' ;
return i ;
}
/
s t r i n d e x :
s i
no
r e t o r n a
e l
i n d i c e
de
en
s ,
{
{
s [ j ]==t [ k ] ; j ++, k++)
return
return 1;
Cdigo 18: Grep propio
El pseudocdigo a muy alto nivel para mayor claridad es:
35
#include
/
en
a t o f :
un
<c t y p e . h>
c o n v i e r t e
punto
un
f l o t a n t e
s t r i n g
de
p r e c i s i n
36
doble
double a t o f ( char s [ ] )
double val , power ;
int i , s i g n ;
/
for
s a l t a
e s p a c i o s
en
blanco
( i = 0 ; i s s p a c e ( s [ i ] ) ; i ++)
;
s i g n = ( s [ i ] == ' ' ) ? 1 : 1 ;
i f ( s [ i ] == '+ ' | | s [ i ] == ' ' )
i ++;
for ( v a l = 0 . 0 ; i s d i g i t ( s [ i ] ) ; i ++)
val = 10.0 val + ( s [ i ] '0 ' ) ;
i f ( s [ i ] == ' . ' )
i ++;
for ( power = 1 . 0 ; i s d i g i t ( s [ i ] ) ; i ++)
{
val = 10.0 val + ( s [ i ] '0 ' ) ;
power = 1 0 ;
}
return s i g n v a l / power ;
Cdigo 19: atof propio
max) ;
sum = 0 ;
while ( g e t l i n e ( l i n e , MAXLINE) > 0 )
p r i n t f ( " \ t %g \n" , sum += a t o f ( l i n e ) ) ;
return 0 ;
37
}
Cdigo 20: Calculadora rudimentaria
La declaracin double sum, atof(char []); indica que la variable sum
es de tipo double y que la funcin atof(char []) toma un argumento de tipo
char[] y retorna un argumento de tipo double. sta ltima debe ser declarada
y denida consistentemente. Esto es, si en el prototipo de la funcin se indica
que retorna o toma parmetros de algn tipo en particular, la denicin de
la funcin debe indicar lo mismo, si no dar un error a nivel de compilacin.
La declaracin y denicin puede parecer redundate, pero este es un estndar utilizado por todos los programadores de este lenguaje. Esto permite
reutilizacin e inclusin eciente de funciones. En caso de no declarar un
prototipo, se asume que la primera aparicin de la funcin es el prototipo.
Dada la funcin atof correctamente declarada, se puede denir las atoi
de la siguiente manera:
Estructura de un programa
39
#include
#include
<s t d i o . h>
< s t d l i b . h>
tamao
un
operando
#define
/
#define
para
a t o f ()
de
o pe ra do r
MAXOP 100
s e a l
numero
mximo
de
fue
que
un
e n c o n t r a d o
int g e t o p ( char [ ] ) ;
void push ( double ) ;
double pop ( void ) ;
/
r e v e r s e
P o l i s h
c a l c u l a t o r
main ( )
{
int type ;
double op2 ;
char s [MAXOP] ;
while ( ( type =
{
switch
g e t o p ( s ) ) != EOF)
( type )
case
NUMBER:
push ( a t o f ( s ) ) ;
break ;
case '+ ' :
push ( pop ( ) + pop ( ) ) ;
break ;
case ' ' :
push ( pop ( ) pop ( ) ) ;
break ;
case ' ' :
40
op2 = pop ( ) ;
push ( pop ( ) op2 ) ;
break ;
case ' / ' :
op2 = pop ( ) ;
i f ( op2 != 0 . 0 )
push ( pop ( ) / op2 ) ;
else
p r i n t f ( " e r r o r : z e r o d i v i s o r \n" ) ;
break ;
case ' \n ' :
}
}
return
0;
Cdigo 21: Calculadora Polaca Reversa
Es importante destacar que en el caso de los operadores de resta y divisin el orden debe ser distinguido, es decir, es incorrecta la operacin
push(pop() - pop()) por esto se utiliza una variable temporal. Las operaciones de pila se denen a continuacin:
#define
/
v a l o r
int sp
double
/
if
maximo
de
p r o f u n d i d a d
= 0; / p r o x i m a
v a l [MAXVAL] ; /
push :
void
MAXVAL 100
empila
p o s i c i o n
p i l a
la
l i b r e
p i l a
de
la
p i l a
push ( double f )
( sp < MAXVAL)
v a l [ sp++] = f ;
else
}
de
41
pop :
double
{
if
d e s e m p i l a
pop ( void )
r e t o r n a
e l
v a l o r
del
top
( sp > 0 )
v a l [ sp ] ;
return
else
p r i n t f ( " e r r o r : s t a c k empty\n" ) ;
return 0 . 0 ;
#include
<c t y p e . h>
int g e t c h ( void ) ;
void ungetch ( int ) ;
/
getop :
c a r a c t e r
int
o b t i e n e
u
e l
operando
g e t o p ( char s [ ] )
proximo
numerico
int i , c ;
while ( ( s [ 0 ]
s [ i ] = ' \0 ' ;
i f ( c != EOF)
ungetch ( c ) ;
return NUMBER;
Cdigo 23: Lectura de operandos y operadores
int
{
}
o b t i e n e
c a r a c t e r
g e t c h ( void )
return
c o l o c a
void
un
if
l i b r e
en
buf
c a r a c t e r
ungetch ( int c )
en
e l
input
else
}
buf [ bufp++] = c ;
Cdigo 24: Lectura de caracteres
#define
Para este programa se pens dado su tamao que solo era necesario un
archivo de encabezado, sin embargo, para programas ms extensos se aconseja
tener tantos archivos de encabezado como lo requiera la estructuracin del
cdigo.
44
if (n > 0)
{
int i;
for (i = 0; i<n; i++)
...
}
El alcance de la variable i se limita al bloque; esa i no esta relacionada
con alguna otra i fuera l. Una variable declarada e inicializada en un bloque
se inicializa cada vez que se ejecuta, por esto no es recomendable utilizar este
estilo de programacin al menos que sea la intencin.
Por ejemplo,
int x;
int y;
f(double x)
{
double y;
}
En este caso, las ocurrencias de la variable x en el bloque no se reere al
entero, si no al doble. Afuera de la funcin f, se reere al entero.
6.4. Inicializacin
La inicializacin consiste en darle un primer valor a una variable, en la
ausencia de una inicializacin explcita, las variables externas y estticas se
le da un valor de cero por defecto.
Las variables escalares pueden ser inicializadas al ser denidas, seguida
de un signo de igualdad y una expresin:
int x = 1;
char squota = '\'';
long day = 1000L * 60L * 60L * 24L;
int days[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
6.5. Recursin
Una funcin recursiva posee en su cuerpo llamadas directas o indirectas
a s misma. Un clsico ejemplo de recursin es el algoritmo de Quicksort
desarrollado por C.A.R. Hoare en 1962.
45
q s o r t :
void
] . . . v [ r i g h t ]
int
v[] ,
( l e f t >= r i g h t )
return ; / m e n o s
left ,
int
de
i,
dos
int
int
en
orden
swap :
i n t e r c a m b i a
swap ( int v [ ] ,
v [ i ]
int
i,
int
j);
e l e m e n t o s
v [ j ]
c r e c i e n t e
right )
swap ( v , l e f t , ( l e f t + r i g h t ) / 2 ) ;
last = left ; / a v [ 0 ] /
for ( i = l e f t + 1 ; i <= r i g h t ; i ++) /
if (v [ i ] < v [ l e f t ])
swap ( v , ++l a s t , i ) ;
swap ( v , l e f t , l a s t ) ; / r e s t a u r a e l e m
q s o r t ( v , l e f t , l a s t 1) ;
q s o r t ( v , l a s t +1, r i g h t ) ;
void
v [ l e f t
q s o r t ( int v [ ] ,
int i , l a s t ;
void swap ( int
if
s o r t
p a r t i c i n
j)
int
temp ;
temp = v [ i ] ;
v[ i ] = v[ j ];
v [ j ] = temp ;
Cdigo 26: Quicksort
46
6.6. El preprocesador de C
Este es el primer paso de la compilacin, contiene un lenguaje peculiar
distinto al usado en un programa en C regularmente, cada expresin es denominada macro. Algunas caractersticas tiles son presentadas en esta seccin.
Si se examina el macro, se puede notar que ambas expresiones son evaluadas dos veces, por ejemplo la llamada max(i++, j++) es errnea ya esto
incrementar la variable i e j en el doble.
De manera contraria, los nombres pueden ser indenidos cuando sea requerido con la directiva #undef
#if !defined(HDR)
#define HDR
...
#endif
Durante el preprocesamiento, el primer archivo que haga la inclusin de
hdr.h dene la variable HDR ; las siguientes inclusiones que hagan otros archivos conseguirn que esta variable se encuentra denida, as que saltar la
parte del cdigo hasta #endif. Esto para evitar incluir un archivo muchas
veces. Si este estilo se sigue de manera consistente, el programador no debe
estar preocupado en cuntas veces incluy un archivo o no.
La siguiente secuencia chequea SYSTEM y decide que versin del encabezado incluir:
#endif
#include HDR
De la misma manera, existen dos directivas #ifdef y ifndef que son
an mas especcas dado que verican si un nombre ya esta denido. Otra
manera de realizar alguno de los ejemplos previos puede ser:
#ifndef HDR
#define HDR
...
#endif
49
7.
Apuntadores
7.1. Qu es un apuntador?
El lenguaje de programacin C est repleto de apuntadores. Es realmente
imposible dominar este lenguaje sin entender completamente qu son los
apuntadores.
El problema de los apuntadores es que la mayora de la gente que programa no tiene un conocimiento bsico de cmo se manejan las variables en
el lenguaje C. Una variable es bsicamente un contenedor declarada de un
tipo (int, short, long, etc) dnde se puede almacenar informacin, que es variable en el tiempo. Esto se d porque el lenguaje C funciona en un mundo
intermedio, entre la programacin de alto nivel y el mundo de los bits.
Cuando se declara una variable en C, se le asigna un tipo y un nombre. Por
debajo el compilar reserva un espacio en memoria del tamao de la variable
deseada (ver Cdigo 2, pgina 9) . Si no se le asigna un valor a la variable
inmediatamente, el espacio sigue reservado (Una razn muy fuerte para no
declarar variables que no se usan). Una vez asignado un valor, el espacio en
memoria mantiene esta informacin en el formato y con los lmites necesarios.
Si se considera a una variable regular de C, que tiene un espacio en memoria donde se guarda el contenido y una direccin de memoria que indica
dnde est la informacin, un apuntador es un tipo de variable que contiene
una direccin de memoria para llegar a un contenido.
Todos los tipos de C tienen apuntadores y existen diversas formas de
declarar estas variables, todas reconocidas por el compilador de C:
int* ptr;
int *ptr;
int * ptr;
Para mantener la consistencia, al declarar la variable apuntador se usar
en esta guia la siguiente:
int *ptr;
En C, las variables declaradas sin asignacin inmediata son inicializadas
por el compilador a un valor predeterminado. Para los tipos nmericos (int,
oat, double) el valor es la representacin del 0. Para todas las variables tipo
apuntador este valor es NULL.
Una vez declarada una variable de tipo apuntador no es necesario usar
* para la asignacin, ya que despus de la asignacin ptr es siempre una
50
variable tipo apuntador a algo. Si se usa * antes de una variable esto tiene
un signicado completamente diferente, que se describir ms adelante.
Existen dos operadores unarios importantes al hablar de apuntadores.
Uno es & y el otro es la misma * usada para declarar variables de tipo
apuntador.
Ya hemos dicho que toda variable en C tiene reservado un pedazo de la
memoria existente. Tomando eso en cuenta tiene que haber una forma de
obtener la direccin de la memoria donde est almacenado una informacin.
El operador & hace justamente esto. Unido a una variable cualquiera, usando
este operador, se obtiene la direccin de memoria donde est la informacin.
Un ejemplo es la siguiente linea de cdigo:
(...)
int k = 6;
printf("La direccion de k es: %p\n", &k);
(...)
Una vez que se tiene una variable tipo apuntador, el agregarle una *
durante su asignacin o utilizarla dentro del cdigo, lo deferencia. Bsicamente *ptr afecta el contenido del espacio de memoria donde apunta ptr. Un
ejemplo pequeo se muestra abajo:
(...)
int *ptr;
int k;
ptr = &k;
*ptr = 8;
(...)
Para concretar estas ideas, mostramos el siguiente ejemplo:
1
2
3
4
5
6
7
8
9
10
11
m;
main ( void ) {
j =
k =
m=
ptr
1;
2;
3;
= &k ;
51
12
13
14
15
16
17
18
19
20
21
22 }
k = m;
p r i n t f ( " \n" ) ;
p r i n t f ( " j t i e n e e l v a l o r % d y e s t a almacenado en %
p\n" , j , ( void )&j ) ;
p r i n t f ( "k t i e n e e l v a l o r % d y e s t a almacenado en %
p\n" , k , ( void )&k ) ;
p r i n t f ( "m t i e n e e l v a l o r % d y e s t a almacenado en %
p\n" , m , ( void )&m) ;
p r i n t f ( " p t r t i e n e e l v a l o r % p y e s t a almacenado en
% p\n" , ptr , ( void
)&p t r ) ;
p r i n t f ( " El v a l o r d e l e n t e r o apuntado por p t r e s % d
\n" , p t r ) ;
return
0;
Cdigo 27: Ejemplo de apuntadores
[hola@maquina]$ ./apuntador
j tiene el valor 1
k tiene el valor 3
m tiene el valor 3
ptr tiene el valor
El valor del entero
Como pueden apreciar, los valores que resultan de la corrida del programa
son los deseados. Lo importante notar es la diferencia entre la asignacin
hecha a ptr en la linea 10 y la posterior asignacin de k a m. Esto refuerza
52
la idea de que una variable tipo apuntador slo tiene la informacin sobre
dnde est el contenido de algo ya que de la primera asignacin de 2 a k,
ste se convierte en 3 y cambia el contenido al que apunta tambin ptr. Esto
se puede ver en la lnea de salida nmero 4 donde la direccin en memoria
es igual a la direccin de la variable k.
En el cdigo 19 tambin se muestran los usos de & y * en la linea 13
hasta la 18.
La idea de apuntadores no es fcil de entender y slo con la prctica se
puede reforzar este concepto. Tmese un tiempo para digerir la informacin
de est seccin antes de pasar ms adelante.
7.2.1. Arreglos
El arreglo es la estructura de datos ms bsica en computacin. La idea
de un arreglo es sencilla. Imagnese los vagones de un tren, cada uno con
un nmero, siendo 0 el primer vagn. En cada vagon se puede meter algun
contenido, pero todos los contenidos del tren tienen que ser del mismo tipo.
A diferencia de otros lenguajes de programacin los arreglos en C debe
ser inicializados sabiendo su tamao mximo. Un ejemplo es:
int miarreglo[10];
En este ejemplo se crea un arreglo de enteros con 10 casillas, del 0 al 9.
Otro ejemplo es:
int
main ( void ) {
int
i;
p r i n t f ( " \n\n" ) ;
( i = 0 ; i < 6 ; i ++) {
p r i n t f ( " m i a r r e g l o[ %d ] = %d\n" , i , m i a r r e g l o [ i ] )
;
}
return 0 ;
for
hola *ptr;
La expresin:
ptr + 1
se resolvera con la ecuacin:
ptr++
++ptr
ptr---ptr
son tambin vlidas dentro de C. Ha que tener cuidado con estas ya que
su ejecucin cambia en que momento de la ejecucin se le agrega (o resta)
al apuntador. Recuerdense que si uno por error intenta acceder a un espacio
de memoria no reservado, el programa explota, en el caso de acceder a un
espacio reservado para otra variable, pueden pasar cosas interesantes, pero
difciles de corregir.
Si han entendido el concepto hasta ahora, se preguntarn cmo es que con
un arreglo puedo hacer miarreglo[x] donde x es un nmero denido dentro
del arreglo y cmo es que puedo usar un apuntador para hacer exactamente
lo mismo. Esto se debe a que la declaracin de una variable tipo arreglo es
en verdad un apuntador.
Para demostrarlo tenemos este ltimo pedazo de cdigo:
int
main ( void ) {
int
i;
p r i n t f ( " \n\n" ) ;
for ( i = 0 ; i < 6 ; i ++) {
p r i n t f ( " m i a r r e g l o[ %d ] = %d\n" , i , ( m i a r r e g l o +
i));
56
return
0;
main ( ) {
c o n t e n e d o r [ 5 0 ] = " Hola e s t o e s una prueba " ;
contenedor2 [ 5 0 ] ;
char
char
char pA ;
char pB ;
apuntador
un
t i p o
apuntador
otro
char
t i p o
char
pA = c o n t e n e d o r ; / a p u n t a p A a c o n t e n e d o r /
p r i n t f ( " %s > Muestra a l o que apunta t i e n e
c o n t e n e d o r \n" , c o n t e n e d o r ) ;
p r i n t f ( " %s > Muestra a l o que apunta pA\n" , pA) ;
pB = c o n t e n e d o r 2 ;
p r i n t f ( " \n" ) ;
while ( pA
apunta
!= ' \0 ' ) {
58
pB
c o n t e n e d o r 2
pB++ = pA++;
pB = ' \0 ' ;
p r i n t f ( " %s > Muestra a l o que apunta c o n t e n e d o r 2
a l f i n a l de todo . \ n" ,
contenedor2 ) ;
return ( 0 ) ;
Cdigo 31: Declarando y imprimiendo un arreglo de char (String)
Lo importante es ver aqu cmo la variable %s en printf, imprime el arreglo de char sin problema. Tambien hay un fuerte uso de apuntadores y de sus
asignaciones. Fjese bien lo que pasa dentro del ciclo del while. Aqui se est
asignando a cada casilla del contenedor2 la informacin de la variable contenedor. Tambin muy importante es el agregado del \0 despus de haber
copiado caracter por caracter.
Si bien esto no es como se copiara un arreglo normalmente en C, es una
demostracin muy interesante de como los arreglos de char, los apuntadores
y los strings estn unidos en este lenguaje.
59
8.
Herramientas
8.1. Makele
Un archivo Makele es una especicacin de dependencias entre archivos y de cmo resolverlas para lograr un objetivo global. Estos archivos son
procesados por la utilidad make, disponible es cualquier sistema UNIX.
El programa make actualiza todos los objetivos a travs de la actualizacin de todas sus dependencias. Un grafo potencialmente complejo es formado
cuando se realiza el procesamiento de estos archivos. Un ejemplo sencillo es:
a l l : foo
f o o : f o o . o bar . o baz . o
.c.o:
$ (CC) $ (CFLAGS) c $< o $@@
. l .c:
$ (LEX) $< && mv l e x . yy . c $@@
Cdigo 32: Ejemplo Sencillo Makele
El grafo de fuentes asociado a este ejemplo es:
clean:
rm *.o core
Esto indicar que al ejecutar el comando make clean, se borraran los
archivos con extensin .o y el llamado core.
Los comentarios de un archivo Makele comienzan con el caracter #
hasta el nal de la lnea. El siguiente ejemplo muestra tres objetivos individuales con dependencias individuales:
CC=gcc
Al ser procesado, donde se haya especicado la variable $(CC) se sustituye
literalmente con el valor gcc. Make tiene un conjunto de variables denidas
por defecto, el valor primario de $CC es cc.
Las macros mas comunes son $@ y $<. stas representan los nombres de
los objetivos y la primera dependencia de la regla en la que aparece. En el
siguiente Makele se puede ilustrar,
a l l : dummy
61
$ make
touch dummy
all depends on dummy
62
8.2.
Al escribir un programa en cualquier lenguaje suele ocurrir que cometemos errores. Los programas escritos pueden tener un comportamiento diferente al que esperbamos debido a que nuestro algoritmo era incorrecto o que
nos equivocamos al escribirlo. El proceso de depuracin, ms comnmente
conocido como debugging consiste en la bsqueda y eliminacin de errores en
un programa ya escrito.
Dado que realizar debugging de un programa puede llegar a ser una tarea
muy tediosa, existe una variedad de herramientas que ayudan en el proceso.
En particular est disponible GDB, un debugger que forma parte del proyecto GNU. ste permite examinar la ejecucin de un programa mientras est
ejecutndose o al haber terminado su ejecucin. Permite detener el programa
en puntos especicados para observar su estado e intentar localizar la fuente
de cualquier error detectado.
Para que nuestro programa ejecutable contenga informacin de debugging
que nos ayude durante el uso de GDB, debemos compilar usando la opcin
-ggdb de gcc. Por ejemplo, podemos hacer:
gdb bugs
Al hacer esto estaremos frente a un prompt de gdb en el cual podemos
escribir comandos. Hay una gran cantidad de comandos disponibles; los usuarios ms experimentados tienen muchas posibilidades al utilizar esta herramienta. Sin embargo, conociendo un pequeo conjunto de comandos importantes podemos aprovecharla. Por supuesto, la mejor manera de aprenderlos
es usndolos. Experimente con los siguientes comandos. Puede utilizar el
comando help para obtener ms ayuda sobre cada uno. Encuentre los errores
en el programa 35, que intenta encontrar el menor nmero n tal que b(n)
es mayor que 10000000.
break
print
display
bt
list
63
run
set args
next
step
#include
int
int
<s t d i o . h>
f i b ( unsigned
if
( n == 0 )
if
(n = 1)
return
return
return
int
0;
1;
f i b ( n 1)+f i b ( n 2) ;
int
if
{
}
char
argv )
i = 0 , encontrado = 0 ;
while
n)
( encontrado = 0)
( f i b ( i ) > 10000000)
encontrado = 1 ;
p r i n t f ( " f i b( %d ) = %d\n" , i , f i b ( i ) ) ;
}
return
0;
Cdigo 34: programa incorrecto
break main
o, dado que main empieza en la lnea 14,
break 14
Hecho esto, escribimos
run
para iniciar la ejecucin del programa. ste se detendr inmediatamente,
permitindonos darle ms comandos a GDB. Podemos usar los comandos
print y display para ver el contenido de una variable dada. next y step avanzan un paso en la ejecucin.
65
9.
Ejercicios
1. Escriba una funcin strindex(s,t) que retorne la posicin de la ocurrencia ubicada ms a la derecha, del elemento t en el string s. En caso de
que haya ninguna ocurrencia retornar cero.
2. Escriba una funcin similar a la estudiada previamente atof para manejar la notacin cientca de la forma: 123.45e-6, cuando un punto
otante este seguido de una e o E y opcionalmente un signo en el
exponente.
3. Dena un macro swap(t,x,y) que intercambie dos argumentos, x e y, de
tipo t.
4. Escriba un programa para comparar dos archivos, imprimiendo las primero lnea donde dieren.
5. Modique el programa de Grep propio dado en captulos previos, que
tomen como entrada de parmetros un conjunto de nombres de archivos
y busque el patrn en todos esos archivos.
6. Escriba un programa que imprima en pantalla un conjunto de archivos,
entre cada archivo debe imprimir una lnea en blanco y se debe indicar
el nombre del archivo correspondiente en la salida.
66