Vous êtes sur la page 1sur 79

Tutorial de Lenguaje C

por Cesar Rodriguez

Este tutorial es gratuito, puedes distribuirlo libremente siempre que sea en forma gratuita y
en forma completa sin alteración alguna. La distribución en cualquier medio de este tutorial
comercialmente queda estrictamente prohibida sin consentimiento por escrito del autor.

Copyright 2003-2005, todos los derechos reservados por Rogelio Cesar Rodriguez Cervantes

Informacion de contacto:
e-mail 1 : cesarrdz@gmail.com
e-mail 2 : cesarrdz@excite.com
Sito web : http://cesar.crbyte.com

Visita:
http://cesar.crbyte.com para mas tutoriales de C++, Base de datos, programas con codigo
fuente, etc.
http://crbyte.com para juegos de computadora que puedes bajar gratis

1
Contenido
Prefacio 4
1. Fundamentos del lenguaje 5
Funciónes 5
Identificadores 6
Tipos de datos 6
Declaración de variables 6
Funcion printf 7
Constantes 9
Construcción cast 10
Constantes simbólicas 11
Operadores 11
Conversiones de tipo 13
Funcion scanf 14
Precedencia y orden de evaluación 15
2. Control de flujo 16
Sentencia if-else 16
Iteraciones while y for 17
Sentencia break 18
Saltos y etiquetas 18
Sentencia switch 19
Iteración do-while 20
3. Arreglos y cadenas de caracteres 22
Arreglos unidimensionales 22
Cadenas de caracteres 24
Funciones mas comunes para manejo de cadenas 25
Arreglos bidimensionales 26
4. Funciones 29
Tipos de variables: Parte 1 29
Funciones 30
Reglas de alcance de funciones 31
Argumentos de funciones: Llamada por valor 32
Archivos de encabezados 33
5. Apuntadores 34
Definición de apuntadores 34
Indireccion 35
Llamadas por apuntadores 36
Aritmética de apuntadores 37
Apuntadores y arreglos 38
Apuntadores a caracteres 39
Manejo dinámico de memoria 42
Arreglos de apuntadores 43
Indireccion simple y múltiple 44
Diferiencias arreglos bidimensionales y de apuntadores 46
Argumentos para main 46
6. Estructuras 48
Definición 48
Arreglo de estructuras 50
2
Paso de estructuras a funciones 53
Apuntadores a estructuras 55
7. Archivos 58
Entrada y salida 58
Corrientes de flujo 58
Función fopen 58
Función fclose 59
Función getc 59
Función putc 61
Función ferror 62
Función fwrite 64
Ejemplo de fread 64
8. Temas avanzados 68
Tipos de variables: Parte 2 68
Operadores: Parte 2 69
Definición de tipos: 74
Funciones recursivas 76
9. Bibliografia 79

3
Prefacio
El lenguaje C es de propósito general y se clasifica relativamente de "bajo nivel".
Fue creado para poder manejar todos los recursos del computador en uso, así como
facilitar el desarrollo de sistemas: pocas restricciones, código rápido y eficaz.

Por ejemplo veamos PASCAL; este fue diseñado por el profesor WRITH para
enseñar la programación estructurada, no para aprovechar todos los recursos ni
para realizar grandes sistemas.
Por lo tanto al desarrollar el C no se tuvo en mente facilitar la programación sino
aprovechar al máximo los recurso disponibles.

El código C es altamente transportable. La portabilidad significa que se puede


adaptar el código escrito para un tipo de computadora a otro tipo.

El lenguaje C solo cuenta con 32 palabras reservadas definidas por el estándar


ANSI.

El origen del lenguaje C empezó con un lenguaje llamado BCPL , desarrollado por
Martín Richards; que a la vez influyo en un lenguaje llamado B inventado por Ken
Thompson.
Basándose en este ultimo Dennis Ritchie desarrollo el C, y fue implementado por
primera vez en una PDP-11 que usaba el sistema operativo UNIX.

Este material esta pensado para impartir un curso de Lenguaje C en un semestre


a nivel introductorio, pero cubriendo el contenido suficiente para realizar
programas profesionales.

He tratado de evitar en todo lo posible el tipo de escritura compacto tradicional de


los programas en C para lograr mayor claridad en los ejemplos.

Agradecimiento
A mis Padres Rogelio Rodríguez y Rosario Cervantes. Por haberme apoyado para
estudiar la carrera de Ingeniería en Sistemas Computacionales.

4
1
Fundamentos del lenguaje
Funciones
Un programa en C puede verse como un grupo de blocs construidos llamados
funciones.
Una función es una o mas instrucciones en C designadas para una tarea especifica.
Similares a los procedimientos y funciones en PASCAL.

/* Programa : Primer.c */
/* Descripcion: Escribe un mensaje en pantalla */
#include<stdio.h>

main() {
printf("Este es mi primer programa en C\n");
}

/* */
Enmarcan comentarios. Se usan en cualquier lugar del programa.

#include <stdio.h>
La palabra include especifica la inclusión de un archivo, en este caso del archivo
stdio.h, el cual define prototipos de funciones y macros diversas.

main()
Todo programa tiene una función especial llamada main la cual indica donde
empieza la ejecución. Esta función debe aparecer solo una vez en el programa.

{}
Las llaves enmarcan las sentencias o proposiciones que integran la función;
análogas al begin y end de PASCAL.

printf()
El sistema C contiene una biblioteca estandard de funciones para usarse en los
programas; esta función imprime en pantalla una cadena de caracteres.

'\n'
Esta secuencia de caracteres significa Nueva Linea; representa solo un carácter.
Printf no efectúa salto de linea automático.

Ejercicio: Datosper.c
Escriba un programa que despliegue sus datos personales en pantalla.
5
Identificadores
El lenguaje C define identificadores con los nombres que se utilizan para
referenciar variables, funciones, etiquetas y otros objetos definidos por el usuario.

Reglas
1.- Debe Comenzar con una letra
2.- Formarse con letra, dígitos y guiones bajos
3.- El tamaño no debe exceder de 31

El estandard C permite identificadores hasta de 31 caracteres.


El BORLAND C permite hasta 32; además de un carácter extra: $ (pero no como
carácter inicial).
Esto es para identificadores internos, para identificadores externos depende de
donde se este implementando pueden ser 6 u 8.

Tipos de datos
TIPO BITS
char 8
int 16
float 32
double 64
void 0

Modificadores de tipo.
Excepto para void, los tipos de datos básicos tienen varios modificadores que los
proceden. Se usa un modificador para alterar el significado de un tipo base para
encajar con las necesidades diversas mas precisamente.
signed
unsigned
long
short

Declaración de variables
Todas las variables deben de ser declaradas antes de utilizarlas, aunque ciertas
declaraciones se realizan implícitamente por el contexto. Una declaración
especifica un tipo , y le sigue una lista de una o mas variables de ese tipo.
Ejemplo:
int a, x;
char c, linea[10];

También:
int a;
int x;
char c;
char linea[10];

6
Las variables también se pueden inicial izar en su declaración.
Ejemplo:
char tabulador = '\t';
int i = 0;
float x = 3.4;

/* Programa : DECVAR.C
Descripcion: Declaracion de variables*/
#include <stdio.h>

main() {
int n, c;

c = 2;
n = c * c;
printf("\nEl cuadrado de 2 es %d ", n);
}

El lenguaje C hace distinción entre minúsculas y mayúsculas.


Ejemplo:
'linea' es diferente de 'LINEA' y de 'Linea'

printf()
#include <stdio.h>
int printf(const char *formato ...);
Da formato e imprime texto en el flujo de salida estandard.

Descripcion:
El formato apunta a un string que contiene texto, constantes de caracter, y una o
mas especificaciones de conversion.

Constantes de carácter no imprimibles.


CÓDIGO SIGNIFICADO
\b retroceso
\f alimentación de hoja
\n nueva linea
\r retorno de carro
\t tabulador horizontal
\" doble comilla
\' apóstrofe o simple comilla
\0 nulo
\\ barra invertida
\v tabulador vertical
\a alerta
\o constante octal
\x constante hexadecimal

7
Variaciones de printf().
Numéric Significado
o
%d Decimal (base 10)
%o Octal (base 8)
%x Hexadecimal (base 16)
%u Decimal sin signo.
%e Notación científica (double o float 1.23E23)
%f Decimal (double o float 1.23E23)
%g %e o %f selecciona el mas corto.

String. Significado
%c Un solo carácter.
%s Conjunto de caracteres (string).

Ejercicio: Datosper2.c
Realize un programa que imprima sus datos personales centrados lo mas posible en
pantalla, utilizando las constantes de carácter no imprimibles, y dando efectos de
sonido utilizando las mismas constantes.

Combinaciones posible de los tipos y modificadores en la IBM-PC.


TIPO BITS RANGO
char 8 -128 a 127
unsigned char 8 0 a 255
signed char 8 -128 a 127
int 16 -32768 a 32767
unsigned int 16 0 a 65535
unsigned int 16 -32768 a 32767
short int 16 -32768 a 32768
unsigned short int 16 0 a 65535
signed short int 16 -32768 a 32767
long in 32 -2147483648 a 2147483647
signed long int 32 -2147483648 a 2147483647
unsigned long int 32 0 a 429496727295
float 32 3.4E-38 a 3.4E+38
double 64 1.7E-308 a 1.7E+308
long double 64 1.7e-308 a 1.7E+308

/*Programa : sizeof.c
Descripción: Ejemplo de la función sizeof
Nota.......: Genera 2 warnings (BC++ 3.1/Opcion ANSI):
a i, j se le asigna valores y no son usados (i y j).
No hay problema. Solo es un ejemplo de los bytes usados
cuando se suma un int y un float en memoria.
*/
#include <stdio.h>
#include <conio.h> /* clrscr() */

int main() {
int i;
float j;
8
i = 9;
j = 1.0;

clrscr();
printf("\n j + i %d bytes ", sizeof(j+i));
printf("\n char %d bytes ", sizeof(char));
printf("\n int %d bytes ", sizeof(int));
printf("\n short %d bytes ", sizeof(short));
printf("\n unsigned %d bytes ", sizeof(unsigned));
printf("\n long %d bytes ", sizeof(long));
printf("\n float %d bytes ", sizeof(float));
printf("\n double %d bytes ", sizeof(double));

return 0;
}

Constantes
Las constantes de C son los valores fijos que el programa no puede alterar.
Las constantes de carácter están encerradas entre apóstrofes.
Ejemplo:
'a', '%'

Distíngase que son diferentes:


'a' y "a"

'a' es una constante de carácter compuesta por la letra a y "a" es una cadena de
caracteres compuesta por la letra a y el carácter nulo y esta formada de dos
elementos.
"" es una cadena nula con un elemento (\0).

EJEMPLOS DE CONSTANTES.
123L constante long
123.3 constante float

Notación para constantes octales y hexadecimales.


Un cero que encabeza un numero significa octal
Ejemplo:
011 9 en decimal

Un cero y una equis antes significa hexadecimal.


Ejemplo:
0xFF 255 en hexadecimal.

/*Programa: TIPOS.C
Desc. : Ejemplo de constantes*/
#include <stdio.h>

main(){
printf("\n%f", 1/2);
9
printf("\n%f", 1.0/2);
}

Construcción cast
Se usa para forzar la conversión explícita del tipo de una variable.
Uso:
(tipo) expresión
La expresión se convierte al tipo citado.

