Вы находитесь на странице: 1из 54

HISTORIA DE C

C evolucion a partir de dos lenguajes previos, BCPL y B.CPL fue desarrollado en 1967 por Partir Richards; como un lenguaje para escribir software y compiladores de sistemas operativos. Ken Thompson model muchas caractersticas de su lenguaje B siguiendo sus contrapartidas en BCPL, y utiliz B en 1930 para crear versiones iniciales del sistema operativo UNIX en los laboratorios bell. Tanto el BCLP como B eran lenguajes sin tipo cada elemento de datos ocupaba una palabra en memoria y quedaba a cargo del programador el tratar
un elemento de datos como si tratar de un nmero entero o de un nmero real.

Elementos para crear un programa en C


El lenguaje C es un lenguaje compilado, todos los sistemas de C consisten en general en tres partes: el entorno, el lenguaje y la biblioteca estndar. Los programas C pasan a travs de seis fases para su ejecucin. Estas fases son: 1.Editar: El programa es creado en el editor y almacenado en disco, adems de
ser corregido de ser necesario.

Localizacin <locale.h> Matemticas <math.h> Salto no locales <setjmp.h> Manejo de seales <signal.h> Argumentos variables <stdarg.h> Entrada/Salida <stdio.h> Utileras generales <stdlib.h> Manejo de cadenas <string.h> Fecha y hora <time.h>

El lenguaje C fue derivado del lenguaje del lenguaje B por Dennis Ritchie, de los Laboratorios Bell, y al inicio se hizo muy conocido como lenguaje de desarrollo del sistema operativo UNIX. A lo largo de las ultimas dos dcadas el siglo anterior, C se hizo disponible para la mayor parte de las computadoras. C es independiente del Hardware. Con un diseo cuidadoso, es posible escribir programas en c que sean porttiles hacia la mayor parte de las computadoras. C utiliza muchos conceptos importantes de BCPL y de B, adems de aadir los
tipos de datos y otras caractersticas poderosas.

Hacia finales de los 70, C haba evolucionado a lo que se conoce como C tradicional, la expansin rpida de C sobre varios tipos de computadoras trajo consigo muchas variantes. Estas eran similares, pero a menudo no eran compatibles. Esto resultaba en un problema serio para los desarrolladores de programas , que necesitaban escribir cdigos que funcionaran en varias plataformas. Se hizo cada vez ms evidente que era necesaria una versin estndar de C. En 1983, se cre un comit tcnico bajo el American National Standards Committee on Computers and Information Processing para proporcionar una definicin no ambigua e independiente de tipo de mquina del lenguaje. En 1989 el standard quedo aprobado. El documento se conoce como ANSI/ISO 9899: Actualmente se cuenta con diversos compiladores par C los ms conocido con Turbo C (TC) , Borland C(BC) y gcc, las variantes mas importantes entre los estos compiladores es que tanto el TC como el BC cuentan con un procesador de textos para editar los programas, a diferencia de gcc para el cual primero debemos editar el programa con un procesador de textos y posteriormente compilarlo, otra variante es que BC cuenta con la librera conio.h la cual no se encuentra entre las libreras Standard de C las cuales son: Errores <errno.h> Definiciones comunes <stddef.h> Diagnsticos <assert.h> Manejo de Caracteres <ctype.h>

2.Preprocesar : El programa preprocesador procesa el cdigo, el preprocesador C obedece comandos especiales que se llaman directrices de preprocesador que indican que antes de la compilacin se deben ejecutar ciertas manipulaciones sobre el programa que consisten en la inclusin de otros archivos en el archivo a compilar y en el reemplazo de smbolos especiales en el texto del programa, tambin elimina los comentarios del programa. 3.Compilar: El compilador crea el cdigo objeto es decir traduce el programa C a cdigo de lenguaje de mquina y lo almacena en disco. 4.Enlazar: El enlazados vincula el cdigo objeto con las bibliotecas, crea el archivo a.out y lo almacena en disco. 5.Cargar: El cargador coloca el programa en memoria. 6.Ejecutar: El CPU toma cada una de las instrucciones y las ejecuta, almacenando posiblemente nuevos valores de datos conforme se ejecuta el programa.

Elementos bsicos de un programa en C


Un programa en C tiene la siguiente organizacin: 1. 2. 3. 4. 5.
Directrices el preprocesador:

2. Float (flotante) 4 bytes 3. double (Un flotante mas grande) 8 bytes 4. char (carcter) 1 bytes El tamao en bytes mencionado anteriormente pude variar de acuerdo al procesador. A partir de los datos anteriores podemos combinarlos con algunos modificadores para aumentar su capacidad de almacenamiento estos modificadores son los siguientes: long unsigned signed short El estndar ANSI especifica que el rango mnimo de valores para
enteros short es de -32767 a +32767. Para la gran mayora de los clculos enteros,

libreras. Variables globales. variables estticas.


Definicin de las funciones Definicin de estructuras

Funciona principal
Cuerpo de las funciones definidas al inicio

Un ejemplo sencillo de un programa en C es el siguiente: #include<stdio.h> main()


{

printf(" \nHola mundo\n");


}

La compilacin y salida del programa se realiza de la siguiente manera: [rolando@localhost rolando]$ gcc hola.c [rolando@localhost rolando]$ ./a.out Hola mundo [rolando@localhost rolando]$

los enteros long son suficientes. El estndar define que el rango mnimo de valores para enteros long es de -2147483647 a +2147483647. En la mayora de las computadoras, los int son equivalentes ya sea a short o long. El estndar indica que el rango de valores para un int, es por lo menos igual al rango de los enteros short y no mayor que el rango de valores para un long. El tipo de dato char puede ser utilizado para representar enteros en el rango de -127 a +127, o cualquiera de los caracteres del conjunto de la computadora. Rangos comunes para los tipos de datos son los siguientes: Con signo int -32767 a 32767 float -3.4 x 10^35 a -3.4 x 10^35 Double -1.7 x10^308 a +1.7 x10^308 char -127 a 128 Sin signo 0 a 65535 0 a 6.8 x 10^38 0 a 3.4 x 10^308 255

A pesar de que el anterior fue un programa muy sencillo nos permite identificar las partes por las que se compone todo programa en C ms adelante tendremos la oportunidad de ver programas ms complejos en los cuales utilizaremos elementos como estructuras, funciones etc.

Int Flota Double Char

Tipos de datos en C:
En el lenguaje C tenemos 4 tipos de datos principales: 1. int (entero), 2 bytes

Con long Int 4 bytes Flota 8 bytes Double 10 bytes

Variables.
Las variables son algo que no contiene un valor predeterminado, un
espacio de memoria al que nosotros asignamos un nombre y en el que podremos

