Qdoc - Tips Aaa Clases 2013
Qdoc - Tips Aaa Clases 2013
Qdoc - Tips Aaa Clases 2013
Bibliografía:
"THE C PROGRAMING LANGUAGE" Kernighan y Ritchie - Pre ntice-Hall, Segunda Edición.
“FUNDAMENTOS DE PROGRAMACIÓN: ALGORITMOS, ESTRUCTURAS DE DATOS Y
OBJETOS" Luis Joyanes Aguilar - McGraw-Hill – Cuarta Edición
“CÓMO PROGRAMAR EN C/C++ Y JAVA” Deitel – Prentice-Hall – Cuarta Edición.
Algoritmo: (del griego y latín, dixit algorithmus y este a su vez del matemático persa Al-Juarismi,
siglo IX). Enunció paso a paso las reglas para ejecutar las operaciones básicas en matemáticas.
Características
Características de un algoritmo
Preciso: indicar el orden de realización en cada paso.
Definido: si se sigue dos veces, obtiene el m ismo resultado cada vez (dudoso)
Finito: tiene un fin. Tiene un número determinado de pasos.
Fórmulas
Ejemplo: Hallar las raíces de
(√ )
)
(√ )
)
Diagramas de flujo
Diagramas de flujo
Pseudocódigo
Ejemplo: Suma de dos números
Entrada: a,b → números
Salida: s, la suma de a y b
inicio
leer (a,b)
s=a+b
escribir (s)
fin
Programa
#include<stdio.h>
#include<conio.h>
void main()
{
int a, b, s;
printf("Ingrese el primer número ");
scanf("%d",&a);
printf("\n\nIngrese el segundo numero ");
scanf("%d",&b);
s=a+b;
printf("\n\nEl resultado de la adicion es: %d",s);
getch();
}
Lenguajes de programación
Contadores y acumuladores
CON ← CON + 1
ACU ← ACU + X
Diseño de programas
Programación modular (lógica fácil de seguir)
Programación estructurada (escritura fácil de comprender)
Reglas de la programación
programación estructurada
El programa tiene un diseño modular
Los módulos son diseñados de modo descendente.
Diagramas de flujo
Pseudocódigo
Ejemplo: Suma de dos números
Entrada: a,b → números
Salida: s, la suma de a y b
inicio
leer (a,b)
s=a+b
escribir (s)
fin
Programa
#include<stdio.h>
#include<conio.h>
void main()
{
int a, b, s;
printf("Ingrese el primer número ");
scanf("%d",&a);
printf("\n\nIngrese el segundo numero ");
scanf("%d",&b);
s=a+b;
printf("\n\nEl resultado de la adicion es: %d",s);
getch();
}
Lenguajes de programación
Contadores y acumuladores
CON ← CON + 1
ACU ← ACU + X
Diseño de programas
Programación modular (lógica fácil de seguir)
Programación estructurada (escritura fácil de comprender)
Reglas de la programación
programación estructurada
El programa tiene un diseño modular
Los módulos son diseñados de modo descendente.
Contadores y acumuladores
CON ← CON + 1
ACU ← ACU + X
Diseño de programas
Programación modular (lógica fácil de seguir)
Programación estructurada (escritura fácil de comprender)
Reglas de la programación
programación estructurada
El programa tiene un diseño modular
Los módulos son diseñados de modo descendente.
Cada módulo se codifica utilizando las tres estructuras de control básicas .
El término control de flujo (o flujo de control) se refiere al orden en que se ejecutan las sentencias
del programa. A menos que se especifique expresamente, el flujo normal de control de todos los
programas es el secuencial . Este término significa que las sentencias se ejecutan en secuencia, una
después de otra, en el orden
or den en que se sitúan dentro del programa.
Acción 1
Acción 2
Acción N
Estructuras selectivas
Si (if )
Según-sea (switch case)
Sentencia SI (Simple – Doble – Anidamiento)
Diagrama de Flujo
si ( condicion )
{
Instrucción 1
Instrucción 2
… Acciones Condición Acciones
sino
Instrucción 3
…
}
Sentencia SI (Simple – Doble – Anidamiento)
Diagrama de Flujo
si ( condicion )
{
Instrucción 1
Instrucción 2
… Acciones Condición Acciones
sino
Instrucción 3
…
}
Ejemplo
Determinar si un número es par o impar
Anidamiento – Ejemplo: Mostrar la calificación de un alumno a partir del puntaje obte nido en el
examen.
inicio
cls()
imprimir ("Ingrese la nota del alumno: ")
leer (pnt)
si (pnt >= 90)
{
cal = 5
sino si (pnt >= 80)
cal = 4
sino si (pnt >= 70)
cal = 3
sino si (pnt >= 60)
cal = 2
sino
cal = 1
}
imprimir(cal)
fin
Estructuras repetitivas
Hay una gran variedad de situaciones que requieren que una o varias
instrucciones se repitan varias veces, ya sean cálculos u otro tipo de
instrucciones. Las estructuras repetitivas abren la posibilidad de realizar una
secuencia de instrucciones más de una vez. L as más conocidas son:
Mientras (while)
Repetir (do-while)
Para ( for )
Mientras – Sintaxis
mientras(condicion)
{
Conceptos de bucle (lazo) e iteración
Un bucle, es una sección de código que se repite. Es decir cuando se termina de ejecutar la última
instrucción del conjunto, el flujo de control retorna a la primera sentencia y comienza una nueva
repetición de las sentencias que forman esa sección de código. Se denomina iteración al hecho de
repetir la ejecución de una secuencia de acciones, la iteración se asocia a un número entero que
indica el número de veces que se repite un trozo de código.
Mientras – Sintaxis
mientras(condicion)
{
instrucciones
}
Repetir – Sintaxis
repetir
{
instrucciones
} hasta(condición)
Ejercicios
10. Realizar la división entre 2 números naturales ( y ) por restas sucesivas, y mostrar el
cociente y el resto de la división.
11. Desarrolle un algoritmo que permita determinar a partir de un número de días (ingresado
por pantalla), los años, meses, semanas y días que constituyen el número de días
proporcionado.
12. Ingresar números por teclado hasta o btener uno que sea positivo e imprimirlo.
13. Calcular el factorial de un número N.
Estructuras de datos
Hasta ahora sólo hemos trabajado con variables simples, que únicamente pueden almacenar un
10. Realizar la división entre 2 números naturales ( y ) por restas sucesivas, y mostrar el
cociente y el resto de la división.
11. Desarrolle un algoritmo que permita determinar a partir de un número de días (ingresado
por pantalla), los años, meses, semanas y días que constituyen el número de días
proporcionado.
12. Ingresar números por teclado hasta o btener uno que sea positivo e imprimirlo.
13. Calcular el factorial de un número N.
Estructuras de datos
Hasta ahora sólo hemos trabajado con variables simples, que únicamente pueden almacenar un
dato. Por ejemplo, al trabajar con listas de números (calificaciones de alumnos de una misma
clase, trabajadores de una empresa, serie de números enteros, etc.), su manejo con variables
simples puede ser un poco impráctico, además de no considerar el almacenamiento de las
entradas.
Para salvar estas situaciones, la mayoría de los lenguajes de programación incluyen características
de estructuras de datos. Una estructura de datos es una colección de datos que pueden ser
caracterizados por su organización y las operaciones que se definen en ella.
Las estructuras de datos básicas que soportan la mayoría de los lenguajes son los arreglos (arrays)
(siendo el vector un arreglo de una dimensión, y la matriz uno de dos dimensiones). Se dividen en:
Un arreglo es una secuencia de posiciones de la memoria central a las que se puede acceder
directamente, que contiene datos del mismo tipo y pueden ser seleccionados individualmente
mediante el uso de subíndices.
Podemos considerar al vector como un conjunto de variables simples con datos del mismo tipo.
El vector nota tiene subíndices o índices de sus elementos (1, 2, …, , …, ) que indican la posición
de un elemento particular dentro del arreglo. Por ejemplo, si se desea modificar el tercer
elemento de un vector de tipo numérico:
nota[3] = 80
Matrices
Matrices
La matriz puede verse como un vector de vectores. Por ello, se necesita especificar dos subíndices
para poder identificar cada uno de sus e lementos.
Columna
Nombre de la matriz
Fila
Lenguaje C
Se ha creado en 1972 por Brian W. Kernighan y Dennis M. Ritchie en los Laboratorios Bell como evolución del
Apuntes de la clase de PED 2 Fecha: 15 de febrero de 2012
MSc. José Colbes
Lenguaje C
Se ha creado en 1972 por Brian W. Kernighan y Dennis M. Ritchie en los Laboratorios Bell como evolución del
anterior lenguaje B, a su vez basado en BCPL.
“Se trata de un lenguaje fuertemente tipificado de medio nivel pero con muchas características de bajo nivel.
Dispone de las estructuras típicas de los lenguajes de alto nivel pero, a su vez, dispone de construcciones del
lenguaje que permiten un control a muy bajo nivel. Los compiladores suelen ofrecer extensiones al lenguaje que
posibilitan mezclar código en ensamblador con código C o acceder directamente a memoria o dispositivos
periféricos.” (Wikipedia)
Empezando en C
#include <stdio.h>
main()
{
printf("hello, world\n");
}
Algunas observaciones:
stdio.h (Standard input/output) es la librería principal en C, con funciones básicas de entrada/salida.
Cuando en un archivo .c se encuentra una línea con un #include seguido de un nombre de archivo, el
preprocesador la sustituye por el contenido de ese archivo.
La función main es la principal (el programa se ejecuta al comienzo de la misma).
Un método de comunicación de datos entre funciones es que la función que llama proporcione una
lista de valores, llamados argumentos , a la función llamada. Los paréntesis que van después del
nombre contienen la lista de argumentos. En este ejemplo, main se define como una función que no
espera argumentos, lo cual está indicado por la lista vacía ().
Las sentencias de una función están encerradas por llaves {}.
Una función es llamada por su nombre, seguido por una lista de argumentos entre ():
o Por ejemplo, se llama a printf con el argumento “hola mundo!!\n”.
El fin de una instrucción lleva un punto y coma ;
La secuencia \n indica un salto de línea. Representa un solo carácter y se denomina secuencia de
escape, que son caracteres invisibles o difíciles de escribir.
Función printf – Secuencias de escape
Tipos de datos
int a=5;
char b=’a’; ó char b=97;
float f=2.45;
int d=05; (un cero antes del número indica que es el valor octal)
int e=0xD; (el 0x ó 0X indica que se tiene un valor hexadecimal)
Obs1: para operaciones con caracteres, se debe manejar el código de representación (por ejemplo, tabla
ASCII). Por ejemplo, no es lo mismo el entero 9 que el carácter ’9’.
Ejemplo 2
#include<stdio.h>
main(){
int a;
a=3;
printf(“%d\n”);
}
Ejercicio: escribir un programa que permita asignar valores enteros a 2 variables y que calcule e imprima por
pantalla el producto de ambos.
#include<stdio.h> #include<stdio.h>
main(){ main(){
int a,b,c; int a,b,c;
a=3; a=3; b=4;
b=4; printf(“%d\n”,c=a*b);
c=a*b }
printf(“%d\n”,c);
}
Para tener en cuenta: supongamos que F es un entero que indica la temperatura en grados Fahrenheit, y
queremos transformarla en grados Celsius, almacenando el valor en el entero C. ¿Son equivalentes las
siguientes expresiones?:
C=5*(F-32)/9;
C=5/9*(F-32)
Funciones
Ejemplo: escribir un programa que sume dos números x e
e y .
#include<stdio.h> #include<stdio.h>
int suma(int,int); int suma(int x, int y){
main(){ return (x+y);
int a,b,c; }
a=3; b=5; main(){
c=suma(a,b); int a,b,c;
printf(“%d\n”,c); a=3; b=5;
} c=suma(a,b);
int suma(int x, int y){ printf(“%d\n”,c);
return (x+y); }
}
getch() y getche()
Están incluidos en conio.h ( Console input and output ). Introducen caracteres sin la necesidad de un “enter” al
final. La diferencia entre ambas es que con getch() lo ingresado por teclado no aparece en pantalla, pero con
getche() sí.
scanf()
Es análogo a printf, y se encuentra en stdio.h. La forma general de esta función es la siguiente:
int scanf("%x1%x2...", &arg1, &arg2, ...);
Ejemplo:
int a;
scanf(“%d”,&a);
printf(“%d”,a);
#include<stdio.h> #include<stdio.h>
int cuad(int); void cuad();
main(){ int a;
int a; main(){
scanf(“%d”,&a); scanf(“%d”,&a);
printf(“%d\n”,cuad(a)); cuad();
} printf(“%d\n”,a);
int cuad(int a){ }
return (a*a); void cuad(){
} a=a*a;
}
El uso más o menos frecuente de las variables globales dependen de las necesidades del programa y
estilo de programación adoptado.
#include<stdio.h>
void intercambiar (int,int);
int main(){
int a,b;
scanf("%d%d",&a,&b);
intercambiar(a,b);
printf("%d %d",a,b);
return 0;
}
void intercambiar(int a, int b){
int aux;
aux=b;
b=a;
a=aux;
}
Operadores en C
Aritméticos:
o Suma: +
o Resta: -
o Multiplicación: *
o División: /
o Resto: %
Asignación:
o Operador de igualdad: =
o Operadores +=, -=, *= y /= . Es lo mismo poner a+=1; que a=a+1; (y análogamente para los
demás casos).
o Operadores incrementales (++ y --)
i = 2;
j = 2;
m = i++; // despues de ejecutarse esta sentencia m=2 e i=3
n = ++j; // despues de ejecutarse esta sentencia n=3 y j=3
Relacionales:
o Igual que: ==
o Menor que: <
o Mayor que: >
o Menor o igual que: <=
o Mayor o igual que: >=
o Distinto que: !=
Lógicos:
o And: &&
o Or: ||
o Operador negación lógica (!): Este operador devuelve un cero (false) si se aplica a un valor
distinto de cero (true), y devuelve un 1 (true) si se aplica a un valor cero (false). Su forma
general es: !expresion
Reglas de precedencia y asociatividad
Ejercicios
Luego de ingresar el radio por teclado, hallar la cía y el área del círculo.
Realizar un programa que permita la introducción por teclado (mediante getchar) de un valor entero de
una cifra y que calcule e imprima por pantalla el cuadrado del mismo.
Apuntes de la clase de PED 2 Fecha: 22 de febrero de 2012
MSc. José Colbes
Páginas interesantes:
Stackoverflow (http://stackoverflow.com/): comunidad donde se puede encontrar mucha información
sobre varios algoritmos y lenguajes.
ACM-ICPC Live Archive (https://icpcarchive.ecs.baylor.edu/): Repositorio que contiene una variedad de
problemas que aparecieron en las olimpiadas de programación de la ACM (Association for Computing
Machinery) Tienen “jueces virtuales” para verificar las soluciones a lo s problemas. Las olimpiadas se
centran en el diseño de algoritmos para los problemas planteados en las competencias; pudiéndose
emplear C, C++ y Java.
SPOJ (http://www.spoj.com/): lo mismo que lo anterior.
Ahmed-Aly (http://ahmed-aly.com/): sitio donde se realizan competencias virtuales de programación
(o bien entrenamientos), usando los repositorios de problemas antes mencionados (y otros más).
Estructuras selectivas
En C las sentencias se ejecutan sucesivamente una tras otra. Esto define un camino que va desarrollado el
programa. Sin embargo, habrá momentos en que el programa deba ejecutar determinadas partes dependiendo
del estado en el que se halle el programa o de las variables externas. Para ello pueden utilizarse las sentencias
condicionales o incondicionales.
Una sentencia condicional es una instrucción o grupo de instrucciones que se pueden ejecutar o no, en función
del valor de una condición.
if(expresión)
sentencia1;
if(expresión)
sentencia1;
else
sentencia2;
Por ejemplo:
if(numero1==1)
printf(“la variable numero1 vale 1” );
else
printf(“la variable numero1 no vale 1” );
Tras evaluarse la expresión if y ejecutarse la sentencia adecuada, el programa continúa con la línea siguiente
a la de la última sentencia del if. Como sentencia vale cualquier tipo de sentencia válida en C, entre ellas la
propia sentencia if.
if(num>0)
if(num==1)
printf(“num es igual a 1” );
else
printf(“num es mayor que 1”);
else
printf(“num es menor que 1” );
Cuando hay dos if anidados y a continuación hay un else, éste pertenece al último if. Así en el caso
anterior el primer else corresponde al segundo if. Si queremos que un else pertenezca al primer if de un
if anidado deberemos encerrar al segundo entre llaves. Por ejemplo:
if(num>=0)
{
if(num==1)
printf (“num es igual a 1” );
}
else
printf(“num es menor que 0” );
• Cuando necesitamos ejecutar varias sentencias que dependen de un if, utilizaremos los bloques de
sentencias. Un bloque de sentencias es un grupo de sentencias encerradas entre llaves { y }. Por ejemplo:
if (num 0){
printf(“num %d \n”);
if (num>=0)
printf(“num igual a 0” );
if (num>=1)
printf(“num mayor o igual a 1” );
}
Ejemplo
Determinar si un número es par o impar
main(){
int a,b;
printf("Ingrese el nro:\n");
scanf("%d",&a);
if((a%2)==0) //Condicion para que el numero sea par
printf("El numero es par\n");
else
printf("El numero es impar\n");
}
Obs: Los comentarios son partes del código ignorados por el compilador, sólo sirven para o rientar al
programador o a otra persona que lee el código. Puede señalarse mediante /* */ y // (este último sólo sirve
para una línea)
#include<stdio.h>
main(){
int nota, cal;
printf("Ingrese la nota:\n");
scanf("%d",¬a);
if(nota>=90) cal=5;
else if(nota>=80) cal=4;
else if(nota>=70) cal=3;
else if(nota>=60) cal=2;
else cal=1;
printf("\nLa calificacion es: %d\n",cal);
}
Estructuras repetitivas
Hasta ahora se ha trabajado con instrucciones de entrada, salida, expresiones y operadores; asignaciones,
instrucciones secuenciales y de selección. Hay una gran variedad de situaciones que requieren que una o varias
instrucciones se repitan varias veces, ya sean cálculos u otro tipo de instrucciones. Las estructuras repetitivas
abren la posibilidad de realizar una secuencia de instrucciones más de una vez.
Estructuras repetitivas
Mientras (while)
Repetir (do-while)
Desde (for)
Un bucle, es una sección de código que se repite. Es decir cuando se termina de ejecutar la última instrucción
del conjunto, el flujo de control retorna a la primera sentencia y comienza una nueva repetición de las
sentencias que forman esa sección de código. Se denomina iteración al hecho de repetir la ejecución de una
secuencia de acciones, la iteración se asocia a un número entero que indica el número de veces que se repite
un trozo de código.
El bucle while
while(expresión)
sentencia;
El bucle while comienza por evaluar la expresión. Si es cierta, se ejecuta la sentencia. Entonces se vuelve a
evaluar la expresión. De nuevo, si es verdadera, se vuelve a ejecutar la sentencia. Este proceso continúa hasta
que el resultado de evaluar la expresión es falso. Por esto se le llama a esta expresión la condición de salida.
Normalmente, en las sentencias del bucle while se coloca alguna instrucción que modifique la expresión de
control. Lo más habitual es utilizar un bloque de sentencias en vez de una sentencia única. Por ejemplo:
int variable=10;
while(variable>1){
printf(“la variable vale %d \n“, variable);
variable=variable-1;
printf(“valor tras decrementar la variable %d\n”, variable);
}
Ejemplo 1
Dado un número natural , desarrolle un algoritmo que calcule la sumatoria y el promedio de los números
menores a y que sean múltiplos de 3.
#include<stdio.h>
int main(){
int n,i=1,sum=0,cont=0;
float prom;
printf("\nIngrese el numero: ");
scanf("%d",&n);
while(i<n){
if((i%3)==0){sum+=i;cont++;}
i++;
}
if(cont){
prom=1.0*sum/cont;
printf("\nLa suma es: %d",sum);
printf("\nEl promedio es: %.3f",prom);
}
else{
printf("\nEl nro ingresado es menor que 4");
}
return 0;
}
Ejemplo 2
Desarrolle un algoritmo que permita determinar a partir de un número de días (ingresado por pantalla), los
años, meses, semanas y días que constituyen el número de días proporcionado.
#include<stdio.h>
int main(){
int dias,meses=0,anhos=0,semanas=0;
printf("Ingrese el numero de dias: ");
scanf("%d",&dias);
while(dias>=365){
dias-=365;
anhos++;
}
while(dias>=30){
dias-=30;
meses++;
}
while(dias>=7){
dias-=7;
semanas++;
}
printf("\nLa cantidad de anhos es: %d",anhos);
printf("\nLa cantidad de meses es: %d",meses);
printf("\nLa cantidad de semanas es: %d",semanas);
printf("\nLa cantidad de dias es: %d",dias);
return 0;
}
#include<stdio.h>
int main(){
int n;
printf("\nIngrese un numero positivo: ");
scanf("%d",&n);
while(n<=0){
printf("\nEl numero debe ser positivo, ingreselo de nuevo: ");
scanf("%d",&n);
}
return 0;
}
El bucle do-while
do
sentencia;
while(expresión);
Su funcionamiento es análogo el del bucle while, salvo que la expresión de control se evalúa al final del bucle.
Esto nos garantiza que el bucle do-while se ejecuta al menos una vez. Es menos habitual que el bucle
while.
Podemos incluir dentro del bucle un grupo de sentencias, en vez de sólo la sentencia. Por ejemplo:
int variable=10;
do{
printf(“numero actual %d \n”, variable);
variable=variable-1;
}while(variable>1);
Ejemplo 1 – Validación de datos
Ingresar un número positivo
#include<stdio.h>
int main(){
int n;
do{
printf("\nIngrese un numero positivo: ");
scanf("%d",&n);
}while(n<=0);
return 0;
}
Ejemplo 2
Calcular el factorial de un número .
#include<stdio.h>
int main(){
int n,fact=1,i=1;
printf("\nIngrese el nro n: ");
scanf("%d",&n);
do{
fact*=i;
i++;
}while(i<=n);
printf("\nEl factorial de %d es: %d",n,fact);
return 0;
}
El bucle for
Este bucle se utiliza para realizar una acción un número determinado de veces. Está compuesto de tres
expresiones: la de inicio, la de control y la de incremento, y de una sentencia.
Esta versión del bucle imprime un mensaje en la pantalla mientras que no se alcance la condición de salida,
i<10.
En un bucle for podemos omitir la expresión de inicio; por ejemplo, si sabemos que la variable ya está
inicializada:
int i=0;
for ( ; i<10; i++)
printf(“i vale %d \n”, i);
También podemos omitir la expresión de incremento. Esto es habitual cuando la variable de control ya se
modifica dentro del bucle. Por ejemplo:
int i=0;
for( ; i<10; )
printf(“i vale %d \n”,i);
inicio;
while(control)
sentencia;
incremento;
El bucle for se sigue el orden: evaluación de control, ejecución de sentencia y evaluación de incremento.
#include<stdio.h>
int main(){
int i;
printf("\nNumeros del 1 al 100");
for(i=1;i<101;i++){
if((i%3==0)&&(i%5==0)) printf("\nFizzBuzz");
else if(i%3==0) printf("\nFizz");
else if(i%5==0) printf("\nBuzz");
else printf("\n%d",i);
}
return 0;
}
Ejemplo
Calcular el producto de todos los números primos comprendidos entre 1 y .
#include<stdio.h>
int main(){
int i,k,n,primo,prod=1;
printf("\nIngrese el valor de n: ");
scanf("%d",&n);
for(i=1;i<=n;i++){
primo=1;
for(k=2;k<i;k++){
if(i%k==0){
primo=0;
k=i;
}
}
if(primo) prod*=i;
}
printf("\nEl producto de los numeros primos es: %d",prod);
return 0;
}
Uso de etiquetas.
Una etiqueta se define mediante su nombre (identificador) seguido del carácter dos puntos (:).
Uso de GOTO.
Es una sentencia de salto incondicional dentro del ámbito de una función. La sentencia goto nos permite dar
un salto a la parte del programa donde se encuentre la etiqueta respectiva. Un programa debidamente
estructurado debe evitar la utilización del goto, de hecho es la sentencia prohibida en la programación
estructurada.
La sentencia goto sirve para indicar al programa que continúe ejecutándose desde la línea de código indicada.
Su sintaxis es:
ETIQUETA:
Cuando se ejecute la sentencia goto, el programa saltará y continuará su ejecución a partir de la etiqueta
marcada.
Como se puede observar se puede usar para crear un bucle, salir de bucles anidados, o para ir a una parte del
código u otra si se combina con una sentencia if...else. Pero por lo general puede obtenerse el mismo
efecto utilizando los bucles anteriormente vistos.
Uso de BREAK.
En lenguaje C, para escribir una instrucción de salto break (interrumpir), se utiliza la sintaxis:
break;
La instrucción de salto break se usa para interrumpir (romper) la ejecución normal de un bucle, es decir, la
instrucción break finaliza (termina) la ejecución de un bucle y, por tanto, el control del programa se
transfiere (salta) a la primera instrucción después del bucle.
Uso de CONTINUE.
continue;
La instrucción de salto continue siempre se usa para interrumpir (romper) la ejecución normal de un bucle.
Sin embargo, el control del programa no se transfiere a la primera instrucción después del bucle (como sí hace
la instrucción break), es decir, el bucle no finaliza, sino que, finaliza la iteración en curso, transfiriéndose el
control del programa a la condición de salida del bucle, para decidir si se debe realizar una nueva iteración o
no.
Por tanto, la instrucción continue finaliza (termina) la ejecución de una iteración de un bucle, pero, no la
ejecución del bucle en sí. De forma que, la instrucción continue salta (no ejecuta) las instrucciones que
existan después de ella, en la iteración de un bucle.
Puede tener como argumento a 0, exit(0), que significa terminación normal, en caso de que tenga como
argumento a un número diferente de 0, se supone que puede acceder a analizar el error con ese argumento.
Bifurcación múltiple: SWITCH
Esta sentencia sirve para agrupar varias sentencias if en una sola, en el caso particular en el que una variable
es comparada a diferentes valores, todos ellos constantes, y que realiza acciones si coincide con ellos. Su
sintaxis es:
switch(control){
case expresion1_const:
sentencia1;
break;
case expresion2_const:
sentencia2;
break;
default:
sentencia0;
break;
}
Ejemplo:
#include<stdio.h>
int main(){
int num;
printf("\nIngrese un numero: ");
scanf("%d",&num);
switch(num){
case 1:
printf("Es un 1\n");
break;
case 2:
printf("Es un 2\n");
break;
case 3:
printf("Es un 3\n");
break;
default:
printf("No es ni 1, ni 2, ni 3\n");
}
return 0;
}
Lista de ejercicios sobre estructuras de decisión
1) Dado un número natural , desarrolle un algoritmo que calcule la sumatoria y el promedio de los
números menores a n y que sean múltiplos de 3.
2) Realizar la división entre 2 números naturales ( y ) por restas sucesivas, y mostrar el cociente y el
resto de la división.
3) Desarrolle un algoritmo que permita determinar a partir de un número de días (ingresado por teclado),
los años, meses, semanas y días que constituyen el número de días proporcionado. Se supone que un
año siempre tiene 360 días, y que un mes tiene 30 días.
4) Asegurándose de que el número ingresado sea un número natural, calcular el factorial de dicho
número.
5) Extraído de http://en.wikipedia.org/wiki/Bizz_buzz: Imprimir en pantalla los números comprendidos
entre 1 y 100. Pero para los múltiplos de 3, imprimir “Fizz” en lugar del número, mientras que para los
múltiplos de 5 se imprime “Buzz” en lugar del número. Si el número es múltiplo de 3 y de 5, mostrar
“Fizzbuzz” en lugar del número.
6) Calcular el producto de todos los números primos comprendidos entre 2 y .
7) Una empresa consta de empleados. A fin de mes, se debe hacer una liquidación de los sueldos de los
mismos. Para ello, se debe diseñar un algoritmo que (en cada paso) lea los sueldos brutos de los
empleados, y devuelva el sueldo neto aplicando un descuento de 9% para IPS y 16% para jubilación.
Por disposición de la gerencia, también se desea conocer la diferencia entre el mayor y el menor sueldo
neto.
8) Calcule e imprima:
9) Calcular el máximo común divisor (MCD) de dos números A y B (usando el algoritmo de Euclides).
10) Diseñe un programa que acepte un número y muestre los primeros elementos de la sucesión:
11) Dado un número , imprimir en pantalla sus divisores.
12) Realice un programa para encontrar los 100 primeros números perfectos. Un número es perfecto
cuando la suma de sus divisores (sin contar el mismo número) es igual al mismo número. Por ejemplo:
6 = 1+2+3.
13) Dado un número , imprimir una tabla de multiplicar de la siguiente manera:
* 1 2 …
1 1 2 …
*1
2 2 4 …
*2
… … … … …
1* 2* … *
Obs: Recordar que las tabulaciones se realizan con “ \t” y los saltos de línea con “ \n”.
14) Escribir un programa que, al ingresar un número entero (sea positivo o negativo), devuelva el número
en orden inverso. Ej: -375 -573
→
15) Dado un número entero con un número par de dígitos, escribir un programa que muestre el número
con cada par de dígitos intercambiado. Por ejemplo: si =654321 se debe mostrar 563412.
16) Dado un número , imprimir en pantalla sus factores primos de la siguiente manera (ejemplo, 150):
17) Calcular el enésimo término de la serie de Fibonacci ( ) definida por:
18) Dado un valor como entrada, calcular el menor valor de tal que se cumpla:
∑
19) Leer un número y diseñar un programa que calcule:
20) Una maestra desea obtener las estadísticas acerca del rendimiento de sus alumnos en la materia. Para
ello, se debe diseñar un algoritmo que lea las notas (números entre 1 y 100) y calcule:
La mejor nota
La peor nota
La media de las notas.
La cantidad de alumnos que aprueban y reprueban, considerando que se aprueba la materia
con una calificación de 60 o más.
El algoritmo finaliza cuando una nota ingresada sale fuera del rango.
21) Escribir un algoritmo que permita saber la cantidad de días entre 2 fechas cualesquiera.
22) Un comercio dispone de dos tipos de artículos en fichas correspondientes a diversas sucursales con los
siguientes campos:
Código del artículo (A o B)
Precio unitario del artículo
Número de artículos
La última ficha del archivo de artículos tiene un código de artículo, una letra X. Se pide:
El número de artículos existentes en cada categoría.
El importe total de los artículos de cada categoría.
23) Una estación climática proporciona un par de temperaturas diarias (máxima, mínima). Ninguna de las
temperaturas debería ser igual a 9 grados. El algoritmo finaliza cuando se ingrese 0,0. Se pide
determinar el número de días cuyas temperaturas se han proporcionado; las medias máxima y mínima,
el número de errores (temperaturas de 9 grados) y el porcentaje que representan en relación al total
de valores de temperaturas consideradas.
24) Se desea conocer una serie de datos de una empresa con 50 empleados, conociendo su edad y salario:
¿Cuántos ganan más de 5.000.000 Gs?
¿Cuántos ganan entre 2.500.000 y 5.000.000 Gs?
¿Cuántos ganan menos que 2.500.000?
¿Cuántos empleados mayores a 50 años están en la empresa y cuál es la media de sus salarios?
25) Existe un juego llamado “Adivina mi número”, el cual consiste en que un niño trata de adivinar el
número (entero, y entre 1 y 100) pensado por el otro niño. Las reglas del juego son las siguientes:
El niño pregunta al otro si un número que dice es el pensado por el segundo.
Si el número es correcto, el primer niño gana el juego. Si no lo acierta, el segundo niño debe
indicarle si el número es mayor o menor del que pensó. El primer niño tiene 10 oportunidades
para adivinar el número, y si no lo hace, el juego es ganado por el segundo.
El algoritmo debe indicar el ganador del juego.
Obs: en este problema, el segundo niño es la computadora (el número puede ser generado
aleatoriamente o predeterminado por el programador); mientras que el usuario es el primer niño.
Problema 3
#include<stdio.h>
char mayusculas(char c);
int main(){
char c;
printf("Ingrese el caracter: \n");
scanf("%c",&c);
c=mayusculas(c);
printf("El nuevo caracter es: %c",c);
return 0;
}
Problema 12
#include<stdio.h>
int main(){
int sel;
double a,b,c=0.0;
printf("Ingrese el nro a: ");
scanf("%lf",&a);
printf("\nIngrese el nro b: ");
scanf("%lf",&b);
printf("\nIngrese el codigo de seleccion: ");
scanf("%d",&sel);
if(sel==1) c=a+b;
else if(sel==2) c=a*b;
else if(sel==3) c=a/b;
else printf("\nNo existe operacion asignada a ese codigo.");
printf("\n\nEl resultado es: %lf",c);
return 0;
}
Problema 1
#include<stdio.h>
int main(){
int i,n,sum=0,con=0;
float prom;
printf("Ingrese el valor de n: ");
scanf("%d",&n);
for(i=1;i<=n;i++){
if((i%3)==0){
sum+=i;
con++;
}
}
if(con) prom=1.0*sum/con;
else prom=-1; //esto indica que no se puede hallar el promedio
printf("\nLa suma es: %d",sum);
printf("\nEl promedio es: %.2f\n",prom);
return 0;
}
Problema 2
#include<stdio.h>
int main(){
int a,b,c=0;
printf("Ingrese el valor de a: ");
scanf("%d",&a);
printf("\nIngrese el valor de b: ");
scanf("%d",&b);
while(a>b){
c++;
a-=b;
}
printf("\nEl cociente es: %d",c);
printf("\nEl resto es: %d",a);
return 0;
}
Problema 4
#include<stdio.h>
long long factorial(int n);
int main(){
double n;
long long m;
int c;
printf("\nIngrese el numero: ");
scanf("%lf",&n);
while(n!=((int) n)){
printf("\nIngrese un numero natural: ");
scanf("%lf",&n);
}
m=factorial(n);
printf("\nEl resultado es: %lld",m);
return 0;
}
Problema 5
#include<stdio.h>
int main(){
int i;
for(i=1;i<101;i++){
if((i%15)==0) printf("FizzBuzz\n");
else if((i%5)==0) printf("Buzz\n");
else if((i%3)==0) printf("Fizz\n");
else printf("%d\n",i);
}
return 0;
}
Problema 6
#include<stdio.h>
int es_primo(int x);
int main(){
int n,i;
long long prod=1;
printf("Ingrese el numero: ");
scanf("%d",&n);
prod=1;
for(i=2;i<=n;i++){
if(es_primo(i)) prod*=i;
}
printf("\nEl producto de los numeros primos es: %lld",prod);
return 0;
}
Estructuras de datos
Hasta ahora sólo hemos trabajado con variables simples, que únicamente pueden almacenar un dato (sea éste
numérico o caracter). En ejercicios anteriores, hemos trabajado con listas de números (calificaciones de
alumnos de una misma clase, trabajadores de una empresa, serie de números enteros, etc.); y su manejo con
variables simples puede ser un poco impráctico, además de no co nsiderar el almacenamiento de las entradas.
Para salvar estas situaciones, la mayoría de los lenguajes de programación incluyen características de
estructuras de datos. Una estructura de datos es una colección de datos que pueden ser caracterizados por su
organización y las operaciones que se definen en e lla.
Las estructuras de datos básicas que soportan la mayoría de los lenguajes son los arreglos (arrays) (siendo el
vector un arreglo de una dimensión, y la matriz uno de dos dimensiones). Se dividen en:
Un arreglo es una secuencia de posiciones de la memoria central a las que se puede acceder directamente, que
contiene datos del mismo tipo y pueden ser seleccionados individualmente mediante el uso de subíndices.
Podemos considerar al vector como un conjunto de variables simples con datos del mismo tipo.
El vector nota tiene subíndices o índices de sus elementos (0, 1, 2, …, , …, ) que indican la posición de un
elemento particular dentro del arreglo (de tamaño ). Por ejemplo, si se desea modificar el tercer elemento de
un vector de tipo numérico:
nota[2] = 80
int vector[tamaño];
Observaciones importantes: el primer elemento se representa por el índice 0. Si el tamaño del vector es ,
entonces el último elemento tiene un índice .
Declaración de vectores
tipo nombre_vector[tam];
Es muy similar a la declaración de las variables simples, pero el tamaño del vector va en corchetes luego del
nombre del mismo. El tamaño tam debe tener valores asignados previamente (en el caso de que sean
variables), o bien ser constantes.
int a[5]={1,2,3,4,5};
Ejemplo 1: realizar un programa que permita leer un vector de números enteros introducidos por teclado y
que calcule e imprima por pantalla la suma de todos sus elementos, el promedio, el mayor y el menor.
#include<stdio.h>
int main(){
int i,n,max,min,sum=0;
float p;
printf("Ingrese la cantidad de elementos del vector: ");
scanf("%d",&n);
int vec[n]; //Se dimensiona el vector
for(i=0;i<n;i++) scanf("%d",&vec[i]);
max=vec[0]; min=vec[0]; sum+=vec[0];
for(i=1;i<n;i++){
if(min>vec[i]) min=vec[i];
if(max<vec[i]) max=vec[i];
sum+=vec[i];
}
printf("\nEl menor es: %d",min);
printf("\nEl mayor es: %d",max);
printf("\nEl promedio es: %.2f",(1.0*sum/n));
return 0;
}
Cadenas en C
En C, no existe el tipo de dato “cadena” (ó string). Pero existe una convención en la forma de representar una
cadena, y es a través de un arreglo de caracteres. El fin de una cadena se indica con el carácter especial “\0”.
char a[7]={'h','e','l','l','o','\n','\0'};
char b[7]="hello\n";
La diferencia entre ambas es que gets() lee los espacios en blanco, mientras que scanf() no.
strcpy(cad1,cad2)
void copia(char cad1[], char cad2[]){
int i=0;
while(cad2[i]!='\0'){
cad1[i]=cad2[i];
i++;
}
cad1[i]='\0';
}
strcat(cad1,cad2)
void concatenar(char cad1[], char cad2[]){
int i=0;
while(cad1[i]!='\0') i++;
int j=0;
while(cad2[j]!='\0'){
cad1[i]=cad2[j];
i++;j++;
}
cad1[i]='\0';
}
strlen(cad1)
int longitud(char cad1[]){
int i=0;
while(cad1[i]!='\0') i++;
return i;
}
strcmp(cad1,cad2)
int comparar(char cad1[], char cad2[]){
int i=0,a;
while((cad1[i]!='\0')&&(cad1[i]==cad2[i])) i++;
a=cad1[i]-cad2[i];
return a;
}
int main(){
char s[30];
printf("Ingrese la cadena:\n");
gets(s);
int num=atoi(s);
printf("%d\n",num);
return 0;
}
int atoi(char s[])
{
int i=0, n=0, signo=1;
while(s[i]==' ') i++; /* se eliminan los espacios en blanco */
if ((s[i]=='+')||(s[i]=='-')){ /* se toma el signo */
if(s[i] == '-') signo=-1;
i++;
}
while(s[i]!='\0'){
n=10*n +(s[i]-'0');
i++;
}
return (signo*n);
}
Entero a cadena(itoa):
#include<stdio.h>
int itoa(char s[], int n);
int main(){
char s[30];
int num;
printf("Ingrese el numero: ");
scanf("%d",&num);
itoa(s,num);
puts(s);
return 0;
}
1) Calcular la cantidad de alumnos que obtuvieron nota inferior al promedio del curso en cierta materia. Hay
20 alumnos, y todos rindieron. Las notas van del 0 al 100 (se asume que todas las notas son correctas).
2) Se tienen las temperaturas (promedio) de todos los días del mes de febrero de 2012 almacenados en el
vector Temp (luego de cargar por teclado). Diseñar un algoritmo que obtenga las temperaturas máxima,
mínima (e indica los días correspondientes), y el promedio de las que se encuentran entre los días 21 y 27.
3) Diseñar un algoritmo que obtenga el producto escalar de dos vectores (de enteros) de tamaño .
4) Se tiene un vector de números binarios de tamaño (siendo el mismo un múltiplo de 3). Un ejemplo es el
siguiente:
1 0 1 0 0 0 1 0 0
Diseñar un algoritmo que cree nuevo vector a partir del vector de entrada, donde después de cada 3
elementos del vector original, se agregue un elemento que indique la cantidad de 1’s en esos tres
elementos. En nuestro caso, la salida sería:
1 0 1 2 0 0 0 0 1 0 0 1
5) Escribir un algoritmo que muestre la cantidad de números positivos, negativos y los ceros de un vector de
100 elementos.
6) Calcular la suma y promedio de los valores positivos de un vector de 50 elementos.
7) Se dispone de un vector T de n elementos distintos de cero. Crear un nuevo vector en lo que todos sus
elementos resulten de dividir los elementos del vector T por el elemento T[k], siendo k un valor dado.
8) Dado un número x, verificar que exista ese valor dentro del vector A e indicar la posición del elemento. Si
no se encuentra, mostrar un mensaje de notificación.
9) Se tienen dos vectores A y B, donde se indican los promedios de los parciales y notas de los finales de 30
alumnos. Según la siguiente fórmula:
10) Considere los 100 primeros números naturales. El programa debe devolver una matriz lógica (0=falso,
1=verdadero) donde se indique si el elemento i es o no primo.
11) Se cuenta con una lista L1 de números enteros en el cual existen numerosos valores re petidos.
A fin de economizar el espacio de almacenamiento, se desea crear una nueva lista L2 en la cual cada valor
diferente aparece una sola vez, sin repetición, pero indicando la cantidad de veces que se repite dicho valor en
la lista L1.
Ejemplo:
Lista original L1 (dato para el algoritmo)
23 27 8 14 23 23 8 23 27 23 27 8 27
8 3 14 1 23 5 27 4
La interpretación de la lista L2 es como sigue: el valor 8 aparece 3 veces en L1, el valor 14 aparece 1 vez en L1,
el valor 23 aparece 5 veces en L1, y el valor 27 aparece 4 veces en L1.
int main(){
int i,sum=0,nota[20],con=0;
float prom;
for(i=0;i<20;i++){
printf("Ingrese la nota del alumno %d: ",(i+1));
scanf("%d",¬a[i]);
sum+=nota[i];
}
prom=1.0*sum/20;
for(i=0;i<20;i++){
if(nota[i]<prom) con++;
}
printf("\nLa cantidad de alumnos con nota inferior al promedio es: %d",con);
return 0;
}
Ejercicio 3
#include<stdio.h>
int main(){
int n,i;
//Lectura de datos
printf("Ingrese el tamanho de los vectores: ");
scanf("%d",&n);
int A[n],B[n];
printf("\nIngrese los elementos del vector A:\n");
for(i=0;i<n;i++){
printf("A[%d]: ",i);
scanf("%d",&A[i]);
}
printf("\nIngrese los elementos del vector B:\n");
for(i=0;i<n;i++){
printf("B[%d]: ",i);
scanf("%d",&B[i]);
}
//Proceso
int prod=0;
for(i=0;i<n;i++) prod+=(A[i]*B[i]);
//Impresion del resultado
printf("\nEl producto escalar es: %d\n",prod);
return 0;
}
Ejercicio 10
#include<stdio.h>
int main(){
int i,j,primo[101];
for(i=0;i<=100;i++) primo[i]=1;
//Se usará el metodo de Eratóstenes
for(i=2;(i*i)<=100;i++){
if(primo[i]){
for(j=(i*i);j<=100;j+=i) primo[j]=0;
}
}
//impresion de resultados
printf("\nLa tabla de numeros primos del 1 al 100 es:\n");
for(i=0;i<10;i++){
for(j=0;j<10;j++){
printf("%d ",primo[i*10+j+1]);
}
printf("\n");
}
return 0;
}
Ejercicio 11
#include<stdio.h>
int main(){
int i,n,aux,j;
printf("Ingrese el tamanho del vector: ");
scanf("%d",&n);
int vec[n];
printf("Ingrese los elementos del vector:\n");
for(i=0;i<n;i++){
printf("vec[%d]: ",i);
scanf("%d",&vec[i]);
}
//Ordenamiento del vector
int band=1;j=1;
while(band){
band=0;
for(i=0;i<(n-j);i++){
if(vec[i]>vec[i+1]){
band=1;
aux=vec[i];
vec[i]=vec[i+1];
vec[i+1]=aux;
}
}
j++;
}
//Proceso
int sol[(2*n)];
int cant=1,pos=0;
int act=vec[0];
for(i=1;i<n;i++){
if(vec[i]==act) cant++;
else{
sol[pos]=act;
sol[pos+1]=cant;
pos+=2;
act=vec[i];
cant=1;
}
}
sol[pos]=act;
sol[pos+1]=cant;
pos+=2;
printf("\nImpresion del resultado:\n");
for(i=0;i<pos;i++) printf("%d\t",sol[i]);
return 0;
}
Matrices y arreglos multidimensionales
Hasta el momento, hemos considerado sólo los arreglos unidimensionales (vectores), y en ellos cada elemento
se define o referencia por un índice o subíndice. Estos vectores son elementos de datos escritos en una
secuencia.
Sin embargo, existen grupos de datos que son mejor representados en forma de tabla o matriz con dos o más
subíndices. Por ejemplo: tablas kilométricas entre ciudades, informes de ventas periódicas, etc.
Se pueden definir tablas o matrices como arreglos multidimensionales, cuyos elementos se pueden
referenciar por dos, tres o más subíndices. El término matriz se asocia generalmente a un arreglo
bidimensional. Como en el caso del vector o arreglo unidimensional, sus elementos son del mismo tipo (int,
char, float, etc).
Matrices
La matriz puede verse como un vector de vectores. Por ello, se necesita especificar dos subíndices para poder
identificar cada uno de sus elementos.
Columna
Nombre de la matriz
Fila
tipo nombre_matriz[cant_filas][cant_columnas];
Se deben tener los mismos cuidados que los mencionados para el caso de los vectores en relación al
dimensionamiento de la matriz (c antidad de filas y columnas).
int matrix[3][6] = {{16, 21, 8, 3, -7, 9},{-3, 11, 0, 5, 9, 7},{13, 7, -64, 19, 14, 2}}
Las dimensiones de los arreglos van aumentando a medida que se incluyen más corchetes en la declaración de
los arreglos.
Por ejemplo:
int M3[3][5][2] //define un arreglo tridimensional
Matriz de tres
dimensiones
Lectura de datos de una matriz
for(i=0;i<cant_filas;i++){
for(j=0;j<cant_columnas;j++){
scanf("%d",&A[i][j]);
}
}
for(i=0;i<cant_filas;i++){
for(j=0;j<cant_columnas;j++){
printf("%d\t",A[i][j]);
}
printf("\n");
}
Observación: un vector de cadenas puede verse como una matriz de caracteres, donde se tiene una palabra en
cada fila.
Ejemplo
Realizar un programa que permita leer una matriz de FxC elementos, de números enteros introducidos por
teclado. Calcular e imprimir el siguiente mensaje dependiendo de la suma de los elementos: “Filas mágicas”, si
todas las sumas de las filas dan el mismo resultado y “Columnas mágicas” si todas las sumas de las columnas
dan el mismo resultado.
#include<stdio.h>
int main(){
int i,j,M,N,suma_ref_fil=0, suma_ref_col=0;
printf("Ingrese el numero de filas: ");scanf("%d",&M);
printf("Ingrese el numero de columnas: ");scanf("%d",&N);
int A[M][N];
for(i=0;i<M;i++){
for(j=0;j<N;j++){
printf("Ingrese A[%d][%d]: ",i,j);
scanf("%d",&A[i][j]);
}
}
for(i=0;i<N;i++) suma_ref_fil+=A[0][i];
for(i=0;i<M;i++) suma_ref_col+=A[i][0];
//Se revisan las filas
int fil=1,sum; //bandera que indica si es o no magica en cuanto a filas
for(i=1;i<M;i++){
sum=0;
for(j=0;j<N;j++) sum+=A[i][j];
if(sum!=suma_ref_fil){fil=0;break;}
}
int col=1; //bandera que indica si es o no magica en cuanto a columnas
for(i=1;i<N;i++){
sum=0;
for(j=0;j<M;j++) sum+=A[j][i];
if(sum!=suma_ref_col){col=0;break;}
}
if(fil) printf("\nFilas magicas!!");
else printf("\nLas filas no son magicas!!");
if(col) printf("\nColumnas magicas!!");
else printf("\nLas columnas no son magicas!!");
}
Lista de ejercicios sobre matrices
1) Dada una matriz A de tamaño m*n, obtener un vector B que contenga los me nores elementos de cada fila.
2) Multiplicar (si es posible) dos matrices A y B de tamaños m*n y p*q respectivamente (siendo estos últimos
números naturales). Guardar el resultado en una matriz C e imprimirla en pantalla.
3) Escribir un algoritmo que indique si una matriz es o no simétrica (condición de simetría: mat[i][j]=mat[j][i]).
4) Dadas la cantidad de filas y columnas maximas, generar una matriz "car acol"
5) Para transmitir mensajes de texto de hasta 100 caracteres de longitud se ha propuesto el siguiente método
de codificación:
a) Se genera una clave de 10 dígitos distintos (entre 0 y 9) (asumir que esa clave se introduce por
teclado y es válida).
b) Si el texto a codificar tiene menos de 100 caracteres, se completa al final con tantos asteriscos
como sea necesario para completar los 100 caracteres.
c) El texto resultante se coloca en una matriz de 10 filas y 10 columnas, de modo que cada carácter
ocupe un elemento de la matriz, fila por fila, de izquierda a derecha y de arriba hacia abajo.
d) Se toma el primer dígito de la clave y la columna correspondiente a ese dígito se pasa al texto
codificado. Se toma el segundo dígito de la clave y la columna correspondiente a ese dígito se
agrega al final del texto codificado. Se repite este procedimiento hasta utilizar todos los dígitos de
la clave.
e) Se agrega al final del texto codificado la clave utilizada para su codificación.
f) El resultado final es un mensaje codificado de 110 caracteres de longitud.
Se requiere:
a) Escribir un algoritmo para codificar un mensaje utilizando este método
b) Escribir otro algoritmo para decodificar un mensaje codificado con este método
Ejemplo
Mensaje original: “LA CRIPTOGRAFIA ES LA CIENCIA DE CIFRAR Y DESCIFRAR MENSAJES USANDO TECNICAS
MATEMATICAS”
Clave generada: 8204975613
La matriz es:
L A C R I P T O G
R A F I A E S L
A C I E N C I A
D E C I F R A R
Y D E S C I F R A
R M E N S A J E S
U S A N D O T E
C N I C A S M A T
E M A T I C A S * *
* * * * * * * * * *
6) Una empresa tiene cuatro vendedores (1 a 4), los cuales venden 5 productos distintos (1 a 5). Una vez al
día, cada vendedor introduce un registro para cada tipo de producto vendido. Cada registro contiene lo
siguiente:
a. El número de vendedor.
b. El número de producto.
c. El número total del producto vendido del día.
Suponga que están disponibles los registros del último mes (para nuestro ejercicio, suponemos que se
ingresan por teclado). Escriba un programa que lea toda la información hasta que se ingrese un número de
vendedor no válido, y sume el total de las ventas por vendedor y por producto. Todos los totales se deben
almacenar en la matriz VENTAS. Una vez procesada toda la información del último mes, despliegue los
resultados en forma tabular; en donde cada una de las columnas representa a un vendedor y cada una de
las filas representa un producto en particular. Obtenga la suma de cada fila para el total de ventas de cada
producto del último mes; obtenga la suma de cada columna para el total de ventas por vendedor del
último mes. Su salida tabular debe incluir esos totales a la derecha para las filas y en el fondo para las
columnas.
7) Teniendo un sistema de ecuaciones representado en una matriz, obtener la solución al sistema (suponer
que siempre hay una solución y es única) mediante el método de eliminación de Gauss:
d. Ir a la columna no cero extrema izquierda.
e. Si el primer renglón tiene un cero en esta columna, intercambiarlo con ot ro que no lo tenga.
f. Luego, obtener ceros debajo de este elemento delantero, sumando múltiplos adecuados del
renglón superior a los renglones debajo de él.
g. Cubrir el renglón superior y repetir el proceso anterior con la submatriz restante. Repetir con el
resto de los renglones (en este punto la matriz se encuentra en la forma de escalón).
h. Comenzando con el último renglón no cero, avanzar hacia arriba: para cada renglón obtener un 1
delantero e introducir ceros arriba de éste sumando múltiplos correspondientes a los renglones
correspondientes.
E S T A \0
E S \0
U N A \0 \0
P R U E B A \0
9) Teniendo como entrada una matriz de filas y columnas, escriba un programa que solicite los
elementos de esta matriz y luego la procese para ordenar sus valores según la regla que se muestra en el
siguiente ejemplo:
Entrada:
34 23 63 27 72
56 8 33 42 11
78 21 86 6 29
22 75 10 30 13
54 77 36 74 55
28 22 56 41 1
Salida:
1 6 8 10 11
23 22 22 21 13
27 28 29 30 33
54 42 41 36 34
55 56 56 63 72
86 78 77 75 74
10) Un mensaje de texto ha sido codificado cambiando cada letra por la siguiente en el abecedario (en el
caso de tener la última letra del abecedario, cambiar por la primera) y modificando el orden de las mismas
en el texto en función a una clave ingresada por teclado. La longitud máxima de una palabra es de 30 y la
cantidad máxima de palabras es 10. Al final de cada palabra se agrega un número que indica cuál es su
posición en el mensaje.
Caso de prueba:
Mensaje original: “este es el examen de programacion”
Clave: 512403
Mensaje codificado: “qsphsbnbdjpo5ft1fm2ef4ftuf0fybnfo3”
#include<stdio.h>
int main(){
int m,n,p,q,i,j,k;
printf("Ingrese la cantidad de filas y columnas de la matriz A:");
printf("\nFilas: "); scanf("%d",&m);
printf("Columnas: "); scanf("%d",&n);
printf("\n\nIngrese la cantidad de filas y columnas de la matriz B:");
printf("\nFilas: "); scanf("%d",&p);
printf("Columnas: "); scanf("%d",&q);
if(n!=p){printf("\n\nLas matrices no pueden multiplicarse\n"); return 0;}
int A[m][n],B[p][q],C[m][q];
//Lectura de datos
printf("\n\nIngrese los datos de la matriz A:\n");
for(i=0;i<m;i++){
for(j=0;j<n;j++){
printf("A[%d][%d]: ",i,j); scanf("%d",&A[i][j]);
}
}
printf("\n\nIngrese los datos de la matriz B:\n");
for(i=0;i<p;i++){
for(j=0;j<q;j++){
printf("B[%d][%d]: ",i,j); scanf("%d",&B[i][j]);
}
}
//Proceso
for(i=0;i<m;i++){
for(j=0;j<q;j++){
C[i][j]=0;
for(k=0;k<n;k++){
C[i][j]=C[i][j]+A[i][k]*B[k][j];
}
}
}
//Impresion de resultados
printf("\n\nMatriz C:\n");
for(i=0;i<m;i++){
for(j=0;j<q;j++){
printf("%d\t",C[i][j]);
}
printf("\n");
}
return 0;
}
Ejercicio 4
#include<stdio.h>
int main(){
int m,n,i,j,k,fil,col,filmin,filmax,colmin,colmax,dir,cont=0;
printf("Ingrese la cantidad de filas y columnas de la matriz C:");
printf("\nFilas: "); scanf("%d",&m);
printf("Columnas: "); scanf("%d",&n);
int C[m][n];
dir=1; filmin=0; filmax=m-1; colmin=0; colmax=n-1; fil=0; col=0;
for(k=1;k<=(m*n);k++){
C[fil][col]=k;
if(dir==1){
col++;
if(col==colmax){
dir=2;
filmin++;
}
}
else if(dir==2){
fil++;
if(fil==filmax){
dir=3;
colmax--;
}
}
else if(dir==3){
col--;
if(col==colmin){
dir=4;
filmax--;
}
}
else{
fil--;
if(fil==filmin){
dir=1;
colmin++;
}
}
}
printf("\n\nMatriz C:\n");
for(i=0;i<m;i++){
for(j=0;j<n;j++){
printf("%d\t",C[i][j]);
}
printf("\n");
}
return 0;
}
Ejercicio 8
#include<stdio.h>
int main(){
int i,n;
printf("Ingrese la cantidad de palabras: "); scanf("%d",&n);
char pal[n][21],cad[21];
printf("\nIngrese las palabras:\n");
for(i=0;i<n;i++){
printf("Palabra %d: ",(i+1));
scanf("%s",pal[i]);
}
//Proceso
int band=1,j=1,a;
while(band){
band=0;
for(i=0;i<(n-j);i++){
a=comparar(pal[i],pal[i+1]);
if(a>0){
band=1;
copia(cad,pal[i]);
copia(pal[i],pal[i+1]);
copia(pal[i+1],cad);
}
}
j++;
}
printf("\nPalabras ordenadas:\n");
for(i=0;i<n;i++){
puts(pal[i]);
}
return 0;
}
Apuntes de la clase de PED 2 Fecha: 8 de marzo de 2013
MSc. José Colbes
Recursiones o Recurrencias
Fuentes:
Libro de Joyanes Aguilar – Capítulo 14: Recursividad
Wikipedia: Recursión (ciencias de la computación), algoritmo recursivo, recursión
( )
Es decir, puede conocerse solucionando el mismo problema, pero para un caso más pequeño ( ). Este
proceso podemos continuar hasta llegar al caso base de . Podemos observar como un problema se
divide en varias (una o más) instancias del mismo problema, pero de tamaño menor gracias a lo cual se puede
aplicar inducción, llegando a un punto donde se conoce el resultado (el caso base).
Recursión es, en ciencias de computación, una forma de atacar y solucionar problemas. De hecho, la recursión
es una de las ideas centrales en ciencias de la computación. Resolver un problema mediante recursión significa
que la solución depende de las soluciones de casos más pequeños del mismo problema.
Dato interesante: La mayoría de los lenguajes de programación dan soporte a la recursión permitiendo a una
función llamarse a sí misma desde el texto del programa. Los lenguajes imperativos definen las estructuras de
loops como while y for que son usadas para realizar tareas repetitivas. Algunos lenguajes de programación
funcionales no definen estructuras de loops sino que posibilitan la recursión llamando código de forma
repetitiva. La teoría de la computabilidad ha demostrado que estos dos tipos de lenguajes son
matemáticamente equivalentes, es decir que pueden resolver los mismos tipos de problemas, aunque los
lenguajes funcionales carezcan de las típicas estructuras while y for.
Por ejemplo, se presentan dos soluciones para hallar el factor ial de un número n:
Método 1: Iteración
#include<stdio.h>
int main(){
int n,i;
long long facto=1;
printf("Ingrese un numero: ");
scanf("%d",&n);
for(i=1;i<=n;i++) facto*=i;
printf("\nEl factorial de %d es: %lld",n,facto);
}
Método 2: Recursión
#include<stdio.h>
long long factorial(int n){
long long facto;
if(n==0) return 1;
facto=n*factorial(n-1); //Aqui esta la recursion
return facto;
}
int main(){
int n;
printf("Ingrese un numero: ");
scanf("%d",&n);
printf("\nEl factorial de %d es: %lld",n,factorial(n));
return 0;
}
Un algoritmo recursivo es un algoritmo que expresa la solución de un problema en términos de una llamada a
sí mismo. La llamada a sí mismo se conoce como llamada recursiva o recurrente.
Los programas examinados hasta ahora, generalmente estructurados, se componen de una serie de funciones
que llaman unas a otras de un modo “disciplinado”. En algunos problemas es útil disponer de funciones que se
llamen a sí mismas. Una función o subprograma recursivo es un subprograma que se llama a sí mismo ya sea
directa o indirectamente.
Se puede utilizar la recursividad como una alternativa a la iteración. De hecho, ciertas soluciones salen de
manera más “natural” ( por las características de los problemas; como ejemplos se tienen muchos problemas
matemáticos) utilizando la recursividad. Por esta causa, la re cursión es una herramienta poderosa e importante
en la resolución de problemas y en la programación.
Una solución recursiva es normalmente menos eficiente, en términos de tiempo de computadora, que una
solución iterativa; esto es debido a las operaciones auxiliares que llevan consigo las llamadas suplementarias a
las funciones. Además, se debe tener en cuenta el espacio de memoria para cada uno de los procesos que
quedan pendientes de resultado durante la ejecución del pro grama recursivo.
tipo_dato_retorno funcion_recursiva(argumentos){
…
funcion_recursiva(argumentos)
…
}
Ejemplo 1: A través de una función recursiva, calcular , donde es un número entero y uno natural.
#include<stdio.h>
long long potencia(int a,int b){
if(b==0) return 1;
return (a*potencia(a,(b-1)));
}
int main(){
int a,b;
printf("Ingrese la base: "); scanf("%d",&a);
printf("Ingrese el exponente: "); scanf("%d",&b);
printf("\nEl valor de %d^%d es: %lld",a,b,potencia(a,b));
return 0;
}
#include<stdio.h>
int sumas(int i){
if(i==0) return 0;
return (i+sumas(i-1));
}
int main(){
printf("\nEl valor de la suma del 1 al 100 es: %d", sumas(100));
return 0;
}
Ejemplo 3: Calcular el enésimo término de la sucesión de Fibonacci ( () ( ) ( )),
sabiendo que los dos primeros términos son 0 y 1.
#include<stdio.h>
int fibo(int i){
if(i==1) return 0;
if(i==2) return 1;
return (fibo(i-1)+fibo(i-2));
}
int main(){
int n;
printf("Ingrese el numero del termino cuyo valor se desea conocer: ");
scanf("%d",&n);
printf("\nEl valor del termino %d de la serie de Fibonacci es: %d",n,fibo(n));
return 0;
}
Recursividad directa e indirecta
Si una función se invoca a sí misma, el proceso se denomina recursión directa (los ejercicios vistos hasta aquí
son de este tipo). Si una función puede invocar a una segunda función que a su vez invoca a la primera, este
proceso se conoce como recursión indirecta o mutua.
#include<stdio.h>
void A(char);
void B(char);
int main(){
printf("El alfabeto es:\n");
A('Z');
return 0;
}
void A(char c){
if(c>'A') B(c);
putchar(c);
}
void B(char c){
A(--c);
}
Explicación: el programa principal llama a la función recursiva () con el argumento (la última letra del
alfabeto), y esta función examina su parámetro . Si está después que (en orden alfabético), la función
llama a (), que inmediatamente llama a (), pasándole un parámetro predecesor de . Esta acción hace que
() vuelva a examinar , y nuevamente llame a (), hasta que sea igual a . En este momento, la recursión
termina ejecutando putchar() 26 veces; y con esto se visualiza todo el alfabeto.
Recursión vs Iteración
Todos los ejercicios anteriores se pueden implementar de modo iterativo o de modo recursivo. A continuación
se compararán ambos enfoques, de manera a que el programador pueda elegir un enfoque u otro de acuerdo a
una determinada situación.
Tanto la iteración como la recursión se basan en una estructura de control: la iteración utiliza una estructura
repetitiva y la recursión utiliza una estructura de selección. La iteración y la recursión implican repetición: la
iteración utiliza explícitamente una estructura repetitiva, mientras que la recursión consigue la repetición
mediante llamadas repetidas. Ambas implican cada una condición de salida (test de terminación). La iteración
termina cuando la condición del bucle no se cumple, mientras que la recursión termina cuando se reconoce un
caso base o la condición de salida se alcanza.
Esta característica puede resultar cara en tiempo de procesador y espacio de memoria. Cada llamada de una
función recursiva produce que otra copia de la función (realmente sólo las variables de la función) sea creada;
esto puede consumir memoria considerable. Por el contrario, la iteración se produce dentro de una función, de
modo que las operaciones suplementarias de las llamadas a la función y asignación de memoria adicional son
omitidas.
En consecuencia, ¿cuáles son las razones para elegir la recursión? La razón fundamental es que existen
numerosos problemas complejos que poseen naturaleza recursiva y por ello son más fáciles de solucionar con
algoritmos de este tipo. Sin embargo, en condiciones típicas de tiempo y memoria, es decir, cuando el
consumo de tiempo y memoria sean decisivos o concluyentes para la resolución del problema, la solución a
elegir generalmente es la iterativa.
Resolución de ejercicios un poco más complejos con recursividad
Problema 1: Torres de Hanoi
El juego, en su forma más tradicional, consiste en tres varillas verticales. En una de las varillas se apila un
número indeterminado de discos (elaborados de madera) que determinará la complejidad de la solución, por
regla general se consideran ocho discos (pero pueden ser de cantidad ). Los discos se apilan sobre una varilla
en tamaño decreciente. No hay dos discos iguales, y todos ellos están apilados de mayor a menor radio en una
de las varillas, quedando las otras dos varillas vacantes. El juego consiste en pasar todos los discos de la varilla
ocupada (es decir la que posee la torre) a una de las otras varillas vacantes. Para realizar este objetivo, es
necesario seguir tres simples reglas:
Existen diversas formas de realizar la solución final, todas ellas siguiendo estrategias diversas. La que
emplearemos es la de recursión, y el programa se muestra a continuación:
#include<stdio.h>
void Hanoi(int inicio, int fin, int centro, int n){
if(n==1){
printf("\nMover disco 1 de la varilla %d a la varilla %d.",inicio,fin);
}
else{
Hanoi(inicio, centro, fin, (n-1));
printf("\nMover disco %d de la varilla %d a la varilla %d.",n,inicio,fin);
Hanoi(centro, fin, inicio, (n-1));
}
}
int main(){
int n;
printf("Ingrese el valor de n: ");
scanf("%d",&n);
Hanoi(1,3,2,n);// 1 es la varilla inicial, 2 la del centro y 3 la final.
return 0;
}
Pedlandia tiene un sistema monetario bastante particular. Cada moneda tiene escrito un número entero que
indica la su valor en ped-dólares. Los bancos (aquí viene lo peculiar) permiten cambiar una moneda por otras
tres de valores , y ; redondeadas hacia abajo.
Además, se tiene que un ped-dólar equivale a un dólar americano. Entonces, teniendo una moneda de un
cierto valor x, ¿cuál es la mayor cantidad de dólares americanos que pueden obtenerse de él?
#include<stdio.h>
int cantidad(int);
int main(){
int n,m;
printf("\nIngrese el valor de la moneda: ");
scanf("%d",&n);
m=cantidad(n);
printf("\nLa cantidad de dolares es: %d",m);
return 0;
}
int cantidad(int x){
if(x==0) return 0;
int a,b,c,y;
a=x/2;
b=x/3;
c=x/4;
y=cantidad(a)+cantidad(b)+cantidad(c);
if(x>y) return x;
else return y;
}
Problema 3: Búsqueda binaria
En este problema, se tiene una lista de números (vamos a suponer que son distintos) ordenados de manera
ascendente. Se ingresa un número cualquiera (lo llamaremos clave) y se desea buscarlo dentro de la lista. Si
está, se debe indicar su posición en el arreglo; si no lo está, se indica con un mensaje.
Se puede realizar una búsqueda secuencial, pero el hecho de que los números estén ordenados nos permite
realizarla de manera más eficiente. Considerando los límites del arreglo, se considera el elemento central. Este
elemento se compara con la clave: si es igual a la clave, se termina y retorna la posición; si es mayor que la
clave, esta última podría encontrarse en el subarreglo izquierdo; si es menor, la clave podría encontrarse en el
subarreglo derecho. Este proceso se realiza e n forma recursiva.
Se tiene una pista rectangular con azulejos cuadrados. En cada uno de estos azulejos, se encuentra
una cierta cantidad de dólares (la pista se representa entonces como una matriz numérica, donde en cada
elemento se indica la cantidad de dólares correspondiente al azulejo en esa posición). El juego consiste en que
el participante obtenga la mayor cantidad de dólares posibles del suelo, siguiendo estas reglas:
Se empieza eligiendo algún azulejo de la primera fila, y recogiendo los dólares en ella. Luego, se mueve
a un azulejo de la siguiente fila, recoge los dólares en él, y así sucesivamente hasta llegar a la última
fila.
Cuando se mueve de un azulejo a otro de la siguiente fila, sólo puede moverse a un azulejo que se
encuentre directamente por debajo de él, o diagonalmente a la izquierda o derecha.
Dados , y la matriz numérica; escribir un programa que calcule la cantidad máxima de dólares que se
puede ganar en un único viaje de la primera a la última fila.
#include<stdio.h>
int mat[200][200],fil,col;
maximo(int a, int b, int c){
if(a>=b){
if(a>=c) return a;
else return c;
}
else{
if(b>=c) return b;
else return c;
}
}
int recursion(int x, int y){
int a,b,c;
if(x==0) return mat[x][y];
b=recursion((x-1),y);
if(y==0){
a=-1;
c=recursion((x-1),(y+1));
}
else if(y==(col-1)){
a=recursion((x-1),(y-1));
c=-1;
}
else{
a=recursion((x-1),(y-1));
c=recursion((x-1),(y+1));
}
int d=maximo(a,b,c)+mat[x][y];
return d;
}
int main(){
int i,j,d;
printf("Ingrese la cantidad de filas: ");scanf("%d",&fil);
printf("Ingrese la cantidad de columnas: ");scanf("%d",&col);
printf("\nLa matriz es:\n");
for(i=0;i<fil;i++){
for(j=0;j<col;j++){
scanf("%d",&mat[i][j]);
printf("%d\t",mat[i][j]);
}
printf("\n");
}
int maxi=recursion((fil-1),0);
for(j=1;j<col;j++){
d=recursion((fil-1),j);
if(d>maxi) maxi=d;
}
printf("\nEl resultado es: %d",maxi);
return 0;
}
Apuntes de la clase de PED 2 Fecha: 15 de marzo de 2013
MSc. José Colbes
Básicamente, el puntero es una variable cuyo contenido es la dirección de otra variable. Los punteros son muy
usados en C, y son una de sus características principales.
Una máquina típica tiene un arreglo de celdas de memoria numerados o direccionados de forma consecutiva
que pueden ser manipuladas de forma individual o en grupos contiguos. Una situación común es que cualquier
byte puede ser un char, un par de celdas de un byte puede ser tratado como un short int, y cuatro bytes
adyacentes forman un long. Un puntero es un grupo de celdas (a menudo dos o cuatro) que puede contener
una dirección. Así que si es un char y es un puntero que apunta a la misma, se podría representar la
situación de la siguiente manera:
Puntero Variable
El operador & da la dirección de un objeto (variable), entonces la sentencia:
asigna al puntero la dirección de la variable . Entonces se dice que “ apunta a ”. El operador “&” sólo se
aplica a elementos almacenados en memoria (como variables y arreglos).
int x = 1, y = 2, z[10];
int *ip; /* ip es un puntero a un int */
ip = &x; /* ip apunta a la variable x */
y = *ip; /* ahora y es 1 */
*ip = 0; /* ahora x es 0 */
ip = &z[0]; /* ahora ip apunta a z[0] */
Observación: un puntero está restringido a apuntar a objetos de un tipo en específico (por eso se definen como
un tipo de dato).
Si , entonces las siguientes operaciones son válidas (para modificar el valor de o para hacer otras
operaciones):
Como los punteros son variables, se pueden hacer operaciones sin “desreferenciar”. Por ejemplo: iq = ip
(con esto, e apuntan al mismo objeto).
x px
En la función En la función
que llama swap
y py
Punteros y vectores
En C, existe una fuerte relación entre los punteros y los vectores (en general, los arreglos); y la mayoría de las
veces pueden usarse indistintamente (en realidad los nombres de los vectores son punteros constantes que
apuntan al primer elemento del vector).
La notación se refiere al elemento del vector. Si es un puntero a int, entonces:
int *pa;
pa = &a[0];
hace que apunte el elemento cero de . Es decir, contiene la dirección de .
Si se supone que p=vect, la relación entre punteros y vectores puede resumirse como se indica en las líneas
siguientes:
*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]
Observación: Existe una diferencia entre el nombre de un vector y un puntero: un puntero es una variable, por
lo que pa=a and pa++ son operaciones legales. Pero el nombre de un vector (y en general, el de un arreglo)
es una constante, por lo que a=pa and a++ son operaciones ilegales.
Vectores y funciones
Cuando el nombre de un vector se pasa a una función, lo que se pasa es la ubicación del elemento inicial.
Dentro de la función llamada, este argumento es una variable local, y por lo tanto el nombre de un arreglo
como parámetro es un puntero; esto es, una variable que contiene una dirección (paso por referencia). Un
ejemplo sería la función strlen:
#include<stdio.h>
int strlen(char []);
int strlen2(char *);
int main(){
char cad[30];
gets(cad);
int a;
a=strlen(cad);
//a=strlen2(cad);
printf("%d\n",a);
return 0;
}
funcionan correctamente.
Observación importante: la instrucción char cad[30];ya reserva un espacio en memoria para ese vector.
Si sólo definimos un puntero de tipo char, no se reserva memoria. La asignación dinámica de memoria
(asociada a punteros) se verá más adelante.
Una constante “cadena”, escrita como: “Hola”, es una arreglo de caracteres. Estas constantes pueden usarse
como parámetros de funciones. Una forma común de usarlas es a través de la función printf:
printf("Hola mundo\n");
Cuando una constante cadena aparece en un programa, lo hace a través de un puntero a carácter; y printf
recibe un puntero que apunta al inicio del arreglo de caracteres. Estas constantes no solo se restringen a
argumentos de funciones. Si se declara como: char *pc; entonces:
pc = "hola que tal"; asigna a un puntero al arreglo de caracteres. Esto NO es una copia de la
cadena, sólo es una relación entre punteros.
es un arreglo, lo suficientemente grande para contener a la cadena (se pueden modificar los
elementos del vector); mientras que es un puntero, inicializado a apuntar una constante cadena. El
puntero puede cambiar de objetivo, pero el resultado es indefinido si se intenta modificar el contenido de la
cadena.
Ejemplo:
#include<stdio.h>
void copia(char *, char *);
int main(){
char cad1[30];
copia(cad1,"hola chau");
printf("%s\n",cad1);
return 0;
}
En el caso de las matrices la relación con los punteros es un poco más complicada. Supóngase una declaración
como la siguiente:
Recuérdese también que, por la relación entre vectores y punteros, (mat+i) apunta a mat[i]. Recuérdese que la
fórmula de direccionamiento de una matriz de N filas y M columnas establece que la dirección del elemento (i,
j) viene dada por:
Si la matriz tiene M columnas y si se hace q = &mat[0][0] (dirección base de la matriz), 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) // fórmula de direccionamiento
*(mat[i] + j) // primer elemento fila i desplazado j elementos
(*(mat + i))[j] // [j] equivale a sumar j a un puntero
*((*(mat + i)) + j) // accede al elemento mat[i][j]
Todas estas relaciones tienen una gran importancia, pues implican una correcta comprensión de los punteros y
de las matrices. De todas formas, hay que indicar que las matrices no son del todo idénticas a los vectores de
punteros: Si se define una matriz explícitamente por medio de vectores de punteros, las filas pueden tener
diferente número de elementos, y no queda garantizado que estén contiguas en la memoria (aunque se puede
hacer que sí lo sean). No sería pues posible en este caso utilizar la fórmula de direccionamiento y el acceder por
columnas a los elementos de la matriz.
En realidad, una matriz se representa en memoria como un vector, donde se colocan las filas una al lado de
otra; por lo tanto una matriz es un vector, donde sus elementos son vectores. Por ejemplo, una matriz A de
10x10 se representa en memoria por un vector de 100 elementos.
La matriz es un arreglo de arreglos de una dimensión. Como el nombre de un arreglo es un puntero a su primer
elemento (y ese elemento es un arreglo de una dimensión), A es realmente un puntero al primer arreglo de 10
elementos (más información sobre estos temas en http://stackoverflow.com/questions/546860/passing-
arrays-and-matrices-to-functions-as-pointers-and-pointers-to-pointers-in).
void funcion(int (*arreglo)[10]);
void funcion(int arreglo[][10]);
void funcion(int arreglo[10][10]);
void funcion(int arreglo[42][10]);
#include<stdio.h>
int main(){
int i,j,m,n;
printf("Ingrese la cantidad de filas y columnas de la matriz:");
printf("\nFilas: "); scanf("%d",&m);
printf("Columnas: "); scanf("%d",&n);
int mat[m][n];
int *p[m],(*r)[n],**q;
r=mat;
q=p;
for(i=0;i<m;i++) *(p+i)=*(mat+i);
for(i=0;i<m;i++){
for(j=0;j<n;j++){
printf("mat[%d][%d]: ",i,j); scanf("%d",(*(p+i)+j));
}
}
printf("\nImprimiendo con p\n");
for(i=0;i<m;i++){
for(j=0;j<n;j++){
printf("%d\t",*(*(p+i)+j));
}
printf("\n");
}
printf("\nImprimiendo con r\n");
for(i=0;i<m;i++){
for(j=0;j<n;j++){
printf("%d\t",(*(r+i))[j]);
}
printf("\n");
}
printf("\nImprimiendo con q\n");
for(i=0;i<m;i++){
for(j=0;j<n;j++){
printf("%d\t",*(*(q+i)+j));
}
printf("\n");
}
return 0;
}
Lista de ejercicios sobre punteros
1) Dada una matriz A de tamaño m*n, obtener un vector B que contenga los me nores elementos de cada fila.
2) Ordenar los elementos de un vector de forma descendente.
3) Ordenar alfabéticamente un conjunto de palabras que se representan mediante una matriz de
caracteres. Cada palabra es una fila de la matriz y no supera los 20 c aracteres.
E S T A \0
E S \0
U N A \0 \0
P R U E B A \0
4) Escribir un programa que convierta una cadena en m ayúsculas y otro que la convierta en minúsculas.
5) Escribir un algoritmo que elimine todos los espacios de una cadena ingresada mediante la función gets().
6) Escribir una función contpar(int *a, int tam) que recibe un vector y su tamaño, y devuelve el número de
elementos pares del arreglo.
7) Escribir una función letra(char *cad) que reciba una cadena y devuelva el carácter con más apariciones de
la misma.
int main(){
int i,j,m,n;
printf("Ingrese la cantidad de filas y columnas de la matriz:");
printf("\nFilas: "); scanf("%d",&m);
printf("Columnas: "); scanf("%d",&n);
int mat[m][n],vec[m];
int *p[m],**q=p,*t=vec;
for(i=0;i<m;i++) *(p+i)=*(mat+i);
for(i=0;i<m;i++){
for(j=0;j<n;j++){
printf("mat[%d][%d]: ",i,j); scanf("%d",(*(p+i)+j));
}
}
for(i=0;i<m;i++) menor_elemento(i,n,q,t);
printf("\nEl vector de menores elementos es:\n");
for(i=0;i<m;i++) printf("%d\n",vec[i]);
return 0;
}
Ejercicio 3
#include<stdio.h>
int comparar(char *, char *);
void ordenar(char **, int n);
int main(){
int i,n;
printf("Ingrese la cantidad de palabras: "); scanf("%d",&n);
char pal[n][21];
printf("\nIngrese las palabras:\n");
for(i=0;i<n;i++){
printf("Palabra %d: ",(i+1));
scanf("%s",pal[i]);
}
char *p[n];
for(i=0;i<n;i++) *(p+i)=*(pal+i);
ordenar(p,n);
printf("\nPalabras ordenadas:\n");
for(i=0;i<n;i++){
puts(*(p+i));
}
return 0;
}
Ejercicio 5
#include<stdio.h>
#include<string.h>
int main(){
int i,pos=0;
char cad[100];
char cad2[100];
char *p,*q;
printf("\nIngrese la cadena:\n");
gets(cad);
p=cad;
q=cad2;
for(i=0;i<strlen(p);i++){
if(*(p+i)!=' '){
*(q+pos)=*(p+i);
pos++;
}
}
*(q+pos)='\0';
printf("\nLa cadena sin espacios es:\n");
puts(cad2);
return 0;
}
Ejercicio 7
#include<stdio.h>
#include<string.h>
char letra(char *, int n);
int main(){
int i,pos=0,n;
char cad[100];
char *p;
printf("\nIngrese la cadena:\n");
gets(cad);
p=cad;
n=strlen(cad);
//Se ordenan los elementos de la cadena
int j=1,band=1;
char t;
while(band){
band=0;
for(i=0;i<(n-j);i++){
if((*(p+i))>(*(p+i+1))){
t=*(p+i);
*(p+i)=*(p+i+1);
*(p+i+1)=t;
band=1;
}
}
j++;
}
puts(cad);
char c=letra(p,n);
printf("\nEl caracter que mas aparece es %c.",c);
return 0;
}
La batalla es feroz, y muchos soldados en la línea de ataque están siendo asesinados por disparos de fuego,
granadas y bombas. Pero siguiendo las órdenes del general en jefe, inmediatamente después de conocer
acerca de las pérdidas en la línea de ataque, la división de sistemas de información del Ejército tiene que
informar a los soldados quiénes son sus nuevos compañeros.
A usted se le da el número de soldados en la línea de ataque, y una secuencia de informes de pérdidas. Cada
informe describe la pérdida de un grupo de soldados contiguos en la línea de ataque que fueron asesinados
recientemente en la batalla. Escriba un programa que, para cada informe de pérdida, imprime los nuevos
compañeros formados.
El último caso de prueba es seguido por una línea que contiene dos ceros.
1 1 * *
1 1 -
10 4 1 6
2 5 1 10
6 9 * 10
1 1 * *
10 10 -
5 1 * 2
1 1 -
0 0
Tema 2: “Números Palíndromos”
Se dice que un número es un Palíndromo (capicúa) si es el mismo cuando se lee de izquierda a derecha o de
derecha a izquierda. Por ejemplo, e l número 75457 es un palíndromo.
El objetivo de este problema es el de verificar si un conjunto de números dados son palíndromos en cualquier
base desde 2 a 16.
Si el número no es un palíndromo en cualquier base entre 2 y 16, el programa debe imprimir el mensaje ‘El
numero no es palindromo’.
Tema 3: “Permutaciones”
Dada una lista de elementos, definir todas las permutaciones que pueden obtenerse con los mismos.
3 2 4 1 1 2 3 4
1 2 4 3
1 3 2 4
1 3 4 2
1 4 2 3
1 4 3 2
2 1 3 4
2 1 4 3
2 3 1 4
2 3 4 1
2 4 1 3
2 4 3 1
3 1 2 4
3 1 4 2
3 2 1 4
3 2 4 1
3 4 1 2
3 4 2 1
4 1 2 3
4 1 3 2
4 2 1 3
4 2 3 1
4 3 1 2
4 3 2 1
Tema 4: “Dados”
La gente de PEDlandia tienen una tradición de tirar un dado de seis caras para determinar quién elegirá dónde
cenar esa noche.
Una persona tira el dado, y la otra elige “par” o “impar”. Si esta segunda persona acierta, entonces él o ella
tiene que elegir el restaurante, de lo contrario, la persona que lanzó lo hace. Por suerte es obvio que “impar”
gana cuando se lanza un 1, 3 ó 5; y “even” cuando se obtiene 2, 4 ó 6!
Los PEDlandianos también tienen una tradición de repetir la misma elección (ya sea par o impar) varias veces
en sucesión.
Su tarea es verificar los resultados de lanzamientos grabados y determinar cuántas veces gana cada persona.
La primera línea contiene, en orden, los nombres de dos personas, y una de las palabras “par” o
“impar” (en minúsculas), separadas por un espacio. La primera persona nombrada siempre es la
lanzadora del dado, y la segunda persona nombrada será la que elija “par” o “impar”, como se indica.
En PEDlandia, un nombre es una secuencia no vacía de hasta 20 (inclusive) letras (cualquiera de las
cuales puede estar en mayúsculas o minúsculas).
La segunda línea será un número entero, , , que representa el número de lanzamientos
del dado.
La tercera línea contiene enteros, cada uno entre 1 y 6 (inclusive); separados por espacios, que
representan los resultados de cada lanzamiento.
La entrada será terminada por una línea que consta de tres numerales (#), separados por espacios. Esta línea
no debe ser procesada.
La cerradura viene con tres números de código , , . Estos son números enteros no-negativos y cada uno
de ellos es menor que . Ningún par de estos tres códigos son iguales.
Dados los números , el objetivo de este problema es encontrar el número promedio de posiciones
que el disco debe girar (cambiar) con el fin de abrir la cerradura. Para cualesquiera y una
configuración inicial particular de la cerradura, el número de posiciones giradas (cambiadas) se define como la
suma de las posiciones giradas (cambiadas) en las tres etapas descritas ante riormente.
80 20 40 50 369.500
80 10 79 12 415.500
0 0 0 0
Apuntes de la clase de PED 2 Fecha: 22 de marzo de 2013
MSc. José Colbes
La batalla es feroz, y muchos soldados en la línea de ataque están siendo asesinados por disparos de fuego,
granadas y bombas. Pero siguiendo las órdenes del general en jefe, inmediatamente después de conocer
acerca de las pérdidas en la línea de ataque, la división de sistemas de información del Ejército tiene que
informar a los soldados quiénes son sus nuevos compañeros.
A usted se le da el número de soldados en la línea de ataque, y una secuencia de informes de pérdidas. Cada
informe describe la pérdida de un grupo de soldados contiguos en la línea de ataque que fueron asesinados
recientemente en la batalla. Escriba un programa que, para cada informe de pérdida, imprime los nuevos
compañeros formados.
El último caso de prueba es seguido por una línea que contiene dos ceros.
1 1 * *
1 1 -
10 4 1 6
2 5 1 10
6 9 * 10
1 1 * *
10 10 -
5 1 * 2
1 1 -
0 0
#include<stdio.h>
int izq[100001];
int der[100001];
int B,S,L,R;
int main(){
scanf("%d %d",&S,&B);
int a,b,i;
while((S!=0)&&(B!=0)){
izq[1]=-1;
der[S]=-1;
for(i=1;i<S;i++){
izq[i+1]=i;
der[i]=i+1;
}
for(i=0;i<B;i++){
scanf("%d %d",&L,&R);
a=izq[L];
b=der[R];
der[a]=b;
izq[b]=a;
if(a==-1) printf("* ");
else printf("%d ",a);
if(b==-1) printf("*\n");
else printf("%d\n",b);
}
printf("-\n");
scanf("%d %d",&S,&B);
}
return 0;
}
El objetivo de este problema es el de verificar si un conjunto de números dados son palíndromos en cualquier
base desde 2 a 16.
Si el número no es un palíndromo en cualquier base entre 2 y 16, el programa debe imprimir el mensaje ‘El
numero no es palindromo’.
#include<stdio.h>
int main(){
int N,i,aux,d,j;
int dig[16];
int band,band2;
scanf("%d",&N);
while(N!=0){
band=0;
for(i=2;i<17;i++){
d=0;
aux=N;
while(aux>=i){
dig[d]=aux%i;
aux=aux/i;
d++;
}
dig[d]=aux;
d++;
band2=1;
for(j=0;j<d;j++){
if(dig[j]!=dig[d-1-j]){
band2=0;
j=d;
}
}
if(band2==1){
if(band==0){ printf("El numero %d es palindromo en base %d",N,i); band=1;}
else printf(" %d",i);
}
}
if(band==0) printf("El numero %d no es palindromo",N);
printf("\n");
scanf("%d",&N);
}
return 0;
}
Tema 3: “Permutaciones”
Dada una lista de elementos, definir todas las permutaciones que pueden obtenerse con los mismos.
3 2 4 1 1 2 3 4
1 2 4 3
1 3 2 4
1 3 4 2
1 4 2 3
1 4 3 2
2 1 3 4
2 1 4 3
2 3 1 4
2 3 4 1
2 4 1 3
2 4 3 1
3 1 2 4
3 1 4 2
3 2 1 4
3 2 4 1
3 4 1 2
3 4 2 1
4 1 2 3
4 1 3 2
4 2 1 3
4 2 3 1
4 3 1 2
4 3 2 1
#include<stdio.h>
int lista[100];
int n;
void ordenar(int lista[]){
int b,i=1,j,aux;
do{
b=0;
for(j=0;j<(n-i);j++){
if(lista[j]>lista[j+1]){
aux=lista[j];
lista[j]=lista[j+1];
lista[j+1]=aux;
b=1;
}
}
i++;
}while(b);
}
void imprimir(){
int i;
for(i=0;i<(n-1);i++) printf("%d ",lista[i]); printf("%d\n",lista[n-1]);
}
void Permutacion(int pos){
int i,j,aux;
if(pos==(n-1)) imprimir();
else{
for(i=pos;i<n;i++){
aux=lista[i];
for(j=i;j>pos;j--) lista[j]=lista[j-1];
lista[pos]=aux;
Permutacion((pos+1));
aux=lista[pos];
for(j=pos;j<i;j++) lista[j]=lista[j+1];
lista[i]=aux;
}
}
}
int main(){
n=0;
while(scanf("%d",&lista[n])!=EOF) {n++;}
ordenar(lista);
Permutacion(0);
return 0;
}
Tema 4: “Dados”
La gente de PEDlandia tiene una tradición de tirar un dado de seis caras para determinar quién elegirá dónde
cenar esa noche.
Una persona tira el dado, y la otra elige “par” o “impar”. Si esta segunda persona acierta, entonces él o ella
tiene que elegir el restaurante, de lo contrario, la persona que lanzó lo hace. Por suerte es obvio que “impar”
gana cuando se lanza un 1, 3 ó 5; y “par” cuando se obtiene 2, 4 ó 6!
Los PEDlandianos también tienen una tradición de repetir la misma elección (ya sea par o impar) varias veces
en sucesión.
Su tarea es verificar los resultados de lanzamientos grabados y determinar cuántas veces gana cada persona.
La primera línea contiene, en orden, los nombres de dos personas, y una de las palabras “par” o
“impar” (en minúsculas), separadas por un espacio. La primera persona nombrada siempre es la
lanzadora del dado, y la segunda persona nombrada será la que elija “par” o “impar”, como se indica.
En PEDlandia, un nombre es una secuencia no vacía de hasta 20 (inclusive) letras (cualquiera de las
cuales puede estar en mayúsculas o minúsculas).
La segunda línea será un número entero, , , que representa el número de lanzamientos
del dado.
La tercera línea contiene enteros, cada uno entre 1 y 6 (inclusive); separados por espacios, que
representan los resultados de cada lanzamiento.
La entrada será terminada por una línea que consta de tres numerales (#), separados por espacios. Esta línea
no debe ser procesada.
#include<stdio.h>
#include<string.h>
char P1[30],P2[30],J[30];
int N;
int vec[256];
int main(){
int i,par,impar;
scanf("%s%s%s",P1,P2,J);
while(strcmp(J,"#")!=0){
scanf("%d",&N);
impar=0;
for(i=0;i<N;i++){
scanf("%d",&vec[i]);
if((vec[i]%2)==1) impar++;
}
par=N-impar;
if(strcmp(J,"impar")==0) printf("%s %d %s %d\n",P1,par,P2,impar);
else printf("%s %d %s %d\n",P1,impar,P2,par);
scanf("%s%s%s",P1,P2,J);
}
return 0;
}
La cerradura viene con tres números de código , , . Estos son números enteros no-negativos y cada uno
de ellos es menor que . Ningún par de estos tres códigos son iguales.
Dados los números , el objetivo de este problema es encontrar el número promedio de posiciones
que el disco debe girar (cambiar) con el fin de abrir la cerradura. Para cualesquiera y una
configuración inicial particular de la cerradura, el número de posiciones giradas (cambiadas) se define como la
suma de las posiciones giradas (cambiadas) en las tres etapas descritas ante riormente.
80 20 40 50 369.500
80 10 79 12 415.500
0 0 0 0
#include<stdio.h>
int N,T1,T2,T3;
double cant;
int main(){
scanf("%d%d%d%d",&N,&T1,&T2,&T3);
int x,y;
while((N!=0)||(T1!=0)||(T2!=0)||(T3!=0)){
cant=0.0;
cant+=(1.0*(N-1)/2);
cant+=(3.0*N);
x=abs(T2-T1);
y=abs(T3-T2);
if(T2>T1) cant+=(1.0*x);
else cant+=(1.0*(N-x));
if(T3>T2) cant+=(1.0*(N-y));
else cant+=(1.0*y);
printf("%.3lf\n",cant);
scanf("%d%d%d%d",&N,&T1,&T2,&T3);
}
return 0;
}
Apuntes de la clase de PED 2 Fecha: 5 de abril de 2013
MSc. José Colbes
Estructuras
Los arreglos son estructuras de datos que contienen un número determinado de elementos del mismo tipo de
dato. Esta característica (estructura de datos homogénea) supone una limitación cuando se requieren grupos
de elementos con tipos diferentes de datos.
Por ejemplo, si se dispone de una lista de información de clientes que contengan elementos tales como el
nombre, la edad, la dirección, el número de cuenta, etc., los arreglos no son adecuados. La solución a este
problema es utilizar un tipo de dato registro, denominado estructura en C.
Los componentes individuales de una estructura se llaman campos o miembros. Cada campo puede contener
datos de un tipo diferente en relación a otros campos. Por ejemplo, se puede utilizar una estructura para
almacenar diferentes tipos de información sobre una persona, tal como nombre, estado civil, edad y fecha de
nacimiento.
Entonces, una estructura es una colección de uno o más tipos de elementos denominados campos, cada uno de
los cuales puede ser de un tipo de dato diferente.
En otros lenguajes como C#, C++ y Java ( programación orientada a objetos), las estructuras pueden actuar
como clases (en realidad, serían los atributos de la clase).
struct paciente{
char nombre[30];
int edad,peso;
char direccion[60];
};
struct complejo{
float parte_real;
float parte_imaginaria;
};
struct coleccion_CD{
char titulo[30],artista[25];
int num_canciones;
float precio;
char fecha_compra[8];
};
struct coleccion_CD{
char titulo[30],artista[25];
int num_canciones;
float precio;
char fecha_compra[8];
} cd1, cd2, cd3;
2. Listando el tipo de la estructura creado, seguida por las variables correspondientes en cualquier lugar
del programa antes de utilizarlas.
paciente1 = paciente4;
paciente2 = paciente4;
paciente3 = paciente4;
struct infoLibro{
char titulo[60];
char autor[30];
char editorial[30];
int anho;
} libro1= {"Maravilla del saber",
"Lucas Garcia",
"McGraw-Hill",
2003};
O bien:
#include<stdio.h>
struct estructura_amigo {
char nombre[30];
char apellido[40];
char telefono[10];
int edad;
};
int main(){
struct estructura_amigo amigo= {
"Juanjo",
"Lopez",
"592-0483",
30
};
return 0;
}
#include<stdio.h>
struct persona{
char nombre[30];
int edad;
float altura;
float peso;
};
void main(){
struct persona jose;
printf("sizeof(persona) = %d\n",sizeof(jose));
}
Al ejecutar este programa se produce la salida (tomando int con dos bytes):
sizeof(persona) = 40
Acceso a estructuras
Se puede acceder a las estructuras de dos formas: mediante el operador punto y mediante el operador
puntero.
A los miembros de la estructura estudiante se les puede asignar datos como sigue:
ptrEst = &mejor;
strcpy(ptrEst->nombre,"Juan Perez");
ptrEst->numEstudiante=3425;
ptrEst->nota=8.5;
Para la utilización de los campos de una estructura para operaciones, también se accede a los elementos de la
estructura mediante los dos operadores mencionados. Por ejemplo, considerando la estructura complejo:
struct complejo{
float pr;
float pi;
float modulo;
};
Se puede hacer lo siguiente:
struct complejo z;
struct complejo *pz;
pz=&z;
scanf("%f",&z.pr);scanf("%f",&z.pi);
z.modulo=sqrt(z.pr*z.pr+(pz->pi)*(pz->pi));
printf("\nNumero complejo (%.1f,%.1f)",z.pi,z.pr);
printf("\nModulo: %.2f",pz->modulo);
Estructuras anidadas
Una estructura puede contener otras estructuras llamadas estructuras anidadas. Las mismas ahorran tiempo
en la escritura de programas que utilizan estructuras similares. Se han de definir los miembros comunes sólo
una vez en su propia estructura y a continuación utilizar esa estructura como un miembro de otra estructura
(sería una forma básica de aplicar el concepto de herencia de la programación orientada a objetos).
struct empleado{
char nombre[30];
char direccion[25];
char ciudad[20];
char departamento[20];
int edad;
double salario;
};
struct cliente{
char nombre[30];
char direccion[25];
char ciudad[20];
char departamento[20];
int edad;
double saldo;
};
Estas estructuras contienen datos diferentes, aunque hay datos que están solapados. Así, se podría disponer de
otra estructura, persona, que contenga los miembros comunes.
struct persona{
char nombre[30];
char direccion[25];
char ciudad[20];
char departamento[20];
int edad;
};
struct empleado{
struct persona emp;
double salario;
};
struct cliente{
struct persona cli;
double saldo;
};
Así, si se desea por ejemplo leer el nombre de un cliente c1, se puede hacer lo siguiente:
gets(c1.cli.nombre)
Con c1.cli se accede a la estructura persona que está contenida en c1. Luego, haciendo c1.cli.nombre se accede
al nombre de ese cliente. Esta misma lógica se sigue para acceder a los campos de estructuras con un
anidamiento de mayor nivel.
Entonces, el acceso a los campos dato de estructuras anidadas requiere el uso de múltiples operadores punto.
Arreglos de estructuras
Se puede crear un arreglo de estructuras tal como se crea un arreglo de otros tipos. Los arreglos de estructuras
son idóneos para almacenar en memoria un archivo completo de empleados, un archivo de inventario, o
cualquier otro conjunto de datos que se adapte a un formato de estructura. Mientras que los arreglos
proporcionan un medio práctico de almacenar diversos valores del mismo tipo, los arreglos de estructuras
permiten almacenar juntos diversos valores de diferentes t ipos, agrupados como estructuras.
La declaración de un array de estructuras infoLibro se puede hacer de un modo similar a cualquier arreglo; es
decir:
asigna un arreglo de 100 elementos denominado libros. Para acceder a los miembros de cada uno de los
elementos estructura se utiliza la notación de índices de los arreglos. Para inicializar el primer elemento de
libros, por ejemplo, su código debe hacer referencia a los miembros de libros[0] de la forma siguiente:
strcpy(libros[0].titulo,"Estructuras en C");
strcpy(libros[0].autor,"Luis Joyanes");
strcpy(libros[0].editorial,"McGraw-Hill");
libros[0].anho=1999;
El listado siguiente muestra la llamada a una función para entrada de datos que pasa la dirección de una
estructura. La misma variable estructura se pasa por valor a otra función para salida de los campos.
#include<stdio.h>
struct info_persona{
char nombre[20];
char calle[30];
char ciudad[25];
char provincia[25];
char codigopostal[6];
};
void entradaPersona(struct info_persona *pp);
void verInfoPersona(struct info_persona p);
int main(){
struct info_persona per;
entradaPersona(&per); //referencia
verInfoPersona(per); //valor
return 0;
}
void entradaPersona(struct info_persona *pp){} //no nos centramos en estas
subrutinas
void verInfoPersona(struct info_persona p){}
Uniones
Las uniones son similares a las estructuras en cuanto que agrupa a una serie de variables, pero la forma de
almacenamiento es diferente y por consiguiente tiene efectos diferentes. Una estructura (struct) permite
almacenar variables relacionadas juntas en posiciones contiguas de memoria. Las uniones, declaradas con la
palabra reservada unión, almacenan también miembros múltiples en un paquete; sin embargo, en lugar de
situar sus miembros uno detrás de otros, en una unión, todos los campos se solapan entre sí en la misma
posición. El tamaño ocupado por una unión se determina analizando el tamaño de cada variable de la unión; el
tamaño mayor de todas las variables será el tamaño de la unión.
Por ejemplo:
union PruebaUnion{
float Item1;
int Item2;
};
La cantidad de memoria reservada para una unión es igual a la longitud de la variable más grande. En el tipo
unión, cada uno de los miembros dato comparten memoria con los otros miembros de la unión. La cantidad
total de memoria utilizada por la unión comparte es de 8 bytes, ya que el elemento double es el miembro dato
mayor de la unión.
union comparte{
char letra;
int elemento;
float precio;
double z;
};
Una razón para utilizar una unión es ahorrar memoria. En muchos programas se pueden tener varias variables,
pero no necesitan utilizarse todas al mismo tiempo.
Para referirse a los miembros de una unión, se utiliza el operador punto (.), o bien el operador -> si se hace
desde un puntero a unión. Así:
cadenas.ayuda;
cadenas.mensaje_error;
pc -> mensaje_error;
En este ejemplo se considera el supuesto de que una pequeña biblioteca tiene dos tipos de objetos: libros y
revistas. Se puede definir una estructura libro, una estructura revista y una unión con ambas
denominada elemento. En un momento dado la unión va a repre sentar a un libro, en otro a una revista.
struct libro{
char titulo[40];
char autor[30];
char editorial[50];
int numPagina;
};
struct revista{
char nombre[40];
char tema[30];
char fecha[10];
float precio;
};
union elemento{
struct libro lb;
struct revista r;
};
Enumeraciones
Una enumeración (enum) es un tipo definido por el usuario con constantes de nombre de tipo entero. En la
declaración de un tipo enum se escribe una lista de identificadores que internamente se asocian con las
constantes enteras 0,1,2,…
Sintaxis:
enum nombre{
enumerador1,enumerador2,...,enumeradorn
};
En la declaración del tipo enum pueden asociarse a los identificadores valores constantes en vez de la
asociación que por defecto se hace (0,1,2,…). Para ello se utiliza este formato:
enum nombre{
enumerador1 = expresion_constante1,
enumerador2 = expresion_constante2,
...
enumeradorn = expresion_constanten
};
Usos típicos de enum
enum Interruptor{
ENCENDIDO,
APAGADO
};
enum Boolean{
FALSE,
TRUE
};
enum colores{
ROJO, VERDE, AZUL, NEGRO, AMARILLO, BLANCO
};
En la siguiente declaración de tipo enumerado se proporciona un nombre al tipo. Además, la primera constante
tiene valor 1 y las demás los siguientes valores e nteros.
enum dias_semana{
LUNES=1, MARTES, MIERCOLES, JUEVES, VIERNES, SABADO, DOMINGO
};
Haciendo lo siguiente:
enum dias_semana dia;
for(dia=LUNES;dia<=DOMINGO;dia++) printf("%d ",dia);
Ejercicio propuesto: Leer un texto y contar las vocales leídas. Utilizando la enumeración Boolean, escribir
una función vocal() que devuelva TRUE si el carácter considerado es vocal.
#include<stdio.h>
enum Boolean{
FALSE, TRUE
};
enum Boolean vocal(char c);
int main(){
char cad[100];
int numvocal=0,i=0;
puts("Introduzca una cadena");
gets(cad);
while(cad[i]!='\0'){
if(vocal(tolower(cad[i]))) numvocal++;
i++;
}
printf("\nTotal de vocales leidas: %d\n",numvocal);
return 0;
}
enum Boolean vocal(char c){
switch(c){
case 'a':
case 'e':
case 'i':
case 'o':
case 'u':
return TRUE;
default:
return FALSE;
}
}
typedef
Un operador typedef permite a un programador crear un sinónimo de un tipo de dato definido por el
usuario o de un tipo ya existente. La sintaxis es:
A partir de la declaración hecha con typedef se puede hacer uso del nuevo símbolo del tipo de dato para
definir variables, en general, donde se utiliza los tipos de datos.
Normalmente los programadores utilizan sinónimos de tipos de datos estructura para simplificar los tipos y
hacerlos más amigables. En primer lugar se declara el tipo estructura, lo mismo con el tipo unión, y a
continuación se asocia el tipo estructura a un nombre c on typedef.
Ejemplos:
struct num_complejo{
float x,y;
};
typedef struct num_complejo complejo;
complejo v[12];
typedef struct{
char apellido[21];
char nombre[21];
int edad;
char pasaporte[26];
char sexo;
} Pasajero;
//Prototipo de funcion
Pasajero entrada();
La ventaja de typedef es que permite dar nombres a tipos de datos más acordes con lo que representan en
una determinada aplicación.
Ejercicios propuestos
Ejercicio 1: Dada una lista que contiene los nombres (en el formato “Apellido - Nombre”) de los alumnos y sus
notas, calcular el promedio de notas de los alumnos con apellidos de A hasta N (o a hasta n). Mostrar también
el nombre del alumno con la mejor nota (o de los alumnos, si se da el caso).
Ejercicio 2: Como parte de un experimento para estudiar el comportamiento laboral de su personal, una
empresa decidió liberar los horarios para los 10 empleados de un mismo departamento. Esto quiere decir que
cada uno de estos empleados puede llegar o retirarse de la empresa a la hora que consideren conveniente,
pero siempre dentro del horario de at ención al público que es de 7:00 a 17 :00 hs.
Las condiciones de trabajo normales para estos empleados les exigen cumplir una asistencia mínima de 30
horas por semana, trabajando de lunes a viernes.
El experimento tuvo una semana de duración, y se utilizó un reloj marcador digital para registrar las entradas y
salidas a la empresa de los empleados participantes del est udio.
Cada empleado fue identificado por un número, de 1 a 10, y por cada marcación del reloj se generó un registro
con los siguientes datos:
Nuestro trabajo consiste en escribir un algoritmo que realice la lectura de los 100 registros obtenidos mediante
el reloj, los cuales se encuentran en el mismo orden en que fueron registrados, y luego procese dicha
información para obtener un arreglo de registros, ordenado por código de empleado, en el formato que se
muestra:
Ejercicio 3: Escribir un programa para calcular el número de días que hay entre dos fechas: declarar fecha como
una estructura.
Ejercicio 4: Escribir un programa que permita hacer las operaciones de suma, resta, multiplicación y división de
números complejos. El tipo complejo ha de definirse como una est ructura.
Ejercicio 5: Un punto en el plano se puede representar mediante una estructura con dos campos. Escribir un
programa que realice las siguientes operaciones en e l plano:
Dados dos puntos, calcular la distancia entre ellos.
Dados dos puntos determinar la ecuación de la rect a que pasa por ellos.
Dados tres puntos, que representan los vértice s de un triángulo, calcular su área.
Ejercicio 6: Escribir un programa de facturación de clientes. Los clientes tienen un nombre, el número de
unidades solicitadas, el precio de cada unidad y el estado en que se encuentra: moroso, atrasado, pagado. A
partir de una lista de clientes, obte ner los siguientes listados:
Clientes en estado moroso.
Clientes en estado pagado con factura mayor a una determinada cantidad.
Apuntes de la clase de PED2 Fecha: 12 de abril de 2013
MSc. José Colbes
Los datos que hemos tratado hasta el momento han residido en la memoria principal. Sin embargo, las grandes
cantidades de datos se almacenan normalmente en un dispositivo de memoria secundaria. Estas colecciones
de datos se conocen como archivos (antiguamente ficheros).
Un flujo (stream) es una abstracción que se refiere a un flujo o corriente de datos que fluyen entre un origen y
un destino. Entre el origen y el destino debe existir una conexión o canal por la que circulen los datos. La
apertura de un archivo supone establecer la conexión del programa con el dispositivo que contiene el archivo,
por el canal que comunica el archivo co n el programa van a fluir las secuencias de datos.
Hay dos tipos de archivos, archivos de texto y archivos binarios. Un archivo de texto es una secuencia de
caracteres organizadas en líneas terminadas por un carácter de nueva línea. En estos archivos se pueden
almacenar canciones, fuentes de programas, base de datos simples, etc. Los archivos de texto se caracterizan
por ser planos, es decir, todas las letras tienen el mismo formato y no hay palabras subrayadas, en negrita, o
letras de distinto tamaño o ancho.
Un archivo binario es una secuencia de bytes que tienen una correspondencia uno a uno con un dispositivo
externo. Así que no tendrá lugar ninguna traducción de caracteres. Además, el número de bytes escritos
(leídos) será el mismo que los encontrados en el dispositivo externo. Ejemplos de estos archivos son
fotografías, imágenes, texto con formatos, archivos ejec utables (aplicaciones), etc.
En C, un archivo es un concepto lógico que puede aplicarse a muchas cosas desde archivos de disco hasta
terminales o una impresora. Se asocia una secuencia con un archivo específico realizando una operación de
apertura. Una vez que el archivo está abierto, la información puede ser intercambiada entre éste y el
programa.
Se puede conseguir la entrada y la salida de datos a un archivo a través del uso de la biblioteca de funciones; C
no tiene palabras claves que realicen las operaciones de E/S. Para ello, se utiliza la librería stdio.h. A
continuación se muestra un programa básico que copia el conteni do de un archivo en otro nuevo:
#include<stdio.h>
int main(){
FILE *fp1,*fp2;
char c;
char entrada[100],salida[100];
printf("Ingrese el nombre del archivo origen:\n");
scanf("%s",entrada);
if((fp1=fopen(entrada,"r"))==NULL){
printf("\nError al abrir el archivo %s",entrada);
return 0;
}
printf("Ingrese el nombre del archivo destino:\n");
scanf("%s",salida);
if((fp2=fopen(salida,"w"))==NULL){
printf("\nError al crear el archivo %s",salida);
return 0;
}
c=fgetc(fp1);
while(feof(fp1)==0){
fputc(c,fp2);
c=fgetc(fp1);
}
fclose(fp1);
fclose(fp2);
printf("\nArchivo duplicado con exito");
return 0;
}
y son punteros “especiales”, denominados punteros a archivos. El puntero a un archivo es el hilo
común que unifica el sistema de E/S con buffer. Un puntero a un archivo es un puntero a una información que
define varias cosas sobre él, incluyendo el nombre, el estado y la posición actual del archivo. En esencia
identifica un archivo específico y utiliza la secuencia asociada para dirigir el funcionamiento de las funciones de
E/S con buffer. Un puntero a un archivo es una variable de tipo puntero al tipo FILE que se define en stdio.h. Un
programa necesita utilizar punteros a archivos para leer o escribir en los mismos. Para obtener una variable de
este tipo se utiliza una secuencia como e sta:
FILE *fp;
Apertura de un archivo
En el ejemplo, se usa la instrucción fopen(). La misma abre una secuencia para que pueda ser utilizada y la
asocia a un archivo. Su sintaxis es:
fp = fopen(nombre,modo)
Donde nombre es un puntero a una cadena de caracteres que representan un nombre válido del archivo y
puede incluir una especificación del directorio. La cadena a la que apunta modo determina como se abre el
archivo. La siguiente tabla muestra los valores permitidos para modo:
La función fopen() devuelve un puntero a archivo. Un programa nunca debe alterar el valor de ese puntero.
Si se produce un error cuando se esta intentando abrir un archivo, fopen() devuelve un puntero nulo (NULL).
Se puede abrir un archivo bien en modo texto o binario. En la mayoría de las implementaciones, en modo
texto, la secuencias de retorno de carro / salto de línea se convierten a caracteres de salto de línea en lectura.
En la escritura, ocurre lo contrario: los caracteres de salto de línea se convierten en salto de línea. Estas
conversiones no ocurren en archivos binarios.
La macro NULL está definida en stdio.h. Este método detecta cualquier error al abrir un archivo: como por
ejemplo disco lleno o protegido contra escritura antes de comenzar a escribir en él.
Si se usa fopen() para abrir un archivo para escritura, entonces cualquier archivo existente con el mismo
nombre se borrará y se crea uno nuevo. Si no existe un archivo con el mismo nombre, entonces se creará. Si se
quiere añadir al final del archivo entonces debe usar el modo “a”. La apertura de un archivo para las
operaciones de lectura requiere que exista el archivo; si no existe, fopen() devolverá un error. Finalmente, si
se abre un archivo para las operaciones de leer/escribir, la computadora no lo borrará si existe; sin embargo, si
no existe, la computadora lo creará.
Cierre de un archivo.
La función fclose() cierra una secuencia que fue abierta mediante una llamada a fopen(). Escribe toda la
información que todavía se encuentre en el buffer en el disco y realiza un cierre formal del archivo a nivel del
sistema operativo. Un error en el cierre de una secuencia puede generar todo tipo de problemas, incluyendo la
pérdida de datos, destrucción de archivos y posibles errore s intermitentes en el programa. Su sintaxis es:
fclose(fp);
Donde es el puntero al archivo devuelto por la llamada a fopen(). Si se devuelve un valor cero significa
que la operación de cierre ha tenido éxito. Generalmente, esta función solo falla cuando un disco se ha retirado
antes de tiempo o cuando no queda espacio libre en el mismo.
La siguiente tabla da un breve resumen de las funciones que se pueden utilizar (para archivos de texto). Se
debe incluir la librería stdio.h. Observe que la mayoría de las funciones comienzan con la letra “F” (de File).
fgetc y fputc
Estas funciones son análogas a getchar() y putchar(), pero aplicadas a archivos. Estas funciones
escriben o leen un carácter de un fichero determinado por un puntero a fichero (FILE *fp).
Usando iterativamente fgetc, los caracteres se leen en forma secuencial (no se necesita usar una aritmética de
punteros para el desplazamiento).
fprintf() y fscanf()
Estas funciones se comportan exactamente como prinft() y scanf() que ya conocemos, excepto que
operan sobre archivo. La sintaxis es de la siguiente forma:
fprintf(fp, “contenido de la cadena”, variables);
fscanf(fp, “formatos”, variables);
Donde es un puntero al archivo devuelto por una llamada a fopen(). fprintf() y fscanf()
dirigen sus operaciones de E/S al archivo al que apunta .
fgets y fputs
Las funciones fgets() y fputs() pueden leer y escribir cadenas a o desde los archivos. Los prototipos de estas
funciones son:
fputs(“contenido”, fp);
fgets(“contenido”, long, fp );
La función fputs() escribe la cadena a un archivo especifico. La función fgets() lee una cadena desde el
archivo especificado hasta que lee un carácter de nueva línea o longitud-1 caracteres. Si se produce un EOF
(End of File) la función fgets() retorna un NULL.
feof()
Esta función indica con un número distinto a cero que se ha alcanzado el fin del archivo. Su sintaxis es:
feof(fp)
rewind()
La función rewind() inicializa el indicador de posición, al principio del archivo, indicado por su argumento. Su
sintaxis es:
rewind(fp)
ferror()
Determina (devolviendo un número distinto a cero) si se ha producido en error en una operación sobre un
archivo. Su sintaxis es:
ferror(fp)
fseek()
Permite el acceso aleatorio. Su sintaxis es la siguiente:
fseek(fp, offset, posición)
Con esta función, se puede considerar al archivo como una gran cadena. El valor de posición indica la
posición de referencia para el indicador de posición, y puede ser 0 (inicio), 1(actual) ó 2 (final). El valor de
offset indica el desplazamiento en relación a la posición de referencia. Un ejemplo de uso es:
fseek(fp, 2, 0);
Con esta instrucción, posicionamos el indicador a 2 lugares por delante del origen del archivo.
Para saber cuál es la posición en la que está el puntero del archivo, C proporciona la función siguiente: ftell
(fp); que devuelve la posición actual en bytes del puntero del archivo con respecto al principio del mismo.
Para leer y escribir en archivos que no sean de texto las operaciones que se deben utilizar son fread y fwrite.
Escribe tantos datos como indique numero de datos en e l fichero, tomando los datos a partir de la dirección del
dato. Los datos tienen que tener tantos bytes como especifique tamaño. La función fwrite devuelve el
número de elementos escritos, este valor debe coincidir con numero de datos.
Para calcular el tamaño en bytes de un dato o un tipo de dato se suele utilizar la función sizeof(dato) o
sizeof(tipo-de-dato);
Por ejemplo:
int i, V[3]; --> sizeof(i) daría lo mismo que sizeof(int)
--> sizeof(V) daría 3 veces el resultado de sizeof(V[1])
#include<stdio.h>
int main(){
int v[6],elem_escritos,i;
int car=(2+'0');
FILE *f;
f=fopen("datos.txt","wb");
for(i=0;i<6;i++) v[i]=i+1+'0';
/* Para escribir los 3 últimos elementos de v (el 2, el 3 y el 4) */
elem_escritos=fwrite(&v[2],sizeof(int),3,f);
/* Para escribir el primer elemento de v, valen las 2 instrucciones siguientes */
fwrite(v,sizeof(int),1,f);
fwrite(&v[0],sizeof(int),1,f);
/* Para escribir un entero valen las dos siguientes */
fwrite(&car,sizeof(int),1,f);
fwrite(&car,sizeof(int),1,f);
return 0;
}
Lee tantos datos como indique numero de datos del fichero, colocando los datos leídos a partir de la dirección
del dato. Los datos tienen que tener tantos bytes como especifique tamaño del dato. La función fread
devuelve el número de elementos leídos, y e l valor devuelto debe coincidir con numero de datos.
Ejemplo:
#include<stdio.h>
int main(){
FILE *f;
int v[6],elem_escritos,i,num;
f=fopen("datos.txt","rb");
elem_escritos=fread(&v[2],sizeof(int),3,f);
fread(v,sizeof(int),1,f);
fread(&v[0],sizeof(int),1,f);
fread(&num,sizeof(int),1,f);
fread(&num,sizeof(num),1,f);
for(i=0;i<6;i++) printf("%d\n",v[i]);
printf("\nnum: %d",num);
return 0;
}
Lista de ejercicios sobre archivos
1) Copiar el contenido de un archivo de tex to dado a otro. Se debe solicitar un nombre del archivo de entrada,
y otro para la copia.
2) Se tiene un archivo con un conjunto de palabras. Se tiene una palabra por línea, y cada palabra tiene
menos que 50 caracteres. Generar un archivo donde dichas palabras estén ordenadas.
3) Dado un archivo de texto que co ntiene una matriz de enteros, convertirlo a una matriz numérica e imprimir
el resultado. Los elementos de una misma fila están separados por un espacio simple, y la misma termina
con un punto y coma. Sólo se puede usar la sentencia fgetc() para leer el archivo.
4) Diseñar un programa que permita partir un archivo e n partes, donde es un entero dado por el usuario.
5) Realizar un programa que cuente el número de líneas, caracteres y palabras que tiene un archivo de texto
pasado cuya dirección se introduce por teclado.
6) Crear una estructura paciente (nombre, edad, direccion), y crear un archivo que contenga los registros de
pacientes introducidos por teclado. Hacer también lo inverso, un programa que lea un archivo e imprima
en pantalla los datos de los pacientes que contiene.
7) Obtener una raiz de la función en el intervalo [0,1], considerando
como criterio de parada un error absoluto (que es valor absoluto de la diferencia de dos aproximaciones
consecutivas y ) menor a . En un archivo de salida (llamado “salida.tex”) deben almacenarse
almacenarse los
resultados de las iteraciones siguiendo a grandes rasgos el siguiente esquema (las líneas divisorias
representan tabulaciones y saltos de línea):
Iter
Error
1 … … …
2 … … …
3 … … …
4 … … …
5 … … …
6 … … …
7 … … …
8) Realizar un programa que muestre por pantalla el contenido de un fichero fuente de lenguaje C, realizando
una traducción a pseudocódigo sencilla, según la siguiente tabla de traducción:
Lenguaje C Texto a mostrar por pantalla
{ INICIO
} FIN
while MIENTRAS
for PARA
printf MOSTRAR
scanf LEER
9) Se tiene un archivo en el
e l siguiente formato:
Nombre//Apellido//Salario
Las personas están separadas por un salto de línea. Se debe calcular el promedio de todos los salarios de las
personas con apellidos de A a la M. Además, se debe generar un nuevo archivo donde estén todas esas
personas, con sus apellidos en mayúsculas.
int main(){
FILE *fp1,*fp2;
char c;
int i;
char entrada[100],salida[100];
printf("Ingrese el nombre del archivo origen:\n");
scanf("%s",entrada);
if((fp1=fopen(entrada,"r"))==NULL){
printf("\nError al abrir el archivo %s",entrada);
return 0;
}
printf("Ingrese el nombre del archivo destino:\n");
scanf("%s",salida);
if((fp2=fopen(salida,"w"))==NULL){
printf("\nError al crear el archivo %s",salida);
return 0;
}
int cant_pal=0;
c=fgetc(fp1);
while(!feof(fp1)){
if (c == '\n'){
cant_pal = cant_pal + 1;
}
c=fgetc(fp1);
}
rewind(fp1);
char cad[cant_pal][50];
for(i=0;i<cant_pal;i++){
fscanf(fp1,"%s",cad[i]);
}
fclose(fp1);
char *p[cant_pal];
for(i=0;i<cant_pal;i++) *(p+i)=*(cad+i);
ordenar(p,cant_pal);
Ejercicio 3
#include<stdio.h>
int main(){
FILE *fp1;
char c;
int i,m,n,j;
char entrada[100],salida[100];
printf("Ingrese el nombre del archivo origen:\n");
scanf("%s",entrada);
if((fp1=fopen(entrada,"r"))==NULL){
printf("\nError al abrir el archivo %s",entrada);
return 0;
}
n=1;
c=fgetc(fp1);
while((!feof(fp1))&&(c!='\n')){
if(c==' ') n++;
c=fgetc(fp1);
}
rewind(fp1);
c=fgetc(fp1);
m=0;
while(!feof(fp1)){
if (c == '\n'){
m++;
}
c=fgetc(fp1);
}
rewind(fp1);
printf("\nLa cantidad de filas es: %d",m);
printf("\nLa cantidad de columnas es: %d\n",n);
int mat[m][n],num,x;
for(i=0;i<m;i++){
num=0;
x=0;
c=fgetc(fp1);
while(c!='\n'){
if(c==' '){
mat[i][x]=num;
num=0;
x++;
}
else num=num*10+(c-'0');
c=fgetc(fp1);
}
mat[i][x]=num;
}
//salida
printf("\n La matriz es:\n");
for(i=0;i<m;i++){
for(j=0;j<n;j++){
printf("%d\t",mat[i][j]);
}
printf("\n");
}
return 0;
}
Apuntes de la clase de PED2 Fecha: 19 de abril de 2013
MSc. José Colbes
Según lo visto hasta ahora, la reserva o asignación de memoria para vectores y matrices se hace de forma
automática con la declaración de dichas variables, asignando suficiente memoria para resolver el problema de
tamaño máximo, dejando el resto sin usar para problemas más pequeños. Así, si en una función encargada de
realizar un producto de matrices, éstas se dimensionan para un tamaño máximo (100, 100), con dicha función
se podrá calcular cualquier producto de un tamaño igual o inferior, pero aun en el caso de que el producto sea
por ejemplo de tamaño (3, 3), la memoria reservada corresponderá al tamaño máximo (100, 100). Esta idea de
considerar el tamaño máximo fue aplicada en varios ejercicios, sobre todo en los trabajos prácticos. Sin
embargo, puede notarse que por ejemplo, si aplicamos el mismo concepto para varias estructuras de datos que
podrían aparecer en un mismo programa, el desperdicio de espacio en memoria puede ser enorme o inclusive
el programa podría detenerse por falta de memoria disponible.
Es muy útil el poder reservar más o menos memoria en tiempo de ejecución, según el tamaño del caso
concreto que se vaya a resolver. A esto se llama reserva, gestión o administración dinámica de memoria.
Existen en C dos funciones que reservan la cantidad de memoria deseada en tiempo de ejecución. Dichas
funciones devuelven –es decir, tienen como valor de retorno – un puntero a la primera posición de la zona de
memoria reservada. Estas funciones se llaman malloc() ycalloc(), y sus declaraciones, que están en la librería
stdlib.h, son como sigue:
Existe también una función llamada free() que deja libre la memoria reservada por malloc() o calloc() y que ya
no se va a utilizar. Esta función usa como argumento el puntero devuelto por calloc() o malloc(). La memoria no
se libera por defecto, sino que el programador tiene que liberarla explícitamente con la función free(). El
prototipo de esta función es el siguiente:
void free(void *)
A continuación se presenta un ejemplo de gestión dinámica de memoria para el producto de matriz por vector.
Hay que tener en cuenta que reservando memoria por separado para cada fila de la matriz, no se garantiza que
las filas estén contiguas en la memoria. Por otra parte, de esta forma se pueden considerar filas de distinto
tamaño (veremos con un ejemplo más adelante). El nombre de la matriz se declara como puntero a vector de
punteros, y los nombres de los vectores c omo punteros.
Ahora, volvamos a considerar el problema de ordenar un conjunto de palabras. Esta vez, utilizaremos sólo el
espacio necesario para almacenar cada palabra. O sea, los tamaños de cada fila no serán necesariamente
iguales.
int main(){
int i,n;
printf("Ingrese la cantidad de palabras: "); scanf("%d",&n);
char *pal[n];
char cad[31];
printf("\nIngrese las palabras:\n");
for(i=0;i<n;i++){
printf("Palabra %d: ",(i+1));
scanf("%s",cad);
if ((pal[i] = (char*) malloc((strlen(cad)+1)*sizeof(char))) == NULL){
printf("\nmalloc fallido para la palabra %d\n",i);
return 0;
}
strcpy(pal[i],cad);
}
ordenar(pal,n);
printf("\nPalabras ordenadas:\n");
for(i=0;i<n;i++){
puts(*(pal+i));
}
return 0;
}
C provee seis operadores para manipulación de bits; estos sólo pueden ser aplicados a operadores enteros,
esto es: char, short, int y long, sean con signo o sin él.
El operador binario AND es a menudo utilizado para desenmascarar un conjunto de bits, por ejemplo:
n = n & 127;
Pone a cero todos los bits de n, excepto los 7 bits de más bajo orden.
x = x | SET_ON;
El operador binario XOR asigna un 1 en cada posición de bit en donde sus operandos tienen diferentes bits, y
cero cuando son los mismos.
Los operadores de desplazamiento << y >> realizan desplazamientos a la izquierda o derecha tantas veces
como lo indique el operando derecho, el cual debe ser no-negativo.
Entonces, x<<2 desplaza el valor binario de x en dos posiciones, llenando los bits vacantes con cero; esto es
equivalente a la multiplicación por 4. El operador >> realiza un desplazamiento de bits a la derecha del valor de
la izquierda, introduciendo cero por la izquierda, tantas veces como indique el segundo operador; equivale a
dividir por 2 tantas veces como indique el se gundo operando.
El operador unario devuelve el complemento a uno de un entero; esto es, convierte cada bit 1 en 0 y viceversa.
Por ejemplo:
Coloca los seis últimos bits (hacia la derecha) de x a cero. Nótese que es independiente de la longitud de los
números involucrados.
Como una ilustración de algunos operadores de bits, considere la función getbits(x,p,n) que retorna el
valor del campo de n bits de x que comienza en la posición p. Asumimos que la el bit de posición cero está
hacia la derecha y que n y p son enteros positivos. Por ejemplo, getbits(x,4,3) retorna los tres bits en las
posiciones 4, 3 y 2.
#include<stdio.h>
int getbits(int x, int p, int n){
return (x >> (p+1-n)) & ~(~0 << n);
}
int main(){
int a,b,c,d;
printf("Ingrese el numero: ");
scanf("%d",&a);
printf("Ingrese la posicion inicial: ");
scanf("%d",&b);
printf("Ingrese el tamanho del campo: ");
scanf("%d",&c);
d=getbits(a,b,c);
printf("\nResultado: %d",d);
return 0;
}
La expresión x >> (p+1-n) mueve el campo deseado a la punta derecha. ~0 hace que todos los bits sean 1;
desplazándolo a la izquierda n posiciones con ~0 << n coloca ceros en los n bits más a la derecha;
complementándolo con ~ se logra una máscara con unos en los n bits más a la derecha.
Existe el operador ternario “ ?:” que puede utilizarse en una expresión condicional, lo cual provee una
alternativa a la estructura if-else. En la expresión:
z = (a > b) ? a : b; /* z = max(a, b) */
Se debe notar que la expresión condicional es en realidad una expresión, y puede ser usada donde sea que
pueda estar cualquier otra expresión. Si expr2 y expr3 son de diferentes tipos, el tipo del resultado es
determinado por las reglas de conversión. Por ejemplo, si f es float y n es int, entonces la expresión:
(n > 0) ? f : n
La expresión condicional usualmente lleva a código abreviado. Por ejemplo, este ciclo imprime n elementos de
un arreglo, 10 por línea, con cada columna separada por un espacio, y con cada línea (incluida la última)
terminada por un salto de línea.
for(i=0;i<n;i++)
printf("%d%c", a[i], (i%10==9 || i==n-1) ? '\n' : ' ');
Un salto de línea se imprime cada 10 elementos, y luego del último. Todos los demás elementos van seguidos
de un espacio simple. Esto puede verse confuso, pero es más compacto que el if-else equivalente. Otro
buen ejemplo es:
Cuando se ejecuta un programa desde la línea de comando tecleando su nombre, existe la posibilidad de
pasarle algunos datos, tecleándolos a continuación en la misma línea. Por ejemplo, se le puede pasar algún
valor numérico o los nombres de algunos ficheros en los que tiene que leer o escribir información. Esto se
consigue por medio de argumentos que se pasan a la función main(), como se hace con otras funciones.
Así pues, a la función main() se le pueden pasar argumentos y también puede tener valor de retorno. El
primero de los argumentos de main() se suele llamar argc, y es una variable int que contiene el número
de palabras que se teclean a continuación del nombre del programa cuando éste se ejecuta. El segundo
argumento se llama argv, y es un vector de punteros a carácter que contiene las direcciones de la primera
letra o carácter de dichas palabras. A continuación 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;
}
Ejemplo: ordenar ascendentemente números y guardar el resultado en un archivo. Tanto el nombre del
archivo de salida como los números se introducen por línea de comando.
#include<stdio.h>
void ordenar(int a[], int n){
int i,j,b,aux;
do{
b=0;
for(j=0;j<(n-1);j++){
if(a[j]>a[j+1]){
b=1;
aux=a[j];a[j]=a[j+1];a[j+1]=aux;
}
}
}while(b);
}
int main(int argc, char *argv[]){
int i;
int n=argc-2; //por el nombre del programa y el archivo de salida
int vec[n];
if(n<3){
printf("No hay suficientes parametros de entrada\n");
return 0;
}
for(i=0;i<n;i++) vec[i]=atoi(argv[i+2]);
ordenar(vec,n);
FILE *fp;
if((fp=fopen(argv[1],"w"))==NULL){
printf("Error al crear el archivo\n");
return 0;
}
for(i=0;i<n;i++) fprintf(fp,"%d\n",vec[i]);
fclose(fp);
printf("El archivo ha sido creado con exito!!");
return 0;
}
Ejercicios
1. Escribir un programa (calculadora.c) que acepte como parámetros dos números enteros y una
operación básica (+,-,*,/,^) y muestre el resultado de la operación en pantalla.
2. Escribir un programa que reciba como argumento una cadena e imprima la misma pero en mayúsculas.
3. Escribir un programa que permita copiar un archivo. Se deben recibir como parámetros en la línea de
comando el nombre del archivo original y el del archivo copia.
4. Escribir una función invert(x,p,n) que retorne x con los n bits que comienzan en la posición p de
manera invertida (es decir, el 1 se cambia por el cero y viceversa), dejando los demás sin cambio.
5. Escribir una función righrot(x,n) que retorne el valor de un entero x rotado n posiciones a la
derecha.
6. Escribir una función lower que reciba una cadena y convierta letras mayúsculas en minúsculas,
utilizando expresiones condicionales en lugar de if-else.
Apuntes de la clase de PED2 Fecha: 26 de abril de 2013
MSc. José Colbes
Clasificación:
Listas simplemente enlazadas
Listas doblemente enlazadas
Lista circular simplemente enlazada
Lista circular doblemente enlazada
Implementaciones
Asignación fija (mediante arreglos)
Asignación dinámica (mediante punteros y reserva dinámica de memoria)
1. Declaración de un nodo
Nodo *cabeza;
Cabeza = NULL;
3. Creación de un nodo
Ejercicio de aplicación
Se va a formar una lista enlazada de números aleatorios. El programa que realiza esta tarea inserta los nuevos
nodos por la cabeza de la lista. Una vez creada la lista, se recorre los nodos para mostrar los números pares.
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#define MAX 20
typedef int Item;
typedef struct Elemento{
Item dato;
struct Elemento* siguiente;
}Nodo;
int main(){
Item d;
Nodo *cabeza, *ptr;
int k;
cabeza=NULL; /* lista vacía */
srand(time(NULL));
d=rand()%MAX;
while(d){/* Bucle termina cuando se genera el número 0 */
printf("%d ",d); //para mostrar los numeros que se generan
inserPrimero(&cabeza,d);
d=rand()%MAX;
}
printf("\n\n");
for(k=0,ptr=cabeza;ptr!=NULL;ptr=ptr->siguiente){
if((ptr->dato)%2==0) /* se recorre la lista para escribir los pares */
{ printf("%d ",ptr->dato);
k++;
printf("%c",(k%12 ? ' ' : '\n')); /* 12 datos por línea */
}
}
printf("\n\n");
return 0;
}
Ejercicio de aplicación
Se desea crear una lista enlazada de números enteros ordenada. La lista se organiza de tal forma que el nodo
cabecera tenga el menor elemento, y así en orden creciente los demás nodos. Al iniciar el programa, se debe
desplegar (y por supuesto, implementar) estas opciones para el usuario:
Agregar elemento con un cierto valor
Eliminar un elemento con un cierto valor. Si no se encuentra, indicarlo con un mensaje.
Mostrar la lista
Salir del programa
Apuntes de la clase de PED2 Fecha: 3 de mayo de 2013
MSc. José Colbes
Pilas y Colas (Capítulo 12 – “Fundamentos de la Programación” – Luis Joyanes Aguilar; Capítulos 10 y 1 1 –
“Estructuras de datos, una perspectiva en C” – Luis Joyanes Aguilar)
Pilas
Una pila (stack ) es un tipo especial de lista lineal en la que la inserción y borrado de nuevos elementos se
realiza sólo por un extremo que se denomina cima o tope (top).
La pila es una estructura con numerosas analogías en la vida real: una pila de platos, una pila de monedas, una
pila de cajas de zapatos, una pila de camisas, una pila de bandejas, etc.
Dado que las operaciones de insertar y eliminar se realizan por un solo extremo (el superior), los elementos
sólo pueden eliminarse en orden inverso al que se insertan en la pila. El último elemento que se pone en la pila
es el primero que se puede sacar; por ello, a estas estructuras se les conoce por el nombre de LIFO (last-in,
first-out, último en entrar, primero en salir ).
La pila se puede implementar mediante arrays, en cuyo caso su dimensión o longitud es fija; y mediante listas
enlazadas, en cuyo caso se utiliza memoria dinámica y no existe limitación en su tamaño excepto la memoria
libre del ordenador.
Una pila puede estar vacía (no tiene elementos) o llena (en el caso de tener tamaño fijo, si no caben más
elementos en la pila). Si un programa intenta sacar un elemento de una pila vacía, se producirá un error,
debido a que esa operación es imposible; esta situación se denomina desbordamiento negativo (underflow ).
Por el contrario, si un programa intenta poner un elemento en una pila se produce un error llamado
desbordamiento (overflow ) o rebosamiento . Para evitar estas situaciones se diseñan funciones, que
comprueban si la pila está llena o vacía.
Una implementación estática se realiza utilizando un arreglo de tamaño fijo y una implementación dinámica
mediante una lista enlazada.
La pila implementada con arreglos incluye un arreglo y un índice a la cima de la pila; además, una constante
con el máximo número de elementos.
include<stdio.h>
#include<stdlib.h>
La realización dinámica de una pila se hace almacenando los elementos como nodos de una lista enlazada. Con
la particularidad de que siempre que se desee insertar o poner (empujar) un elemento se hace por el mismo
extremo que se extrae.
Esta realización tiene la ventaja de que el tamaño se ajusta exactamente a los elementos de la pila, no hay un
máximo de elementos y, por tanto, no existe la condición de pila llena. Sin embargo, para cada elemento es
necesaria una memoria ya que hay que guardar el campo de enlace. En la realización de una pila con arreglos
se requiere establecer un máximo de posibles elementos, como contrapartida el acceso es más rápido ya que
se hace con una variable subindicada.
#include<stdio.h>
#include<stdlib.h>
int main(){
Item x;
int n,i=0;
Nodo *pila;
crearPila(&pila);
scanf("%d",&n);
while(n!=0){
i++;
insertar(&pila,n);
scanf("%d",&n);
}
while(i--){
x=quitar(&pila);
printf("%d ",x);
}
return 0;
}
Ejercicio de aplicación 1
Leer una cadena, y con la ayuda de una pila determinar si es palíndroma o no.
#include<stdio.h>
#include<stdlib.h>
int main(){
Item x;
int j;
Nodo *pila;
crearPila(&pila);
Item cad[100];
printf("\nIngrese una cadena:\n");
for(j=0;(x=getchar())!='\n';){
cad[j++]=x;
insertar(&pila,x);
}
cad[j]='\0';
int es_pal=1;
for(j=0;es_pal && !pilaVacia(pila);){
es_pal = cad[j++]==quitar(&pila);
}
limpiarPila(&pila);
if(es_pal) printf("\nEs un palindromo");
else printf("\nNo es un palindromo");
return 0;
}
Ejercicio de aplicación 2
Dada una línea que representa una expresión matemática, donde se usan paréntesis, llaves y corchetes; indicar
si la misma es correcta o no en relación a estos símbolos.
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
typedef char Item;
typedef struct nodo{
Item elemento;
struct nodo *siguiente;
}Nodo;
int main(){
Item x;
int i=0;
Nodo *pila;
crearPila(&pila);
Item cad[100];
printf("\nIngrese una cadena:\n");
gets(cad);
int correcto=1;
while(cad[i]!='\0'){
if(cad[i]=='(' || cad[i]=='[' || cad[i]=='{'){
insertar(&pila,cad[i]);
}
if(cad[i]==')' || cad[i]==']' || cad[i]=='}'){
x=quitar(&pila);
if(cad[i]==')' && x!='('){
correcto=0;
break;
}
if(cad[i]==']' && x!='['){
correcto=0;
break;
}
if(cad[i]=='}' && x!='{'){
correcto=0;
break;
}
}
i++;
}
if(!pilaVacia(pila)) correcto=0;
if(correcto) printf("\nLa expresion es correcta");
else printf("\nLa expresion es incorrecta");
return 0;
}
Colas
Una cola es una estructura de datos que almacena elementos en una lista y permite acceder a los datos por
uno de los dos extremos de la lista. Un elemento se inserta en la cola (parte final) de la lista y se suprime o
eliminar por el frente (parte inicial) de la lista. Las aplicaciones utilizan una cola para almacenar elementos en
su orden de aparición o concurrencia.
Los elementos se eliminan (o quitan) de la cola en el mismo orden en que se almacenan y, por consiguiente,
una cola es una estructura de tipo FIFO ( first-in/first-out , primero en entrar/primero en salir). La fila de
atención a clientes es un ejemplo de cola, o bien una cola de trabajos en una impresora.
Las operaciones usuales en las colas son Insertar y Quitar . La operación Insertar añade un elemento por el
extremo final de la cola, y la operación Quitar elimina o extrae un elemento por el extremo opuesto, el frente
de la cola. La organización de elementos en forma de cola asegura que el primero en entrar es el primero en
salir.
La forma que los lenguajes tienen para representar el TAD Cola depende de dónde se almacenen los
elementos, en un arreglo o en una lista dinámica. La utilización de arreglos tiene el problema de que la cola no
puede crecer indefinidamente, está limitada por el tamaño del arreglo; como contrapartida, el acceso a los
extremos es muy eficiente. Utilizar una lista dinámica permite que el número de nodos se ajuste al de
elementos de la cola; por el contrario, cada nodo necesita memoria extra para el enlace y también está el
límite de memoria de la pila del computador.
Cola implementada con arreglos
La definición de una Cola ha de contener un arreglo para almacenar los elementos de la cola, y dos marcadores
o apuntadores para mantener las posiciones frente y final de la cola; es decir, un marcador apuntando a la
posición de la cabeza de la cola y el otro al primer espacio vacío que sigue al final de la cola.
El avance lineal de frente y final tiene un grave problema, deja huecos por la izquierda del arreglo. Puede
ocurrir que final alcance el índice más alto del arreglo, no pudiéndose insertar nuevos elementos y, sin
embargo, existir posiciones libres a la izquierda de frente.
Una alternativa a esta situación sería mantener fijo el frente de la cola al comienzo del arreglo, lo que supone
mover todos los elementos de la cola una posición cada vez que se desea retirar un elemento. Estos problemas
quedan resueltos considerando el arreglo como circular .
En este último caso, el arreglo sigue siendo lineal, pero se simula un movimiento circular mediante los restos,
de la siguiente forma:
#include<stdio.h>
#include<stdlib.h>
La alternativa a los arreglos reside e n utilizar memoria que se ajusta en todo momento al número de elementos
de la cola, empleando memoria dinámica mediante una lista enlazada, aún a pesar de tener el inconveniente
de la memoria extra utilizada para realizar los encadenamientos entre nodos. Esta implementación utiliza dos
punteros para acceder a la lista, frente y final (los extremos por donde salen los elementos y por donde se
insertan respectivamente).
La variable puntero frente referencia al primer elemento de la cola, el primero en ser retirado de la cola. La
otra variable puntero, final, referencia al último elemento en ser añadido, que será el último en ser retirado.
Al ser una estructura dinámica, puede crecer y decrecer según las necesidades (el límite está en la memoria
libre del computador), y por tanto, no tie ne sentido la operación que prueba si la cola está llena.
#include<stdio.h>
#include<stdlib.h>
typedef struct {
Nodo *frente;
Nodo *final;
}Cola;
Ejercicio de aplicación
Una variación del problema matemático llamado “problema de José” permite generar números de la suerte. Se
parte de una lista inicial de n números; esta lista se va reduciendo con el siguiente algoritmo:
int main(){
Cola cola;
int n,n1,n2,n3,i;
crearCola(&cola);
srand(time(NULL));
//Numero de elementos de la lista
n=1+rand()%50;printf("n=%d\n",n);
//Se generan n numeros aleatorios
for(i=0;i<n;i++)
insertar(&cola, 1+rand()%1001);
n1=1+rand()%11;
while(n1<=n){
printf("\nSe quitan elementos a distancia %d",n1);
n2=0; //Contador de elementos que quedan
for(i=1;i<=n;i++){
n3=quitar(&cola);
if(i%n1==1) printf("\t%d se quita.",n3);
else{
insertar(&cola,n3); //Se vuelve a meter en la cola
n2++;
}
}
n=n2;
n1=1+rand()%11;
}
printf("\n\nLos numeros de la suerte son:\n");
while(!colaVacia(cola))
printf("%d ",quitar(&cola));
return 0;
}
Apuntes de la clase de PED2 Fecha: 10 de mayo de 2013
MSc. José Colbes
class Hora{
public:
Hora();
void estableceHora(int,int,int);
void imprimeEstandar();
private:
int hora;
int minuto;
int segundo;
};
Hora::Hora(){
hora=minuto=segundo=0;
}
void Hora::imprimeEstandar(){
cout<<((hora==0 || hora==12) ? 12 : hora%12)
<<":"<<((minuto<10) ? "0" : "")<<minuto
<<":"<<((segundo<10) ? "0" : "")<<segundo
<<((hora<12) ? " AM" : " PM");
}
h.estableceHora(13,27,6);
cout<<"\n\nLa hora estandar despues de estableceHora es: ";
h.imprimeEstandar();
h.estableceHora(99,99,99);
cout<<"\n\nDespues de intentar establecer valores invalidos:";
cout<<"\nHora estandar: ";
h.imprimeEstandar();
cout<<endl;
return 0;
}
class Rectangulo{
public:
Rectangulo();
double Perimetro();
double Area();
void establecer_ancho(double);
void establecer_longitud(double);
private:
double longitud,ancho;
};
Rectangulo::Rectangulo(){
longitud=1.0;
ancho=1.0;
}
double Rectangulo::Perimetro(){
double p;
p=2*(longitud+ancho);
return p;
}
double Rectangulo::Area(){
double a;
a=longitud*ancho;
return a;
}
int main(){
Rectangulo rect;
rect.establecer_ancho(2.5);
rect.establecer_longitud(3.5);
cout<<"El perimetro es: "<<rect.Perimetro()<<endl;
cout<<"El area es: "<<rect.Area()<<endl;
class Cadena{
public:
Cadena(const char *);
~Cadena();
void mayusculas();
void minusculas();
void asignar(const char *);
void imprimir();
private:
char *c;
};
Cadena::~Cadena(){
delete[] c;
cout<<"Se ejecuto el destructor"<<endl;
}
void Cadena::imprimir(){
cout<<c<<endl;
}
void Cadena::mayusculas(){
int i=0;
while(c[i]!=0){
if(c[i]>='a' && c[i]<='z') c[i]+=('A'-'a');
i++;
}
}
void Cadena::minusculas(){
int i=0;
while(c[i]!=0){
if(c[i]>='A' && c[i]<='Z') c[i]-=('A'-'a');
i++;
}
}
int main(){
char c[1000];
cin.getline(c,1000);
Cadena cad(c);
Cadena *cad2 = new Cadena("Prueba");
cad.imprimir();
cad.mayusculas();
cad.imprimir();
cad.minusculas();
cad.imprimir();
cad2->imprimir();
delete cad2;
return 0;
}
class Operacion{
public:
//Operacion();
int sumar(int,int);
int sumar(int,int,int);
double sumar(double,double);
};
//Operacion::Operacion(){}
int main(){
Operacion oper;
int a=5,b=6,c=7;
double d=1.0,e=2.5;
cout<<"Suma 1: "<<oper.sumar(a,b)<<endl;
cout<<"Suma 2: "<<oper.sumar(a,b,c)<<endl;
cout<<"Suma 3: "<<oper.sumar(e,d)<<endl;
return 0;
}
class CuentaAhorros{
private:
static double tasaInteresAnual;
double saldoAhorro;
public:
CuentaAhorros(double);
void ultimoInteres();
static void modificaTasaInteres(double);
};
double CuentaAhorros::tasaInteresAnual=3.0;
CuentaAhorros::CuentaAhorros(double s){
saldoAhorro=s;
}
void CuentaAhorros::ultimoInteres(){
double interes;
interes=saldoAhorro*tasaInteresAnual/100;
cout<<"El interes es: "<<interes<<endl;
saldoAhorro+=interes;