Ejemplo:
La función de la biblioteca estandard sqrt() espera que se le pase un argumento de
tipo double, si no es as¡ producirá resultados sin significado.
int n;
sqrt( (double) n);

Esto convierte el valor de n a double antes de pasárselo a sqrt.


Nota: El tipo de n no se altera.

/*Programa: CAST.C
Desc. : Demostracion de la construccion cast*/
#include <stdio.h>

main(){
printf("\n%f", (float)1/2);
printf("\n%f", 1.0/2);
}

getchar()
#include <stdio.h>
int getchar(void)
Lee un caracter de el flujo de entrada estandard.

Descripcion:
getchar lee y regresa un caracter del archivo o dispositivo asociado con stdin.

/* Programa: GETCHAR.C
Desc. : Uso del getchar, lee un caracter de la entada y despliega
el caracter junto con su numero assci*/
#include <stdio.h>

main(){
char c;

c = getchar();
printf("\n%c %d", c, c);
}

10
Ejercicio: Vecinos.c
Escriba un programa vecinos.c que lea un carácter de la entrada estandard y
despliegue el carácter anterior, el leído y el siguiente.

Constantes simbólicas
#define
Mediante esta construcción al principio de un programa se puede definir un nombre
simbólico o constante simbólica como una determinada cadena de caracteres.

/*Programa: PASCUAL.C
Desc. : Emulacion del PASCAL mediante el preprocesador
*/
#include <stdio.h>

#define PROGRAM main()


#define BEGIN {
#define END }
#define WRITE printf

PROGRAM
BEGIN
WRITE("Hola");
END

/*Programa: UNALINEA.C
Desc. : Demostracion del #define
*/
#include <stdio.h>

#define HOLA main() { printf("Hola"); }

HOLA

El preprocesador remplaza todas las apariciones no entre comillas del nombre por
su cadena correspondiente.

Ejercicio:
Investigar todas las palabras reservadas del preprocesador, su sintaxis y un ejemplo
de cada una.

Operadores
Operadores aritméticos.
Operador Acción
- resta y menos unario
+ suma
* multiplicación
/ división
11
% modulo de división
-- decremento
++ incremento

Operadores relaciónales.
Operador Acción
> mayor que
>= mayor que o igual que
< menor que
<= menor o igual que
== igual
!= no igual

El doble signo igual == es la notación de C para "es igual a". Este símbolo se
distingue de la condición de igualdad del simple = empleado en las asignaciones.
Las asignaciones son aproximadamente 2 veces mas frecuentes que la
comprobación de igualdad en típicos programas de C; por esto es adecuado que el
operador ocupe la mitad de espacio.

Operadores lógicos.
Operador Acción
&& and
|| or
! not

Incremento decremento
++ --

Ejemplo:
++n incrementa la variable n en 1. (op. prefijos)
n++ incrementa la variable n en 1. (op. posfijos)
--n decrementa la variable n en 1. (op. prefijos)
n-- decrementa la variable n en 1. (op. posfijos)

Equivalentes.
++n n=n+1
--n n=n-1

Al usar la forma de prefijo la variable se incrementa o decrementa antes de ser


usada; en la forma pos fijo la variable primero se usa y después se incrementa o
decrementa según sea el caso.
Ejemplo: n = 5;

valores después de ejecutarse la linea.


x = ++n; x = 6; n = 6;
x = n++; x = 5; n = 6;

12
/*Programa : mas_mas.c
Descripción: Desmostracion de los operadores unarios ++ y --
*/
#include <stdio.h>

int main() {
int i, j;

i = 10;
j = 20;
printf("\n\ni %d, j %d", i, j);
printf("\ni++ %d", i++);
printf("\n++j %d", ++j);

printf("\n\ni %d, j %d", i, j);


printf("\ni-- %d", i--);
printf("\n--j %d", --j);

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

return 0;
}

Conversiones de tipo
Cuando se mezclan constantes y variables de diferentes tipos en una expresión, C
las convierte en el mismo tipo. El compilador de C convertirá todos los operandos
al tipo del operando mas grande en una operación según la base de esta operación.

Reglas de conversión de tipos.


1.- Todos los char y short int se convierten a int.
Todos los float a double
2.- Para todo par de operandos, lo siguiente ocurre
en secuencia:
Si uno de los operandos es long double, el otro
se convierte a long double.
Si uno de los operandos es double el otro se
convierte a double si uno de los operandos es
long, el otro se convierte a long.
Si uno de los operandos es unsigned, el otro se
convierte a unsigned.

Después de que el compilador aplique estas reglas de conversión, cada par de


operandos será del mismo tipo, y el resultado de la operación será del tipo de los
operandos.
Ejemplo:
char c;
13
int i;
float f;

c = 65;
i = 10;
f = i + c;

La variable c se convierte a int al hacer la suma i + c, al realizar la asignación el


resultado de la suma se convierte a float.

scanf()
#include <stdio.h>
int scanf(const char *formato ...);
Lee e interpreta texto desde el flujo de entrada estandard.

Descripcion:
scanf lee caracteres desde el flujo de entrada estandard y usa el string formato para
interpretar que es leído en los tipos de datos correspondientes.

/* Programa: CONDOLAR.C
Desc. : Convierte pesos a dolares, pide el tipo de cambio*/
#include <stdio.h>

main(){
int pesos, cambio;
float dolares;

printf("\nCuantos pesos desea convertir a dolares? ");


scanf("%d", &pesos);
printf("Cual es el tipo de cambio? ");
scanf("%d", &cambio);

dolares = pesos / cambio;


printf("Dolares = %f", dolares);
}

Ejercicio: Dolpesos.c
Realize un programa que convierta una cantidad en dolares a pesos. Pida el tipo de
cambio

14
Precedencia y orden de evaluación de operadores.
Operador Asociatividad
() [] -> . izquierda a derecha
! ~ ++ -- - (tipo) * & sizeof derecha a izquierda
* / % izquierda a derecha
+ - izquierda a derecha
<< >> izquierda a derecha
< <= > >= izquierda a derecha
== != izquierda a derecha
& izquierda a derecha
^ izquierda a derecha
| izquierda a derecha
&& izquierda a derecha
|| izquierda a derecha
?: derecha a izquierda
= += -= etc. izquierda a derecha
, derecha a izquierda

15
2
Control de flujo
Las sentencias o proposiciones de flujo de un lenguaje especifican el orden que se
realizan las computaciones.

if-else
Para tomar decisiones.

Forma general:
if (condición)
sentencia-1;
else
sentencia-2;

La parte else es opcional. Si la condición es verdadera; distinta de cero, se ejecuta


la sentencia-1, si la condición es falsa; igual a cero se ejecuta la sentencia-2.

Nota:
El lenguaje C no hace restricción el tipo de expresiones a solo las que invocan
operadores relacionales y lógicos.
Todo lo que se requiere es que la expresión evaluada de cero o no cero.

/* Programa : divide.c
Descripcion: Divide el primer numero por el segundo */
#include <stdio.h>

int main() {
int a, b;

printf("\nIntroduce dos numeros enteros: ");


scanf("%d%d", &a, &b);

if (b != 0) /* <- Se podria poner: if (b) */


printf("%f\n", (float)a/b);
else
printf("No puedo dividir por cero\n");
return 0;
}

16
Iteraciones while y for
Forma general:
while (condición) proposición;

/* Programa : tolower.c
Descripcion: Muestra el uso de la funcion tolower */
#include <stdio.h>
#include <ctype.h>

int main() {
int c;

c = getchar();
while (c != EOF) {
printf(" %c ", tolower( c ) );
c = getchar();
}

return 0;
}

Forma general:
for (inicialización; condición; incremento)
proposición;

/* Programa : lineas.c
Descripcion: Muestra el uso del for*/
#include <stdio.h>
#define NUMLIN 24

int main() {
int i;

for (i=0; i < NUMLIN; i++)


printf("%d\n", i);

return 0;
}

En los dos, el ciclo se ejecuta hasta que condición sea falsa (cero).

La proposición for es equivalente a:

inicialización;
while (condición) {
proposición;
incremento;
}

Gramaticalmente, los tres componentes de un for son expresiones.


17
Mas comúnmente, inicialización y incremento son asignaciones o llamadas a
función y condición es una expresión de relación.
Puede omitirse cualquiera de las tres partes, aunque deben permanecer los puntos y
comas.
Ejemplo:
for (; ;)
;
Este es un ciclo infinito; que podríamos terminar con un break o con un return.

Ejercicio: Lineas2.c
Realizar el programa lineas.c utilizando la iteración while.

Ejercicio: Toupper.c
Hacer el programa tolower.c utilizando la iteración for.

Sentencia break
Proporciona una salida forzada de for, while, do, en la misma forma que
switch.
Una sentencia break obliga a una salida inmediata del ciclo ( switch) mas
interior.

Ejemplo:
Saca raíz cuadrada mientras no se de un numero negativo.
for (; ;) {
scanf("%f", &n);
if (n < 0.0)
break;
printf("\n %f", sqrt(n));
}
/* la sentencia break salta hasta aquí */

Saltos y etiquetas
goto
El goto requiere una etiqueta para funcionar. Una etiqueta es un identificador
valido de C que se sigue por dos puntos. Normalmente no es necesario y es fácil de
prescindir de el. Sin embargo hay ocasiones en que puede se útil.

/* Programa : goto.c
Descripcion: Saca ra¡z cuadrada mientras no se de un numero negativo */
#include <stdio.h>
#include <math.h> /* sqrt() */

int main() {
float n;

while (1) {
scanf("%f", &n);
if (n < 0.0)
18
goto alerta;
printf("\n %f\n", sqrt(n));
}
alerta: printf("\nHa ocurrido un valor negativo ");

return 0;
}

Sentencia switch
Herramienta especial para decisiones múltiples que comprueba si una expresión
iguala uno entre varios valores constantes, y se bifurca a partir de ellos.

Forma General:
switch (variable) {
case constante1:
secuencia de sentencias;
case constante2:
secuencia de sentencias;
break;
case constante3:
secuencia de sentencias;
break;
.
.
.
default:
secuencia de sentencias;
}

Esta sentencia es "similar" al CASE de PASCAL.

Reglas.
1.- El switch difiere del if porque la primera solo puede
comprobar por igualdad, mientras que la expresión
condicional del if puede ser de cualquier tipo.
2.- No pueden tener dos constantes case con idénticos
valores en el mismo switch. Una sentencia switch que
esta encerrada por otro switch puede tener constantes
case que son las mismas.

case
Actúa como una etiqueta.

break
Opcional dentro de la sentencia switch. Se usa para terminar la secuencia
que esta asociada con cada constante. Si se omite, la ejecución continuara
en las sentencias del siguiente case hasta encontrar un break, un return
o el final del switch.

19
default
Es opcional y se ejecuta si no se satisface ninguna opción.

/* Programa : EFECTOS.C
Descripcion: Muestra el uso de la sentencia switch*/
#include <stdio.h>
#include <dos.h> /* delay() */
#include <conio.h> /* _setcursortype() */

#define ESPERA 50
#define VECES 30