La salida del programa es: [rolando@localhost rolando]$ gcc tipos_datos.c -o tipos_datos [rolando@localhost rolando]$ ./tipos_datos Este es un int:134513646 Este es un float:-1.999253 Este es un double:2.096863 Este es un chart:` Este es un short:2052 Este es un long int:1107383425 Este es un long double: 34491238139347996037038598042188955453984703839315781376243249341
17115935808642371305695231899200882402333059739432375519256045676 30651697720252407185278828944812291542645206908255387372383509164 81267174505736132890706889231621593340021009175205033185985111772 04232666347450526913789012177668545405021767241250159729830416451 54834896075990195742652982168480999069435581465604667621581480795 83984789653626511631810507995637598270964134410380427447005749594 02519649276572208213146363677227148399774896143410659817289382843 40381137702116047333985365992761986784011028017052191613770100261 99520466320887170862208311864913237356418429785331869286658842846 60911255018842971976794407168847104033188850860481939296558260854 68297463065354330541196756260963313678462426211387981524963282118 56118459515276991965500436814679032628456133029356113605480586014 89530946930234041745389191571188797285563576149152566527931064004 79385798598516031838468532127510910620487398669887498714109121177 54948384175961872706181491880289829004513335589326936846383907239 68539944854935005396891577314991364570363994122753190178967989566 87213904162748447086228034311607216290158475737823684280869414638 32181715667387876353728884859955495444359714958044012712114280390 58492325329131114277206049136472871152983361056179471545200098396 88620454132459346731833309625139342974962424092215968178586418417 21504865231412120000229221479532850830283648741976238749846143113 67545763681491823433658972798881197021998855973604618306262301527 37543916812966411893203552858937887547367180235351562003791283368 00578734231907291668944133131937705739108977673807032355289029531 44840409080333961267972517513375295921764576408272159468612185591

almacenar datos, Nosotros podemos diferenciar de entre las variables gracias a cada variable tiene un identificador (nombre) que nos permite distinguir entre las variables que existen. La forma de declarar una variable es escribir el tipo de dato que almacenar seguida de su identificador, el cual puede ser casi cualquier
combinacin de caracteres. Existen algunas restricciones para ponerle un

identificador a un a variable estas restricciones consisten en que no podemos usar las palabras reservadas (ms adelante se aclarar cuales son), no podemos iniciar el nombre se nuestras variables con carcter numrico y no podemos exceder de una combinacin de 31 caracteres para variables locales y 6 para variables
globales (en el apartado de funciones se especifica la diferencia entre estas). Algo

86482738293522740482423996211082640476546513796936916142153849603

que es importante sealar es que C es sensible al tamao esto quiere decir que
para C la variable CaRrItO ser diferente de la variable cArRiTo.

Veamos ahora un ejemplo que nos permitir apreciar el uso y la declaracin de variables as como apreciar el tamao de los tipos de datos en C, la codificacin del programa es: #include<stdio.h> main()
{

int a; float b; double c; char d[5]; short e; long int f; long double h; unsigned int i;
printf(" \n Este es un int:%i",a);

27642071385454132729160553894055011448466462344496171223471364503

printf(" \n Este es un float:%f",b);


printf(" \n Este es un double:%f",c);

printf(" \n Este es un chart:%c",d); printf(" \n Este es un short:%hd",e); printf(" \n Este es un long int:%lu",f); printf(" \n Este es un long double: %Lf",h); printf(" \n Este es un unsigned int:%u\n",i);
}

89327792342903447765054080998043006001435027631936053836466632408

04257071399052843083949496172553347008439315921621411739757 61149081510910654354044347897957971405144949167893846419574
05239266344073367077540156830843122750068635098652770856309 39818948010543115533491897301911678745868040073496673922655

base 8 si el primer dgito por la izquierda es un cero (0). Anlogamente, una secuencia de dgitos (del 0 al 9) y de letras (A, B, C, D, E, F) precedida por 0x o por 0X, se interpreta como una constante entera hexadecimal, esto es, una
constante numrica expresada en base 16. Por ejemplo:

97649079071888996564992.000000 Este es un unsigned int:1073830456

011 constante octal (igual a 9 en base 10) 11 constante entera decimal (no es igual a 011) 0xA constante haxadecimal (igual a 10 en base 10) 0xFF constante hexadecimal (igual a 162-1=255 en base 10)

Con ayuda del programa anterior pudimos ver las dimensiones de los
tipos de datos que podemos manejar en el lenguaje C. Hay un operador en C que nos permite saber el valor en bytes que maneja nuestra computadora para

cada tipo de datos esta funcin es: sizeof(tipo_de_dato) Este operador puede ser la calve para garantizar la portabilidad de
nuestros programas en C.

Es probable que no haya necesidad de utilizar constantes octales y hexadecimales, pero conviene conocer su existencia y saber interpretarlas por si hiciera falta. La ventaja de los nmeros expresados en base 8 y base 16 proviene
de su estrecha relacin con la base 2 ( 8 y 16 son potencias de 2), que es la forma

en la que la computadora almacena la informacin almacena la informacin.


Constantes de punto flotante.

Constantes
Se entiende por constantes aquel tipo de informacin numrica o
alfanumrica que no puede cambiar ms que con una nueva compilacin del

programa. En el cdigo de un programa en C pueden aparecer diversos tipos de


constantes que se van a explicar a continuacin. Constantes enteras Una constante entera decimal est formada por una secuencia de dgitos del 0 sujetas a las mismas restricciones de rango que las variables tipo int y long,

al 9, constituyendo un nmero entero. Las constantes enteras decimales estn pudiendo tambin ser unsigned. El tipo de una constante se puede determinar automticamente segn su magnitud, o de modo explcito posponiendo ciertos caracteres, como en los ejemplos que siguen: 23484 constante tipo int 45815 constante tipo long (es mayor que 32767) 253u 253U constante tipo unsigned int 739l 739L constante tipo long 583ul 583UL constante tipo unsigned long

Como es natural, existen tambin constantes de punto flotante, que pueden ser de tipo float, double y long double. Una constante de punto flotante se almacena de la misma forma que la variable correspondiente del mismo tipo. Por defecto (si no se indica otra cosa) las constantes de punto flotante son de tipo double. Para indicar que una constante es de tipo float se le aade una f o una F; para indicar que es de tipo long double, se le aade una l o una L. En cualquier caso, el punto decimal siempre debe estar presente si se trata de representar un nmero real. Estas constantes se pueden expresar de varias formas. La ms sencilla es un conjunto de dgitos del 0 al 9, incluyendo un punto decimal. Para constantes muy grandes o muy pequeas puede utilizarse la notacin cientfica; en este caso la constante tiene una parte entera, un punto decimal, una parte fraccionaria, una e o E, y un exponente entero (afectando a la base 10), con un signo opcional. Se puede omitir la parte entera o la fraccionaria, pero no ambas a la vez. Las constantes de punto flotante son siempre positivas. A continuacin se presentan algunos ejemplos vlidos: 1.23 constante tipo double (opcin por defecto) 23.963f constante tipo float .00874 constante tipo double 23e2 constante tipo double (igual a 2300.0) .874e-2 constante tipo double en notacin cientfica (=.00874) .874e-2f constante tipo float en notacin cientfica seguidos de otros que no son correctos: 1,23 error: la coma no esta permitida

En C se pueden definir tambin constantes enteras octales, esto es, expresadas en

base 8 con dgitos del 0 al 7. Se considera que una constante est expresada en

23963f error: no hay punto decimal ni carcter e E .e4 error: no hay ni parte entera ni fraccionaria -3.14 error: slo el exponente puede llevar signo

Constantes carcter

Una constante carcter es un carcter cualquiera encerrado entre apstrofos (tal como 'x' o 't'). El valor de una constante carcter es el valor
numrico asignado a ese carcter segn el cdigo ASCII. Conviene indicar que en

C no existen constantes tipo char; lo que se llama aqu constantes carcter son en realidad constantes enteras. Hay que sealar que el valor ASCII de los nmeros del 0 al 9 no coincide con el propio valor numrico. Por ejemplo, el valor ASCII de la constante carcter '7' es 55. Ciertos caracteres no representables grficamente, el apstrofo (') y la barra invertida (\) y otros caracteres, se representan mediante secuencias de escape, las cuales se vern mas a detalle en el apartado de la funcin printf. La manera de declarar las constantes es similar a como se lo hacemos con las variables con la diferencia de que entre el tipo de dato y la variable debemos poner la sentencia const, en caso de que no especifiquemos el tipo de dato el compilador tomar por omisin el tipo int Veamos un programa para que nos quede ms claro: #include<stdio.h> void main()
{

int const x,y; float const m=0651.1531; x++; y--; m=m*161;


}

Al intentar compilar este programa el se nos mostrar un mensaje de error que nos indicar que no podemos modificar el valor de una constante.

Palabras reservadas

menos palabras clave que otros lenguajes. A continuacin se presenta la lista de las 32 palabras clave del ANSI C: auto break case float int short struct unsigned char const continue for long signed switch void default do double goto registrer sizeof typed volatile else enum exterm if return static union while

En C, como en cualquier otro lenguaje, existen una serie de palabras clave o reservadas que el usuario no puede utilizar como identificadores (nombres de variables y/o de funciones), ya que estas palabras sirven para indicar a la computadora que realice una tarea muy determinada (desde evaluar una comparacin, hasta definir el tipo de una variable) y tienen un especial significado para el compilador. El C es un lenguaje muy conciso, con muchas

Definicin de un bloque
Un bloque es un conjunto de instrucciones delimitadas por llaves la

llave { indica el inicio del bloque y el final es delimitado por }. Los bloque pueden estar contenidos unos dentro de otros, en los programas que hemos visto hasta ahora slo hemos necesitado un bloque de

instrucciones pero ms adelante utilizaremos programas en los que ser necesario tener muchos mas.

Comentarios.

Muchas veces es necesario poner comentarios en nuestros programas para hacerlos mas fcil de comprender tanto para nosotros mismos como para otras personas si queremos poner un comentario de una sola lnea lo debemos hacer de la siguiente manera: //este es un comentario de una lnea En caso de que el comentario que necesitemos poner sea de ms de una lnea la forma de ponerlo es de la siguiente manera: /*As de pone un comentario de varias lneas */ Es importante no olvidar poner // o /* y */ cuando escribamos un programa ya que de lo contrario el compilador nos mostrar un error.

double c,z; char d[10]; printf("\n Dame tu nombre "); scanf("%s",d); printf("\n %s Dame un numero entero ",d); scanf("%i",&a); printf("\n %s Dame un numero entero y dos flotantes separados por comas ",d); scanf("%i,%f,%f",&x,&b,&y); printf("\n %s Dame dos flotantes separados por un espacio ",d); scanf("%lf %lf",&c,&z); printf("\n %s Tus nmeros fueron: \t %i \t%i \t%f \t%.2f \t%.3f \t%.0f \n",d,a,x,b,y,c,z);
}

La salida del programa es: [rolando@localhost rolando]$ gcc printf_scanf.c -o printf_scanf [rolando@localhost rolando]$ ./printf_scanf Dame tu nombre Roalndo Roalndo Dame un numero entero 1
Roalndo Dame un numero entero y dos flotantes separados por comas

Como compilar en Linux


de la siguiente manera: gcc nombre_del archivo -[banderas] [nombre_de salida]
Algunas banderas son las siguientes: -c El comando que se utiliza para compilar en Linux es gcc su sintaxis es

2,98.63,98.546
Roalndo Dame dos flotantes separados por un espacio 96.1 78.1

Con esta bandera se compila el programa pero no se realiza la fase de compilacin con esta bandera lo que se nos devuelve es un archivo objeto. -o nombre_de_salida Esta bandera es muy til para cambiar el nombre del archivo ejecutable que nos devuelve el compilador ya que podemos especificar el nombre que queremos.

Roalndo Tus nmeros fueron: 78 [rolando@localhost rolando]$

98.629997

98.55 96.100

Funciones printf y scanf


Analicemos el siguiente programa: #include<stdio.h> main()
{

Las funciones printf y scanf se encuentran en la librera estndar de entrada/salida de C (stdio.h) estas dos funciones son de gran importancia ya por medio de ella podemos obtener informacin de los usuarios de nuestros programa
as como mostrarles la informacin que es procesada por nuestros programas.

int a,x; float b,y;

La funcin printf nos permite mostrar mensajes a los usuarios esta funcin es el medio por el cual sale la informacin. Para escribir un mensaje o una variable en la pantalla, debe estar encerrada entre comillas dobles, es posible imprimir en un mensaje ms de una

variable para hacer esto debemos escribir las secuencias de escape necesarias y
las variables separarlas con comas.

Como podemos ver en el mensaje donde se imprimen todos los nmeros que da el usuario podemos controlar el numero de dgitos que aparecen despus
del nmero decimal, lo anterior para el caso que el tipo de dato que manejamos es

Tipos de dato long double double float unsigned long int long int unsigned int int short char

flotante o double. Tambin usamos un par se secuencias de escape para darle una mejor
presentacin a nuestro texto como \n y \t cuyos efectos sobre el texto se explican

en las tablas de abajo.

La funcin scanf nos permite leer los valores que el usuario ingresa

por medio del teclado, es necesario que indiquemos que tipo de valor se va leer, mediante su correspondiente secuencia de escape, en este caso las variables que
no sean del tipo carcter deben ser antecedidas por & ya que si omitimos esto la

Especificaciones de conversin printf %Lf %f %f %lu %ld %u %d %hd %c,%s

Especificaciones de conversin scanf %Lf %lf %f %lu %ld %u %d %hd


%c,%s

variable no se leer . Otro aspecto importante que podemos ver en el programa es que cuando se leen ms de un valor podremos especificar cual ser la forma en la estarn separados los valores en este caso utilizamos comas y espacios pero pudimos haber escogido algn otro carcter. A continuacin se presentan dos tablas con secuencias de escape la primera tablas presenta secuencias de escapes principalmente para dar formato al
texto de salida y la segunda secuencias de escape para tipos de datos.

Otras secuencias de escape importantes son las siguientes:

Secuencia de escape %o %x %X %P

descripcin Lee un entero octal. El argumento correspondiente es un apuntador a un entero no signado. Lene un entero hexadecimal. El argumento correspondiente es un apuntador a un entero no signado. Lee una direccin de apuntador producido de la misma forma que cuando una direccin es extrada con %p en un enunciado printf.
Almacena el nmero de caracteres introducidos hasta el

\ Doble comilla. Imprime un carcter de doble comilla en un enunciado printf. \t Tabulador horizontal. Mueve el cursor al siguiente tabulador. \r Retorno de carro. Coloca el cursor al principio de la lnea actual; no avanza a la lnea siguiente. \a Alerta. Hace sonar la alarma de sistema. \\ Diagonal invertida. Imprime un carcter de diagonal invertida en un enunciado printf \' Salida del carcter de una sola comilla \? Salida del signo de interrogacin \b Mueve el curso hacia atrs una posicin en la lnea actual \v Mueve el cursor hacia la siguiente posicin del tabulador vertical. \f Mueve el cursor al inicio de la siguiente pagina lgica

%n %%
%e %E

momento en este scanf. El argumento correspondiente es un apuntador a entero.


Saltarse un signo de por ciento (%) en al entrada.

Muestra un valor en punto flotante en notacin exponencial. Despliega un valor en punto flotante, ya sea en forma de punta
flotante f, o en la forma e xponencial.

%g %G

Operadores

Los operadores en C son ciertos smbolos que nos permiten realizar

ciertas operaciones tanto aritmticas como lgicas, as mismo algunos operadores nos permiten establecer la relacin entre dos variables. En C tenemos 6 tipos de

operadores: 1. Aritmticos: Suma: + Resta: -

2.

Modulo: % Multiplicacin: * Divisin: / Relacinales: Comparacin: = = Mayor: > Menor: < Diferente: != Menor o igual: <=
Mayor o igual: >=

Ahora veamos un ejemplo en el cual emplearemos los operadores que se citaron anteriormente: #include<stdio.h> main()
{

3. 4. 5. 6.

Lgicos: AND: && OR : || NOT: ! A nivel de bits: AND: & OR: | NOT: ! Corrimiento a la derecha: >> Corrimiento ala izquierda: <<
Complemento a 1: ?

int a,b,c; a=34;b=56;c=-17; printf("\n a=%i b=%i c=%i",a,b,c); printf("\n\n Esta es la suma de a+b= %i",a+b); printf("\n Esta es la resta de c-b= %i",c-b); printf("\n Esta es la multiplicacion de a*c= %i",a*c); printf("\n Esta es la division de a/b= %i",a/b); printf("\n Esta es el modulo de b y a= %i",b%a); printf("\n\n Esta es la comparacion si a y b son iguales a==b %i",a==b); printf("\n Esta es la comparacion si c es mayor que b c>b %i",c>b); printf("\n Esta es la comparacion si a es menor b a<b %i",a<b);
printf("\n Esta es la comparacion si a es diferente de c a!=b %i",a!=b);

Asignacin:
a+=b...................a=a+b a-=b....................a=a-b a*=b...................a=a*b a/=b....................a=a/b a%=b..................a=a%b a=b......................a=b

printf("\n Esta es la comparacion si a es mayor o igual que b c>=b %i",c>=b); printf("\n Esta es la comparacion si a es menor o igual que b a<=b %i",a<=b); printf("\n Esta es la prueba de si a es mayor que cero y menor de 100 a>0&&a<100 %i ",a>0&&a<100); printf("\n Esta es la prueba de si c es menor que cero o mayor de 10 c<0||c<10 %i ",c<0||c>10); printf("\n Esta es la aplicacin el operador NOT en la varable b !b %i",!b);
printf("\n\n Esta es la aplicacin de b AND a en nivel de bits %i" ,b&a); printf("\n Este es un corrimiento a al derecha de de a a>>5 %i",a>>5);

printf("\n Esta es la aplicacin de a OR b a nivel de bits %i",a|b); printf("\n Este es un corrimiento a la izquierda de b b<<2 %i",b<<2); printf("\n\n Esto es el reusltado de a+=b %i",a+=b); printf("\n Esto es el resultado de a-=b %i",a-=b); printf("\n Esto es el resultado de a*=b %i",a*=b); printf("\n Esto es el resultado de a/=b %i",a/=b); printf("\n Esto es el resultado de a%=b %i",a%=b); printf("\n Esto es el resultado de a++ %i",a++); printf("\n Esto es el resultado de a-- %i",a--); printf("\n Esto es el resultado de ++a %i",++a); printf("\n Esto es el resultado de --a %i\n",--a);
}

unarios:
a++......................a=a+1 a--........................a=a -1

++a --a

La salida del programa es la siguiente: a=34 b=56 c=-17


Esta es la suma de a+b= 90 Esta es la resta de c -b= -73

no sean muy comunes para nosotros, por ejemplo el operador modulo, Qu es lo


que hace este operador? Pues bien lo que nos devuelve este operador es el residuo

Esta es la multiplicacion de a*c= -578 Esta es la division de a/b= 0


Esta es el modulo de b y a= 22 Esta es la comparacion si a y b son iguales a==b 0 Esta es la comparacion si c es mayor que b c>b 0 Esta es la comparacion si a es menor b a<b 1 Esta es la comparacion si a es diferente de c a!=b 1 Esta es la comparacion si a es mayor o igual que b c>=b 0 Esta es la comparacion si a es menor o igual que b a<=b 1 Esta es la prueba de si a es mayor que cero y menor de 100 a>0&&a<100

de la divisin de dos enteros tomando como referencia el ejemplo tenemos que el residuo de dividir 56 entre 34 es 22, el hecho de que este operador slo de pueda aplicar a datos de tipo entero es porque si realizamos una divisin entre nmeros de punto flotante el resulta ser exacto ya que se pueden manejar nmeros despus del punto decimal a deferencia del los datos de tipo entero en los cuales
si el resultado no es entero se trunca su parte decimal.

Otros operadores tal vez no muy comunes son los de corrimiento de bits, lo que hacen estos operadores es recorrer los bits ya sea hacia la izquierda o hacia la derecha segn sea el caso, tantos lugares como se indique, dejando en blanco los espacios que indicamos de nuevo tomando como referencia el ejemplo
tenemos que el numero 56 se representa de la siguiente manera:

111000 Si recorremos los bits 2 lugares hacia la izquierda tenemos: 1110000 Que es la representacin el binario del nmero decimal 224, lo anterior funciona igual cuando se aplica el corrimiento hacia la derecha con la diferencia de que el desplazamiento de lleva a cabo en este sentido. El operador de complemento a 1 a nivel binario bsicamente lo que hace
es que los 1 los convierte en ceros y los ceros los convierte en 1.

1
Esta es la prueba de si c es menor que cero o mayor de 10 c<0||c<10 1 Esta es la aplicacin el operador NOT en la varable b !b 0 Esta es la aplicacin de b AND a en nivel de bits 32 Esta es la aplicacin de a OR b a nivel de bits 58 Este es un corrimiento a al derecha de de a a>>5 1

Este es un corrimiento a la izquierda de b b<<2 224


Esto es el reusltado de a+=b 90 Esto es el resultado de a-=b 34

Esto es el resultado de a*=b 1904


Esto es el resultado de a/=b 34 Esto es el resultado de a%=b 34 Esto es el resultado de a-- 35

Esto es el resultado de a++ 34 Esto es el resultado de ++a 35

Hay otro operador llamado el operador coma (,) Su uso principal es permitir al programador utilizar mltiples expresiones de de inicializacin y/o mltiples incrementos. Finalmente tenemos a otro operador llamado el operador condicional (?:). Lo que hace este operador es evaluar una expresin y realizar una determina accin la forma de utilizarlo es la siguiente: Expresin_1 ? :Expresin_2 : Expresin_3 Donde la Expresin_1 representa la expresin a partir de la cual se tomar la decisin, la Expresin_2 es la accin a seguir en caso de la Expresin_1 se cumpla y la Expresin_3 es la accin que se tomar en caso de que no se cumpla 1. Finalmente veamos la precedencia que tienen los operadores, en la siguiente tabla se presentan ordenados de arriba hacia abajo los operadores que
tiene mayor precedencia a los operadores que tienen menor:

Esto es el resultado de a/=b 34

Esto es el resultado de --a 34

Aunque en este ejemplo slo se emplearon nmeros del tipo enteros todos los operadores con excepcin del operador modulo se pueden aplicar a cualquier tipo de datos. Ahora veamos que est pasando con algunos operadores que puede que

Operadores () [] + - ++ --

* &

* / % + < <= > >= = = != && || ?:


= += -= *= /= %=

Asociatividad De izquierda a derecha De izquierda a derecha De izquierda a derecha De izquierda a derecha De izquierda a derecha De izquierda a derecha De izquierda a derecha De izquierda a derecha De izquierda a derecha De izquierda a derecha De izquierda a derecha

Tipo Oculto Unario Multiplicativo Aditivo Relacin Igualdad Y lgico O lgico Condicional Asignacin Coma

Dame tres nmeros enteros separados por comas 25,-9,-6 El calor de c antes de aplicar complementeo a 1 es -6

Ahora c vale 5 El resultado de aplicar el oprador condicional es: 30 Ahora veamos un ejemplo de precedencia en lo operadores: (a+b)/c)= -2 a+(b/c)= 26 a+b/c*9= 34 (a+b/c)*9= 234 Por medio del programa anterior podemos apreciar el funcionamiento de lo operadores complemento a uno, condicional as como de la precedencia de operadores aritmticos.

Veamos ahora el siguiente programa: #include<stdio.h> main()


{

Conversin de datos.
En algunos casos nos podemos ver en la necesidad de realizar operaciones con tipo de datos diferentes, es decir se podra dar el caso de que necesitemos sumar un flotante con un entero, para estos casos podemos transformar los tipos de datos que contienen nuestros datos en otros, hay dos maneras de llevar acabo esto: Conversin implcita. Este tipo de conversin se realiza asignando a una
variable del tipo de dato que deseamos el valor que deseamos cambiar

int a,b,c,d;
printf("\ Dame tres nmeros enteros separados por comas "); scanf("%i,%i,%i",&a,&b,&c); printf("\n El calor de c antes de aplicar complementeo a 1 es %i ",c);

printf("\n Ahora c vale %i ",~c); a>10 ? (d=30) : (d=10); printf("\n El resultado de aplicar el oprador condicional es: %i",d); printf("\n\nAhora veamos un ejemplo de precedencia en lo operadores: "); printf("\n (a+b)/c)= %i",(a+b)/c);
printf(" \n a+(b/c)= %i",a+(b/c));

printf("\n a+b/c*9= %i",a+b/c*9); printf("\n (a+b/c)*9= %i",(a+b/c)*9);


}

Conversin explicita. Esta conversin de lleva a cabo por medio de algo llamado casting. Esto se hace colocando entre parntesis el tipo de dato al que deseamos llevar cabo la conversin seguido del valor al cual queremos hacer la conversin, veamos el siguiente programa para que esto quede ms claro:

#include<stdio.h> main()
{

La salida del programa es:

int a=9,b=7,c=26; float x,d,f,e=964.23; long float j=165116489;

10

printf("\n a= %i b= %i c= %i e=%f j=%lf ",a,b,c,e,j); d=c; c=e; f=j; x=(float)a/b;


j=(int)x;

printf("\n\n El valor de c en flotante es: %f",d);


printf("\n El valor de e en entero es %i",c);

printf("\n El el valor de j en flotante es %lf",f); printf("\n El resultado del casting de a/b es %f",x); printf("\n El valor de pasar el resultado anterior a entero es %i",j);

El enunciado switch maneja una serie de decisiones en las cuales una variable o expresin particular se prueba para cada uno de los valores que puede asumir, y se toman diferentes acciones. En un enunciado switch, cada case puede hacer que se ejecuten muchos enunciados. En la mayor parte de los programas, resulta necesario incluir un enunciado break despus de los enunciados de cada case, de lo contrario el programa ejecutar los enunciados en cada case, hasta que un enunciado break se encuentre o hasta que se alcance el final del enunciado switch. Enlistando las etiquetas case juntas antes de los enunciados, varios case pueden ejecutar los mismos enunciados. La estructura switch slo puede probar expresiones integrales constantes. Ahora plasmemos lo anterior en un programa:

a= 9 b= 7 c= 26 e=964.229980 j=165116489.000000 El valor de c en flotante es: 26.000000


El valor de e en entero es 964

#include<stdio.h> main()
{

El el valor de j en flotante es 165116496.000000 El resultado del casting de a/b es 1.285714


El valor de pasar el rsultado anterior a entero es 0

int edad; char nombre[20],res; printf("\n Como te llamas ");


scanf("%s",&nombre); printf("\n Dame tu edad en aos ");

Es importante mencionar que debemos ser muy cuidadosos cuando realicemos cambio de dato ya que si pasamos un dato de mayor capacidad a uno
de menor nos puede representar perdida de informacin.

scanf("%i",&edad); if(edad<=0) printf("\n No seas mentiroso es imposible que tengas esa edad"); else
{

Estructuras de control de Seleccin


El lenguaje C nos proporciona tres maneras de controlar los eventos que
se pueden presentar en nuestros programas esto se realiza mediante las estructuras

printf("\nQue te gusta mas: ");


printf("\nDormir.....................a"); printf("\nComer......................b"); printf("\nVer t.v....................c"); printf("\nEstuidar...................d");

de control de seleccin las cuales son: if if-else switch if Ejecuta una accin indicada slo cuando la condicin es verdadera. if-else
Define acciones diferentes a ejecutarse cuando la condicin es

printf("\nNinguna de la anteriones...e \n"); fflush(stdin);


res=getchar(); switch(res) { case'a': case'A': printf("\n %s eres una persona muy floja",nombre); break; case'b': case'B': printf("\n %s eres una persona muy tragona ",nombre); break; case'c': case'C':

verdadera, y cuando la condicin es falsa. switch

11

printf("\n %s eres una persona que no tiene nada que hacer",nombre); break; case'd': case'D': printf("\n %s eres una persona muy aburrida",nombre); break; case'e': case'E': printf("\n Que complicado(a) me saliste %s ",nombre); break; default: printf("\n Acaso te d como opccin %c (s/n)",res); res=getchar(); if(res=='s'||res=='S') printf("\n Pues aprende a leer %s",nombre); if(res=='n'||res=='N'); printf("\n Entonces fijate %s",nombre); break; }

Como te llamas Rolando


Dame tu edad en aos 20

Que te gusta mas:


Dormir.....................a Comer......................b Ver t.v....................c Estuidar...................d

Ninguna de la anteriones...e t Acaso te d como opccin t (s/n)s


Pues aprende a leer Rolando

} } En este caso presentaremos varias pantallas de salida ya que hay diversas situaciones que se pueden presentar en la ejecucin de este programa:

Entonces fijate Rolando Las pantallas anteriores nos permiten ver algunos de los posibles eventos que se pueden presentar en nuestros programas, tambin utilizamos dos nuevas funciones que nos ofrece C, estas son: fflush(stdin):Limpia el buffer de la entrada estndar (teclado). getchar(): Nos permite recoger un carcter de la entrada estndar (teclado).

Como te llamas rolando


Dame tu edad en aos -9 No seas mentiroso es imposible que tengas esa edad

Otro aspecto importante que pudimos apreciar es en relacin de la estructura de seleccin switch ya que como vemos en el cdigo es posible establecer la misma accin para diferentes case sin la necesidad de escribir muchas veces la
accin a tomar. Finalmente cabe comentar que es posible meter (comnmente a

Como te llamas rolando


Dame tu edad en aos 20

esto se le llama anidar) tantas estructuras de seleccin dentro otras como nos sea necesario.

Estructuras de control de repeticin


Uno de los principales objetivos de la programacin es reducir el tiempo que empleamos en realizar clculo o llevar a cabo tareas repetitivas cientos de veces por ello es que C nos brinda estructuras de control de repeticin que nos permiten llevar acabo tareas de forma ms rpida, Una estructura de repeticin define que una accin deber ser repetida. En tanto cierta condicin siga siendo
verdadera .En C existen las siguientes estructuras de control de repeticin:

Que te gusta mas:


Dormir.....................a Comer......................b Ver t.v....................c Estuidar...................d

Ninguna de la anteriones...e c
rolando eres una persona que no tiene nada que hacer

12

while. El formato para esta estructura de repeticin es: while (condicin)


enunciado

enunciados siguientes de la actual iteracin del cuerpo de la estructura, y contina con la siguiente iteracin.
Ahora veamos algunos programas que nos permitan ver como funcionan

estas estructuras de seleccin empezaremos con un ciclo while controlado por contador: #include<stdio.h>
/*Programa para calcular el costo de n nmero de articulos

El enunciado (o enunciado compuesto o bloque), contenido en la estructura de repeticin while constituye el cuerpo del ciclo.
Por lo regular alguna accin especificada dentro del cuerpo de

while eventualmente debe hacer que la condicin se convierta falsa. De lo contrario, el ciclo no terminara nunca y caeramos en un error conocido como ciclo infinito. do/while. Esta estructura de repeticin es muy parecida a while pero la estructura do/while prueba la condicin de continuacin del ciclo al final del ciclo, por lo que el cuerpo del ciclo por lo menos se ejecutar una vez. El formato para el enunciado do/while es: do enunciado while (condicin); for. La estructura de repeticin for maneja de manera automtica todos los detalles de la repeticin controlada por contador, e inclusive no es necesario que siempre utilicemos este contador para controlar el ciclo. El formato general de la estructura for es:

que se compren*/ main()


{

float total,precio; int n,i;


printf(" \n Cuantos articulos quieres comprar ");

scanf("%i",&n); i=1; //iniciamos la varible del contador a 1 total=0; //iniciamos el precio total a cero while(i<=n) //ciclo while controlado por contador printf("\nDame el precio del articulo %i ",i);
scanf("%f",&precio); total+=precio; //sumamos el precio al total i++; //incrementamos el contador en 1

printf(" \n\nEl costo total es: $%.2f",total);


}

for (expresin 1; expresin 2; expresin 3) enunciado Donde expresin 1 inicia la variable de control del ciclo, expresin 2 es la condicin de continuacin del ciclo, y la expresin 3 incrementa la variable de control. Hay dos elementos del lenguaje C que nos ser de gran ayuda y estos son continue y break de hecho este ultimo ya lo utilizamos en la estructura de
seleccin switch, las caractersticas ms importantes de estos elementos son:

Cuantos articulos quieres comprar 5 Dame el precio del articulo 1 12.6 Dame el precio del articulo 2 31.06 Dame el precio del articulo 3 9.5 Dame el precio del articulo 4 5 Dame el precio del articulo 5 46.10 El costo total es: $104.26

break .El enunciado break cuando se ejecuta en una de las estructuras de repeticin (for, while, do/while), causa la salida inmediata de la estructura. La ejecucin continua con el siguiente primer enunciado despus del ciclo. Continue. El enunciado continue, cuando se ejecuta en una de las estructuras de repeticin (for, while, do/while), salta todos los

13

Ahora presentamos el mismo programa pero con el ciclo while controlado por centinela:

#include<stdio.h>
/*Programa para calcular el costo de n nmero de articulos

Podemos ver que en el primer caso el ciclo while se termina cuando el contador llega al nmero de artculos que el usuario dice que va comprar es decir tiene como limite el nmero que ingresa el usuario, y en el segundo caso el ciclo termina cuando el usuario ingresa un cero como precio, es decir en este caso el ciclo no sabe cual ser el limite de artculos que sumar. Ahora pasemos con un ciclo do/while: #include<stdio.h> main()
{

que se compren*/ main()


{

float total,precio; precio=1; //iniciamos la variable que nos serivr como centinela
printf("\n Presiona 0 (cero) cuando acabes de dar todos los articulos ")

int c=1; do
{

total=0; //iniciamos el precio total a cero while(precio!=-0) //ciclo while controlado por centinela
{

printf(" %i ",c);
}while(++c<=10); } 1 2 3 4 5 6 7 8 9 10

printf("\nDame el precio del siguiente articulo ");


scanf("%f",&precio); total+=precio; //sumamos el precio al total }

printf("\n\nEl costo total es: $%.2f",total);


} Presiona 0 (cero) cuando acabes de dar todos los articulos

Dame el precio del siguiente articulo 56.15 Dame el precio del siguiente articulo .5156 Dame el precio del siguiente articulo 0.1651 Dame el precio del siguiente articulo 41651 Dame el precio del siguiente articulo .1351 Dame el precio del siguiente articulo 161.91 Dame el precio del siguiente articulo 0 El costo total es: $41869.88

A pesar de que el programa anterior es muy sencillo nos permite apreciar el como en este tipo de estructura de repeticin a diferencia de la expresin while primero se llevan a cabo las instrucciones en el cuerpo de la estructura y posteriormente se evala la continuidad del ciclo. Veamos un ejemplo de cmo utilizar la estructura de repeticin for: #include<stdio.h> main()
{

int a,b; for(a=1;a<=11;a++)


{

for(b=1;b<=11;b++)
printf(" \n %i por %i = %i ",a,b,a*b); if(a!=11) { printf("\n\n Presiona enter para continuar "); b=getchar();

14

} else

continue :

printf("\n\n Fin del programa ");

#include<stdio.h> main()
{

} }

int c; for(c=1;c<=1000;c++)
{ if(c==5)

10 por 3 = 30 10 por 4 = 40 10 por 5 = 50 10 por 6 = 60 10 por 7 = 70 10 por 8 = 80 10 por 9 = 90 10 por 10 = 100 10 por 11 = 110 Presiona enter para continuar 11 por 1 = 11 11 por 2 = 22 11 por 3 = 33 11 por 4 = 44 11 por 5 = 55 11 por 6 = 66 11 por 7 = 77 11 por 8 = 88 11 por 9 = 99 11 por 10 = 110 11 por 11 = 121 Fin del programa

continue; if(c==11)
break;

printf("%i ",c);
} } 1 2 3 4 6 7 8 9 10

En el programa anterior podemos ver como cuando se cumple la condicin para que se ejecute la sentencia continue el programa se salta la iteracin actual, por ello no vemos que se imprima en la pantalla el nmero 5, y cuando se cumpli la condicin necesaria para que se ejecutara la sentencia break el programa automticamente termin el programa a pesar de que limite de terminacin del ciclo era el nmero 100.

Funciones.

En el programa anterior utilizamos dos estructuras

for una anidada

Los programas necesarios para resolver todos los problemas que se nos presentan en la vida real debe son mucho ms complejos y extensos de los programas que hasta ahora hemos utilizado como ejemplos, una forma de hacer mas sencillo y comprensible un programa extenso y complejo es dividir el programa principal mdulos, lo anterior es algo comnmente llamado programacin modular. En C los mdulos reciben el nombre de funciones y hay dos tipos de estas que podemos manejar en el lenguaje C estas son:
Funciones de la librera estndar

dentro de la otra para poder obtener las tablas de multiplicar, como ya se haba

mencionado en el uso de esta estructura en la misma sentencia de inicializa la variable que usamos como contador, se hace la prueba de continuacin del ciclo y se aumenta el contador. Finalmente veamos como podemos emplear las sentencias
break y

Este tipo de funciones se encuentran disponibles en todas las versiones de C, estas funciones son muy variadas nosotros de hecho ya hemos utilizado algunas de estas funciones como: printf, scanf, getchar, etc. Hay otras de las cuales ilustraremos su uso en este apartado. Veamos un ejemplo en el cual utilizamos funciones matemticas definidas en el archivo de cabecera <math.h>

15

por ello ser necesario que cuando compilemos utilicemos la bandera -l seguida de la letra mes decir se debe compilar de la siguiente forma: El cdigo de nuestro programa es: #include<stdio.h> #include<math.h> void main()
{

Este tipo de funciones son las el usuario crea para simplificar sus programas.
Y son a las que trataremos con ms profundidad en este capitulo.

Las funciones en C se declaran despus de los archivos de cabecera y antes de la instruccin main y las instrucciones que contendr nuestra funcin se pondrn despus del la funcin main la forma en se define una funcin es la siguiente:
Tipo_de_dato_de_regreso { declaraciones enunciados } nombre_de_ la_ funcin (Lista de parmetros)

float a;
printf("\n Dame un numero ");

scanf("%f",&a); if(a>0)
printf("\nLa raz cuadrada de %.3f es %.3f",a,sqrt(a)); printf("\n La funcin exponencial para %.3f es %.3f",a,exp(a));

printf("\n El logaritmo natural de %.3f es %.3f",a,log(a)); printf("\n El valor absoluto de %.3f es %.3f",a,fabs(a));

printf("\n El logaritmo base 10 de %.3f es %.3f",a,log10(a)); printf("\n %.3f redondeado al entero mas pequeo es %.3f",a,ceil(a)); printf("\n %.3f redondeado al entero mas grande es %.3f",a,floor(a)); printf("\n %.3f elevado a la potencia 8 es %.3f",a,pow(a,8)); }

El nombre_de_la_funcin es cualquier identificador valido. El Tipo_de_dato_de_regreso es el tipo de los datos del resultado que nos dar la funcin, en cas de que la funcin no regrese ningn valor en lugar de poner un tipo de dato se pone la palabra void, en caso de que no especifiquemos un valor de regreso el compilador supondr que es tipo int.
Las declaraciones, junto los enunciados dentro de las llaves, forman el cuerpo de la funcin . El cuerpo de la funcin tambin se conoce como un bloque .

Dame un numero 63.25 La raz cuadrada de 63.250 es 7.953 La funcin exponencial para 63.250 es 2945275877129289740000000000.000 El logaritmo natural de 63.250 es 4.147 El logaritmo base 10 de 63.250 es 1.801 El valor absoluto de 63.250 es 63.250 63.250 redondeado al entero mas pequeo es 64.000
63.250 redondeado al entero mas grande es 63.000

Un bloque es un enunciado compuesto que incluye declaraciones. Las variables pueden ser declaradas en cualquier bloque, y los bloques pueden estar anidados. Bajo ninguna circunstancia puede ser definida una funcin en el interior de otra funcin. Ntese que lo anterior significa que no podemos poner las instrucciones que har una funcin dentro de las instrucciones que definen a otra pero esto no significa que no podamos mandar a llamar a una funcin dentro de la declaracin de una segunda funcin.
Prototipo de funciones

63.250 elevado a la potencia 8 es 256144030377847.750

En este programa podemos apreciar como llamamos a las funciones de que C nos proporciona en la librera <math.h>, hay muchas ms funciones contenidas en esta librera como las funciones trigonometricas circulares e hiperblicas.
Funciones definidas por el usuario

Un prototipo de funcin de funcin le indica al compilador el tipo de dato regresado por la funcin, el nmero de parmetros que la funcin espera recibir, los tipos de dichos parmetros, y el orden en el cual se esperan dichos parmetros. Veamos un par ejemplos sencillos para ilustrar lo ms importante de lo expuesto anteriormente: #include <stdio.h> void compara( int a, int b ); /* Metemos los parmetros a y b a la funcin */ void main()
{

16

int num1, num2; printf( "Introduzca dos nmeros separados por un espacio : " ); scanf( "%i %i", &num1, &num2 ); compara( num1, num2 );/* Llamamos a la funcin con sus dos argumentos */
} }

mayor = a;

else
mayor = b; return mayor;

void compara( int a, int b ) /*Definimos que va hacer nuestra funci{on*/


{ if ( a>b ) printf( "%i es mayor que %i\n" , a, b ); else printf( "%i es mayor que %i\n", b, a );

Introduzca dos nmeros separados por un espacio: 2 -9 El mayor de los dos es 2

return;
} Introduzca dos nmeros separados por un espacio : 2 -9 Llamada de funciones por valor y por referencia

2 es mayor que -9

el anterior con la diferencia de que la funcin que definiremos ahora nos regresar un valor y el mayor nmero ser impreso en la pantalla en la funcin principal y no en la funcin llamada compara: #include <stdio.h> int compara( int a, int b ); void main()
{

Veamos este otro programa que es otra manera de hacer lo mismo que en

Cuando los argumentos se pasan a una funcin en llamada por un valor, se efecta una copia de valor del argumento y sta se pasa a la funcin llamada. Las modificaciones a la copia no afectan al valor original de la variable del llamador. Cuando un argumento es pasado en llamada por referencia, el llamador de hecho permite que la funcin llamada modifique el valor original de la variable. La llamada por valor debera ser utilizada siempre que la funcin llamada no necesite modificar el valor de la variable original del llamador. La llamada por referencia debe ser utilizada slo en funciones llamadas confiables, que necesitan modificar la variable original. En C todas las llamadas son llamadas por valor. Como veremos ms adelante, es posible simular la llamada por referencia mediante el uso de operadores de direccin y de indireccin.
Variables locales y globales.

int num1, num2; int resultado; printf( "Introduzca dos nmeros separados por un espacio: " ); scanf( "%i %i", &num1, &num2 ); resultado = compara( num1, num2 );/* Recogemos el valor que devuelve la funcin en resultado */ printf( "El mayor de los dos es %i\n", resultado );
}

int compara( int a, int b ) /* Metemos los parmetros a y b a la funcin */


{

Una variable global se la que se define despus de los archivos de cabecera (o de las funciones secundarias en caso de que existan) y antes de la funcin principal (main). Una variable local es la que es valida solo en un bloque es decir si en una funcin se define una variable solo ser existir mientras se este ejecutando la esta a diferencia de la variable local la cual existir durante toda la ejecucin del programa. De hecho es posible que en un programa tengamos ms de una variable con el mismo nombre siempre y cuando no estn en el mismo bloque. Veamos el siguiente programa para entender mejor lo anterior: #include <stdio.h> int x; //variable global void imprime (void);

int mayor; /* Esta funcin define su propia variable, esta variable slo se puede usar aqu */
if ( a>b )

17

void main()
{ printf(" \n Dame un n mero entero ");

usar palabras reservadas de C como nombre de variables. Clases de almacenamiento.

scanf("%i",&x); printf("El valor de lavariable x global es %i",x); imprime(); printf(" \n Pero el valor de la x global sigue siendo %i \n",x);
}

C proporciona 4 clases de almacenamiento, que se indican por los


especificadotes de almacenamiento

void imprime(void)
{

auto register
extern

static

int x; //variable local printf(" \n Dame otro nmero "); scanf("%i",&x); printf(" \n El valor de la variable x local es %i ",x); x=20+x;
printf(" \n Si le sumamos 20 a la x local su valor ahora es %i \n",x); }

La clase de almacenamiento de un identificador ayuda a determinar su duracin de almacenamiento, su alcance y su enlace. La duracin de almacenamiento de un identificador es el periodo durante el

cual dicho identificador existe en memoria. Algunos identificadores tienen una existencia breve, otros son creados y destruidos en forma repetida y otros existen durante toda la ejecucin del programa.
Las palabras reservados auto y registrer se utilizan para declarar variables de

Dame un nmero entero 3

El valor de lavariable x global es 3 Dame otro nmero 1 El valor de la variable x local es 1 Si le sumamos 20 a la x local su valor ahora es 21 Pero el valor de la x global sigue siendo 3 En el programa anterior pudimos ver que a pesar de que hay dos variables definidas con el mismo nombre no nos causa ningn conflicto ya que una es una variable local y la otra es una global, tambin se puede apreciar que las variables locales tienen precedencia sobre las globales ya que en la funcin a
la variable a la que se le sum 20 fue a la variable local de la funcin imprime ya

persistencia automtica. Estas variables se crean al introducirse al mbito de un bloque en el cual estn declaradas, existen mientras dicho bloque este activo, y se destruyen cuando se sale de este bloque. Las variables locales de una funcin por lo regular una persistencia automtica, por omisin, las variables locales tienen persistencia automtica por lo que la palabra reservada auto rara vez es utilizada. Las palabras reservadas extern y static se utilizan para declarar identificadores de variables y funciones de persistencia esttica. Los identificadores de persistencia esttica existen a partir del momento de que el programa se inicia en ejecucin. Se asigna y se inicializa almacenamiento para las variables desde el momento que se empieza a operar el programa. Para las funciones, existe el nombre de la funcin al iniciarse el programa. Sin embargo aunque las variables y los nombres de funcin existen a partir del inicio de la ejecucin del programa, esto no significa que estos identificadores pudieran ser utilizados pudieran a todo lo largo del mismo.
Existen dos tipos de identificadores con persistencia esttica: los

que una vez que se termino de ejecutar la funcin y mandamos a imprimir el valor de x, el valor que se mostr en la pantalla fue el de la variable global. En general no se recomienda que dos variables tengan el mismo nombre y traten de
que evitar que esto ocurra para darle ms claridad a los progra mas.

identificadores externos (como son las variables globales y los nombres de funcin) y las variables locales declaras con el especificador de clase de almacenamiento static. Las variables globales y los nombres de funcin son pro omisin da la clase de almacenamiento extern.
Funciones recursivas

El nombre que le demos a una variable global puede tener hasta 6 y a una local hasta 31. En los nombres de las variables podemos poner nmeros siempre y cuando el no sea el primer carcter del nombre es decir podemos llamar a una variable como global2 pero no como 2global, tampoco podemos

Una funcin recursiva es una funcin que se llama a s misma, ya sea directa o indirecta a travs de otra funcin Una funcin recursiva es llamada para resolver un problema. La funcin de hecho sabe slo como resolver el problema ms simple, es decir el llamado base case (caso base). Si la funcin es llamada con un problema ms complejo, la funcin divide dicho problema en dos partes conceptuales: una parte que la

18

funcin ya sabe como ejecutar, y una parte que la funcin no sabe como ejecutar. Para hacer factible la recursin, esta ultima, esta ltima parte debe parecerse al problema original, pero resulta ligeramente ms simple o una versin ligeramente ms pequea del problema original. Dado que este nuevo problema aparenta o se ve similar al problema original, la funcin emite (llama) a una copia nueva de s misma , para que empiece a trabajar sobre el problema ms pequeo y esto se conoce como una llamada recursiva y tambin se llama el paso de recursin.El paso de recursin se ejecuta en tanto la llamada original a al funcin est abierta,
es decir, que no se haya terminado su ejecucin. El paso de reexcursin puede dar

como resultado muchas ms llamadas recursivas. El ejemplo para mostrar la recursin por excelencia es el clculo del factorial de un nmero el cual presento a continuacin: #include <stdio.h> long factorial (long ); void main()
{

13! = 1932053504 14! = 1278945280 15! = 2004310016 16! = 2004189184 17! = -288522240 18! = -898433024 19! = 109641728 20! = -2102132736 En el ejemplo anterior podemos ver como la funcin factorial se va llamando a s misma hasta que reduce el problema tanto que se limita al caso ms simple.

int i; for(i=1; i<=20;i++) printf(" \n %2d! = %ld ",i, factorial(i));


}

Arreglos
Un elemento importante que nos brinda el lenguaje de programacin C son los arreglos, ya que nos brindan un ahorro muy grande de tiempo ya que si para realizar un programa necesitramos 100 variables imagina el gasto de tiempo en darle nombre a las 100 variables. Veamos ahora las caractersticas principales de los arreglos. C almacena listas de valores en arreglos. Un arreglo es un grupo de posiciones relacionadas en memoria. Estas posiciones estn relacionadas por el hecho de que todas tienen el mismo nombre y son del mismo tipo. Para referirse a una posicin particular o algn elemento dentro del arreglo, especificamos el nombre del arreglo y el subndice. Un subndice puede ser un enero o una expresin entera. Si un programa utiliza como subndice una expresin, entonces la expresin se evala para determinar el elemento particular del arreglo. Es importante notar la diferencia entre referirse al sptimo elemento del arreglo y la referirse al elemento 7 del arreglo. El sptimo elemento tiene un subndice de 6, en tanto que el electo 7 del arreglo tiene un subndice de 7 (y de hecho dentro del arreglo es el octavo elemento). Esta es una fuente de errores por diferencia de 1. Los arreglos ocupan espacio en memoria. Para reservar 100 elementos para el elemento entero b y 27 elementos para el elemento entero x, el programador escribe: int b[100], x[27]; Un arreglo del tipo char puede ser utilizado para almacenar una cadena de

long factorial (long number)


{

if (number <= 1) return 1; else return (number*factorial(number-1));


}

1! = 1 2! = 2 3! = 6 4! = 24 5! = 120 6! = 720 7! = 5040 8! = 40320 9! = 362880 10! = 3628800 11! = 39916800 12! = 479001600

19

caracteres.

Los elementos de un arreglo pueden ser inicializados de tres formas distintas: por declaracin, por asignacin y por entrada. Si existen menos inicializadotes que elementos en el arreglo, C
inicializar de manera automtica a cero los elementos restantes.

La forma de declarar el arreglo anterior es:

int c[10]; Los corchetes utilizados para encerrar el subndice de un arreglo, son hecho en C considerados como un operador. Tienen el mismo nivel de precedencia que los parntesis. Arreglos multidimensionales: En c los arreglos pueden tener mltiples subndices. Una utilizacin comn de los arreglos con mltiples subndices es la representacin de tablas de valores, consistiendo de informacin arreglada en renglones y columnas. Para identificar un elemento particular de la tabla, deberemos especificar dos subndices; el primero (por regla convencional) identifica el rengln del elemento y el segundo (tambin por regla convencional) identifica la columna del elemento. Es necesario precisar que los arreglos de mltiples subndices pueden tener ms de dos subndices. El estndar ANSI indica que un sistema ANSI C debe soportar por lo menos 12 subndices en de arreglo. Veamos ahora como podemos idealizar un arreglo de dos subndices: Columna 0 Rengln 0 a[0][0] Rengln 1 a[1][0] Rengln 2 a[2][0]
Nombre del arreglo

C no evita la referenciacin de elementos ms all de los limites de un arreglos, es decir si se define un arreglo b[100 ] de 100 elementos y solicitamos el elemento del arreglo nmero 101 C no indicar un error pero ese elemento no existe. Un arreglo de caracteres puede ser inicializado utilizando una literal de
cadena. Todas las cadenas en C terminan con un carcter nulo. La representacin

de la constante de carcter nulo, es \o. Los arreglos de caracteres pueden ser inicializados en la lista de inicializacin con constantes de caracteres. Los caracteres individuales en una cadena almacenada en un arreglo pueden ser accesibles de forma directa utilizando una notacin de subndices de arreglos. Se puede introducir de manera directa una cadena en un arreglo de caracteres desde el teclado utilizando scanf y la especificacin de conversin %s. Un arreglo de caracteres que represente una cadena puede ser extrado utilizando printf y el especificador de conversin %s, lo anterior ya lo hemos utilizados en algunos programas anteriores. El lenguaje C nos proporciona do tipos de arreglos que podemos manejar: Arreglos unidimensionales. En este tipo de arreglos tambin llamados vectores son arreglos los cuales slo tiene un subndice. Y los podramos idealizar de la siguiente manera:
Nombre del arreglo (note que todos los elementos de este arreglo

Columna 1 Columna 2 Columna 3 a[0][1] a[0][2] a[0][3] a[1][1] a[1][2] a[1][3] a[2][1] a[2][2] a[2][3]
Subndice de columna

tienen el mismo nombre C)

C[0] C[1] C[2] C[3] C[4] C[5] C[6]

-45 202 5 0 9 41 -15 C[7] 654 C[8] 651 C[9] 16


Posicin numrica del elemento en el arreglo

Subndice de rengln

Es importante mencionar el hecho de que en el caso anterior se mencion que el esquema anterior es una forma de idealizar como es un arreglo matricial, ya que es ms fcil verlo de esa manera, debido a que en realidad C como ve la matriz anterior es como un arreglo de 3 elementos cada uno de los cuales a su vez contienen arreglos de 4 elementos, y de hecho la forma en que C los organiza es la siguiente: a[0][0] a[0][1] a[0][2] a[0][3] a[1][0] a[1][1] a[1][2] a[1][3] Creo que ya contamos con elementos suficientes para analizar algunos

20

programas. #include <stdio.h> main()


{ int temp[24]; /* Con esto ya tenemos declaradas las 24 variables */

Sin la ayuda de los arreglos para codificar el programa anterior hubiramos tenido que hacer algo as: #include<stdio.h> main()
{ /* Declaramos 24 variables, una para cada hora del dia */

float media; int hora;


/* Ahora tenemos que dar el valor de cada una */ for( hora=0; hora<24; hora++ ) { printf( "Temperatura de las %i: ", hora ); scanf( "%i", &temp[hora] ); media += temp[hora]; }

int temp1, temp2, temp3, temp4, temp5, temp6, temp7, temp8; int temp9, temp10, temp11, temp12, temp13, temp14, temp15, temp16; int temp17, temp18, temp19, temp20, temp21, temp22, temp23, temp0; float media;

/* Ahora tenemos que dar el valor de cada una */

printf( "Introduzca las temperaturas desde las 0 hasta las 23 separadas por un espacion: " );
scanf( "%i %i %i ... %i", &temp0, &temp1, &temp2, ... &temp23 ); media = ( temp0 + temp1 + temp2 + temp3 + temp4 + ... + temp23 ) / 24;

media = media / 24;


printf( "\nLa temperatura media es %f\n", media ); } }

printf( "\nLa temperatura media es %i\n", media ); Temperatura de las 2: 30 Temperatura de las 3: 10 Temperatura de las 4: 20 Temperatura de las 5: 30 Temperatura de las 6: 40 Temperatura de las 7: 10 Temperatura de las 8: 20 Temperatura de las 9: 30 Temperatura de las 10: 40 Temperatura de las 11: 10 Temperatura de las 12: 20 Temperatura de las 13: 30 Temperatura de las 14: 40 Temperatura de las 15: 10 Temperatura de las 16: 20 Temperatura de las 17: 30 Temperatura de las 18: 40 Temperatura de las 19: 10 Temperatura de las 20: 20 Temperatura de las 21: 30 Temperatura de las 22: 40 Temperatura de las 23: 50
La temperatura media es 25.417

Es evidente que la diferencia entre el tiempo que debemos invertir en la codificacin del programa y la eficiencia del mismo es un factor importante par decidirnos por utilizar arreglos en lugar de variables independientes. El anterior fue un ejemplo que tambin nos permiti ver como se puede inicializar un arreglo mediante la lectura de los valores, ahora observemos otra forma de inicializar los arreglos:

#include <stdio.h> main()


{

int hora; int temperaturas[24] = { 15, 18, 20, 23, 22, 24, 22, 25, 26, 25, 24,
22, 21, 20, 18, 17, 16, 17, 15, 14, 14 }; for (hora=0 ; hora<24 ; hora++ ) {

printf( "La temperatura a las %i era de %i grados.\n", hora, tempera turas[hora] );


} }

21

{ float a[50][50]={0},b[50][50]={0},s[50][50]={0};

int r,c,y,x;

printf("\n Cuantos renglones tienen las matrices que vas a sumar ");

scanf("%i",&r);
La temperatura a las 0 era de 15 grados. La temperatura a las 1 era de 18 grados. La temperatura a las 2 era de 20 grados. La temperatura a las 3 era de 23 grados. La temperatura a las 4 era de 22 grados.
La temperatura a las 5 era de 24 grados. La temperatura a las 6 era de 22 grados. La temperatura a las 7 era de 25 grados. La temperatura a las 8 era de 26 grados. La temperatura a las 9 era de 25 grados.

printf("\n Cuantas columnas tienen las matrices que vas a sumar ");

scanf("%i",&c); for(y=0;y<r;y++)
{

for(x=0;x<c;x++)
{ printf("\n Dame el valor para el elemento a[%i][%i]= ",y,x); scanf("%f",&a[y][x]); } }

La temperatura a las 10 era de 24 grados.


La temperatura a las 11 era de 22 grados. La temperatura a las 12 era de 21 grados. La temperatura a las 13 era de 20 grados. La temperatura a las 14 era de 18 grados. La temperatura a las 15 era de 17 grados. La temperatura a las 16 era de 16 grados. La temperatura a las 17 era de 17 grados. La temperatura a las 18 era de 15 grados. La temperatura a las 19 era de 14 grados. La temperatura a las 20 era de 14 grados.

for(y=0;y<r;y++)
{

for(x=0;x<c;x++)
{ printf("\n Dame el valor para el elemento b[%i][%i]= ",y,x); scanf("%f",&b[y][x]); } }

for(y=0;y<r;y++)
{

La temperatura a las 21 era de 0 grados. La temperatura a las 22 era de 0 grados. La temperatura a las 23 era de 0 grados.

for(x=0;x<c;x++)
{ s[y][x]=a[y][x]+b[y][x]; }

Este programa tambin nos ayud a ilustrar un aspecto que ya se haba mencionado esto es el hecho de que cuando no se inicializa un elemento del
arreglo, C le da el valor de cero.

for(y=0;y<r;y++)
{

El siguiente programa ilustra un sencillo ejemplo del empleo de un arreglo bidimensional, mejor conocido como matriz, donde se muestra un procedimiento para realizar una suma de matrices. Los detalles a destacar es que se note que la forma de manejar las matrices es muy semejante a como se utilizaron los vectores con la diferencia que en este caso el arreglo tiene 2 subndices. #include<stdio.h>
//Programa para sumar matrices;

for(x=0;x<c;x++)
{ printf("\nLa suma para el elemento (%i,%i) es %.3f",y,x,s[y][x]); }

} }

main()

22

main()
{ char palabra[50];

int i; printf("\nEscribe una palabra ");


Cuantos renglones tienen las matrices que vas a sumar 2 Cuantas columnas tienen las matrices que vas a sumar 2 Dame el valor para el elemento a[0][0]= 2 scanf("%s",palabra);

printf("\nTu palabra fue: "); for(i=0;palabra[i];i++)


{

printf("\n\n\t\t%c",palabra[i]); }
}

Dameel valor para el elemento a[0][1]= 2


Dame el valor para el elemento a[1][0]= 2 Dame el valor para el elemento a[1][1]= 2 Dame el valor para el elemento b[0][0]= 2 Dame el valor para el elemento b[0][1]= 2 Dame el valor para el elemento b[1][0]= 2 Dame el valor para el elemento b[1][1]= 2 La suma para el elemento (0,0) es 4.000 La suma para el elemento (0,1) es 4.000 La suma para el elemento (1,0) es 4.000 La suma para el elemento (1,1) es 4.000

Escribe una palabra M@li Tu palabra fue:


M @ l i

Para acabar con este tema veamos el manejo que le podemos dar a un arreglo de caracteres o cadenas utilizando los conceptos que hemos visto en
relacin con los arreglos. Si vemos a una cadena de caracteres como un arreglo de

Apuntadores

caracteres, podemos tener acceso a cada elemento que conforma este arreglo y por lo tanto podemos manipularlo a nuestro antojo, en el siguiente programa muestra lo anterior leyendo una palabra del teclado y escribindola en forma vertical en la pantalla. Aprovechando este ejemplo tambin se mostrar como no es necesario que siempre utilicemos un expresin aritmtica como prueba de continuidad para una estructura de repeticin ya que como podremos apreciar el ciclo for del siguiente programa terminar cuando el programa encuentre el
carcter nulo en la cadena.

El valor de cada variable est almacenado en un lugar determinado de la memoria, caracterizado por una direccin (que se suele expresar con un nmero hexadecimal). La computadora mantiene una tabla de direcciones que relaciona el nombre de cada variable con su direccin en la memoria. Gracias a los nombres de las variables (identificadores), por o regular no hace falta que el programador se preocupe de la direccin de memoria donde estn almacenados sus datos. Sin embargo, en ciertas ocasiones es ms til trabajar con las direcciones que con los propios nombres de las variables. El lenguaje C dispone del operador direccin (&) que permite determinar la direccin de una variable, y de un tipo especial de variables destinadas a contener direcciones de variables. Estas variables se llaman punteros o apuntadores (en ingls pointers).
As pues, un apuntador es una variable que puede contener la direccin

#include<stdio.h>

de otra variable. Por supuesto, los apuntadores estn almacenados en algn lugar

23

de la memoria y tienen su propia direccin (ms adelante se ver que existen apuntadores a apuntadores). Se dice que un apuntador apunta a una variable si su contenido es la direccin de esa variable. Un apuntador ocupa de ordinario 4 bytes de memoria, y se debe declarar o definir de acuerdo con el tipo del dato al que apunta. Por ejemplo, un apuntador a una variable de tipo int se declara del siguiente modo: int *direc; lo cual quiere decir que a partir de este momento, la variable direc podr contener la direccin de cualquier variable entera. La regla nemotcnica es que el valor al que apunta direc (es decir *direc, como luego se ver), es de tipo int. Los punteros a long, char, float y double se definen anlogamente a los punteros a int.
Operadores de direccin (&) e indireccin (*)

int *p; double *q; void *r;


p = q;

p = (int *)q;
p = r = q;

// ilegal // legal

// legal

Veamos un ejemplo: #include<stdio.h> void main()


{

Como se ha dicho, el lenguaje C dispone del operador direccin (&) que permite hallar la direccin de la variable a la que se aplica. Un puntero es una verdadera variable, y por tanto puede cambiar de valor, es decir, puede cambiar la variable a la que apunta. Para acceder al valor depositado en la zona de memoria a la que apunta un puntero se debe utilizar el operador indireccin (*). Por ejemplo, supngase las siguientes declaraciones y sentencias: int i, j, *p;
p = &i; *p = 10; p = &j; *p = -2;

int i, j, *p;
printf("\nEsta es la direccion de memoria que contiene p %p ",p);

// p es un puntero a int
// p contiene la direccin de i // i toma el valor 10 // p contiene ahora la direccin de j // j toma el valor -2

Las constantes y las expresiones no tienen direccin, por lo que no se les puede aplicar el operador (&). Tampoco puede cambiarse la direccin de una variable. Los valores posibles para un puntero son las direcciones posibles de memoria. Un puntero puede tener valor 0 (equivalente a la constante simblica predefinida NULL). No se puede asignar una direccin absoluta directamente (habra que hacer un casting). Las siguientes sentencias son ilegales:
p = &34; // las constantes no tienen direccin

printf("\nEsta es la direccion de memoria que donde se ubica i %p ",&i); p = &i; printf("\nEsta es la direccion de memoria a la que apunta p despues de la asignacion %p",p); printf("\nEste es el valor de i antes de la asignacin %i",i); *p = 10; printf("\nEste es el valor de i despues de la asignacin %i",i); printf("\nEsta es la direccin de memoria de j %p",&j); p = &j; printf("\nEsta es la direccion de memoria que guarda ahora p %p",p); printf("\nEste es el valor de j antes de la asignacin %i",j); *p = -2; printf("\nEste es el valor de j despues de la asignacin %i",j);
}

p = &(i+1);
&i = p; p = 17654;

// las expresiones no tienen direccin


// las direcciones no se pueden cambiar // habra que escribir p = (int *)17654;

Esta es la direccion de memoria que contiene p 0510 Esta es la direccion de memoria que donde se ubica i FFF4 Esta es la direccion de memoria a la que apunta p despues de la asignacion FFF4
Este es el valor de i antes de la asignacin 0

Para imprimir punteros con la funcin printf() se deben utilizar los formatos %u y %p , como se ver ms adelante. No se permiten asignaciones directas (sin casting) entre punteros que apuntan a distintos tipos de variables. Sin embargo, existe un tipo indefinido de punteros (void *, o punteros a void ), que puede asignarse y al que puede asignarse cualquier tipo de puntero. Por ejemplo:

Este es el valor de i despues de la asignacin 10 Esta es la direccin de memoria de j FFF2


Esta es la direccion de memoria que guarda ahora p FFF2 Este es el valor de j antes de la asignacin 680

Este es el valor de j despues de la asignacin -2

24

En el programa anterior pudimos observar como es posible asignar una direccin de memoria a un apuntador y como por medio del operador de indireccin nos es posible cambiar el contenido de la variable a la que apunta un determinado apuntador. El siguiente programa nos muestra el uso de un apuntador del tipo void y
aunque como podremos ver en la pantalla de salida que si realizar el cambio de

estas operaciones se realizan de un modo correcto, pero que no es el ordinario. As, la sentencia:
p = p+1;

direccin en el apuntador p desde que lo asignamos directo a r esto no garantiza que nuestro programa funcione correctamente, como lo har si realizamos la asignacin a de r a p de los siguiente dos mtodos. #include<stdio.h> void main()
{

int *p; double *q; void *r; printf("\n La direccion de memoria que guarda p es %p",p); printf("\n La direccion de memoria que guarda q es %p",q); printf("\n La direccion de memoria que guarda r es %p",r); p=q; // ilegal printf("\n Despues de la asignacion p= %p",p); p=(int *)q; // legal printf("\n Ahora p= %p",p); p=r=q; // legal printf("\n El valor de p es %p",p);

hace que p apunte a la direccin siguiente de la que apuntaba, teniendo en cuenta el tipo de dato. Por ejemplo, si el valor apuntado por p es short int y ocupa 2 bytes, el sumar 1 a p implica aadir 2 bytes a la direccin que contiene, mientras que si p apunta a un double, sumarle 1 implica aadirle 8 bytes. Tambin tiene sentido la diferencia de puntadores al mismo tipo de variable. El resultado es la distancia entre las direcciones de las variables apuntadas por ellos, no en bytes sino en datos de ese mismo tipo. Las siguientes expresiones tienen pleno sentido en C: p = p + 1; p = p + i;
p += 1;

p++; El siguiente ejemplo ilustra la aritmtica de apuntadores: #include<stdio.h> void main()


{

int a,b;
int *p1,*p2;

} La direccion de memoria que guarda p es 077C

La direccion de memoria que guarda q es 0788


La direccion de memoria que guarda r es 15A1

p1=&a; p2=&b; printf("\n El valor de a es %i",a); printf("\n El valor de b es %i",b); printf("\n Este es el valor al que apunta p1 %p",p1); printf("\n Este es el valor al que apunta p2 %p",p2); p1++; p2--;
printf("\n Es te es el valor al que apunta ahora p1 %p",p1);

Despues de la asignacion p= 0788 Ahora p= 0788 El valor de p es 0788


} Aritmtica de apuntadores.

printf("\n Este es el valor al que apunta ahora p2 %p",p2); *p1=23; *p2=-16; printf("\n El valor de a es %i",a); printf("\n El valor de b es %i",b); El valor de a es 0 El valor de b es 450 Este es el valor al que apunta p1 FFF4 Este es el valor al que apunta p2 FFF2
Este es el valor al que apunta ahora p1 FFF6 Este es el valor al que apunta ahora p2 FFF0

Como ya se ha visto, los apuntadores son unas variables un poco especiales, ya que guardan informacin (no slo de la direccin a la que apuntan), sino tambin del tipo de variable almacenado en esa direccin. Esto implica que no van a estar permitidas las operaciones que no tienen sentido con direcciones de variables, como multiplicar o dividir, pero s otras como sumar o restar. Adems

El valor de a es 0 El valor de b es 450

25

Como ejemplo de la relacin entre vectores y punteros, se van a ver varias formas posibles para sumar los N elementos de un vector a[ ]. Supngase la siguiente declaracin y las siguientes sentencias: modificar la direccin a la que apunta un apuntador sumndole o restndole un
entero, cabe aclarar que no a contrario como hacemos con las variables numricas Ya con este programa nos pudimos percatar del hecho de cmo se puede

no podemos a un apuntador sumarle otro apuntador, es decir la siguiente expresin es ilegal: int *p, *b, *c; p=c+b;
Relacin de arreglos unidimensionales con los apuntadores.

int a[N], suma, i, *p; for(i=0, suma=0; i<N; ++i) // forma 1 suma += a[i]; for(i=0, suma=0; i<N; ++i) // forma 2 suma += *(a+i); for(p=a, i=0, suma=0; i<N; ++i) // forma 3 suma += p[i];
for(p=a, suma=0; p<&a[N]; ++p) // forma 4

Existe una relacin muy estrecha entre los vectores y los apuntadores. De hecho, el nombre de un vector es un apuntador (un apuntador constante, en el sentido de que no puede apuntar a otra variable distinta de aqulla a la que apunta) a la direccin de memoria que contiene el primer elemento del vector. Supnganse las siguientes declaraciones y sentencias:
double vect[10]; // vect es un puntero a vect[0] // p = vect;

suma += *p; Veamos un programa aplicando las formas anteriores donde el valor de N ser 2: #include <stdio.h> void main()
{

double *p;
p = &vect[0];

int a[2], suma, i, *p; a[0]=20;a[1]=-5; for(i=0, suma=0; i<2; ++i) // forma 1
{

El identificador vect, es decir el nombre del vector, es un puntero al primer elemento del vector vect[ ]. Esto es lo mismo que decir que el valor de vect es &vect[0]. Existen ms puntos de coincidencia entre los vectores y los punteros: Puesto que el nombre de un vector es un puntero , obedecer las leyes de la aritmtica de punteros. Por tanto, si vect apunta a vect[0], (vect+1) apuntar a vect[1], y (vect+i ) apuntar a vect[i]. Recprocamente (y esto resulta quizs ms sorprendente), a los punteros se les pueden poner subndices , igual que a los vectores. As pues, si p apunta a vect[0] se puede escribir:
p[3]=p[2]*2.0; // equivalente a vect[3]=vect[2]*2.0;

suma += a[i]; printf("\n suma = %i",suma);


}

for(i=0, suma=0; i<2; ++i) // forma 2


{

suma += *(a+i); printf("\n suma = %i",suma);

for(p=a, i=0, suma=0; i<2; ++i) // forma 3


{

Si se supone que p=vect, la relacin entre punteros y vectores puede resumirse como se indica en las lneas siguientes:

suma += p[i]; printf("\n suma = %i",suma);


}

*p equivale a vect[0], a *vect y a p[0]


*(p+1) equivale a vect[1], a *(vect+1) y a p[1] *(p+2) equivale a vect[2], a *(vect+2) y a p[2]

for(p=a, suma=0; p<&a[2]; ++p) // forma 4


{

26

suma += *p; printf("\n suma = %i",suma);


}

Teniendo esto en cuenta y haciendo **p = mat; donde **p se conoce como doble apuntador se tendrn las siguientes formas de acceder a los elementos de la matriz: *p es el valor de mat[0] **p es mat[0][0] *(p+1) es el valor de mat[1] **(p+1) es mat[1][0] *(*(p+1)+1) es mat[1][1] Por otra parte, si la matriz tiene M columnas y si se hace; q = &mat[0][0] (direccin base de la matriz. Recurdese que esto es diferente del caso anterior p = mat), el elemento mat[i][j] puede ser accedido de varias formas. Basta recordar que dicho elemento tiene por delante i filas completas, y j elementos de su fila: *(q + M*i + j) // frmula de direccionamiento *(mat[i] + j) // primer elemento fila i desplazado j elementos
(*(mat + i))[j] // [j] equivale a sumar j a un puntero

printf(" \n");
}

suma = 20 suma = 15 suma = 20 suma = 15 suma = 20 suma = 15 suma = 20 suma = 15 El programa anterior nos deja ver como los diversos mtodos que se presentan son validos para acceder al contenido de nuestro arreglo, ya se por la forma tradicional de asignacin o bien utilizando los la herramientas que nos brindan los apuntadores.

Relacin entre matrices y apuntadores.

En el caso de las matrices la relacin con los punteros es un poco ms complicada. Supngase una declaracin como la siguiente: int mat[5][3], **p, *q;
El nombre de la matriz (mat) es un puntero al primer elemento de un

*((*(mat + i)) + j)

vector de punteros mat[ ] (por tanto, existe un vector de punteros que tiene tambin el mismo nombre que la matriz), cuyos elementos contienen las direcciones del primer elemento de cada fila de la matriz. El nombre mat es pues un puntero a puntero. El vector de punteros mat[ ] se crea automticamente al crearse la matriz. As pues, mat es igual a &mat[0]; y mat[0] es &mat[0][0]. Anlogamente, mat[1] es &mat[1][0], mat[2] es &mat[2][0], etc. La direccin base sobre la que se direccionan todos los elementos de la matriz no es mat, sino &mat[0][0]. Recurdese tambin que, por la relacin entre vectores y punteros, (mat+i) apunta a mat[i]. Por lo tanto tenemos que la frmula de direccionamiento de una matriz de N filas y M columnas establece que la direccin del elemento (i, j) viene dada por: direccin (i, j) = direccin (0, 0) + i*M + j

Todas estas relaciones tienen una gran importancia, pues implican una
correcta comprensin de los punteros y de las matrices. De todas formas, hay que

indicar que las matrices no son del todo idnticas a los vectores de punteros: Si se define una matriz explcitamente por medio de vectores de punteros, las filas pueden tener diferente nmero de elementos, y no queda garantizado que estn contiguas en la memoria (aunque se puede hacer que por suerte s lo sean). No sera pues posible en este caso utilizar la frmula de direccionamiento y el acceder por columnas a los elementos de la matriz. Veamos el siguiente programa en donde ponemos en prctica todo lo anterior:

27

#include <stdio.h> void main()


{

int i,j,mat[5][3], **p, *q; for(i=0;i<5;i++)


{

for(j=0;j<3;j++)
{

Los valores de (0,0) son: 4611 Los valores de (0,1) son: 64 Los valores de (0,2) son: 12290 Los valores de (1,0) son: 4611 Los valores de (1,1) son: 4608 Los valores de (1,2) son: 64 Los valores de (2,0) son: 12290
Los valo res de (2,1) son: 4608

Los valores de (2,2) son: 1824 printf(" \n Los valores de (%i,%i) son: %i",i,j,mat[i][j]); Los valores de (3,0) son: 64 } Los valores de (3,1) son: 12291 } Los valores de (3,2) son: 1824 **p = mat; Los valores de (4,0) son: 64 printf(" \n\n *p es el valor de mat[0] %i",*p); Los valores de (4,1) son: 3467 printf(" \n **p es mat[0][0] %i",**p); Los valores de (4,2) son: -29180 printf(" \n *(p+1) es el valor de mat[1] %i",(p+1)); printf(" \n **(p+1) es mat[1][0] %i",**(p+1)); *p es el valor de mat[0] 767 printf(" \n *(*(p+1)+1) es mat[1][1] %i",*(*(p+1)+1)); **p es mat[0][0] -44 q=&mat[0][0]; *(p+1) es el valor de mat[1] 1098 printf(" \n *(mat[3] + 1) vale %i ",*(mat[3] + 1)); **(p+1) es mat[1][0] 0 printf(" \n (*(mat + 1))[0] vale %i",(*(mat + 1))[0] ); *(*(p+1)+1) es mat[1][1] 0 printf(" \n *((*(mat + 2)) + 1) vale %i\n",*((*(mat + 2)) + 1)); *(mat[3] + 1) vale 12291 (*(mat + 1))[0] vale 4611 *((*(mat + 2)) + 1) vale 4608

28

Paso de arreglos como argumentos a una funcin.

bash$ gcc func_arre.c -o func_arre

En realidad en C no podemos pasar un arreglo completo a una funcin. Lo que tenemos que hacer es pasar un apuntador al arreglo. Con este apuntador
podemos recorrer el arreglo, veamos el siguiente programa :

#include <stdio.h> int sumar( int *m ); int main()


{

int contador;
int matriz[10] = { 10, 11, 13, 10, 14, 9, 10, 18, 10, 10 };

bash$ ./func_arre 10 11 13 10 14 9 10 18 10 10 115 bash$

for( contador=0; contador<10; contador++ ) printf( " %3i\n", matriz[contador] ); printf( " %3i \n", sumar( matriz ) );

Como vemos cada vez que se manda llamar una la funcin se pasa como argumento el apuntador al arreglo y a partir de este se realiza la operacin que se indica en la funcin. En ese caso se utiliz un arreglo unidimensional, pero lo anterior tambin lo podemos hacer con arreglos multidimensionales
Apuntadores a funciones. De modo similar a como el nombre de un arreglo en C es un apuntado r,

int sumar( int *m )


{

int suma, i; suma = 0; for( i=0; i<10; i++ )


{

suma += m[i];
}

tambin el nombre de una funcin es un apuntador. Esto es interesante porque permite pasar como argumento a una funcin el nombre de otra funcin. Por ejemplo, si pfunc es un puntero a una funcin que devuelve un entero y tiene dos argumentos que son punteros, dicha funcin puede declararse del siguiente modo: int (*pfunc)(void *, void *); El primer parntesis es necesario pues la declaracin: int *pfunc(void * , void *); // incorrecto corresponde a una funcin llamada pfunc que devuelve un puntero a entero. Considrese el siguiente ejemplo para llamar de un modo alternativo a las funciones sin() y cos(x):

return suma;

29

#include <stdio.h> #include <math.h> void main(void)


{

double (*pf)(double); pf = sin; printf(" \n%.2lf\n", (*pf)(3.141592654/2)); pf = cos; printf("\n%.2f\n", (*pf)(3.141592654/2));


}

Como podemos ver el programa toma tantos argumentos como le tecleemos, el


programa C distingue que se acaba un argumento cuando se encuentra un espacio

si un programa necesita que le pasemos argumentos y no lo hacemos nos mandar lo siguiente: bash$ ./linea_coman El argumento 0 es: ./linea_coman

1.00 -0.00

bash$

Argumentos desde la lnea de comandos.


Cuando se ejecuta un programa desde la lnea de comandos tecleando su nombre, existe la posibilidad de pasarle algunos datos, teclendolos a continuacin en la misma lnea. Por ejemplo, se le puede pasar algn valor
numrico. Esto se consigue por medio de argumentos que se pasan a la funcin

Estructuras.
Las estructuras son una herramienta de mucha ayuda en el lenguaje C, ya

main(), como se hace con otras funciones. As pues, a la funcin main() se le pueden pasar argumentos y tambin puede tener valor de retorno. El primero de los argumentos de main() se suele llamar argc, y es una variable int que contiene el nmero de palabras que se teclean a continuacin del nombre del programa
cuando ste se ejecuta. El segundo argumento se llama argv, y es un vector de punteros a carcter que contiene las direcciones de la primera letra o carcter de

por medio de estas podemos definir nuestros propios tipos de datos. Las estructuras son colecciones de variables relacionadas, algunas veces conocidas como agregados bajo el mismo nombre. Las estructuras pueden contener variables de varios tipos de datos. Para definir una estructura usamos el siguiente formato:
struct nombre_de_la_estructura { campos de estructura; }; La palabra reservada struct empieza toda definicin de estructura.

dichas palabras. A continuacin se presenta un ejemplo: #include <stdio.h> int main(int argc, char *argv[])
{

int cont; for (cont=0; cont<argc; cont++) printf("El argumento %d es: %s \n", cont, argv[cont]); printf(" \n"); return 0;
}

Dentro de las llaves de la definicin de estructura, estn las declaraciones de los miembros de la estructura, los cuales deben tener nombre nicos. Una definicin de estructura crea un nuevo tipo datos que puede ser utilizado para declarar variables dentro de la(s) funcin(es) que tengamos en nuestro programa. Existen dos mtodos para declarar variables de estructura. El primer mtodo es declarar las variables en una declaracin, como se hace con las variables de otros tipos de datos, utilizando: Struct nombre_estructura nombre_de_variable; El segundo mtodo es incluir las variables encerradas entre las llaves de la definicin de la estructura en el punto y coma que termina la definicin de

bash$ ./linea_coman 65465 46546 afas 46564 El argumento 0 es: ./linea_coman El argumento 1 es: 65465 El argumento 2 es: 46546 El argumento 3 es: afas El argumento 4 es: 46564 bash$

30

estructura. Una estructura puede ser inicializada con una lista de inicializacin, siguiendo el nombre de la variable en la declaracin de escritura con un signo igual y una lista de inicializadores, separados por comas y encerrados en llaves.
Si en la lista existen menos inicializadores que miembros en la estructura, los miembros restantes sern automticamente inicializados a cero (o a NULL si el

int main()
{

miembro es un apuntador). Estructuras completas pueden ser asignadas a variables de estructura del mismo tipo. Supongamos que queremos hacer una agenda con los nmeros de telfono de nuestros amigos. Necesitaramos un arreglo de cadenas para almacenar sus nombres, otro para sus apellidos y otro para sus nmeros de
telfono. Esto puede hacer que el programa quede d esordenado y difcil de seguir. podemos crear una estructura en la que almacenaremos los datos de cada persona. Vamos a crear u na declaracin de estructura llamada amigo: struct estructura_amigo { char nombre[30];

Y aqu es donde vienen en nuestro auxilio las estructuras. Para nuestro ejemplo
}

printf( "Escribe el nombre del amigo: " ); fflush( stdin ); scanf( "%s", &amigo.nombre ); printf( "Escribe el apellido del amigo: " ); fflush( stdin ); scanf( "%s", &amigo.apellido ); printf( "Escribe el numero de telefono del amigo: " ); fflush( stdin ); scanf( "%s", &amigo.telefono ); printf( "El amigo %s %s tiene el numero: %s.\n", amigo.nombre, amigo.apellido, amigo.telefono );

char apellido[40]; char telefono[10];


char edad; };

Escribe el nombre del amigo: Rolando Escribe el apellido del amigo: Lima Escribe el numero de telefono del amigo: 56009040 El amigo Rolando Lima tiene el numero: 56009040. Como ya se mencion hay otra manera de declarar la variableamigo esta manera es escribiendo el nombre de la variable entre la llave de terminacin del bloque de la estructura y el punto y coma que indica el fin de la declaracin de la estructura:
struct estructura_amigo { char nombre[30];

A cada elemento de esta estructura (nombre, apellido, telfono) se le llama campo o miembro. Ahora ya tenemos definida la estructura, pero aun no
podemos usarla. Necesitamos declarar una variable con esa estructura :

struct estructura_amigo amigo; Ahora la variable amigo es de tipo estructura_amigo. Para acceder al nombre de amigo usamos: amigo.nombre. Vamos a ver ahora el ejemplo completo: #include<stdio.h>
struct estructura_amigo { char nombre[30]; /* Definimos la estructura estructura_amigo */

char apellido[40]; char telefono[10];

} amigo;

Declarando la variable amigo declararla en la funcin main.

de esta manera ya no tenemos que

char apellido[40]; char telefono[10];


char edad; };

Arreglos de estructuras

Supongamos ahora que queremos guardar la informacin de varios amigos. Con una variable de estructura slo podemos guardar los datos de uno.
Para manejar los datos de ms gente necesitamos declarar arreglos de estructuras.

struct estructura_amigo amigo;

Lo anterior lo podemos hacer de la misma manera que declaramos cualquier

31

arreglo solo que debemos especificar que se trata de una estructura. Para declarar

un arreglo de estructuras como las del ejemplo anterior lo haramos de la siguiente manera: struct estructura_amigo amigo[ELEMENTOS];
Ahora veamo s la manera de inicializar una estructura:

"Juanjo", "Lopez", "504-4342", 30, "Marcos", "Gamindez", "405-4823", 42, "Ana", "Martinez", "533-5694", 20
};

En este caso lo que estaramos haciendo es inicializar el arreglo amigo el cual contendr 3 elementos.
Apuntadores a estructuras

#include <stdio.h>
struct estructura_amigo { char nombre[30];

char apellido[40];
char telefono[10];

int edad;

}; struct estructura_amigo amigo = {

"Juanjo", "Lopez", "592-0483", 30


};

Tambin se pueden usar apuntadores con estructuras. Primero de todo hay que definir la estructura de igual forma que hacamos antes. La diferencia est en que al declara la variable de tipo estructura debemos ponerle el operador '*' para indicarle que es un apuntador. Creo que es importante recordar que un apuntador no debe apuntar a un lugar cualquiera, debemos darle una direccin vlida donde apuntar. No podemos por ejemplo crear un apuntador a estructura y meter los datos directamente mediante ese apuntador, no sabemos dnde apunta el apuntador y los datos se almacenaran en un lugar cualquiera. Y para comprender cmo funcionan veamos este programa que utiliza un apuntador para acceder a la informacin de la estructura: #include <stdio.h>
struct estructura_amigo { char nombre[30];

main()
{

printf( "\n%s tiene ", amigo.apellido ); printf( "\n%i aos ", amigo.edad );
printf( "\ny su telefono es el %s.\n" , amigo.telefono ); }

char apellido[40]; int edad;

char telefono[10]; };

Lopez tiene 30 aos y su telefono es el 592-0483.

struct estructura_amigo amigo = {

"Juanjo", "Lopez", "592-0483", 30


};

Si lo que quisiramos inicializar es un arreglo de estructuras lo podemos llevar a cabo de la siguiente manera: struct estructura_amigo amigo[] =

struct estructura_amigo *p_amigo; main()


{

p_amigo = &amigo;

32

printf( "%s tiene ", p_amigo->apellido );


printf( "%i a os ", p_amigo->edad );

printf( "y su telefono es el %s.\n" , p_amigo->telefono ); getch(); }

/* Mostramos los datos */ printf( "El amigo %s ", p_amigo->nombre ); printf( "%s tiene ", p_amigo->apellido ); printf( "%i aos.\n", p_amigo->edad );

Lopez tiene 30 aos y su telefono es el 592-0483. En este ejemplo pudimos ver otro elemento que nos permite tener acceso a la estructura el cual es el operador flecha (->). En s para tener acceso a los miembros de una estructura se utilizan dos operadores: el operador de miembro de escritura (.) (tambin conocido como operador punto) el cual usamos en los
primeros programas de esta seccin y el operador de apuntador de escritura (->) (tambin conocido como operador flecha ), el cual consiste en un signo de menos

Nombre: rolando Apellido: lima Edad: 20 El amigo rolando lima tiene 20 aos. En el programa anterior aparte de ver como podemos introducir datos a un arreglo por medio del operador flecha a otro elemento nuevo que es la funcin gets() la cual nos permite leer una cadena de la entada estndar (el teclado) y asignarla al elemento nombre del arreglo.
Apuntadores a arreglos de estructuras Tambin podemos usar apuntadores con arreglos de estructuras. La forma de trabajar es la misma, lo nico que tenemos que hacer es asegurarnos que

(-) y un signo de mayor que (>). En el caso de este ltimo apuntador como ya lo vimos en el programa anterior tiene acceso a la estructura por medio de un apuntador a la estructura. los apuntadores:
Ahora veamos como podemos meter datos a una estructura por medio de

#include <stdio.h>
struct estructura_amigo { char nombre[30];

el puntero inicialmente apunte al primer elemento, luego saltar al siguiente hasta llegar al ltimo. #include <stdio.h>
struct estructura_amigo { char nombre[30];

char apellido[40]; int edad;

};

struct estructura_amigo amigo, *p_amigo; main()


{

char apellido[40]; char telefono[10]; int edad;


};

struct estructura_amigo amigo[] =


{

p_amigo = &amigo; /* Introducimos los datos mediante punteros */ printf("Nombre: ");fflush(stdin);


gets(p_amigo->nombre);

"Juanjo", "Lopez", "504-4342", 30, "Marcos", "Gamindez", "405-4823", 42, "Ana", "Martinez", "533-5694", 20

};

printf("Apellido: ");fflush(stdin); gets(p_amigo->apellido); printf("Edad: ");fflush(stdin); scanf( "%i", &p_amigo->edad );

struct estructura_amigo *p_amigo; main()

33

int num_amigo; p_amigo = amigo; /* apuntamos al primer elemento del array */ /* Ahora imprimimos sus datos */ for( num_amigo=0; num_amigo<3; num_amigo++ )
{

printf("Datos amigo %i\n",num_amigo); printf("Nombre: ");fflush(stdin); gets(p_amigo->nombre); printf("Apellido: ");fflush(stdin); gets(p_amigo->apellido); printf("Edad: ");fflush(stdin);
scanf( "%i", &p_amigo->edad ); /* vaciamos el buffer de entrada */ while(getchar()!='\n');

printf( "El amigo %s ", p_amigo->nombre ); printf( "%s tiene ", p_amigo->apellido ); printf( "%i aos ", p_amigo->edad ); printf( "y su telefono es el %s.\n" , p_amigo->telefono ); /* y ahora saltamos al siguiente elemento */ p_amigo++;
} }

/* saltamos al siguiente elemento */ p_amigo++; /* Ahora imprimimos sus datos */ p_amigo = amigo; for( num_amigo=0; num_amigo<3; num_amigo++ )
{ printf( "El amigo %s ", p_amigo->nombre ); }

El amigo Juanjo Lopez tiene 30 aos y su tel3fono es el 504-4342. El amigo Marcos Gamindez tiene 42 a os y su tel3fono es el 405-4823. El amigo Ana Martinez tiene 20 aos y su tel3fono es el 533-5694. Ahora veamos el ejemplo anterior de cmo introducir datos en un arreglo de estructuras mediante apuntadores: #include <stdio.h>
struct estructura_amigo { char nombre[30];

printf( "%s tiene ", p_amigo->apellido );


printf( "%i aos.\n", p_amigo->edad );

p_amigo++;

} }

char apellido[40]; int edad;


};

struct estructura_amigo amigo[3], *p_amigo; main()


{

int num_amigo; /* apuntamos al primer elemento */ p_amigo = amigo; /* Introducimos los datos mediante punteros */ for ( num_amigo=0; num_amigo<3; num_amigo++ )
{

Datos amigo 0 Nombre: rolando Apellido: lima Edad: 20 Datos amigo 1 Nombre: ronaldo Apellido: lima Edad: 29 Datos amigo 2 Nombre: ronaldio Apellido: lima Edad: 33 El amigo rolando lima tiene 20 aos. El amigo ronaldo lima tiene 29 aos. El amigo ronaldio lima tiene 33 aos. Como podemos ver la manera de ingresar datos a un arreglo de estructuras por medio de apuntadores es similar a como de hizo para ingresar los
datos con apuntadores para una sola variable.

34

Paso de estructuras a funciones:

Las estructuras se pueden pasar directamente a una funcin igual que hacamos con las variables. Por supuesto en la definicin de la funcin debemos indicar el tipo de argumento que usamos: int nombre_funcin ( variable_estructura ) struct nombre_de_la_estructura nombre_de_la

int suma( struct estructura_amigo arg_amigo ) {

return arg_amigo.edad+20;
}

Lopez tiene 30 aos y dentro de 20 aos tendra 50.

En el ejemplo siguiente se usa una funcin llamada suma que calcula cual ser la edad 20 aos ms tarde (simplemente suma 20 a la edad). Esta funcin toma como argumento la variable estructura arg_amigo. Cuando se ejecuta el programa llamamos a suma desde main y en esta variable se copia el contenido de la variable amigo. Esta funcin devuelve un valor entero y el valor que devuelve es la
suma :

Estructuras anidadas Es posible crear estructuras que tengan como miembros otras estructuras. Esto tiene diversas utilidades, por ejemplo tener la estructura de datos ms ordenada. Imaginemos la siguiente situacin: una tienda de msica quiere hacer un programa para el inventario de los discos, cintas y cd's que tienen. Para cada ttulo quiere conocer las existencias en cada de cada tipo (cinta, disco, cd), y
los datos del proveedor. Podra pensar en una estructura as: struct inventario {

#include <stdio.h>
struct estructura_amigo { char nombre[30];

char apellido[40]; int edad;

char titulo[30];
char autor[40];

char telefono[10]; }; struct estructura_amigo amigo = {

int existencias_discos; int existencias_cintas; int existencias_cd;


char nombre_proveedor[40]; char telefono_proveedor[10]; char direccion_proveedor[100]; };

"Juanjo", "Lopez", "592-0483", 30


};

Sin embargo utilizando estructuras anidadas se podra hacer de esta otra forma ms ordenada:
struct estruc_existencias {

int suma( struct estructura_amigo arg_amigo ); int main()


{

int discos; int cintas; int cd;


}; struct estruc_proveedor { char nombre_proveedor[40]; char telefono_proveedor[10]; };

printf( "%s tiene ", amigo.apellido );


printf( "%i a os ", amigo.edad ); printf( "y dentro de 20 a os tendra %i.\n", suma(amigo) ); }

char direccion_proveedor[100];

35

struct estruc_inventario {

Para entenderlo mejor vamos a ver un ejemplo: union _persona


{ char nombre[10];

char titulo[30];
char autor[40]; struct estruc_existencias existencias; struct estruc_proveedor proveedor; } inventario;

char inicial;
};

Ahora para acceder al nmero de cd de cierto ttulo usaramos lo siguiente: inventario.existencias.cd


y para acceder al nombre del proveedor:

inventario.proveedor.nombre En s el manejo que se le da a las estructuras anidadas es el mismo que para las estructuras simples solo debemos tener cuidado en dos aspectos el primero es el hecho de que si queremos anidar una estructura dentro de otra es necesario primero declarar la va ir adentro ya que si no lo hacemos el compilador nos mandar un error, es por eso que en el ejemplo de arriba primero se declaran las estructuras del proveedor y de existencias y a hasta el final la estructura inventario que es la que contiene a las otras estructuras. El otro aspecto con el
cual hay que tener cuidado es

Creamos una unin y sus elementos son una variable tipo char nombre de 10 bytes (nombre[10]) y la inicial (1 byte). Como hemos dicho la unin ocupa el espacio de su elemento ms grande, en este caso nombre. Por lo tanto la unin ocupa 10 bytes. Las variables nombre e inicial comparten el mismo sitio de la memoria. Si accedemos a nombre estaremos accediendo a los primeros 10 bytes de la unin (es decir, a toda la unin), si accedemos a inicial lo que tendremos es el primer byte de la unin esquemticamente lo podemos ver la siguiente manera:

Uniones
Hemos visto que las estructuras toman una parte de la memoria y se la reparten entre sus miembros. Cada miembro tiene reservado un espacio para l solo. El tamao total que ocupa una estructura en memoria es la suma del tamao que ocupa cada uno de sus miembros. Las uniones tienen un aspecto similar en cuanto a cmo se definen, pero tienen una diferencia fundamental con respecto a las estructuras: los miembros comparten el mismo trozo de memoria. El espacio que ocupa en memoria una unin es el espacio que ocupa el campo ms grande. Una unin se define de la siguiente manera: union nombre_de_la_unin
{ };

La manera de acceder a los elementos que componen a la unin se debemos proceder de la misma manera que como lo hicimos con las estructuras utilizando el operador punto. Veamos el ejemplo completo: #include <stdio.h> union _persona
{ char nombre[10];

char inicial;
} pers;

int main()
{ printf("Escribe tu nombre: ");

campos de la unin

gets(pers.nombre); printf("\nTu nombre es: %s\n", pers.nombre); printf("Tu inicial es: %c\n", pers.inicial);

36

Escribe tu nombre: rolando Tu nombre es: rolando Tu inicial es: r Para comprender mejor eso de que comparten el mismo espacio en memoria vamos a ampliar el ejemplo. Si aadimos unas lneas al final que modifiquen slo la inicial e imprima el nuevo nombre: #include <stdio.h> union _persona
{ char nombre[10]; char inicial; } pers;

Aqu queda claro que al cambiar el valor de la inicial estamos cambiando tamb in el nombre porque la inicial y la primera letra del nombre son la misma posicin de la memoria. Con las uniones podemos realizar los mismos procedimientos que manejamos con las estructuras en cuanto a la forma de inicializarlas, el manejo con apuntadores y con las funciones.

Uso de #define para constantes y enumeraciones:


En uno de los primeros apartados ya vimos como declarar constantes en nuestros programas por medio del modificador const en esta seccin vamos a ver dos maneras ms de realzar esto. La primera es esta es mediante la directriz de preprocesador #define : #include <stdio.h> #define primero 1 #define segundo 2
#define tercero 3

int main()
{

printf("Escribe tu nombre: ");


gets(pers.nombre);

printf("\nTu nombre es: %s\n", pers.nombre); printf("Tu inicial es: %c\n", pers.inicial); /* Cambiamos la inicial */ pers.inicial='Z'; printf("\nAhora tu nombre es: %s\n", pers.nombre); printf("y tu inicial es: %c\n", pers.inicial);
}

#define cuarto 4 #define quinto 5 int main()


{

int posicion; posicion=segundo; printf("posicion = %i\n", posicion);


}

Escribe tu nombre: Rolando Tu nombre es: Rolando Tu inicial es: R Ahora tu nombre es: Zolando y tu inicial es: Z

posicion = 2 Lo que en realizada est pasando es que cuando se ejecuta el preprocesador sustituye en nuestro cdigo los nombres de las constantes que estn despus de #define de hecho nos es posible no solo definir el valor de

37

constantes numricas sino por ejemplo podrimos hacer lo siguiente: #include <stdio.h> #define primero 1 #define segundo 2
#define tercero 3

De esta forma hemos definido las constantes primero, segundo,... quinto. Si no especificamos nada la primera constante (primero) toma el valor 0, la segunda (segunda) vale 1, la tercera 2,... Podemos cambiar estos valores
predeterminados por los valores que des eemos: enum { primero=1, segundo, tercero, cuarto, quinto };

#define cuarto 4 #define imprime printf int main()


{

Ahora primero vale 1, segundo vale 2, tercero vale 3,... Cada constante toma el valor de la anterior ms uno. Si por ejemplo hacemos:
enum { primero=1, segundo, quinto=5, sexto, septimo };

int posicion; posicion=segundo; imprime("posicion = % i\n", posicion);

Tendremos: primero=1, segundo=2, quinto=5, sexto=6, septimo=7. Con esta nueva tcnica podemos volver a escribir el ejemplo de antes: #include <stdio.h>
enum { primero=1, segundo, tercero, cuarto, quinto } posicion;

posicion = 2

int main()
{

Podemos ver que la salida que obtenemos es exactamente la misma, ya que el preprocesador reemplaza la palabra imprime con printf. Debemos tener cuidado al definir una constate numrica ya que los que el preprocesador hace es
reemplazar en el cdigo todo los que esta al a izquierda del nombre de la variable

posicion=segundo; printf("posicion = %i\n", posicion);

por ejemplo si escribimos lo siguiente: #define uno = 1

posicion = 2

Lo que har el preprocesador es sustituir =1 donde encuentre la palabra uno lo cual nos puede provocar errores de sintaxis. Otra manera de declara constantes es mediante las enumeracioneslas

cuales de definen de la siguiente manera:


enum nombre_de_la_enumeracin { nombres de las constantes };

Las constantes definidas con enum slo pueden tomar valores enteros (pueden ser negativos). Son equivalentes a las variables de tipo int. Un error habitual suele ser pensar que es posible imprimir el nombre de la constante: #include <stdio.h>
enum { primero=1, segundo, tercero, cuarto, quinto } posicion;

int main()
{ }

Por ejemplo:
enum { primero, segundo, tercero, cuarto, quinto };

printf("posicion = %s\n", segundo);

posicion =

38

typedef
La palabra reservada typedef proporciona un mecanismo para la creacin de sinnimos (o alias) para los tipos de datos anteriormente definidos. Los
nombres de los tipos de estructura se definen a menudo utilizando typedef, a fin

de crear nombres de tipo ms breves. Por ejemplo, el enunciado:


typedef struct card Card;

Escribe el nombre del amigo: Rolando Escribe el apellido del amigo: Lima Escribe el numero de telefono del amigo: 56009040 El amigo Rolando Lima tiene el numero: 56009040.

Define al nuevo nombre de tipo Card como un sinnimo paa el tipo Struct card . Los programadores de C utilizan a menudo typedef para definir un tipo de estructura de tal forma que un rtulo de estructura no sea requerido.
Utilicemos unos de los programas vistos en el capitulo de estructuras para ilustrar

typedef

se utiliza a menudo para crear seudnimos para los tipos de datos

lo anteior: #include <stdio.h>


struct estructura_amigo { char nombre[30];

bsicos. Por ejemplo, un programa que requiera de enteros de 4 bytes , pudiera utilizar el tipo int en un sistema y el tipo long en otro. Los programas diseados para portabilidad, a menudo utilizan typedef para crear un alias para que los enteros de bytes como seria Integer. Una vez dentro del programa el alias Integer pude ser modificado, para hacer que el programa funcione en ambos sistemas.

char apellido[40]; char telefono[10];


char edad; };

Manejo de archivos
Hasta el apartado anterior no habamos visto ninguna forma de guardar permanentemente los datos y resultados de nuestros programas. En este captulo vamos a verlo mediante el manejo de archivos. Es importante indicar que los archivos no son nicamente los archivos que guardamos en el disco duro, en C todos los dispositivos de la computadora se tratan como archivos: la impresora, el teclado, la pantalla,...
Lectura de un archivo

typedef struct estructura_amigo amigo; int main()


{

amigo Amigo; printf( "Escribe el nombre del amigo: " ); fflush( stdin ); scanf( "%s", &Amigo.nombre ); printf( "Escribe el apellido del amigo: " ); fflush( stdin ); scanf( "%s", &Amigo.apellido ); printf( "Escribe el numero de telefono del amigo: " ); fflush( stdin ); scanf( "%s", &Amigo.telefono ); printf( "El amigo %s %s tiene el numero: %s.\n", Amigo.nombre, Amigo.apellido, Amigo.telefono );
}

Analicemos el siguiente programa: #include <stdio.h> int main()


{

FILE *archivo; char letra; archivo = fopen("origen.txt","r"); if (archivo==NULL)


{ printf( "No se puede abrir el archivo.\n" ); exit( 1 );

39

Ahora nos toca abrir el fichero. Para ello usamos la funcin fopen. Esta

printf( "Contenido del archivo:\n" ); letra=getc(archivo); while (feof(archivo)==0)


{

funcin tiene el siguiente formato:


FILE *fopen(const char *nombre_archivo, const char *modo);

printf( "%c",letra );
letra=getc(archivo);

En el ejemplo usbamos: archivo = fopen("origen.txt","r"); El nombre de archivo se puede indicar directamente (como en el ejemplo) o usando una variable. El archivo se puede abrir de diversas formas. Esto se especifica con el parmetro modo. Los modos posibles son: r Abre un archivo existente para lectura. w Crea un archivo nuevo (o borra su contenido si existe) y lo abre para escritura. a Abre un archivo (si no existe lo crea) para escritura. El apuntador se sita al
final del archivo, de forma que se puedan aadir datos si borrar los existentes

if (fclose(archivo)!=0)
printf( "Problemas al cerrar el archivo\n" ); }

Contenido del archivo:

Este es un archivo de prueba para demostar como leer un archivo

Vamos a analizar el ejemplo poco a poco:


El apuntador FILE *

Se pueden aadir una serie de modificadores siguiendo a los modos anteriores: b Abre el archivo en modo binario. t Abre el archivo en modo texto + Abre el archivo para lectura y escritura. Ejemplos de combinaciones: rb+ - Abre el fichero en modo binario para lectura y escritura. w+ - Crea (o lo borra si existe) un fichero para lectura y escritura. rt - Abre un archivo existente en modo texto para lectura.
Comprobar si est abierto

Todas las funciones de estrada/salida estndar usan este apuntador para conseguir informacin sobre el archivo abierto. Este apuntador no apunta al archivo sino a una estructura que contiene informacin sobre l. Esta estructura incluye entre otras cosas informacin sobre el nombre del archivo, la direccin de la zona de memoria donde se almacena el archivo, tamao del buffer. Como dato curioso se adjunta la definicin de la estructura FILE definida en la librera stdio.h de la siguiente manera:
typedef struct {

int _cnt; char *_ptr; char *_base; int _bufsiz; int _flag; int _file;
char *_name_to_remove;

Una cosa muy importante despus de abrir un archivo es comprobar si realmente est abierto. El sistema no es infalible y pueden producirse fallos: el
archivo puede no existir, estar daado o no tener permisos de lectura.

int _fillsize;
} FILE; Abrir el archivo - fopen

Si intentamos realizar operaciones sobre un apuntador tipo FILE cuando no se ha conseguido abrir el archivo puede haber problemas. Por eso es importante comprobar si se ha abierto con xito. Si el archivo no s e ha abierto el apuntador archivo (apuntador a FILE) tendr el valor NULL, si se ha abierto con xito tendr un valor distinto de

40

NULL. Por lo tanto para comprobar si ha habido errores nos fijamos en el valor del apuntador: if (fichero==NULL)
{

while ( feof(fichero)==0 )

o
while ( !feof(fichero) )

printf( "No se puede abrir el archivo.\n" ); exit( 1 );

} Si archivo==NULL significa que no se ha podido abrir por algn error. Lo ms conveniente es salir del programa. Para salir utilizamos la funcin exit(1),

La segunda forma que comentaba arriba consiste en comprobar si el carcter ledo es el de fin de fichero EOF:
while ( letra!=EOF )

el 1 indica al sistema operativo que se han producido errores.


Lectura del archivo - getc Ahora ya podemos empezar a leer el archivo. Para ello podemos utilizar

Cuando trabajamos con archivos de texto no hay ningn problema, pero si estamos manejando un archivo binario podemos encontrarnos EOF antes del
fin de archivos. Por eso es mejor usar feof. Cerrar el archivo - fclose

la funcin getc, que lee los caracteres uno a uno. Se puede usar tambin la
funcin fgetc. Adems de estas dos existen otras funciones como fgets, fread que

leen ms de un carcter y que veremos ms adelante. El formato de la funcin getc (y de fgetc) es:
int getc(FILE *archivo);

Una vez realizadas todas las operaciones deseadas sobre el archivo hay que cerrarlo. Es importante no olvidar este paso pues el archivo podra
corromperse. Al cerrarlo se vacan los buffers y se guarda el archivo en disco. Un

En nuestro caso la utilizamos de la siguiente manera:


letra = getc( archivo);

archivo se cierra mediante la funcin fclose(archivo). Si todo va bien fclose devuelve un cero, si hay problemas devuelve otro valor. Estos problemas se pueden producir si el disco est lleno, por ejemplo.
Lectura de lneas

Tomamos un carcter de archivo, lo almacenamos en letra y el puntero


se coloca en el siguiente carcter. Comprobar fin de archivo - feof

La lectura de lneas completas de un archivo se realiza mediante funcin fgets. El formato de esta funcin es:
char *fgets(char *buffer, int longitud_max, FILE *achivo);

Cuando entramos en el ciclo while, la lectura se realiza hasta que se encuentre el final del archivo. Para detectar el final del archivo se pueden usar dos formas: con la funcin feof() comprobando si el valor de letra es EOF. En el ejemplo hemos usado la funcin feof. Esta funcin es de la forma:
int feof(FILE * archivo);

Esta funcin lee desde el archivo hasta que encuentra un carcter '\n' o hasta que lee longitud_max-1 caracteres y aade '\0' al final de la cadena. La cadena leda la almacena en buffer. Si se encuentra EOF antes de leer ningn carcter o si se produce un error la funcin devuelve NULL, en caso contrario devuelve la direccin de buffer. #include <stdio.h> main()
{

Esta funcin comprueba si se ha llegado al final de archivo en cuyo caso devuelve un valor distinto de 0. Si no se ha llegado al final de archivo devuelve un cero. Por eso lo usamos del siguiente modo:

FILE *archivo; char texto[10]; archivo=fopen("origen.txt","r");

41

if (archivo==NULL)
{

printf( "No se puede abrir el fichero.\n" ); exit( 1 );

letra=getc(origen); while (feof(origen)==0)


{

printf( "Contenido del archivo:\n" ); fgets(texto,100,archivo); while (feof(archivo)==0) { printf( "%s",texto ); fgets(texto,100,archivo); } if (fclose(archivo)!=0)
printf( "Problemas al cerrar el fichero \n" ); }

putc(letra,destino); printf( "%c",letra ); letra=getc(origen);


}

if (fclose(origen)!=0) printf( "Problemas al cerrar el archivo origen.txt \n" ); if (fclose(destino)!=0)


printf( "Problemas al cerrar el archivo des tino.txt \n" );

Contenido del archivo:

Este es un archivo de prueba para demostar como leer un archivo

Este es un archivo de prueba para demostar

como leer un archivo


Escritura de un archivo

El apuntador FILE *

Vimos en el apartado anterior el apuntador FILE es la base de la


escritura/lectura de archivos. Por eso definimos dos apuntadores FILE:

En este ejemplo abrimos un archivo 'origen.txt ' y lo copiamos en otro archivo 'destino.txt'. Adems el archivo se muestra en pantalla: #include <stdio.h> int main()
{

El apuntador 'origen' donde vamos a almacenar la informacin sobre el archivo origen.txt y 'destino' donde guardamos la del archivo destino.txt (el nombre del apuntador no necesaria mente tiene por qu coincidir con el del archivo).

FILE *origen, *destino; char letra; origen=fopen("origen.txt","r"); destino=fopen("destino.txt","w"); if (origen==NULL || destino==NULL)


{

Lectura del origen y escritura en destino

Como se puede observar en el ejemplo la lectura del archivo se hace igual que lo que vimos en el programa anterior. Para la escritura usamos la funcin putc:
int putc(int c, FILE *archivo);

printf( "Problemas con los archivos.\n" ); exit( 1 );

Donde c contiene el carcter que queremos escribir en el archivo y el apuntador archivo es el archivo sobre el que trabajamos. De esta forma vamos escribiendo en el archivo destino.txt el contenido del archivo origen.txt.

42

Comprobar fin de archivo

Como siempre que leemos datos de un archivo debemos comprobar si


hemos llegado al final, slo debemos comprobar si estamo s al final del archivo que leemos, no tenemos la necesidad de que comprobar el final del archivo en el

Para que quede ms claro examinemos el siguiente ejemplo que es un programa de una agenda que guarda el nombre, apellido y telfono de cada persona: #include <stdio.h> #include <string.h> struct
{

que escribimos puesto que lo estamos creando y an no tiene final.


Cerrar el archivo

Y por fin lo que nunca debemos olvidar al trabajar con archivos: cerrarlos. Debemos cerrar tanto los archivos que leemos como aquellos sobre los que escribimos.
Escritura de lneas

char nombre[20]; char apellido[20]; trabaja junto con la funcin fgets que vimos en el }registro; main()
{ char telefono[15];

La funcin fputs programa anterior:

int fputs(const char *cadena, FILE *archivo);

fread y fwrite Las funciones que hemos visto hasta ahora (getc, putc, fgets, fputs) son adecuadas para trabajar con caracteres (1 byte) y cadenas. Pero, qu sucede cuando queremos trabajar con otros tipos de datos? Supongamos que queremos almacenar variables de tipo int en un archivo. Como las funciones vistas hasta ahora slo pueden operar con cadenas deberamos convertir los valores a cadenas. Para recuperar luego estos valores
deberamos leerlos como cadenas y pasarlos a enteros.

FILE *archivo; archivo = fopen( "nombres.txt", "a" ); do { printf( "Nombre: " ); fflush(stdout); gets(registro.nombre); if (strcmp(registro.nombre,""))
{

Existe una solucin mucho ms fcil. Vamos a utilizar las funciones fread y fwrite. Estas funciones nos permiten tratar con datos de cualquier tipo, incluso con estructuras. fwrite Empecemos con fwrite, que nos permite escribir en un archivo. Esta funcin tiene el siguiente formato:
size_t fwrite(void *buffer, size_t tamao, size_t numero, FILE *pfichero); }

printf( "Apellido: " ); fflush(stdin); gets(registro.apellido); printf( "Telefono: " ); fflush(stdin); gets(registro.telefono); fwrite(&registro,sizeof(registro),1,archivo);
}

}while (strcmp(registro.nombre,"")!=0); fclose( archivo );

buffer: variable que contiene los datos que vamos a escribir en el archivo. tamao: el tamao del tipo de dato a escribir. Puede ser un int, un float,
una estructura. Para conocer su tamao usamos el operador sizeof. numero : el nmero de datos a escribir.

parchivo: El apuntador al archivo sobre el que trabajamos.

Nombre: rolando Apellido: lima Telefono: 56284065 Nombre: lima Apellido: maciel Telefono: 23132131 Nombre: enrique Apellido: maciel Telefono: 5498498498

43

Siendo buffer la variable donde se van a escribir los datos ledos del fichero parchivo El valor que devuelve la funcin indica el nmero de elementos
de tamao 'tamano' que ha conseguido leer. Nosotros podemos pedirle a fread

que lea 10 elementos (numero=10), pero si en el archivo slo hay 6 elementos fread devolver el n mero 6. Siguiendo con el ejemplo anterior ahora vamos a leer los datos que habamos introducido en "nombres.txt". El ciclo termina cuando el 'nombre' se deja en blanco. Este programa guarda los datos personales mediante fwrite usando la estructura registro. Abrimos el archivo en modo 'a' (aadir), para que los datos que introducimos se a adan al final del archivo. Una vez abierto abrimos estamos en un ciclo do-while mediante el cual introducimos los datos. Los datos se van almacenando en la variable registro (que
es una estructura). Una vez tenemos todos los datos de la persona los metemos en

#include <stdio.h> struct


{

el archivo con fwrite:


fwrite( registro, sizeof(registro), 1, archivo );

char nombre[20]; char apellido[20]; char telefono[15];

} registro;

registro : es la variable (en este caso una estructura) que contiene la informacin a meter al archivo. sizeof(registro): lo utilizamos para saber cul es el nmero de bytes que
vamos a guardar, el tama o en bytes que ocupa la estructura.

int main()
{

FILE *archivo; archivo = fopen( "nombres.txt", "r" ); while (!feof(archivo))


{

1: indica que slo vamos a guardar un elemento. Cada vez que se recorre el ciclo guardamos slo un elemento. Archivo: el apuntador FILE al archivo donde vamos a escribir.

if (fread( &registro, sizeof(registro), 1, archivo ))


{

En el ejemplo anterior tambin hicimos uso de una funcin contenida en la librera <string.h> esta funcin fue strcmp() . La forma de definir esta funcin es:
int strcmp(const char *s1, const char *s2) }

printf( "Nombre: %s\n", registro.nombre ); printf( "Apellido: %s\n", registro.apellido); printf( "Telefono: %s \n", registro.telefono);
}

fclose( archivo );
}

Esta funcin compara la cadena a la cual seala s1 con la cadena a la cual seala s2 . La funcin strcmp devuelve un entero mayor que, igual o menor que cero, segn que cadena a la cual seala s2 sea mayor que, igual o menor que la cadena a la cual seala s2. fread La funcin fread se utiliza para sacar informacin de un archivo. Su formato es:
size_t fread(void *buffer, size_t tamano, size_t numero, FILE *parchivo);

Nombre: rolando Apellido: lima Telefono: 56284065 Nombre: lima Apellido: maciel Telefono: 23132131 Nombre: enrique Apellido: maciel Telefono: 5498498498

44

programa que lee la letra que hay en la posicin que especifica el usuario: #include <stdio.h> int main()
{

Abrimos el archivo nombres.txt en modo lectura. Con el ciclo while nos


aseguramos que recorremos el archivo hasta el final. La funcin fread lee un registro (numero=1) del tamao de la estructura

FILE *archivo; long posicion; int resultado; archivo = fopen( "nombres.txt", "r" ); if(archivo==NULL) printf("\n No se pudo abrir el archivo"); else
{

registro. Si realmente ha conseguido leer un registro la funcin devolver un 1, en cuyo caso la condicin del 'if' ser verdadera y se imprimir el registro en la
pantalla. En caso de que no queden ms registros en el archivo, fread devolver 0 y no se mostrar nada en la pantalla.

fseek

La funcin fseek nos permite situarnos en la posicin que queramos de un archivo abierto. Cuando leemos un archivo hay un 'apuntador' que indica en qu lugar del archivo nos encontramos. Cada vez que leemos datos del archivo este apuntador se desplaza. Con la funcin fseek podemos situar este apuntador en el lugar que deseemos. El formato de fseek es el siguiente:
int fseek(FILE *parchivo, long desplazamiento, int modo);

printf( "Que posicion quieres leer? " ); scanf( "%d", &posicion );


resultado = fseek( archivo, posicion, SEEK_CUR);

if (!resultado)
{

posicion,r ); else

r=getc(archivo); printf( "En la posicion %d esta la letra %c.\n",


}

Como siempre parchivo es un apuntador de tipo FILE que apunta al archivo con el que queremos trabajar. Desplazamiento son las posiciones (o bytes) que queremos desplazar el apuntador. Este desplazamiento puede ser de tres tipos dependiendo del valor de modo: SEEK_SET El apuntador se desplaza desde el principio del archivo.
SEEK_CUR El apuntador se desplaza desde la posicin actual del archivo. SEEK_END El apuntador se desplaza desde el final del archivo. }

printf( "Problemas posicionando el cursor.\n" ); fclose( archivo);


}

Estas tres constantes estn definidas en el archivo <stdio.h>. Como curiosidad se indican a continuacin sus definiciones:
#define SEEK_SET

Que posicion quieres leer? 65 En la posicion 65 esta la letra . ftell

#define SEEK_CUR
#define SEEK_END

0 1
2

Esta funcin es complementaria a fseek , devuelve la posicin actual dentro del archivo. Su formato es el siguiente:
long ftell(FILE *parchivo);

Es posible que los valoresanteriores cambien de un compilador a otro. Si se produce algn error al intentar posicionar el apuntador, la funcin devuelve un valor distinto de 0. Si todo ha ido bien el valor devuelto es un 0. En el siguiente ejemplo se muestra el funcionamiento de fseek , se trata de un

El valor que nos da

ftell puede ser usado por fseek

para volver a la posicin

45

actual. fprintf y fscanf Estas dos funciones trabajan igual que sus equivalentes printf y scanf. La nica diferencia es que podemos especificar el archivo sobre el que operar (si se desea puede ser la pantalla para fprintf o el teclado para fscanf).Los formatos de estas dos funciones son:
int fprintf(FILE *pfichero, const char *formato, ...); int fscanf(FILE *pfichero, const char *formato, ...);

Escribe -1 en edad para salir

Dame la edad: 20 Dame el nombre: rolando Dame la edad: 15 Dame el nombre: enrique Dame la edad: 54 Dame el nombre: silvia Dame la edad: -1 El contenido en nuestro archivo queda de la siguiente forma: rolando 20 enrique 15 silvia 54 Como podemos ver es mucho ms fcil darle formato, ahora veamos un programa donde usamos la funcin fscanf : #include <stdio.h> void main()
{

Veamos un siguiente programa: #include <stdio.h> void main()


{

FILE *archivo; int edad; char nombre[30]; archivo=fopen("pru.txt","w"); if(archivo==NULL) printf("\n Error al crear el archivo"); else
{

printf("Escribe -1 en edad para salir"); do


{

printf(" \n Dame la edad: "); scanf("%i",&edad); if(edad!=-1)


{

printf("\n Dame el nombre: "); scanf("%s",nombre); fprintf(archivo,"%s %i\n",nombre,edad);

} }while(edad!=-1); fclose(archivo); } }

FILE *archivo; int edad; char nombre[30]; archivo=fopen("pru.txt","r"); if(archivo==NULL) printf("\n Error al abrir el archivo"); else
{

46

printf("\n Los datos en el archivo son:"); do


{

mano no importa cundo, cmo, dnde ni porque, el ejecutable ser "autosuficiente" siempre, el problema cuando estas ligando en forma esttica es
que el tamao del ejecutable puede ocupar ms espacio.

fscanf(archivo,"%s %i",nombre,&edad); printf(" \n\n %s %i",nombre,edad);


}while(!feof(archivo)); fclose(archivo); } }

Cuando ligas en forma dinmica lo que haces es simplemente indicarle al programa que cuando se est ejecutando busque en algn directorio de la mquina local la librera que necesita, de este modo el ejecutable ya no ocupar tanto espacio, y cuando necesite utilizar alguna rutina la cargar directamente de la librera que se encuentra en la mquina donde est corriendo, el problema con esto, como podrs imaginarte, es que puede o no existir dicha librera en la mquina donde est corriendo lo que, obviamente, causar que el programa pueda o no correr. La forma en la que se ligan las libreras al cdigo en C es dinmica, y por lo tanto siempre se debe incluir la referencia con la ubicacin de los archivos de librera. Esto se hace con la directiva de preprocesador #include. Existen distintas formas
de invocar a las libreras, la forma que esta ahora se conoce:

Los datos en el archivo son: rolando 20 enrique 15 silvia 54

#include <librera.h> se emplea para las libreras que se encuentran con la distribucin de nuestro compilador de C; en el caso de UNIX el directorio es: /usr/lib/gcc-lib/ y en el caso del windows o ms -dos es ruta_instalacion/C/include/ Si nosotros deseamos especificar la ruta del archivo de librera que vamos a incluir en la cabecera de nuestro programa con #include debemos escribirlo de la siguiente forma: #include ruta_absoluta_o_relativa_del_archivo.h Por ejemplo, si tenemos nuestro archivo de librera .h en el mismo directorio que nuestro codigo fuente solo escribimos: #include archivo.h Si se encuentra dentro de una carpeta dentro del mismo directorio que nuestro codigo escribimos: #include carpeta/mis_librerias/poderoso.h A continuacin veremos un ejemplo muy sencillo del uso de las libreras de
nuestra creacin; tenemos dos archivos, uno con extensin punto C al que

Libreras
Una librera es un conjunto de rutinas (una rutina es un conjunto de instrucciones), previamente construidas, que se enfocan en realizar algn trabajo en especial, para que as el programador ya no tenga que preocuparse por los detalles de cmo hacer ese trabajo, simplemente manda a llamar dentro de su propio cdigo las instrucciones adecuadas que hay en la librera para hacer lo que l busca y ya, las instrucciones harn lo que deban hacer sin que nadie tenga que preocuparse por cmo lo hacen. Como una librera no es parte del cdigo de tu programa, para poder utilizarla es necesario, al momento de compilar tu programa, ligarlo con la librera, es decir,
hacer saber al programa que las rutinas estn en alguna otra parte, y por supuesto,

en que parte estn. Existen dos formas en las que el cdigo y la librera pueden quedar ligados, en forma dinmica y en forma esttica, y cada una tiene sus propias ventajas y desventajas. Cuando ligas en forma esttica lo que haces es "pegarle" a tu ejecutable las rutinas que utilizas de la librera, de modo que todo queda guardado en un mismo archivo, de este modo cuando corres el programa se cargar en memoria al mismo tiempo que las rutinas de la librera, y siempre las tendrs a la

llamaremos main.c y tendremos otro archivo con extensin punto H al que

47

llamaremos sumita.h; a continuacin el codigo de ambos respectivamente. /* main.c */ #include <stdio.h> #include "../jal/sumita.h"
int main(){

finalmente observe que, por un estndar de la programacin NSI, la funcin main


debe devolver un valor entero y por eso se emplea return 0, y se declara:

int main() porque main devolver un valor de tipo entero. La utilidad de esta forma de programacin es hacer programas modulares, es decir, el cdigo por separado con funciones especificas; esto sirve para trabajos en equipo donde cada miembro puede programar un modulo o bloque de un
programa que finalmente solo se unira y se llamaran las funciones creadas por el

int a, b;
printf(" \n\n******ESTE PROGRAMA LLAMA A UNA FUNCION DE SUMA EN UN PUNTO H\n\n");

printf("da a: "); scanf("%d",&a); printf(" \n\nda b: "); scanf("%d",&b); suma(a,b);


printf("LA OPERACION HA FINALIZADO");

return 0;
}

resto del equipo desde un cdigo con la funcin main. La otra gran importancia de esta forma de programar es la reutilizacin del cdigo, as como nuestro compilador incluye rutinas preestablecidas que solo invocamos mediante la llamada a una funcin tambin nosotros podemos llamar las rutinas que mas empleamos para ahorrar cdigo, trabajo y tiempo. Con forme se avance en el estudio de la programacin y de los sistemas basados en software libre se entendern ms la modularidad y la reutilizacin del cdigo.

Macros
Una macro es una operacin definida en la directiva de preprocesador #define. Una macro se define en el cdigo de la siguiente forma:
#define nombre_macro texto_de_reemplazo

/* sumita.h */ #include<stdio.h> void suma(int a, int b)


{

a=a; b=b; int c; c=a+b; printf("\n\nEl resultado de la suma es %d\n\n", c); fflush(stdin);
getchar(); } El main.c llamamos a la funcin suma pasndole dos argumentos capturados

Entonces, dentro del cdigo del programa al realizar una invocacin a la macro por medio del nombre de la misma, se colocar el texto de reemplazo tal como se encuentra en la macro, dentro del cdigo y antes de compilarse todo el programa . Las macros pueden ser definidas con o sin argumentos. Una macro sin argumentos se procesa como si fuera una constante simblica. En una macro con argumentos, los argumentos se sustituyen en el texto de reemplazo, y a continuacin la macro se expande, es decir, en el programa el texto de reemplazo remplaza al identificador y a la lista de argumentos.
A continuacin se presenta un ejemplo donde se ven las diferentes formas de usar

anteriormente. La directiva #include del preprocesador ligara nuestro cdigo principal con el archivo sumita.h; cuando en el cdigo llamemos funciones que no se encuentren en stdio.h las buscara en nuestro archivo. Es importante
recalcar que la forma en que se llaman y crean las funciones es idntica a como lo

las macros: Obsrvese en el cdigo que las definiciones de las macros se ponen despus de la inclusin de libreras , usando el operador #. Por regla o costumbre dentro de la programacin en C los nombres de las macros deben ir con maysculas, e

haramos en un solo archivo punto C, pero con la salvedad de que nuestro archivo sumita.h no contiene una funcin principal, es decir: main. Y

48

igualmente con maysculas ser como se empleen dentro del cdigo del programa. Y por ultimo, despus del nombre en maysculas de la macro va el texto de reemplazo, que se colocar dentro del cdigo cada vez que se encuentre el nombre de la macro. Adems observa que la macro puede tener valores
constantes o puede estar a la espera de argumentos.

En las dos ultimas definiciones de macros tenemos ejemplos de cmo es que una macro va a esperar argumentos dentro del cdigo del programa; la sintaxis es la siguiente: #define nombre_macro(argumentos texto_de_reemplazo_conlos_argumentos separados por coma)

Max es 5 y min es 0 Max es 5 y min es 1 Max es 5 y min es 2 Max es 5 y min es 3 Max es 5 y min es 4 Max es 5 y min es 5 Max es 6 y min es 5 Max es 7 y min es 5 Max es 8 y min es 5 Max es 9 y min es 5

*/ En el cdigo se observo como se define una macro con o sin argumentos; en el ciclo for se llamaron a las macros y se les pasaron argumentos, y ntese que los argumentos en la definicin de la macro (A,B) tienen diferente nombre de las variables enviadas a las macros en el ciclo for (ndice, contador). Posteriormente se anexa la salida en pantalla del programa. Dentro de los argumentos se pueden incluir expresiones como: B+1, 2+3, x+y+z, (2x,x*x); por ejemplo el siguiente fragmento de cdigo: #include <stdio.h>
#define CUAD(x) ((x)*(x))

Para mas claridad ejectese el siguiente programa revisando el cdigo. # include <stdio.h> # define INICIO 0 # define FINAL 9
# define MAX(A,B) ((A)>(B)?(A):(B)) # define MIN(A,B) ((A)>(B)?(B):(A))

/* Punto de inicio del bucle */ /* Fin del bucle */ /* Definicin macro de Max */ /* Definicin macro de Min */

int main( )
{

int indice, mn, mx ; int contador = 5 ; for (indice = INICIO ; indice <= FINAL ; indice++)
{

void main()
{

double a, b;
a = 7./ CUAD(b+1); }

printf(El valor de a es %f,a); En el ejemplo anterior, vamos a reemplazar la macro por el texto de reemplazo el cual va a quedar de la siguiente forma, incluyendo los argumentos: a=7/((b+1)*(b+1)) Por ultimo, se debe tener mucho cuidado en el uso de las macros ya que se deben respetar las reglas de prioridad de operadores para obtener los resultados deseados; adems cualquier argumento enviado a una macro solo ser evaluado dentro del cdigo de sustitucin y en caso de que la variable se modifique podra traer errores en la ejecucin; En el preprocesamiento no se realiza una verificacin de tipos y de sintaxis por lo que no se enviara mensaje de error hasta

mx = MAX(indice, contador) ; mn = MIN(indice, contador) ;


printf ( "Max es %d y min es %d\n", mx, mn) ; }

return 0 ;
}

/* Resultado de la ejecucin:

49

la ejecucin del programa, as que dichas verificaciones y validaciones se deben


llevar a cabo antes de enviar argumentos a la macro y debe tomarse demasiado en importancia de los parntesis en las operaciones que deseamos realizar, compile

serio la colocacin de parntesis en el texto de sustitucin. Para hacer ver la el siguiente cdigo y analice la salida en pantalla. #include <stdio.h>
#define EQUIVOCADA(A) A*A*A EQUIVOCADA para el cubo */

iniciar la copia de archivos se considera el idioma de los usuarios, entonces simplemente imaginase que en el siguiente cdigo en cada uno de los case adems de imprimir hola mundo en el lenguaje seleccionado hace la llamada a archivos que contienen las instrucciones de la instalacin en el lenguaje seleccionado sin tener que crear programas independientes para cada pas, lo cual baja costos y ahorra trabajo en tiempo de programacin. #include <stdio.h> #define ENG "Hello world again!!!!!" #define ESP "Hola mundo, como siempre"
#define ITA "Ciao mondo, ancora!!!!!"

/* Macro
/* Macro correcta para el cubo */

#define CUBO(A) (A)*(A)*(A)


#define CUADRADO(A) (A)*(A)

el cuadrado */ */

/* Macro correcta para

#define SUMA_EQUIVOCADA(A) (A)+(A)/* Macro equivocada para la suma #define SUMA_CORRECTA(A) ((A)+(A)) /* Macro correcta para la suma */

#define INICIO 1 #define FINAL 7 int main( )


{

void main(){ char var; printf("\n\n************\n1-chose your idioma\n3-ha scelto la vostra lingua\n"); scanf("%c",&var); fflush(stdin);
switch(var){

languaje\n2-escoge

tu

case '1': printf("%s",ENG);


break;

int i, offset ; offset = 5 ;


for (i = INICIO ; i <= FINAL ; i++) { printf ("El cuadrado de %3d es %4d, y su cubo es %6d\n", i+offset, CUADRADO(i+offset), CUBO(i+offset)) ;

case '2': printf("%s",ESP); case '3': default:


} break;

printf ("El cubo equivocado de %3d es %6d\n",


i+offset, EQUIVOCADA(i+offset)) ; } printf ("\nProbamos la macro de suma \n") ; for (i = INICIO ; i <= FINAL ; i++) { printf ("La macro de suma EQUIVOCADA = %6d, y la correcta = %6d\n", 5*SUMA_EQUIVOCADA(i), 5*SUMA_CORRECTA(i)) ; }

printf("%s",ITA);
break;

printf("Opcion no disponible"); getchar();


}

return 0 ;
}

Y considere el uso de las cadenas, en la definicion se deben usar las comillas forzosamente y hacer las impresiones como si se tratar de una cadena; ms adelante se ve un ejemplo similar.

El siguiente ejemplo a mayor escala podra ser el lanzador de un programa de instalacin de algn paquete como por ejemplo un juego, en donde antes de

50

Compilacin condicional
El preprocesador de C permite eliminar lneas de cdigo antes de ser compiladas, con lo cual se consigue una compilacin y ejecucin del programa ms rpida; a
esta caracterstica del preprocesador de C se le conoce como compilacin

#include<stdio.h> #include<stdlib.h> #define SIMBOLO 500 void main(void){


int x=SIMBOLO;

condicional. Las directivas de compilacin condicional soportadas por C son: #ifdef <SIMBOLO> #ifndel <SIMBOLO> #endif Si esta definido Si no esta definido Para finalizar la compilacin condicional

#ifdef SIMBOLO printf("\n\n\nEl contenido del simbolo es %i\n", x); #endif printf("El simbolo sera indefinido\n");
#undef SIMBOLO

#ifndef SIMBOLO #endif getchar();


} printf("EL SIMBOLO YA NO ESTA DEFINIDO\n");

#if <expresin_constante> Si la expresin es verdadera (no cero) #else #endif Para finalizar la compilacin condicional #if <expresin_constante> Si la expresin es verdadera (no cero) #elif <expresin_constante> Si la expresin anterior no fue verdadera (cero) #endif Para finalizar la compilacin condicional Adems estas directivas pueden ser empleadas en conjunto con las directivas de preprosesador vistas anteriormente como #define. Tambin existen otras directivas de preprocesador que se emplearan en los siguientes ejemplos, son:
#undef <SIMBOLO A ELIMINAR> Quita la definicin de un smbolo definido.

/*el resultado de esta ejecucin es: El contenido del smbolo es 500 El smbolo sera indefinido
EL SIMBOLO YA NO ESTA DEFINIDO

En el siguiente ejemplo observaremos como las condiciones de compilacin se llevan a cabo fuera de main() y tambin dentro de main() . # include <stdio.h> #include <stdlib.h> # define OPCION_1 /* Esto define el control del preprocesador */
# ifdef OPCION_1

#error <mensaje del usuario> Enva un mensaje de error fatal ms el mensaje que el usuario decida. A continuacin obsrvese el siguiente cdigo, muy sencillo, para comprender el uso de #undef, y de los condicionantes de compilacin; adems fjese que debe
cerrarse cada que se abre alguna condicionante de compilacin con #endif, ya sea

del tipo de comparacin si existe algn smbolo definido o indefinido (#ifdef y


#ifndef) o si se trata de comparaciones entreconstantes (#if, #else y #elif).

int contador_1 = 17; /* Esto existe solo si OPCION_1 es definido */ # endif int main( )
{

int indice ; for (indice = 0 ; indice < 6 ; indice++)


{

printf ("En el bucle, indice = %d", indice) ;


# ifdef OPCION_1 printf (" contador_1 = %d", contador_1) ; /* puede desplegarse */

51

# endif

printf ("\n") ;
} getchar();

return 0 ;
} # undef OPCION_1

void main(void){ int var; printf("\n\nSELECCIONA EL IDIOMA/CHOSE THE LANGUAJE (0/1)\n\n"); scanf("%d", &var);
switch(var){

case 0: printf("\n\n%s",ESPA); break;


case 1: printf("\n\n%s",ENGL);

/* Resultado de la ejecucin: (Con OPCION_1 definido) En el bucle, indice = 0 contador_1 = 17


En el bucle, indice = 1 contador_1 = 17 En el bucle, indice = 2 contador_1 = 17 En el bucle, indice = 3 contador_1 = 17 En el bucle, indice = 4 contador_1 = 17 En el bucle, indice = 5 contador_1 = 17

break; default: printf("\n\nNo juegues/Don`t play"); printf(\n\nAdios!!!!/Bye Bye); fflush(stdin); getchar();


} }