int main() {
char op;
int i;

clrscr();
printf("\n***** MENU *****");
printf("\n1 Sonido");
printf("\n2 Animacion");
printf("\nOpcion? ");
op = getchar();
switch(op) {
case '1':
printf("\a\a");
break;
case '2':
printf("\t\t\t\t"); _setcursortype(_NOCURSOR);
for (i=0; i < VECES; i++) {
printf("|"); delay(ESPERA);
printf("\b/"); delay(ESPERA);
printf("\b-"); delay(ESPERA);
printf("\b\\\b"); delay(ESPERA);
}
_setcursortype(_NORMALCURSOR); break;
default:
printf("\nOpcion no implementada");
}
return 0;
}

Ejercicio: Digitos.C
Escriba un programa que lea un carácter del teclado y si se trata de un numero
despliegue el numero con letra, si no es un numero mencionarlo.

Iteraciones do-while
Hace la comprobación al final después de cada pasada a través del cuerpo del
ciclo. Y este se ejecuta al menos una vez.
Forma general:
do
20
proposición;
while (condición);

/* Programa : MENUEFE.C
Descripcion: Efectos en un menu dentro del do .. while*/
#include <stdio.h>
#include <dos.h> /* delay() */
#include <conio.h> /* _setcursortype() */

#define ESPERA 50
#define VECES 20

int main() {
char op;
int i;
do {
clrscr();
printf("\n***** MENU *****");
printf("\nSonido");
printf("\nAnimacion");
printf("\nFin");
printf("\nOpcion? ");
op = getchar();
switch(op) {
case 'S':
case 's':
printf("\a\a");
break;
case 'A':
case 'a':
printf("\t\t\t\t"); _setcursortype(_NOCURSOR);
for (i=0; i < VECES; i++) {
printf("|"); delay(ESPERA);
printf("\b/"); delay(ESPERA);
printf("\b-"); delay(ESPERA);
printf("\b\\\b"); delay(ESPERA);
}
_setcursortype(_NORMALCURSOR); break;
}
} while (op != 'F' && op != 'f');
return 0;
}

Ejercicio: Digitos2.C
Modifique el programa del ejercicio Digitos.c para que termine hasta que se ingrese
un 'X'. Lea un carácter del teclado y si se trata de un numero despliegue el numero
con letra, si no es un numero mencionarlo.

21
3
Arreglos y cadenas de caracteres
Arreglos unidimensionales
Forma general:
tipo nombre_variable[tamaño]

/* Programa: INVERSO.C
Desc. : Pide una lista de enteros y los despliega en orden inverso*/
#include <stdio.h>

int main(){
int i, lista[10];

printf("Ingresa 10 numeros enteros\n");


for (i=0; i < 10; i++) {
printf("Lista[%d] = ? ", i);
scanf("%d", &lista[i]);
}
printf("\nLa lista en orden inverso:");
for (i=9; i >= 0; i--)
printf("\nlista[%d] = %d ", i, lista[i]);

return 0;
}

En C todos los arreglos usan el cero como índice del primer elemento.
Ejemplo:
int lista[10]

Declara un arreglo llamado lista con diez elementos


desde lista[0] hasta lista[9].

El lenguaje C no realiza comprobación de contornos en los arreglos.


Si se sobrepasa el final durante una operación de asignación, entonces se asignaran
valores a otras variables en memoria o memoria usada por el propio
código del programa.
Ejemplo:
/* Programa que sobrepasa el limite de indexacion */
int main(void) {
int lista[10], i;

for (i = 0; i < 100; i++)


22
lista[i] = i;

return 0;
}

Este programa compilara bien y ejecutara incluso aunque sobrepasa el limite de


lista. Sin provocar ningún mensaje de error.
El lenguaje C fue diseñado para remplazar la codificación en lenguaje ensamblador.
Por lo tanto no incluye virtualmente ninguna comprobación de error porque haría
mas lenta la ejecución del programa. Las comprobaciones es responsabilidad del
programador.

/* Programa: BUSCA.C
Desc. : Pide una lista de enteros y un numero para buscarlo en la lista*/
#include <stdio.h>
#include <conio.h>
#define LIMITE 5

int main(){
int i, n, lista[LIMITE];

clrscr();
printf("Ingresa %d numeros enteros\n", LIMITE);
for (i=0; i < LIMITE; i++) {
printf("Lista[%d] = ? ", i);
scanf("%d", &lista[i]);
}
printf("\nIngresa el numero a buscar ");
scanf("%d", &n);
for (i=0; i < LIMITE; i++)
if (n == lista[i])
break;
if (i == LIMITE)
printf("\nEl numero no se encuentra en la lista");
else
printf("\nEl numero se encuentra en la posicion %d", i);

return 0;
}

Ejercicio: Menor.c
Realice un programa que:
a) Pida diez números enteros y los almacene en un arreglo
b) Busque el numero menor y el numero mayor
c) Despliegue el numero menor y el numero mayor

Ejercicio: Ordena1.c
Hacer un programa que:
a) Pida diez números enteros y los almacene en un arreglo
b) Los ordene de menor a mayor
c) Despliegue el arreglo
23
Cadenas de caracteres
Una cadena de caracteres consiste en un arreglo de caracteres terminado en un
nulo. Un nulo se especifica como '\0' y es cero.
Si se quiere definir un a cadena de 10 elementos se debe definir de 11 para
soportar el carácter nulo.
Ejemplo.
char str[11];

Una constante de cadena es una lista de caracteres que se encierran entre comillas.
Ejemplo.
"HOLA"

Al declarar una constante de cadena el compilador de C agregara automáticamente


el carácter nulo al final.

La cadena "HOLA" se vera en memoria así:


H O L A \0

/* Programa: CADENA.C
Desc. : Cadenas de caracteres */
#include <stdio.h>
#include <conio.h>

int main() {
char s[11];
char s2[] = {'h', 'o', 'l', 'a', '\0'};
char s3[] = "HOLA A TODOS";
char s4[10];
clrscr();
puts("\nIngresa una cadena de caracters: ");
gets(s);
puts(s);
putchar('\n');
puts(s2);
putchar('\n');
puts(s3);
printf("\n%s\n", s3);
s3[5] = '\0';
printf("\n%s\n", s3);
/*s4 = "todos"; <-- esto no esta permitido al menos que s4 haya sido definido como un
apuntador */
return 0;
}

Ejercicio: Printstr.c
Escriba un programa que lee una cadena de caracteres de la entrada estandard y la
imprima en la salida estandard, utilizando la función putchar.

24
Funciones de la biblioteca estandard mas comunes para
el manejo de cadenas de caracteres
strcpy()
formato:
strcpy(destino, fuente)
Copia el contenido de la cadena fuente a la cadena destino.
La cadena destino debe ser lo suficientemente grande para contener la cadena
fuente. (Si no es as¡ producirá resultados inesperados en la ejecución del
programa).

strcat()
formato:
strcat(destino, fuente)
Añade fuente al final de destino.

/* Programa: STRCPY.C
Desc. : Muestra funciones de manejo de Cadenas de caracteres */
#include <stdio.h>
#include <string.h> /* strcpy(), strcat() */

int main() {
char s1[20], s2[10];

strcpy(s1, "hola");
strcpy(s2, " a todos");

strcat(s1, s2);
printf("\n%s", s1);

return 0;
}

strcmp()
formato:
strcmp(s1, s2)
Compara dos cadenas y devuelve 0 si son iguales.
Si s1 es lexicográficamente mayor que s2, entonces la función devuelve un numero
positivo; si s1 es menor que s2, devuelve un numero negativo.

/* Programa: STRCMP.C
Desc. : Muestra funciones de manejo de Cadenas de caracteres */
#include <stdio.h>
#include <string.h> /* strcmp() */

int main() {
char s[80];

printf("\nPalabra de acceso: ");


25
gets(s);

if (strcmp(s, "clave"))
printf("\nPalabra de acceso invalida");

return 0;
}

strlen()
formato:
strlen(s);
Devuelve la longitud de la cadena s.

/* Programa: STRLEN.C
Desc. : Muestra funciones de manejo de Cadenas de caracteres */
#include <stdio.h>
#include <string.h> /* strlen() */

int main() {
char s[80];

printf("\Cadena: ");
gets(s);
printf("\nLonguitud: %d", strlen(s));

return 0;
}

Ejercicio:
Escriba un programa que lea una cadena de caracteres de la entrada estandard la
convierta a mayúsculas, y la imprima en la salida estandard.

Arreglos bidimensionales
formato:
tipo nombre[longitud_1][longitud_2]
Ejemplo:
int digitos[2][10]

/* Programa: DIAGPRIN.C
Desc. : Suma la diagonal principal de una matriz*/
#include <stdio.h>
#define LON 2

int main(){
int i, j, suma, matriz[LON][LON];

printf("Ingresa una matriz de %d X %d\n", LON, LON);


for (i=0; i < LON; i++)
for (j=0; j < LON; j++) {
printf("Matriz[%d][%d] = ? ", i, j);
26
scanf("%d", &matriz[i][j]);
}

/* Suma la diagonal principal: */


suma = 0;
for (i=0; i < LON; i++)
for (j=0; j < LON; j++)
if (i == j)
suma = suma + matriz[i][j];

printf("\nLa suma de la diagonal principal es: %d", suma);

return 0;
}

Ejercicio: Diaginv.c
Realice un programa que:
a) Pida 100 numero enteros y los almacene en una arreglo de 10 X 10
b) Sumar la diagonal principal inversa
c) Despliegue la suma

/* Programa: SUMAMAT.C
Desc. : Suma dos matrices dejando el resultado en una tercera*/
#include <stdio.h>

#define LON 2

int main(){
int i, j, a[LON][LON], b[LON][LON], c[LON][LON];

printf("Ingresa la matriz A\n", LON, LON);

for (i=0; i < LON; i++)


for (j=0; j < LON; j++) {
printf("A[%d][%d] = ? ", i, j);
scanf("%d", &a[i][j]);
}

printf("\nIngresa la matriz B\n", LON, LON);


for (i=0; i < LON; i++)
for (j=0; j < LON; j++) {
printf("B[%d][%d] = ? ", i, j);
scanf("%d", &b[i][j]);
}

/* Suma las matrices y deja el resultado en una tercera matriz */


for (i=0; i < LON; i++)
for (j=0; j < LON; j++)
c[i][j] = a[i][j] + b[i][j];

27
printf("\nEl resultado de la suma de A + B es:\n");
for (i=0; i < LON; i++) {
for (j=0; j < LON; j++)
printf("%2d ", c[i][j]);
putchar('\n');
}

return 0;
}

28
4
FUNCIONES
Tipos de variables: Parte 1

Alcance de variables
Las variables declaradas dentro de una función son privadas a esa función, ninguna
otra función puede tener acceso directo a ellas.

Variables automáticas
Cada variable local de una rutina comienza a existir cuando se llama a la función, y
desaparece cuando la función acaba.
Estas variables no conservan su valor entre dos llamadas sucesivas, y se les ha de
dar un valor en la entrada a la función.

Variables externas
Se definen fuera de las funciones; estas son externas a todas las funciones; esto es
variables globales a las que puede acceder cualquier función mediante su nombre.
A estas variables se les asigna memoria real.
Cuando son declaradas en otro archivo se debe usar la palabra reservada extern.

/*Programa : sqr.c
Descripción: Usa una función que recibe y regresa un valor entero*/
#include <stdio.h>

int sqr(int num) {


return num * num;
}

main() {
int n, c;

n = sqr(2);
printf("\nEl cuadrado de 2 es %d ", n);
}

Ejercicio: Eleva.c
Realice una funcion eleva(n, x) que eleve el valor de n a la potencia x y regrese el
numero elevado.

29
Funciones
Una función es una construcción que realiza una tarea. La cual incluye sentencias y
variables relacionadas, incluyendo los pasados como argumentos.
Una función puede ser compilada y almacenada en una librería o archivo, de
donde el encadenador la extrae.
Ejemplo:
void info(void) {
printf("\n%s\n%s\n%s\n",
"Tambien puede haber una funcion minima",
"por ej: void nada(void) { }",
"la cual no hace nada");
}

Forma general:
especificador_tipo nombre_funcion(declaración de argumentos ) {
cuerpo de la función
}

El especificador_de_tipo especifica el tipo de valor que la funcion devolvera


mediante el return. Si no se especifica el compilador asume que la función regresa
un valor entero (int).

La lista_de_argumentos es una lista de nombres de variables separados por comas


que recibe los valores de los argumentos cuando se llama la función.
Las funciones terminan y devuelven automáticamente al procedimiento que la llamo
cuando se encuentra la ultima llave o mediante la sentencia return.

/* Programa: ABSOLUTO.C
Desc. : Devuelve el valor absoluto de un double */
#include <stdio.h>

float abs(float x) {
if (x >= 0.0)
return x;
else
return -x;
}

int main() {
float c;

scanf("%f", &c);
printf("%f", abs(c));

return 0;
}

Ejercicio: Par.c
Escriba una función par(n) que regrese un 0 si es par.

30
Ejercicio: Fact.c
Realice una función factorial(n) que regrese el factorial de n.

Reglas de alcance de funciones


La comunicación entre funciones se efectúa a través de argumentos y valores
devueltos por la función también mediante variables externas.
Pueden aparecer en cualquier orden en el archivo fuente (y este puede estar
dividido en varios archivos).
Cada función es un bloque discreto de código. El código es privado a la función y
no puede ser acezado por ninguna declaración en ninguna otra función excepto a
través de argumentos.

Nota:
No se puede usar goto para brincar de una función a otra. El código y los datos
definidos en cada función no pueden interactuar con código o datos definidos
dentro de otra función.

Las variables definidas dentro de una función son variables locales dinámicas.
(empiezan a existir cuando la función es llamada y se destruyen al terminar). Estas
variables no pueden guardar valor entre los llamados de las funciones.

/* Programa : LEELINEA.C*/
/* Descripcion: obtiene una linea en s, devuelve la longuitud*/
#include <stdio.h>

#define LIMITE 80

int lee_linea(char s[], int lim) {


int i, c;
printf("- ");
c = getchar();
for (i = 0; i < lim-1 && c != EOF && c != '\n'; i++) {
s[i] = c;
c = getchar();
}
s[i] = '\0';

return(i);
}

int main() {
char linea[LIMITE];
int n;

n = lee_linea(linea, LIMITE);
while (n > 0) {
printf ("Numero de Caracteres %d ", n);
printf("%s\n", linea);
n = lee_linea(linea, LIMITE);

31
}

return 0;
}

Ejercicio: Ar_prom.c
Realice una función ar_prom(arreglo, tam) que reciba un arreglo de enteros, el
tamaño del mismo y que regrese el promedio de la suma de sus elementos.

Argumentos de funciones
Llamada por valor
Este método copia el valor del argumento dentro de los par metros formales de la
función y todos los cambios que sufran los parámetros no afectan el valor del
argumento usado para llamar la función.

Par metros formales de la función; son las declaraciones de las variables que
aceptan los valores de los argumentos.

/* Prog: Cambia.C
Desc: Usa una funcion que intercambia los valores de sus argumentos*/
#include <stdio.h>

void cambia(int a, int b) {


int paso;

paso = a;
a = b;
b = paso;
}

main() {
int x, y;

x = 10;
y = 20;
cambia(x, y);
printf("\nX = %d\nY = %d", x, y);
}

Ejercicio:
Diga cual es la salida del programa anterior.

Ejercicio: Marco.c
Realice una función marco('*', x1, y1, x2, y2) que dibuje un marco en pantalla.
donde '*' es el carácter con el que se dibujara el marco
x1 y y2 son las coordenadas de la esquina superior izquierda
x2 y y2 son las coordenadas de la esquina inferior derecha
32
Ejercicio: Str_busc.c
Escriba una función str_busca(str, c) que reciba una cadena de caracteres str, y un
carácter c. Busque el carácter en la cadena y regrese la posición en que se
encuentra el carácter en la cadena o un -1 si no se encuentra.

Archivos de encabezados
Muchas funciones en la librería estandard trabajan con tipos de datos específicos.
Estos tipos son definidos en archivos de encabezados los cuales vienen con el
compilador, y estos deben ser incluidos (usando #include) en los archivos donde se
haga referencia a una función de la librería estandard.
Estos archivos contienen todos los prototipos de las funciones relacionadas con el
archivo de encabezado.
Ademas contienen definiciones de constantes simbólicas como EOF y macros.

ARCHIVOS DE ENCABEZADOS ESTANDARD


__________________________________________________________________
Header file Propósito
__________________________________________________________________
assert.h Define la macro assert()
ctype.h Manejo de caracteres
errno.h Reportes de error
float.h Define la dependencia de implementacion de valores
de punto flotante.
limits.h Define la dependencia de implementacion de varios
limites
locale.h Soporta la función setlocale()
math.h varias definiciones usadas por la
librería de matemáticas
setjmp.h Soporta saltos no locales
signal.h Define valores de signos
stdarg.h Soporta lista de argumentos de
longitud variable
stddef.h Define las constantes mas comúnmente usadas
stdio.h Soporta manejo de archivos e/s
stdlib.h Declaraciones misceláneas
string.h Soporta funciones de string
time.h Soporta funciones de sistema de tiempo

Nota:
El uso de las funciones mas comunes en los encabezados se irán tratando en el
transcurso del libro.

33
5
Apuntadores
Definición de apuntadores
Un apuntador es una variable que almacena una dirección de memoria.
Esta dirección es usualmente la localidad de otra variable en memoria.
Si una variable contiene la dirección de otra variable, entonces se dice que la
primera variable apunta a la segunda.

MEMORIA
Área libre
Código del programa que esta
en ejecución

Cuando se usan las variables directamente el compilador debe saber:


1.- La dirección de la variable.
2.- Cuantos Bytes son requeridos para el
almacenamiento.
Ejemplo:
int n = 3;
char c = 'x';

D ire c c io n e s e n
m e m o ria 1500 1501 1510

V a ria b le s e n x
0 3
m e m o ria

n c

Nota:
Los apuntadores son también un tipo de variable, una computadora pequeña usa
normalmente dos bytes para un apuntador; algunos sistemas mas grandes usan
cuatro bytes.
Porque un apuntador es un tipo de datos único, necesitamos decirle al compilador:
1.- Crea una dirección para el apuntador.
2.- Crea una escala para el apuntador igual al
numero de bytes asociados con el tipo de dato al
que apunta.
3.- Marca esta variable como un apuntador.

34
Definiendo apuntadores
Ejemplo:
int *pn;

Con lo anterior le decimos al compilador:


1.- Variable del tipo apuntador.
2.- Nombre y necesidad de una dirección para vivir.
3.- Crea la variable con una escala asociada con el
tipo (en este caso int (escala de 2 bytes)).

Inicial izando.
Ejemplo:
int n, *pn;

D ir e c c io n e s e n
m e m o r ia 1500 1501 1600 1601

V a r ia b le s e n * #
& \0
m e m o r ia

B a su ra B asura

n = 3;
pn = &n;

D ir e c c io n e s e n
m e m o r ia 1500 1501 1600 1601

V a r ia b le s e n 0 3 15 00

m e m o r ia

n *pn

La inicialización.
pn = &n
Se puede decir como: toma la dirección de la variable n y almacénala en la
dirección de pn.

Indireccion
Es el proceso de accesar el contenido de una variable a través de una variable
apuntador. Usando el operador *.
Ejemplo:
int n, *pn, x;
n = 3;
pn = &n;
x = *pn; ( x = 3)

35
El operador & solo se puede aplicar a variables y a elementos de un arreglo.
Construcciones como:
&(x+1)
&3
Son ilegales.
También es ilegal obtener la dirección de una variable register.

Pasos a seguir al usar apuntadores


1.- Define el apuntador (Usando el *)
int *pn;
2.- Inicializa el apuntador para apuntar a algo (Usando &)
p = &n;
3.- Toma a lo que este apuntando el apuntador (Usando *)
printf("pn esta apuntando a %d\n", *pn);

Llamada por apuntadores


En este método, la dirección en memoria del argumento es copiada dentro del
parametro de la función, la dirección es usada para accesar el valor en memoria de
la variable usada en la llamada.
Para usar este método pasamos un apuntador de el argumento.
Los apuntadores son pasados a la función como cualquier otro valor pero
declarando los parámetros como del tipo apuntador.
los cambios hechos dentro de la función a la variable la afectan.

/*Programa...: ref.c
Descripcion: Intercambia dos numeros, ejemplo llamada por referencia
*/
#include <stdio.h>

void cambia(int *x, int *y);

int main(void) {
int x = 20, y = 10;

cambia(&x, &y);
printf("\nx = %d y = %d", x, y);

return 0;
}

void cambia(int *x, int *y) {


int paso;

paso = *x;
*x = *y;
*y = paso;
}

36
Ejercicio: Sqr2.c
Realice una funcion sqr(&n) que reciba una direccion a un numero entero, eleve el
numero al cuadrado y deje el resultado en el mismo numero.

/*Programa: LEEINT.C
Desc. : Funcion que lee un entero*/
#include <stdio.h>
#include <stdlib.h> /* atoi() */

void leeint(int *pi) {


char s[10];

gets(s);
*pi = atoi(s);
}

int main() {
int i;

puts("Numero: ");
leeint(&i);
printf("\n%d", i);

return 0;
}

Aritmética de apuntadores
En C se puede usar solo dos operaciones aritméticas sobre apuntadores:
+, -.
Ejemplo:
int *p, x = 1;
p = &x;

D ir e c c io n e s e n
m e m o r ia 1500 1501 1600 1601

V a ria b le s e n 0 1 15 00

m e m o r ia

x p

Si hacemos:
P++;

D ire c c io n e s e n
m e m o ria 1500 1501 1502 ... 1600 1601

V a r ia b le s e n 0 1 15 02
...
m e m o ria

x p

37
Cada vez que la computadora incrementa un apuntador, apuntara a la posición de
memoria del elemento siguiente en función de su tipo base.
También se puede sumar o restar entero a los apuntadores.

Apuntadores y arreglos
Los apuntadores y arreglos se utilizan casi de la misma manera para acceder a la
memoria. Sin embargo:
Un apuntador es una variable cuyos valores son direcciones.
El nombre de un arreglo es una constante que contiene la dirección del inicio del
arreglo también llamado apuntador fijo.
Cuando se declara un arreglo el compilador debe asignar una dirección base y la
cantidad de almacenamiento suficiente para alojar a todos los elementos del
arreglo.
Ejemplo:
int n[3] = {1, 2, 3}, *p;

D ire c c io n e s e n
m e m o ria 2000 2001 2002 2003 2004 2005

V a ria b le s e n 0 1 0 2 0 3

m e m o ria

n [0 ] n [1 ] n[2]

Las asignaciones:
p = n; y p = &n[0];
son equivalentes; y se asignara 2000 a p.

p = n + 1; y p = &n[1];
son equivalentes; y se asignara 2002 a p.

Las proposiciones n[i]; y *(p+i); son equivalentes; as¡ como:


&n[i]; y n+i;

Nota:
Un apuntador es una variable, por lo que las operaciones como:
p = n; y p++;
son correctas.
Pero el nombre de un arreglo es una constante, no es una variable; así que
construcciones como:
n = p; o n++; o p = &n; o n += 2;
Son ilegales.

Nota:
Los arreglos y los apuntadores tienen una estrecha relación.

38
/* Programa....: REL.C
Descripcion.: Relaciones entre arreglos y apuntadores*/
#include <stdio.h>

int main() {
char c = 'A', *p, s[10];

p = &c;
printf("\n%c %c %c", *p, *p+1, *p+2); /* Se incrementa el contenido de donde apunta p */
s[0] = 'A';
s[1] = 'B';
s[2] = 'C';
s[3] = '\0';
p = s;
printf("\n%s %s %c %s", s, p, *(p+1), p+1);

return 0;
}

& - Operador de dirección


&c - La dirección de la variable c en memoria
p = &c - p apunta hacia c

p=s
s = p - Invalido porque s no es una variable es una constante tipo apuntador
que apunta hacia s[0]

*(p+1) y p[1] - Equivalentes


s[1] y *(s+1) - Equivalentes

Ejercicio: Apordena.c
Realice una función ordena(arreglo, tam) que reciba un arreglo, su tamaño y
ordene el arreglo de menor a mayor.

Apuntadores a caracteres
Si se declara mensaje como:
char *mensaje;

La sentencia:
mensaje = "esta es una cadena"

Asigna a mensaje un apuntador a los caracteres. No es la copia de una cadena.

/* Programa: LEESTR.C
Desc. : Funcion que lee un string de la entrada estandard */
#include <stdio.h>

void leestr(char *s) {


do {
39
*s = getchar();
} while (*s++ != '\n');
*s = '\0';
}

main(){
char s[50];
leestr(s);
printf("%s", s);
}

/* Programa: STRLEN3.C
Desc. : Ejemplo de como puede estar implementada la funcion de la
biblioteca estandard strlen (usando apuntadores)*/
int strlen(char *s) {
int n;
for (n = 0; *s != '\0'; s++)
n++;

return n;
}

Ejercicio: Prnstr.c
Realice una función printstr(str) que reciba un apuntador a una cadena de
caracteres y la imprima en la salida estandard usando la función putchar y manejo
de apuntadores.

/* Programa: LEELIN2.C
Desc. : lee_linea (version con apuntadores)*/
#include<stdio.h>

int lee_linea(char *s, int lim) {


int lon, c;

lon = 0;
c = getchar();
while (lon < lim && c != EOF && c != '\n') {
*s = c;
lon++;
s++;
c = getchar();
}
*s = '\0';

return lon;
}

int main(void) {
char s[20];
40
int n;

printf("\nString: ");
n = lee_linea(s, 20);
printf("\n%s longitud = %d ", s, n);

return 0;
}

/* Programa: STRLEN4.C
DEsc. : Ejemplo de como puede estar implementada la funcion de la
biblioteca estandard strlen (implementada usando apuntadores)
Otra Version!*/
int strlen(char *s) {
char *p = s;

while (*p != '\0')


p++;

return p-s;
}

/* Programa: STRCPY2.c
Desc. : Copia origen a destino usando apuntadores */
#include <stdio.h>

void strcpy(char *destino, char *origen) {


do {
*destino = *origen;
destino++;
origen++;
} while(*origen != '\0');
}

main(){
char str1[80], str2[80];

printf("\nIngresa un string ");


gets(str1);
strcpy(str2, str1);
puts(str2);
}

Ejercicio: Strbusca.c
Escriba una función str_busca(str, c) que reciba un apuntador a una cadena de
caracteres str, y un carácter c. Busque el carácter en la cadena y regrese la posición
en que se encuentra el carácter en la cadena o un -1 si no se encuentra utilizando
apuntadores.

41
Ejercicio: Strcon.c
Realice una función concatena(str1, str2) que agregue la cadena de caracteres str2
al final de la cadena str1.

Ejercicio: reversa.c
Realice una función reversa(str) que reciba un apuntador a una cadena de
caracteres e invierta la cadena. Utilizando apuntadores.

Manejo dinámico de memoria


malloc()
#include <stdlib.h>
void *malloc(size_t tam)
Aloja memoria dinámicamente

Descripción
malloc aloja un bloc de memoria del tamaño de tam bytes.
malloc regresa un apuntador al bloc de memoria que ha alojado. Si no puede alojar
la cantidad de memoria requerida, regresa un NULL.

free()
#include <stdlib.h>
void free(void *ptr)
Desaloja memoria dinámica

Descripción
free desaloja un bloc de memoria dinámica que ha sido previamente alojado por
malloc. El Desalojo de memoria la libera para reusa miento.
ptr apunta al bloc de memoria a ser liberado.

/* Programa: NARREGLO.C
Desc. : Ingresa un arreglo de n elementos utilizando manejo de
memoria dinamica*/
#include <stdio.h>
#include <stdlib.h> /* malloc(), free() */

int main() {
int *lista, n, i;

printf("\nDe cuantos elementos quieres la lista ? ");


scanf("%d", &n);
/* Asigna memoria a lista */
lista = (int *)malloc(n);
for (i=0; i < n; i++) {
printf("Lista[%d] = ? ", i);
scanf("%d", &lista[i]);
42
}
for (i=0; i < n; i++)
printf("\nLista[%d] = %d ", i, lista[i]);
/* Libera la memoria */
free(lista);
return 0;
}

Ejercicio: Apmenor.c
Realice un programa que:
a) Pida n números enteros y los almacene en un arreglo de n elementos
b) Busque el numero menor y el numero mayor
c) Despliegue el numero menor y el numero mayor