(Comentando removiendo la lnea 4) En el bucle, indice = 0 En el bucle, indice = 1 En el bucle, indice = 2 En el bucle, indice = 3 En el bucle, indice = 4 En el bucle, indice = 5 */ Ahora, como se menciona en el cdigo, comente la lnea numero cuatro y vea el
resultado de la ejecucin. Esto ocurre porque al comentar dicha lnea es como si

Los smbolos ESPA y ENGL representan dos cadenas de texto, por lo cual deben colocarse comillas dobles al final y al inicio de la cadena; y posteriormente cuando en el cdigo se llama a los smbolos se debe usar el especificador de conversin de cadenas %s. Como ejemplo puede quitarle las comillas a las cadenas en la definicin o no usar el especificador de conversin. Por ultimo, en el siguiente ejemplo se analizaran las directivas de compilacin condicional: #if, #elif, #elseif y #endif. #include<stdio.h> #include<stdlib.h>
#define ESPA "MENSAJE EN ESPAOL, ESPA DEFINIDO" #define ENGL "THIS IS THE ENGLISH TEXT" #define NOESPA "ESPA NO ESTA DEFINIDO" #define NOENGL "ENGL SIMBOL IS UNDEFINED"

nunca se hubiese creado la definicin OPCION_1 con lo cual la constante


contador_1 tampoco es creada aunque no se ha comentado dicha lnea del cdigo.

En los siguientes ejemplos se vera el uso de la definicin de smbolos con cadenas de texto, y el uso de la compilacin condicional con #if, #elif, #elseif y #endif. #include<stdio.h> #include<stdlib.h> #define ESPA "Se ha cometido un error" #define ENGL "An error has ocurred"

void main(void){ printf("\n\n\n************************A VERAS:\n"); #if defined(ESPA) printf("\n%s \n",ESPA); #elif !defined(ESPA) CONTINUACION

52

printf("\n%s\n",NOESPA); #endif fflush(stdin); getchar(); printf(" \n\n\nNOW IN ENGLIS:");


#if defined(ENGL)

#else #endif

printf("\n%s",ENGL); printf("\n%s",NOENGL);

fflush(stdin); getchar();
}

Para visualizar el efecto del cdigo y ejemplificar mas claramente el uso de las directivas condicionales comente las lneas donde se define ESPA y ENGL para poder observar el resultado distinto al evaluar las directivas; este tipo de directivas usan constantes definidas para realizar las condiciones de ejecucin, por lo que si usa estas directivas con evaluaciones como variable==0 obtendr mensaje de error.

53

Вам также может понравиться