Ejercicio: Dinorden.c
Hacer un programa que:
a) Pida n números enteros y los almacene en un arreglo de n elementos
b) Los ordene de menor a mayor
c) Despliegue el arreglo

Arreglos de apuntadores
Los apuntadores son variables, as¡ que los podemos almacenar en un arreglo.
Declaración:
int *x[10];

Asignación:
x[2] = &v;

Encontrar el valor de v.
*x[2]

/* Programa: NOMBRES.C
Desc. : manejo de memoria dinamica, arreglo de apuntadores*/
#include <stdio.h>
#include <stdlib.h> /* malloc(), free() */
#include <string.h>
#include <conio.h>

#define LIMITE 5

int main() {
char str[255], *nombre[LIMITE];
int i;

clrscr();
printf("\n\t\tIngresa una lista de nombres\n");
for (i=0; i < LIMITE; i++) {
printf("%2d.-Nombre ? ", i);
gets(str);

43
nombre[i] = (char *)malloc(strlen(str)+1); /* Asigna memoria a nombre*/
strcpy(nombre[i], str); /* Copia str a nombre */
}

printf("\n\t\tLista de nombres:\n");
for (i=0; i < LIMITE; i++)
printf("\n%2d.- %s", i, nombre[i]);

/* desaloja la memoria */
for (i=0; i < LIMITE; i++)
free(nombre[i]);
return 0;
}

Indireccion simple y múltiple


Apuntador Variable

Direccion Valor

Apuntador Apuntador Variable

Direccion Direccion Valor

Ejemplo:
float **n;

Aquí n no es un apuntador a un numero en punto flotante, sino un apuntador a


aun apuntador float.

/* Programa: INDMULT.C
Desc. : Ejemplo de indireccion multiple */
#include <stdio.h>

int main() {
int x, *p, **q;

x = 10;
p = &x;
q = &p;

printf("\nq = %p\n*q = %p \n**q = %d", q, *q, **q);

return 0;
}

44
/* Programa: NOMBRES2.C
Desc. : manejo de memoria dinamica, apuntadores a apuntadores*/
#include <stdio.h>
#include <stdlib.h> /* malloc(), free() */
#include <string.h>
#include <conio.h>

int main() {
char str[255], **nombre;
int i, n;

clrscr();

printf("\nTama¤o de la lista ? ");


scanf("%d", &n);
flushall(); /* Limpia todos los flujos de entrada */
/* Esto es porque el scanf deja el \n en el buffer de entada
y el primer gets que hacemos lo toma, y no queremos eso*/

*nombre = (char *)malloc(n);

printf("\n\t\tIngresa una lista de nombres\n");


for (i=0; i < n; i++) {
printf("%2d.-Nombre ? ", i);
gets(str);
nombre[i] = (char *)malloc(strlen(str)+1); /* Asigna memoria a nombre*/
strcpy(nombre[i], str); /* Copia str a nombre */
}

printf("\n\t\tLista de nombres:\n");
for (i=0; i < n; i++)
printf("\n%2d.- %s", i, nombre[i]);

/* desaloja la memoria de cada elemento*/


for (i=0; i < n; i++)
free(nombre[i]);

/* Desaloja el total de elementos */


free(*nombre);

return 0;
}

45
Diferencias de arreglos bidimensionales y
arreglos de apuntadores
Ejemplo:
char cap[3][32] = { "introduccion",
"tipos, operadores y expresiones",
"control de flujo" /*No se pone coma*/
};

introduccio n\0

tipos , o peradores y e xpre siones \0

control de flujo\0

Necesita 96 bytes de almacenamiento.

Ejemplo: con apuntadores.


char *cap[3] = { "introduccion",
"tipos, operadores y expresiones",
"control de flujo" /* No se pone coma */
};

2 0 0 0 3 0 0 0 3 0 0 0 in t r o d u c c io n \ 0
2 0 0 1 3 0 0 1 3 0 0 1 t ip o s , o p e r a d o r e s y e x p r e s io n e s
2 0 0 2 3 0 0 2 3 0 0 2 c o n t r o l d e f lu jo \ 0

Necesita 63 bytes de almacenamiento + 6 bytes para el almacenamiento de los


apuntadores.

Argumentos para main


Nota:
Algunas veces es necesario pasar información al programa cuando se va a correr;
esta inf. se pasa a la función main vía la linea de argumentos.

Argumentos en la linea de comandos.


Es la inf. que sigue incluyendo el nombre del programa en la linea de comandos
del sistema operativo separada por espacios.
Cuando se invoca a main al comienzo de la ejecución se llama con dos
argumentos: argc, y argv.

argc
Es el numero de argumentos en la linea de comandos del sistema operativo con
que se invoco el programa.

46
argv
Es un apuntador a un arreglo de cadenas de caracteres; que contienen los
argumentos. Cada cadena contiene un argumento.

Nota:
Es un arreglo de apuntadores. argc siempre será 1 porque el primer arg. es el
nombre del programa (algunos sist. op. incluyen el path).

Ejemplo:
1.- Hacer ejecutable el prog. llamado arg.exe por ejemplo.
2.- Correrlo: arg buenos días

/* Programa ARGS.C*/
#include <stdio.h>
#include <conio.h>

int main(int argc, char *argv[]) {


int i;

clrscr();
printf("\nNumero de Argumentos: %d", argc);
for (i = 0; i < argc; i++)
printf("\n%d.-%s", i, argv[i]);

return 0;
}

47
6
Estructuras
Definición
Una estructura es un conjunto de una o mas variables, posiblemente de tipos,
diferentes, agrupadas bajo un mismo nombre.
Las estructuras en C son equivalentes al record de Pascal.

struct
Palabra reservada para definir una estructura.
Ejemplo:
definición del tipo de estructura.
struct direc {
char nombre[30];
char calle[40];
char ciudad[20];
char estado[10];
unsigned long int zip;
};

Declarando la estructura dir del tipo direc.


struct direc dir;

también es permitido lo siguiente:


struct direc {
char nombre[30];
char calle[40];
char ciudad[20];
char estado[10];
unsigned long int zip;
} dir;

Forma general:
struct etiqueta {
tipo nombre_variable;
tipo nombre_variable;
tipo nombre_variable;
.
.
tipo nombre_variable;
} variables_de_estructura;

48
Referenciando los elementos de la estructura:
nombre_estructura.nombre_elemento
Ejemplo:
dir.nombre

/* Programa: PERSONA.C
Desc. : Ejemplo de estructura.*/
#include <stdio.h>
#include <stdlib.h>

struct direc {
char nombre[30];
char dir[40];
unsigned int tel;
} direc_info;

/* Toma la seleccion del menu */


int menu_op(void) {
char s[80];
int c;

printf("1. Ingresa\n");
printf("2. Consulta\n");
printf("3. Fin\n");
do {
printf("\nOpcion: ");
gets(s);
c = atoi(s);
} while (c < 0 || c > 3);

return c;
}

void agrega(void) {
char s[80];

printf("\nNombre : ");
gets(direc_info.nombre);
printf("Direccion: ");
gets(direc_info.dir);
printf("Telefono : ");
gets(s);
direc_info.tel = strtoul(s, '\0', 10);
}

void consulta(void) {

printf("%s\n", direc_info.nombre);
printf("%s\n", direc_info.dir);
printf("%u\n\n", direc_info.tel);
}
49
int main() {
char opcion;

for (;;) {
opcion = menu_op();
switch(opcion) {
case 1:
agrega();
break;
case 2:
consulta();
break;
case 3:
exit(0);
}
}

return 0;
}

Ejercicio: Alumno.c
Realice un programa alumnos.c que ingrese y consulte la siguiente estructura:
struct alumno {
char nombre[30];
int calif;
int sem;
};

Arreglo de estructuras
Para definir un arreglo de estructuras primero se define la estructura y después se
define el arreglo.

/* Programa: AGENDA.C
Desc. : Arreglo de estructura para simular una agenda*/
#include <stdio.h>
#include <stdlib.h>

#define MAX 100

struct direc {
char nombre[30];
char dir[40];
unsigned int tel;
} direc_info[MAX];

/* Inicializa la listaa */
void ini_lista(void) {
int i;

50
for (i = 0; i < MAX; ++i)
direc_info[i].nombre[0] = '\0';
}

/* Toma la seleccion del menu */


int menu_op(void) {
char s[80];
int c;

printf("\n Agenda de Direcciones\n\n");


printf("1. Ingresa un nombre\n");
printf("2. Da de baja un nombre\n");
printf("3. Lista el archivo\n");
printf("4. Fin\n");
do {
printf("\nOpcion: ");
gets(s);
c = atoi(s);
} while (c < 0 || c > 4);

return c;
}

/* Encuentra una estructura vacia */


int busca_libre(void) {
register int i;

for (i = 0; direc_info[i].nombre[0] != '\0' && i < MAX; ++i)


;
if (i == MAX)
return -1;

return i;
}

/* Entrada de direcciones dentro de la lista */


void agrega(void) {
int i;
char s[80];

i = busca_libre();
if (i == -1) {
printf("\nLista llena");
return;
}
printf("\nNombre : ");
gets(direc_info[i].nombre);
printf("Direccion: ");
gets(direc_info[i].dir);
printf("Telefono : ");
gets(s);

51
direc_info[i].tel = strtoul(s, '\0', 10);
}

/* Da de baja una dirección */


void borra(void) {
register int i;
char s[80];

printf("Numero de registro: ");


gets(s);
i = atoi(s);
if (i >= 0 && i < MAX)
direc_info[i].nombre[0] = '\0';
}

/* Despliega la lista en la pantalla */


void lista(void) {
register int i;

for (i = 0; i < MAX; ++i) {


if (direc_info[i].nombre[0]) {
printf("No. de Registro: %d\n", i);
printf("%s\n", direc_info[i].nombre);
printf("%s\n", direc_info[i].dir);
printf("%u\n\n", direc_info[i].tel);
}
}
printf("\n\n");
}

int main() {
char opcion;

ini_lista(); /*Inicializa el arreglo de estructuras */


for (;;) {
opcion = menu_op();
switch(opcion) {
case 1:
agrega();
break;
case 2:
borra();
break;
case 3:
lista();
break;
case 4:
exit(0);
}
}

52
return 0;
}

Ejercicio: Aralumno.c
Adapte el programa agenda.c para que utilize la estructura alumno presentada en el
ejercicio anterior.

Paso de estructuras a funciones


Cuando una estructura es usada como argumento de una función, la estructura
entera es pasada usando el m‚todo standard de paso por valor.

Nota:
Lo anterior significa que los cambios hechos al contenido de la estructura dentro de
la función no afectara a la estructura pasada como argumento.

/* Programa: PERSONA2.C
Desc. : Ejemplo de paso de estructuras a funciones*/
#include <stdio.h>
#include <stdlib.h>

struct direc {
char nombre[30];
char dir[40];
unsigned int tel;
};

/* Toma la seleccion del menu */


int menu_op(void) {
char s[80];
int c;

printf("1. Ingresa\n");
printf("2. Consulta\n");
printf("3. Fin\n");
do {
printf("\nOpcion: ");
gets(s);
c = atoi(s);
} while (c < 0 || c > 3);

return c;
}

/* No podemos usar esta funcion ya que es paso por valor, en la funcion se crea una copia de la
estructura que le pasamos y actualiza la copia y no nuestra estructura original:
void agrega(struct direc dir_inf) {
char s[80];
printf("\nNombre : ");
gets(dir_inf.nombre);
53
printf("Direccion: ");
gets(dir_inf.dir);
printf("Telefono : ");
gets(s);
dir_inf.tel = strtoul(s, '\0', 10);
}*/

/* Despliega la lista en la pantalla */


void consulta(struct direc dir_inf) {

printf("%s\n", dir_inf.nombre);
printf("%s\n", dir_inf.dir);
printf("%u\n\n", dir_inf.tel);
}

int main() {
struct direc reg;
char opcion, s[80];

for (;;) {
opcion = menu_op();
switch(opcion) {
case 1: /* agregar: */
printf("\nNombre : "); gets(reg.nombre);
printf("Direccion: "); gets(reg.dir);
printf("Telefono : "); gets(s);
reg.tel = strtoul(s, '\0', 10);
break;
case 2:
consulta(reg);
break;
case 3:
exit(0);
}
}

return 0;
}

Ejercicio: Alumno2.c
Adapte el programa persona2.c para que utilize la estructura alumno presentada en
el ejercicio anterior.

54
Apuntadores a estructuras
C permite los apuntadores a estructuras igual que cualquier otro tipo de variable.

Declarando un apuntador a estructura.


Ejemplo:
struct dir *dir_p;

El uso de apuntadores es para generar llamadas por referencia al llamar una


función, para crear listas encadenadas así como estructuras dinámicas de datos.
Cuando un apuntador a estructura es pasado a una función, solo la dirección de la
estructura es puesta en el stack de datos. Esto permite unas llamadas muy rápidas a
las funciones.
Ejemplo:
struct bal {
float balance;
char nombre[40];
} persona;

struct bal *p; /* Declarando un apuntador a la estructura. */

p = &persona;
Esto pone la dirección de la estructura persona en el apuntador p.

Para accesar los elementos de la estructura usando un apuntador a esa estructura,


usamos:

(*p).balance;

Los paréntesis son necesarios alrededor del apuntador porque el operador PUNTO
(.) tiene mayor prioridad mayor que el operador *.

El segundo método de acceso es utilizando el operador ->; lo cual nos facilita la


programación.
Ejemplo:
p->balance;

/* Programa: PERSONA3.C
Desc. : Ejemplo de apuntadores a estructuras*/
#include <stdio.h>
#include <stdlib.h>

struct direc {
char nombre[30];
char dir[40];
unsigned int tel;
};

/* Toma55la seleccion del menu */


int menu_op(void) {
55
char s[80];
int c;

printf("1. Ingresa\n");
printf("2. Consulta\n");
printf("3. Fin\n");
do {
printf("\nOpcion: ");
gets(s);
c = atoi(s);
} while (c < 0 || c > 3);

return c;
}

void agrega(struct direc *dir_inf) {


char s[80];

printf("\nNombre : ");
gets(dir_inf->nombre);
printf("Direccion: ");
gets(dir_inf->dir);
printf("Telefono : ");
gets(s);
dir_inf->tel = strtoul(s, '\0', 10);
}

/* Despliega la estructura en la pantalla */


void consulta(struct direc *dir_inf) {

printf("%s\n", dir_inf->nombre);
printf("%s\n", dir_inf->dir);
printf("%u\n\n", dir_inf->tel);
}

int main() {
struct direc reg;
char opcion, s[80];

for (;;) {
opcion = menu_op();
switch(opcion) {
case 1:
agrega(&reg);
break;
case 2:
consulta(&reg);
break;
case 3:
exit(0);
}

56
}
return 0;
}

Ejercicio: Alumno3.c
Adapte el programa persona3.c para que utilize la estructura alumno presentada en
el ejercicio anterior.

57
7
Archivos
Entrada y salida
La entrada y salida se consigue mediante las funciones de la biblioteca estandard de
C. El archivo de encabezado que se necesita es el stdio.h.
Ejemplo:
#include <stdio.h>

Corrientes de Flujo
Corrientes y archivos
El sistema de E/S de C proporciona una interfaz independiente del dispositivo real
al que se accede; esto es, una abstracción entre el programador y el dispositivo que
se usa. Esta abstracción se llama corriente y el dispositivo real se llama archivo.

Corrientes
Almacenamiento temporal de archivo para transformar los dispositivos físicos en un
almacenamiento lógico. Esto permite que las corrientes sean independientes de los
dispositivos.
Existen dos tipos de corrientes: texto y binarias.

Corrientes de texto
Secuencia de caracteres organizadas en lineas terminadas por un carácter nueva
linea. La traducción del carácter nueva linea depende de la implementacion.

Corrientes binarias
Secuencia de bytes que tienen una correspondencia uno a uno con un dispositivo
externo.

Función fopen
FILE *fopen(const char *path, const char tipo);
Tipo: r w a r+ w+ a+ t b (agrega a un tipo en el modo indicado)
Regresa : un apuntador al archivo abierto si fue exitoso, o NULL si no.

Descripción:
La función fopen abre el archivo especificado por el path. El tipo de cadena de
caracteres especifica el tipo de acceso requerido para el archivo.
Esta cadena puede ser una de las siguientes: "r", "w", "a", "r+", "w+", "a+".

58
En adición a los valores listados arriba, ademas una "t" o una "b" puede ser
agregada al tipo o insertado antes del carácter + para especificar el modo de
traducción para la nueva linea. La "t" especifica el modo texto y la "b" especifica el
modo binario.

Tipos de acceso para los archivos


Constantes: a, a+, r, r+, w, w+
Especifica el tipo de acceso (a, r and w) requerido por el archivo.
El modo de traducción (b o t) puede ser agregado al tipo de acceso.

a Abre el archivo al final para escritura "agregar"); crea primero el archivo si


este no existe
a+ Abre para lectura y agrega; crea primero el archivo si este no existe.
r Abre para lectura. si "r" es el primer caracter en tipo, y si el archivo no
existe o no lo encuentra, el llamado para abrir fallara.
r+ Abre para lectura y escritura; y si el archivo no existe o no lo encuentra, el
llamado para abrir fallara.
w Abre un archivo vacío para escritura; si el archivo dado existe, el contenido
es destruido.
w+ Abre un archivo vacío para lectura y escritura; si el archivo dado existe, el
contenido es destruido.

Función fclose
Prototipo: int fclose(FILE *stream);
int fcloseall(void);
Regresa: (fclose) 0 si es exitoso, o EOF si no.
(fcloseall) el numero total de flujos cerrados si es exitoso,
o EOF si no.

Descripción:
La función fclose cierra la corriente dada. La función fcloseall cierra todas las
corrientes abiertas excepto stdin, stdout, stderr (y en MS-DOS, stdaux, y stdprn).

Función getc(corriente)
prototipo: int getc(stream)
FILE *stream
Regresa el siguiente carácter de la corriente de entrada de la posición actual e
incrementa el indicador de la posición del archivo.

/* Programa : desp.c
Descripcion: lee una archivo ASCII y lo despliega en la pantalla*/
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char *argv[]) {


FILE *fp;
59
char c;

if (argc != 2) {
puts("Uso: desp <nombre_archivo> ");
exit(1);
}

if ((fp = fopen(argv[1], "r")) == NULL) {


puts("No se abrio archivo");
exit(1);
}
c = getc(fp);
while (c != EOF) {
putchar(c);
c = getc(fp);
}
fclose(fp);
return 0;
}

/* Programa : typ.c
Descripcion: Despliega un archivo de texto dado en la linea de
comandos, en la pantalla dividiendolo en paginas,
presenta el # de pagina y hace una pausa entre cada pagina.*/
#define PANT 23
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
void titulos(char *s, int n, char *nom_prog) {
sprintf(s, " Archivo: %s %s %3d", nom_prog,
" Pagina: ", n);
}
int main(int argc, char *argv[]) {
int c, opl=PANT, ln=0, pag=1;
char s[80];
FILE *archivo;

if (argc == 1) {
printf("\n uso: typ archivo.extension \n");
exit(1);
}
archivo = fopen(argv[1], "r");
titulos(s, pag, argv[1]);
printf("%s\n", s);
while ((c=fgetc(archivo)) != EOF) {
fputc(c, stdout); /*Escribe al archivo estandard de salida */
if (c == '\n')
ln++;
if (ln == opl) {
ln=0;
pag++;

60
titulos(s, pag, argv[1]);
printf("Presione ENTER para continuar");
getchar();
printf("\n%s\n", s);
}
}
return 0;
}

Ejercicio: Typeprn.c
Adaptar typ.c para que la salida sea asia la impresora.

Función putc(corriente)
Prototipo: int putc(int c, FILE *fp);

Escribe un carácter a un corriente de salida. fp es un apuntador al archivo, c es el


carácter a escribir. Devuelve el carácter escrito si es exitosa la escritura, y EOF si
hay un error.

/* Programa : Aminus.c
Descripcion: Pasa un archivo con letras mayusculas a otro con minusculas*/
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>

FILE *abre_arch(char *archivo, char *modo) {


FILE *p;

if ((p = fopen(archivo, modo)) == NULL) {


printf("Error al Abrir el archivo %s", archivo);
exit(1);
}
return(p);
}

int main(void) {
FILE *f, *d;
char c;

f = abre_arch("inslist.pas", "r");
d = abre_arch("inslist2.pas", "w");

while ((c = fgetc(f)) != EOF)


fputc(tolower(c), d);
fclose(f);
fclose(d);
return 0;
}

61
Ejercicio: Mayminus.c
Modificar aminus.c para que el archivo de entrada y el de salida lo tome de la linea
de comandos.

Ejercicio: Conviert.c
Adaptar el programa anterior para que convierta de minúsculas a mayúsculas o
viceversa según una opción dada en la linea de comandos.

Función ferror
Incluye : <stdio.h>
Prototipo: int ferror(FILE *stream);
Regresa : un valor no cero para indicar un error en el flujo, o 0 para indicar que
no hubo error.
La rutina ferror evalúa un error en el flujo para lectura o escritura. Si un error ha
ocurrido, el indicador de error para el flujo permanece fijo hasta que el flujo es
cerrado o retrocedido, o hasta que clearerr es llamada contra este (error).

/* Programa : Copia.c
Descripcion: Copia un archivo a otro archivo*/
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char *argv[]) {


FILE *in, *out;
char c;
if (argc != 3) {
puts("uso: copia <archivo_1> <archivo_2> ");
exit(1);
}
if ((in = fopen(argv[1], "rb")) == NULL) {
puts("No abrio archivo fuente ");
exit(1);
}
if ((out = fopen(argv[2], "wb")) == NULL) {
puts("No abrio archivo destino ");
exit(1);
}
while (!feof(in)) {
c = getc(in);
if (ferror(in)) {
printf("\nError de Lectura\n");
clearerr(in);
break;
}
else {
putc(c, out);
if (ferror(out)) {
printf("\nError de Escritura\n");
clearerr(out);
break;
62
}
}
}
if (fclose(in)) printf("Error al Cerrar Archivo: %s\n", argv[1]);
if (fclose(out)) printf("Error al Cerrar Archivo: %s\n", argv[2]);

return 0;
}

/* Programa....: DelTab.c
Descripcion.: El programa sustituye espacios por tabs en el texto
del archivo y hace un chequeo de errores.
Autor.......: R. Cesar Rodriguez C.
Fecha.......: Junio/93*/
#include <stdio.h>
#include <stdlib.h>
#define TAB_SIZE 8
#define IN 0
#define OUT 1

void error(int e) {
if (e == IN)
puts("Error en la entrada");
else
puts("Error en la salida");
exit(1);
}

int main(int argc, char *argv[]) {


FILE *in, *out;
int tab, i;
char c;

if (argc != 3) {
puts("Uso: deltab <archivo_fuente> <archivo_destino>");
exit(1);
}
if ((in = fopen(argv[1], "rb")) == NULL) {
printf("No abrio archivo %s\n", argv[1]);
exit(1);
}
if ((out = fopen(argv[2], "wb")) == NULL) {
printf("No abrio archivo %s\n", argv[2]);
exit(1);
}
tab = 0;
do {
c = getc(in);
if (ferror(in))
error(IN);
63
if (c == '\t') {
for (i = tab; i < 8; i++) {
putc(' ', out);
if (ferror(out))
error(OUT);
}
tab = 0;
}
else {
putc(c, out);
if (ferror(out))
error(OUT);
tab++;
if (tab == TAB_SIZE)
tab = 0;
if (c == '\n' || c == '\r')
tab = 0;
}
} while (!feof(in));
fclose(in);
fclose(out);
return 0;
}

Ejercicio: Sinesp.c
Escriba un programa que reciba un archivo de texto, elimine todos los espacios al
inicio de cada linea y lo deje en otro archivo de texto.

Ejercicio: Concat.c
Escriba un programa que reciba dos archivo de texto, y concatene el primero al
final del segundo archivo.

Función fwrite
Incluye : <stdio.h>
Prototipo: size_t fwrite(const void *buffer, size_t size, size_t count,
Regresa : el numero de campos completos escritos actualmente.

Descripción
La función fwrite escribe hasta el tamaño de la longitud total de campos desde el
buffer a la corriente de output.
El apuntador al archivo asociado con la corriente es incrementado por el numero de
bytes actualmente escritos.
Si el flujo es abierto en modo texto, cada retorno de carro es remplazado con un
par de carriage-return-line-feed. El remplazo no tiene afecto sobre el valor de
regreso.

64
/* Programa....: AGENDA2.C
Descripcion.: Graba un arreglo de estructuras a disco/lo recupera
Autor.......: R. Cesar Rodriguez C.
Fecha.......: Junio/93*/
#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
#include <conio.h>

#define SIZE 50

struct archivo {
char nombre[20],
direc[40];
int cuartos;
int pisos;
float renta;
} reg[SIZE];

/* Inicializa la lista. */
void ini_reg(void) {
register int i;

for (i = 0; i < SIZE; i++)


*reg[i].nombre = '\0';
}

/*------------- altas en la lista.----------------------- */


void altas(void) {
register int i;
char s[20];

for (i = 0; i < SIZE; i++)


if (!*reg[i].nombre)
break;

if (i == SIZE) {
puts("Lista Llena");
return;
}
printf("Nombre : ");
gets(reg[i].nombre);
printf("direccion : ");
gets(reg[i].direc);
printf("Numero de cuartos: ");
gets(s);
reg[i].cuartos = atoi(s);
printf("Numero de Pisos : ");
gets(s);

65
reg[i].pisos = atoi(s);
printf("Precio de Renta : ");
gets(s);
reg[i].renta = atof(s);
}

/* Despliega la lista */
void despliega(void) {
register int i;

for (i = 0; *reg[i].nombre != '\0' && i < SIZE; i++) {


if (*reg[i].nombre) {
printf("\nNombre %s\n", reg[i].nombre);
printf("Direccion %s\n", reg[i].direc);
printf("Numero de Cuartos %d\n",reg[i].cuartos);
printf("Numero de Pisos %d\n", reg[i].pisos);
printf("Precio de Renta %f\n", reg[i].renta);
}
printf("\nCualquier tecla para continuar");
getch();
}
}

/* Graba la lista en el archivo. */


void registra(void) {
FILE *fp;
register int i;

if ((fp = fopen("rentas.dat", "wb")) == NULL) {


puts("No abrio el archivo rentas.dat");
return;
}
for (i = 0; i < SIZE; i++)
if (*reg[i].nombre)

if (fwrite(&reg[i],sizeof(struct archivo),1,fp) != 1)
puts("Error de escritura en el archivo");
}

/* carga archivo a la lista de estructuras */


void carga(void) {
FILE *fp;
register int i;

if ((fp = fopen("rentas.dat", "rb")) == NULL) {


puts("No abrio el archivo rentas.dat");
return;
}
ini_reg();
for (i = 0; i < SIZE; i++)
if (fread(&reg[i],sizeof(struct archivo),1,fp) != 1) {

66
if (feof(fp))
return;
puts("Error de lectura en el archivo");
}
}

/* Toma una seleccion del menu */


int menu(void) {
char s[80];
clrscr();

do {
printf(" M E N U\n");
printf("A)ltas\n");
printf("D)espliega\n");
printf("F)in\n");
printf("Opcion: ");
gets(s);
} while (!strchr("adf", tolower(*s)));
return tolower(*s);
}

int main(void) {
char opcion;

ini_reg();
carga(); /* carga archivo a una lista de estructuras */
for (; ;) {
opcion = menu();
switch(opcion) {
case 'a':
altas();
break;
case 'd':
despliega();
break;
case 'f':
registra();
exit(0);
}
}
return 0;
}

Ejercicio: Stalumno.c
Adapte el programa agenda2.c para que utilice la estructura alumno de los
ejercicios planteados en el capitulo de estructuras.

67
8
Tema avanzados
Tipos de variables: Parte 2

Variables static
Pueden ser internas o externas. Las variables static internas son locales a una
función pero a diferencia de las automáticas, su existencia es permanente.
Proporcionan un medio de almacenamiento permanente y privado en una función.
Una variable static externa es accesible en el resto del archivo fuente donde fue
declarada, pero no en otro.
Ejemplo:
static int i;
static char c;

Nota:
Excepto cuando la variable es declarada static.
Esto causa que el compilador trate a la variable como si fuera una variable global
para propósito de almacenamiento. Pero con el limite de alcance solo dentro de la
función.

Variables registro
Una declaración register avisa al compilador que la variable en cuestión será muy
usada. Cuando es posible, las variables register se colocan en los registros de la
maquina, lo que producirá programas mas cortos y
rápidos.
Ejemplo:
register int x;
register char c;

Si la variable en cuestión es externa o estática, la inicialización se realiza una sola


vez, conceptualmente antes de que empiece la ejecución del programa. Las
variables automáticas, inicializadas explícitamente, se inicializan cada vez que se
ejecuta la función a la que pertenecen.
Las variables automáticas que no son inicializadas explícitamente tienen valores
indefinidos ("basura").Las variables externas y las estáticas se inicializan a cero por
definición pero conviene indicar la inicialización de alguna manera.

68
Operadores: Parte 2

Operador coma ,
Una pareja de expresiones separadas por una coma se evalúa de izquierda
a derecha, y el tipo del resultado es el mismo que el operando derecho.
Ejemplo:
reverse(char s[]) { /* invierte la cadena s */
int c, i, j;

for (i = 0, j = strlen(s)-1; i < j; i++, j--) {


c = s[i];
s[i] = s[j];
s[j] = c;
}
}

Nota:
Las comas que separan argumentos en las funciones o variables en las
declaraciones, etc., no son operadores ni garantizan que la evaluación se realice de
izquierda a derecha.

Operadores lógicos para manejo de bits


Estos operadores no se aplican a operandos de tipo float o double.
Actúan sobre expresiones enteras representadas como cadenas de dígitos binarios.

Nota:
Dependen explícitamente de la maquina.

& Y lógico a nivel de bits (AND) (.)


| O lógico a nivel de bits (OR) (+)
^ O lógico exclusivo de bits (OR EXCLUSIVO)
<< Desplazamiento a la izquierda.
>> Desplazamiento a la derecha.
~ Complemento a 1. (Unitario).

Operador complemento ~
Invierte la representación de la cadena de bits de su argumento, los ceros se
convierten en unos, y los unos en ceros.
Ejemplo:
int x;

Si:
x = 6; => 00110
~x = 25; => 11001

Operadores binarios lógicos de bits.


69
Los dos argumentos, ampliados en forma adecuada operan de bit en bit.
Ejemplo:
Con operadores de bits actuando en un campo de un bit.

X Y X & Y X ^ Y X | Y

0 0 0 0 0

0 1 0 1
1

1 0 0 1 1

1 1 1 0 1

Operadores de desplazamiento a la izquierda y a la


derecha
Los dos operandos de un operador de desplazamiento deben ser expresiones
enteras.
El operando derecho se convierte en int.
El tipo de la expresión en su totalidad será el de su operando izquierda.
El operador de desplazamiento a la izquierda << mueve la representación de bits
del operando izquierdo el numero de lugares que especifique el argumento de la
derecha. Los bits de orden menor se remplazan con ceros; esto depende de la
maquina para asegurarse debe ser unsigned.
Ejemplo:
char x = 'Z';

x 01011010 Sin desplazamiento


x << 1 10110100 Desplazamiento de 1 a la izquierda
x << 4 10100000 Desplazamiento de 4 a la izquierda.

Ejemplo: & (AND)


unsigned int x, y, b;
x = 1; --------> 0001
y = 1; --------> 0001
----
b = x & y; -----> 0001 => b=1

x = 1; --------> 0001
y = 2; --------> 0010
----
b = x & y; -----> 0000 => b = 0

Ejemplo: | (OR)
unsigned int x, y, b;
x = 1; --------> 0001
y = 1; --------> 0001
----
b = x | y; -----> 0001 => b=1

70
x = 1; --------> 0001
y = 2; --------> 0010
----
b = x | y; -----> 0011 => b = 3

x = 2; --------> 0010
y = 3; --------> 0011
----
b = x | y; -----> 0011 => b = 3

Ejemplo: ^ (OR EXCLUSIVO)


unsigned int x, y, b;
x = 1; --------> 0001
y = 1; --------> 0001
----
b = x ^ y; -----> 0001 => b=0

x = 1; --------> 0001
y = 2; --------> 0010
----
b = x ^ y; -----> 0011 => b = 3

x = 2; --------> 0010
y = 3; --------> 0011
----
b = x ^ y; -----> 0001 => b = 1

/* Programa....: DESPIZQ.C
Descripcion.: Desplazamiento de bits a la izquierda
Autor.......: R. Cesar Rodriguez C.
Fecha.......:
Modificacion: Junio/93
*/
#include <stdio.h>

int main(void) {
int a, b;
/* a = 00001101
<< 2 = 00110100
---------- */
a = 13; /* 1001 */
a = a << 2; /* 1010 */
printf(" a = %d ", a ); /* 1011 */
/* 1100 */
return 0;
}

71
/* Programa....: DESPDER.C
Descripcion.: Desplazamiento de bits a la derecha
Autor.......: R. Cesar Rodriguez C.
Fecha.......: Junio/93
Modificacion:
*/
#include <stdio.h>

int main(void) {
int a, b;
/* a = 00001101
>> 1 = 1
---------- */

a = 13; /* 1001 */
a = a >> 1; /* 1010 */
printf(" a = %d ", a ); /* 1011 */
/* 1100 */
return 0;
}

/* Programa....: ANDBITS.C
Descripcion.: AND bits
Autor.......: R. Cesar Rodriguez C.
Fecha.......: Junio/93
Modificacion:
*/
#include <stdio.h>

int main(void) {
int a, b;
/* a = 00000011
& 01 = 0001
---------- */
a = 3;
a = a & 01;
printf(" a = %d ", a );

return 0;
}

72
/* Programa....: BITCOUNT.C
Descripcion.: cuenta los bits 1 en n
Autor.......: R. Cesar Rodriguez C.
Fecha.......: Junio/93
Modificacion:
*/
#include <stdio.h>

int bitcount(unsigned n);

int main(void) {
int a, b;
/* a = 00110100
= 43210
---------- */

a = 52;
printf(" bitcount(a) = %d ",bitcount(a));
return 0;
}

/* cuenta los bits 1 en n */


int bitcount(unsigned n) {
int b;

for ( b = 0; n != 0; n >>= 1 )
if ( n & 01 )
b++;

return b;
}

/* Programa....: LEEBITS.C
Descripcion.: toma n bits a partir de la posicion p*/
#include <stdio.h>

int getbits(unsigned x, unsigned p, unsigned n);

int main(void) {
int a, b;

a = 52;
printf(" getbits(x, 4, 2) = %d ", getbits(a, 4, 2));

return 0;
}

/* tomar n bits a partir de la posición p */


int getbits(unsigned x, unsigned p, unsigned n) {
73
return((x >> (p+1-n)) & ~(~0 << n));
}

Definicion de tipos
Uniones
Una unión es una localidad de memoria la cual es usada por dos o mas
variables diferentes; generalmente de tipos diferentes.
Forma general:
unión etiqueta {
tipo nombre_de_variable;
.
tipo nombre_de_variable;
} variable_union;
Ejemplo:
union int_char {
int i;
char c;
};

union int_char dosv;

En la unión dosv, las 2 variables el int y el char ocupan la


misma localidad de memoria.
i

Byte 0 Byte 1

Cuando una unión es declarada, el compilador crea una variable de la longitud


de la variable mayor definida en la unión.

/* Programa: UNION.C */
#include <stdio.h>

union pw {
int i;
char s[2];
};

void putw(union pw palabra) {


putchar(palabra.s[0]);
putchar(palabra.s[1]);
}

74
Enumeraciones
Es una extensión al lenguaje C agregado por el ANSI C.
Una enumeración es un conjunto de nombres de constantes enteras las cuales
especifican todos los valores legales que debe tener un tipo de variable.

Forma General:
enum nombre {lista de enumeración} lista_variables;

/* Programa: ENUM.C */
#include <stdio.h>
#include <conio.h>

enum numeros {
cero,
uno,
dos,
cinco = 5,
seis
} numero;

int main(void) {
clrscr();
printf("\n%d\n%d\n%d\n%d", uno, dos, cinco, seis);

return 0;
}

Typedef
Permite re definir nuevos nombres de datos.
Forma general:
typdef nombre_tipo;
Ejemplo:
typedef float flotante;
flotante i;
typedef struct registro {
int clave;
char nombre[40];
float calif;
};
registro reg;
reg.clave = 10;

75
Recursion
Una función es recursiva si una sentencia en el cuerpo de la función se llama a si
misma.

/*Programa: FACTREC.C
Desc. : Usa una funcion normal y na recursiva para sacar el factorial*/
#include <stdio.h>
int fact(int n) { /* no recursiva */
int i, f = 1;

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


f = f * i;

return(f);
}
int factr(int n) { /* Funcion recursiva */
if (n == 0)
return 1;
else
return(n*fact(n-1));
}
int main() {
int n;

printf("ingrese un numero ");


scanf("%d", &n);

printf(" el factorial de %d es %d\n", n, fact(n));


printf(" el factorial de %d es %d\n", n, factr(n));

return 0;
}

Ejemplo
Impresión de un numero en forma de cadena de caracteres.
Los dígitos se generan en orden inverso: los de menor peso se encuentran
disponibles antes que los de orden superior, pero deben imprimirse después.

Solución 1
Consiste en almacenar los dígitos en un arreglo según se van generando.

/* Programa: PRINTD.C
Desc. : Convierte un entero a un string*/
#include <stdio.h>
void printd(int n) { /* imprime n en decimal */
char s[10];
int i;

if (n < 0) {
76
putchar('-');
n = -n;
}

i = 0;
do {
s[i++] = n % 10 + '0'; /* toma el siguiente caracter */
} while(( n /= 10) > 0); /* lo descarta */

while ( --i >= 0)


putchar(s[i]);
}
int main() {
int n;

printf(" ingrese un numero ");


scanf("%d", &n);

printf("\n el numero como cadena de caracteres ");


printd(n);

return 0;
}

Solución 2
Usando una función recursiva, en la que cada llamada a printd primero se llama a si
misma para hacer frente a los dígitos delanteros y a continuación imprime el ultimo.

/* Programa: tprintd2.c */
#include <stdio.h>
void printd(int n);
int main(void) {

int n;

printf(" ingrese un numero ");


scanf("%d", &n);

printf("\n el numero como cadena de caracteres ");


printd(n);

return 0;
}

void printd(int n) { /* imprime n en decimal recursiva */


char s[10];
int i;

if (n < 0) {
putchar('-');
77
n = -n;
}
if (( i = n / 10) != 0)
printd( i );
putchar(n % 10 + '0');
}

Ejercicio C3.3.
Escribe una versión recursiva de la rutina reversa, que invierte una cadena.

78
Bibliografia
1. El lenguaje de programacion C, Segunda edicion
Brian W. Kernighan
Dennis M. Ritchie
Prentice Hall.
2. El lenguaje de programacion C
Brian W. Kernighan
Dennis M. Ritchie
Prentice Hall.
3. C the complete reference, second edition
Herbert Schildt
Osborne McGraw Hill

79

Vous aimerez peut-être aussi