DiagrFlujo-ProgramacionC

153
PRÓLOGO 4 CAPÍTULO Nº 0 : INTRODUCCIÓN 5 CAPÍTULO Nº 1 : DIAGRAMAS DE FLUJO 6 Símbolos utilizados en Diagramas de Flujo 6 Instrucciones de Asignación 7 Instrucciones de Entrada y Salida 7 Instrucciones de Control 7 Inicio y Término 8 Niveles de un diagrama de flujo. 9 Contadores 11 Análisis del diagrama. 14 Operadores Matemáticos: 15 Estructura While (Mientras) 17 Resumen Diagramas de Flujo 20 Resumen Diagramas de Flujo 21 Operador Módulo 24 Problemas Resueltos y Propuestos 26 CAPÍTULO Nº 2 : LENGUAJE C 36 Un poco de historia 36 Forma General del Lenguaje C. 37 Estructura de un Programa 38 Bibliotecas 39 Indentación 39 Identificadores 39 Comentarios 40 Tipos de datos fundamentales 40 Resumen Palabras clave 41 Operadores 41 Operadores Matemáticos 41 Operadores de Relación 42 Operadores Lógicos 42 Comenzando a Programar 42 Declaración de variables 42 Asignación: 43 Inicialización 44 Entrada / Salida Estándar 44 printf() 44 scanf() 46

Transcript of DiagrFlujo-ProgramacionC

Page 1: DiagrFlujo-ProgramacionC

PRÓLOGO 4

CAPÍTULO Nº 0 : INTRODUCCIÓN 5

CAPÍTULO Nº 1 : DIAGRAMAS DE FLUJO 6

Símbolos utilizados en Diagramas de Flujo 6 Instrucciones de Asignación 7 Instrucciones de Entrada y Salida 7

Instrucciones de Control 7 Inicio y Término 8 Niveles de un diagrama de flujo. 9

Contadores 11 Análisis del diagrama. 14 Operadores Matemáticos: 15

Estructura While (Mientras) 17

Resumen Diagramas de Flujo 20

Resumen Diagramas de Flujo 21 Operador Módulo 24

Problemas Resueltos y Propuestos 26

CAPÍTULO Nº 2 : LENGUAJE C 36 Un poco de historia 36

Forma General del Lenguaje C. 37 Estructura de un Programa 38 Bibliotecas 39 Indentación 39 Identificadores 39 Comentarios 40

Tipos de datos fundamentales 40

Resumen Palabras clave 41

Operadores 41 Operadores Matemáticos 41 Operadores de Relación 42 Operadores Lógicos 42

Comenzando a Programar 42 Declaración de variables 42 Asignación: 43 Inicialización 44

Entrada / Salida Estándar 44 printf() 44 scanf() 46

Page 2: DiagrFlujo-ProgramacionC

Programación de Computadores y el Lenguaje C (versión 2003)

Profesor: Roberto Uribe P. Pág. Nº 2

Problemas Resueltos y Propuestos 50

CAPÍTULO Nº 3: ESTRUCTURAS DE CONTROL 54 Estructura if 54 Proposición if - else 54 Estructura while (Mientras) 56 Algo más del if 59 Algo más del while 60 Estructura for 61 Ciclo do-while 62 Proposición break 63 Proposición Continue 63 Proposición switch 63

Problemas Resueltos y Propuestos 66

CAPÍTULO Nº 4 : FUNCIONES EN C 72 Instrucción return 73 Variables Globales y Locales 74 Llamadas a Funciones 75 Paso de Parámetros a funciones 77 Paso de variables por valor 78

Problemas Resueltos y Propuestos 83

CAPÍTULO Nº 5 : ARREGLOS O MATRICES 87 Arreglos de n-dimensiones 89 Constantes Simbólicas 90 Cadenas de Caracteres 94 Cadenas sin tamaño definido 95 Como retornar un string 96 Arreglos como parámetros 97

Problemas Resueltos y Propuestos 101

CAPÍTULO Nº 6 : MODOS DE ALMACENAMIENTO 110

Variables Automáticas (auto) 110

Variables Externas (extern) 110

Variables Registro (register) 111

Variables Estáticas (static) 112

Variables estáticas externas 112

CAPÍTULO Nº 7 : PUNTEROS EN C 113 Operador & 113 Operador * 114 Paso de parámetros por referencia 115 Aritmética de Punteros 118 Algo más sobre punteros 119 malloc, free y sizeof 119

Page 3: DiagrFlujo-ProgramacionC

Universidad de Magallanes - Departamento de Ingeniería en Computación

Profesor: Roberto Uribe P. Pág. Nº 3

Un ejercicio gráfico 121

Problemas Propuestos 124

CAPÍTULO Nº 8 : TYPEDEF, STRUCT Y UNION 125

Typedef 125

Estructuras 126 Arreglos de estructuras 127 Algo más sobre estructuras 131 Estructuras Anidadas 131 Funciones y Estructuras 133 Punteros a Estructuras 134 Algo más sobre punteros a estructuras 135

Uniones (unión) 136

Problemas Resueltos y Propuestos 138

CAPÍTULO Nº9 : ARCHIVOS EN C (EN CONSTRUCCIÓN) 144

Tipo usado en archivos 144 FILE 144

Funciones para manejo de archivos 144 Un ejemplo interesante con fread 149

ÚLTIMAS NOTAS 151

ANEXO Nº 1 : ESTRUCTURA DE UN COMPILADOR 152

ANEXO Nº 2 : ALGUNOS CONCEPTOS 153

Page 4: DiagrFlujo-ProgramacionC

Programación de Computadores y el Lenguaje C (versión 2003)

Profesor: Roberto Uribe P. Pág. Nº 4

UNIVERSIDAD DE MAGALLANES

FACULTAD DE INGENIERÍA ESCUELA DE COMPUTACIÓN

Programación de Computadores

Profesor: Roberto Uribe Paredes

Prólogo Cada vez que hacemos uso de una computadora, debemos enfrentarnos a distintos software, que van desde el sistema operativo hasta las planillas electrónicas, procesadores de texto, software de comunicación, etc. Todos ellos son programas, y han demandado un arduo trabajo que involucró tanto a analistas, diseñadores y programadores. Las etapas en el desarrollo de un software son bastantes y generalmente no fáciles, más aún cuando en la actualidad las necesidades son complejas. El presente texto no pretende abarcar el área de desarrollo de software ni las etapas de este, sino insertar al alumno en el pensamiento lógico, necesario para desarrollar nuestros primeros programas. Para cumplir este objetivo primero se emplea un método gráfico para resolución de problemas, Diagramas de Flujo. Luego se inserta al alumno en el formalismo de un lenguaje de programación, C, empezando por resolver los problemas vistos con el método anterior. Finalmente, se van complicando los problemas a resolver, ahora sólo utilizando el lenguaje formal, de tal manera de ir adquiriendo los conocimientos necesarios para resolver ciertos problemas. El texto1 contiene problemas y ejercicios resueltos para cada capítulo, además, cada uno de estos ejercicios tiene el análisis y la explicación del método usado para resolverlo. También se incluye la ejecución de algunos de los programas, es decir, el cómo funciona. Al finalizar el curso el alumno estará capacitado para analizar, resolver e implementar soluciones (programas) a diversos problemas de aplicaciones computacionales.

1 La primera versión del texto comenzó a escribirse en 1995.

Page 5: DiagrFlujo-ProgramacionC

Universidad de Magallanes - Departamento de Ingeniería en Computación

Profesor: Roberto Uribe P. Pág. Nº 5

Capítulo Nº 0 : Introducción Dentro de los métodos usados para resolver problemas en lo que llamamos programación estructurada está el conocido como Diseño Top-Down. Este consiste en dividir el problema en partes, de tal manera de crear subproblemas que son más fáciles de resolver, si estos subproblemas son lo suficientemente complicados, entonces se vuelve a dividir. Esta división se hace hasta dejar pequeños problemas, los cuales se pueden resolver como módulos independientes. Luego de esto se van uniendo los módulos (soluciones) de tal manera de resolver el problema original. El diseño top-down responde a la idea de que es más fácil manejar pequeños problemas y resolverlos uno a uno a enfrentar un sólo problema de mayor complejidad. Cuando se habla de este diseño se lo asocia a la frase dividir para reinar, que es el mismo concepto, es decir, dividir nuestro problema para cumplir nuestros objetivos. En los primeros capítulos de este texto los problemas planteados son lo bastante sencillos para poder resolverlos como un módulo. Posteriormente cuando la complejidad aumenta se aplica el diseño top-down, esto es posible ya que nuestros pequeños problemas los resolvemos en lo que conoceremos como funciones.

Page 6: DiagrFlujo-ProgramacionC

Programación de Computadores y el Lenguaje C (versión 2003)

Profesor: Roberto Uribe P. Pág. Nº 6

Capítulo Nº 1 : Diagramas de Flujo La programación de computadores, es darle órdenes a un computador, comúnmente escritas, para ello existen pasos previos: - ¿Para qué queremos programarlo? - ¿Qué secuencia de órdenes será la más eficiente? - ¿Qué lenguaje emplearemos? Por lo general para responder a estas interrogantes se hace uso de diagramas (se entiende más fácilmente un mapa que una secuencia de coordenadas). Existen a lo menos tres formas gráficas para representar soluciones a problemas: - Diagrama de Flujo. - Diagrama de BOHM y Jacopini - Diagrama F.P.L. (First Programming Languaje). El más común es el Diagrama de Flujo. Los diagramas de flujo están siendo cada vez menos utilizados, esto debido, a que los computadores tienden cada vez más a los lenguajes naturales. Los niveles en que se representa un diagrama pueden ser tres: * Nivel conceptual * Nivel Algorítmico. * Nivel Instrucción. Definir estos tres niveles es arbitrario y depende de la complejidad del problema. Antes de comenzar a solucionar un problema hay que tener muy claro cuales son los objetivos generales que se persiguen, esto es, saber exactamente lo que se esta pidiendo (¿Para qué queremos programarlo?). El próximo paso a seguir es determinar una secuencia de instrucciones que de solución al problema, esta secuencia se conoce como "Algoritmo", para problemas clásicos como ordenar y buscar existen ya diferentes algoritmos de solución. Ejemplos de algoritmos de ordenación: - Método de la Burbuja - Método de Selección - Algoritmo de Mezcla

Símbolos utilizados en Diagramas de Flujo Todo lenguaje de programación posee tres tipos básicos de instrucciones: - Asignación - Entrada/Salida

Page 7: DiagrFlujo-ProgramacionC

Universidad de Magallanes - Departamento de Ingeniería en Computación

Profesor: Roberto Uribe P. Pág. Nº 7

- Control (Los diagramas de flujo no están ajenos a esta clasificación). Instrucciones de Asignación Se refiere comúnmente a acciones a seguir o cálculos que quedan almacenados en algún lugar (variables de memoria): Ejemplos: Se usa el rectángulo para identificar asignaciones. Instrucciones de Entrada y Salida Toda máquina en general requiere comunicarse con el mundo exterior (generalmente usuarios), es decir, necesita de instrucciones de entrada y salida, estas se denotan por un rombo (o romboide). Ejemplos :

Los rombos se acompañan de una flecha, que indica la entrada o salida de información. Instrucciones de Control Un programa es una secuencia de instrucciones que dependiendo de resultados parciales, la secuencia puede ser alterada. Para ello se utiliza el símbolo: Dependiendo de la condición, si es verdadera o falsa toma uno de los sentidos.

Imprimir Datos

Leer Palabra

Pregunta Si No

Tomar Lapiz Tomar Lapiz Sumar A y B

producto=A*B

Salida

Entrada

Page 8: DiagrFlujo-ProgramacionC

Programación de Computadores y el Lenguaje C (versión 2003)

Profesor: Roberto Uribe P. Pág. Nº 8

Ejemplo: Se utiliza a veces el símbolo de decisión con más de dos salidas, por ejemplo: Inicio y Término Para especificar el Principio o fin de una secuencia, se utiliza el símbolo. Ejemplo: Los símbolos de diagramas de flujo se unen con rectas, dos rectas se pueden unir, pero una recta no puede dividirse en dos.

Nota >= 50 Si No

Aprobado Reprobado

NotaFinal >=50<30

Aprobado Reprobado

>=30

Da Examen

Inicio

Fin

Proceso

Page 9: DiagrFlujo-ProgramacionC

Universidad de Magallanes - Departamento de Ingeniería en Computación

Profesor: Roberto Uribe P. Pág. Nº 9

Ejemplos: Para que exista una bifurcación en un Diagrama de Flujo., necesariamente debe existir un símbolo de decisión. Niveles de un diagrama de flujo. - Nivel Conceptual - Nivel Algorítmico - Nivel Instrucción

Paso1

Paso2

Paso4

Paso3

Fin

Correcto !!

Correcto !!

Sumar i y j

Paso1 Paso2

Pregunta? Si No

Sumar i y j

Paso1 Paso2

Incorrecto !!

Error

Page 10: DiagrFlujo-ProgramacionC

Programación de Computadores y el Lenguaje C (versión 2003)

Profesor: Roberto Uribe P. Pág. Nº 10

Ejemplos: 1). Nivel Conceptual: 2) Nivel Algoritmo (Llamada de teléfono) (llamada de teléfono público) 3) Nivel Instrucción. Ejemplo: Leer A y B, luego sumar A y B y asignarla a C.

Inicio

Fin

Buscar Teléfono

Discar

Hablar

Colgar

Inicio

Fin

C=A+B

Leer A

Leer B

C

Inicio

Fin

Levantar fono

¿Hay Tono?

Insertar Moneda

Discar

¿Contestan?

Hablar

Colgar

Si No

Si

No

Page 11: DiagrFlujo-ProgramacionC

Universidad de Magallanes - Departamento de Ingeniería en Computación

Profesor: Roberto Uribe P. Pág. Nº 11

Lo que hace el programa anterior es leer dos variables A y B desde algún dispositivo de entrada (como el teclado), sumarlas y asignar el resultado a C. En este caso A y B ya tenían valores asignados antes de la suma. La suma de "A" y "B" se asigna a "C", independiente del valor anterior de "C". Este ejemplo hace lo mismo que el anterior, pero se inicializan las variables al comienzo del programa. En ambos ejemplos se utilizaron instrucciones básicas de entrada, salida y de asignaciones. El nivel de instrucción es mas próximo a la programación de computadores, los niveles anteriores son simplemente para representar divisiones más gruesas de un problema, como las situaciones cotidianas indicadas.

Contadores Los contadores son variables de memoria (de ahora en adelante "variable"), que se incrementan o decrementan de acuerdo a la necesidad del programador, se denotan de la siguiente manera (asignación): El símbolo igualdad tiene un significado diferente que en aritmética, "lo que existe a la derecha es una expresión y lo que existe a la izquierda es una variable". Una vez realizada la operación la variable asume el valor de la expresión. Para este caso, el contador fue incrementado en 1. Generalmente los contadores son utilizados por ejemplo, para llevar la cuenta de los datos que vamos a ingresar por teclado, cuantos datos estamos imprimiendo en pantalla, es decir, en ciclos o iteraciones. En si un contador forma parte de una asignación.

Inicio

Fin

C=A+B

C

A=5 B=6 C=0

contador=contador+1

Page 12: DiagrFlujo-ProgramacionC

Programación de Computadores y el Lenguaje C (versión 2003)

Profesor: Roberto Uribe P. Pág. Nº 12

Ejemplo de asignaciones e incrementos: La forma de analizar este diagrama es de la siguiente manera: - A parte en 0, B se inicializa en 5. - Luego en la línea A=A+1, esto se reemplaza por A=0+1, es decir A queda igualada a 1 (se incrementa en 1). - Finalmente A=A+B, se reemplaza por A=1+5, es decir 6 (se incrementa en 5). Ejercicio 1.1: Suponga que para aprobar una asignatura, a un alumno se le calcula el promedio aritmético, luego si este es superior o igual a 50, entonces esta aprobado (eximido), si el promedio es menor a 50, pero mayor o igual a 30, tiene derecho a dar examen. Si el promedio es menor que 30, reprueba sin derecho a dar examen, la escala de evaluación es de 0 a 100. Entonces, - Sumar tres notas, si el promedio es mayor o igual a 50, entonces "eximido", si es mayor o igual que 30 y menor que 50, entonces "da examen", si es menor que 30, entonces "reprobado". El examen vale 30% y el promedio 70%. Realizaremos un primer intento de programa. Como son tres notas las que hay que leer, entonces crearemos tres variables, una para cada nota. También una variable para almacenar la suma y otra para el promedio. Sin Lectura del examen, sólo imprimiendo "eximido", "da examen" y "reprobado".

Inicio

Fin

A=A+1

A=0 B=5

A=A+B

A=0,B=5

A=1

A=6

Page 13: DiagrFlujo-ProgramacionC

Universidad de Magallanes - Departamento de Ingeniería en Computación

Profesor: Roberto Uribe P. Pág. Nº 13

El promedio se imprime al final para evitar hacerlo tres veces (una por cada bifurcación). El promedio podría haberse calculado sin necesidad de utilizar la variable suma, es decir, sólo con promedio=(nota1+nota2+nota3)/3, utilizando las mismas normas que en matemáticas (ósea la suma entre paréntesis). Esto depende exclusivamente del programador. En nuestro programa faltó calcular la nota final si el alumno da examen. Una nueva versión de nuestro diagrama sería:

Inicio

Leer Nota1

Es

promedio>=50

“Eximido”

SiNo

Leer Nota2

Leer Nota3

Suma=Nota1+Nota2+Nota3

promedio=suma/3

“Da Examen”“Reprobado”

Fin

promedio

promedio>=30

Es

No Si

Page 14: DiagrFlujo-ProgramacionC

Programación de Computadores y el Lenguaje C (versión 2003)

Profesor: Roberto Uribe P. Pág. Nº 14

Análisis del diagrama. En nuestro problema necesitábamos lee 3 notas, por lo tanto hacemos 3 lecturas, una para cada nota, en diagramas de flujo esto no es tan estricto, podrían haberse leído las 3 notas dentro de un mismo símbolo.

nota1, nota2, nota3

Inicio

Leer Nota1

Es

promedio>=50 SiNo

Leer Nota2

Leer Nota3

Suma=Nota1+Nota2+Nota3

promedio=suma/3

Examen

Fin

NotaFinal

promedio>=30

Es

NotaFinal=Examen*0.3+promedio*0.7

NotaFinal=promedio

NotaFinal=promedio

No Si

Page 15: DiagrFlujo-ProgramacionC

Universidad de Magallanes - Departamento de Ingeniería en Computación

Profesor: Roberto Uribe P. Pág. Nº 15

Luego de esto sumamos las notas y se lo asignamos a la variable suma, posteriormente obtenemos el promedio. Como ya se tiene el promedio de las notas y necesitamos saber si este es mayor o igual a 50, si esta entre 30 y 49 o si es menor a 30. Entonces primeramente preguntamos si es mayor o igual a 50 , para ello utilizamos el símbolo de pregunta (símbolo de control). De esto obtenemos dos líneas, en una sacamos por pantalla aprobado, en la otra línea volvemos preguntar es mayor o igual a 30, con esto tenemos si da examen o reprueba. En este caso se utilizó la variable nota_final para imprimir una sola vez el resultado al final del programa (diagrama). Las variables utilizadas fueron: nota1 : primera nota leída. nota2 : segunda nota que se lee. nota3 : es la variable donde se almacena la tercera nota. suma : variable que guarda el resultado de la suma de las notas., promedio : promedio de las notas. nota_final : nota final obtenida. Operadores Matemáticos: Estos se utilizan para realizar los cálculos necesarios en los programas, estos van en el lado derecho de una asignación, es decir en la expresión. En el ejemplo anterior se utilizó el operador suma en la línea suma=nota1+nota2+nota3 y el operador división en la línea promedio=promedio/3. Los operadores matemáticos son: + : Suma - : Resta * : Multiplicación / : División Existe otro operador, el módulo o resto de la división entera, este se verá al final de este capítulo. Ejercicio 1.2: En la mayoría de los problemas se necesita repetir alguna operación (repeticiones o iteraciones). Entonces, resolver un algoritmo para el cálculo del promedio con "N" notas, donde el número de notas es un valor conocido. Cuando se dice que una variable tiene un valor conocido, esto implica que se ingresa al programa, en este caso vía teclado. Para simplificar el problema, eliminar la condición cuando la nota es menor de 30, o sea con nota menor a 50 se da examen.

Page 16: DiagrFlujo-ProgramacionC

Programación de Computadores y el Lenguaje C (versión 2003)

Profesor: Roberto Uribe P. Pág. Nº 16

Solución (Existen otras): - Utilizaremos las variables del ejercicio anterior. - Para el ejercicio 2 agregaremos dos variables: Num_Notas : número de notas a leer. cont : contador de notas, servirá para saber cuando terminar el programa. Esta variable se inicializará en 1. - Entonces:

Fin

Inicio

cont=1 suma=0 promedio=0 nota_final=0 Num_Notas=0nota=0

Leer Num_ Notas

cont=cont+1

suma=suma+nota

Es

cont<=Num_Notas

Leer nota

Si No

promedio=suma/Num_Notas

Es

Promedio>=50

nota_final=promedio

Leer Examen

nota_final=examen*0.3+ promedio*0.7

imprimir nota_final

SiNo

Page 17: DiagrFlujo-ProgramacionC

Universidad de Magallanes - Departamento de Ingeniería en Computación

Profesor: Roberto Uribe P. Pág. Nº 17

En este ejercicio se inicializó todas las variables, esto se usa como norma para ayudar a mantener el orden en los programas.

Estructura While (Mientras) La estructura denotada por: Se puede utilizar como "estructura while", la palabra "Es" se reemplaza por "Mientras", en el ejercicio 2. Entonces se produce un ciclo o "Loop". La idea del ciclo while es formular la siguiente pregunta: Mientras, la condición sea verdadera .... hacer .... Una forma adecuada de ver si un programa funciona correctamente es trazándolo, en nuestra jerga diríamos que lo ejecutamos en papel. Para ello creamos tantas columnas como variables declaradas tengamos, luego vamos colocando valores en las columnas a medida que las variables asuman estos valores, con esto vemos el comportamiento de cada variable y de los ciclos. Por ejemplo veamos la ejecución del ejercicio Nº 1.2. Primero supongamos que las notas que se van a leer son tres, 60, 60 y 50, entonces el comportamiento de las variables sería como se muestra en la tabla siguiente:

Num_Notas nota suma cont promedio nota_final examen 1 0 0 0 1 0 0 2 3 60 60 2 3 60 120 3 4 50 170 4 55 55

Es ?

Si No

cont<=Num_Notas Si No

Proceso

Mientras

Page 18: DiagrFlujo-ProgramacionC

Programación de Computadores y el Lenguaje C (versión 2003)

Profesor: Roberto Uribe P. Pág. Nº 18

Imagine que se va ejecutando sentencia a sentencia (cada sentencia esta representada por una figura). En la fila 1 se muestra la inicialización de todas las variables, menos examen. La siguiente instrucción es la lectura de Num_Notas (cantidad de notas), se ingresa un 3 al programa, y en la ejecución se coloca el 3 en la variable que corresponde, esto se ve en la fila 2. Luego se para en la instrucción de control y se pregunta Es cont<=Num_Notas, entonces cont tiene el valor 1 y Num_Notas es 3, por lo tanto el control del programa se va por la alternativa si. Después de esto viene la instrucción de lectura, donde se ingresa la primera nota, si ingresamos el 60, entonces la variable nota asume este valor, y lo agregamos en nuestra tabla en la fila 2 (columna nota). Pasamos a la siguiente instrucción, suma=suma+nota, el valor se suma es 0 y el valor de nota es 60, entonces la suma da 60, este valor lo colocamos en la fila 2 columna suma. Seguido se incrementa el contador en cont=cont+1, como cont valía 1, entonces ahora vale 2, con esto se termina el primer ciclo ya que el control vuelve hacia arriba y nuevamente se hace la pregunta Es cont<=Num_Notas. Como cont es menor que Num_Notas, entonces el control se va por la derecha (alternativa si), se lee la segunda nota (60) y se asigna a la variable nota, luego a suma se le asigna suma+nota, el valor de suma es 60 y nota vale 60, por lo tanto ahora suma tiene el valor 120, este valor se coloca en la fila 3, columna suma. La siguiente instrucción es el incremento del contador, cont tenía el valor 2, ahora se incrementa en 1, por lo tanto ahora tiene el valor 3. El control vuelve a la instrucción de control, como cont sigue siendo menor o igual que Num_Notas, entonces el control entra nuevamente en el ciclo. Se lee la nota 50, se hace la suma de nota que vale 50 y suma que vale 120, entonces ahora suma vale 170. Luego el contador cont se incrementa en 1, es decir, queda en 4. Retorna a la instrucción de control y ahora la condición va a ser falsa, ya que cont tiene el valor 4 y Num_Notas 3, entonces el control elige la alternativa no. La siguiente instrucción es promedio=suma/Num_Notas, con esto promedio asume el valor 55, este se coloca en la fila 4. Luego se pregunta Es promedio>=50, esto verdadero, por lo tanto se va por la alternativa si. Luego se realiza la asignación nota_final=promedio, este se ve en la fila 4, ahora nota_final vale 55. Finalmente el valor 55 se imprime en pantalla. Como ejercicio analice cuando las notas ingresadas son 60, 45, 40 y 50. La ejecución resultaría como la siguiente tabla:

Num_Notas nota suma cont promedio nota_final examen 1 0 0 0 1 0 0 2 4 60 60 2 3 45 105 3 4 40 145 4 5 50 195 5 48,75 50,625 55

Page 19: DiagrFlujo-ProgramacionC

Universidad de Magallanes - Departamento de Ingeniería en Computación

Profesor: Roberto Uribe P. Pág. Nº 19

En este caso el promedio es menor que 50, entonces se debe dar examen, suponiendo que en el examen se obtuvo un 55, entonces se calcula la nota final multiplicando promedio por 0.7 y examen por 0.3. Finalmente nota_final va a valer 50,625. El problema recién resuelto, sólo permite 1 alumno. Ejercicio 1.3: - Para el problema anterior (Nº 1.2), resolver para un curso completo, donde el Nº de alumnos es un dato conocido. Obtener promedio e imprimirlo. - Para simplificar el ejercicio, sólo se calculará el promedio de los alumnos, no la determinación si se dará o no examen.

Page 20: DiagrFlujo-ProgramacionC

Programación de Computadores y el Lenguaje C (versión 2003)

Profesor: Roberto Uribe P. Pág. Nº 20

Fin

No

promedio=suma/Num_Notas

Inicio

cont_alum=1 Num_Alum=0 cont_notas=1 suma=0 promedio=0 Num_Notas=0 nota=0

Leer Num_ Alum

cont_alum<= Num_Alum

Mientras

Leer Num_ Notas

cont_notas=cont_notas+1

suma=suma+nota

Leer nota

Si cont_notas<= Num_Notas

Mientras

cont_notas=1 suma=0

Leer Nombre_ Alumno

promedio Nombre_ Alumno

Si

cont_alum=cont_alum+1

No

Page 21: DiagrFlujo-ProgramacionC

Universidad de Magallanes - Departamento de Ingeniería en Computación

Profesor: Roberto Uribe P. Pág. Nº 21

Resumen Diagramas de Flujo Como regla general, un programa se hace de la siguiente forma:

- Estructura If.....Then.....Else - Estructura While

Condición ?

(Verdadero) (Falso)

PROCESO PROCESO

IF

No Si

ELSE THEN

Inicio

Inicialización de Variables

Fin

Resúmenes

Impresión de Resultados

Ingreso de Datos

Cálculos

Condición ?

(Verdadero)(Falso)

Proceso

WHILE

HAGA

Page 22: DiagrFlujo-ProgramacionC

Programación de Computadores y el Lenguaje C (versión 2003)

Profesor: Roberto Uribe P. Pág. Nº 22

Existen dos tipos de estructura while: 1.- Cuando se conoce a priori el número de veces que se repite un proceso, - Promedio Nota - Cálculo para n alumnos 2.- Cuando no se conoce las veces que se repite un proceso. Ejemplo: ∞

F = Σ 1/i i=1 Determinar F de tal manera que si 1/i ≤ 10 -3, entonces terminar. Es decir, se van obteniendo resultados hasta que el rango sea menor o igual a 0,001. Con esto se obtiene una aproximación del resultado. Como el contador i va aumentando, entonces 1/i va disminuyendo. - Cuando se conoce antes la cantidad de iteraciones de un proceso, la estructura While puede reemplazarse por una estructura For (Para), esta se simboliza de la sgte. manera:

Inicio

F=0 i=1 Eps=0,001

i = i + 1

F = F + 1/ i

While

1/i >= Eps

i , F

Si No

Fin

Page 23: DiagrFlujo-ProgramacionC

Universidad de Magallanes - Departamento de Ingeniería en Computación

Profesor: Roberto Uribe P. Pág. Nº 23

Es decir, el contador contanotas se irá incrementando de 1 hasta n, y por lo tanto el proceso dentro del ciclo for se realizará n veces. Para el ejercicio del promedio de un curso con "N" alumnos y "N" notas (ejercicio Nº 3), si utilizamos ciclos for, nuestro diagrama quedaría:

Si

Proceso

contanotas: 1 n

Fin

promedio=suma/Num_Notas

Inicio

cont_alum=1 Num_Alum=0 cont_notas=1 suma=0 promedio=0 Num_Notas=0 nota=0

Leer Num_ Alum

suma=suma+nota

Leer nota

Si

suma=0

Num_Notas Nombre_ Alumno

promedio Nombre_ Alumno

Si cont_alum: 1 Num_Alum

cont_notas: 1 Num_Notas

Page 24: DiagrFlujo-ProgramacionC

Programación de Computadores y el Lenguaje C (versión 2003)

Profesor: Roberto Uribe P. Pág. Nº 24

Con estructura for se eliminan la inicialización de contadores y el incremento de estos. Más adelante veremos que el for proporcionado por el lenguaje C es mucho más flexible. Nota: Todos los lenguajes de programación poseen las estructuras hasta ahora vistas, o por lo menos asignación, if y while (o alguna instrucción para iteraciones). Operador Módulo Aparte de los operadores aritméticos comunes, existe uno llamado módulo, este se usa en variadas aplicaciones y su uso es bastante frecuente en programación. El operador módulo nos permite obtener el resto de la división entera, ejemplo: 5 : 2 = 2 1 -------> resto de la división ósea, 5 dividido 2 es igual a 2 y sobra 1, este es el módulo o resto. Utilizaremos el símbolo % para denotar el operador módulo, que es precisamente el que utiliza C. Para el ejemplo anterior, 5 % 2 -----> 1 Otros ejemplos: 12 : 3 = 4 0 -------> resto (módulo) 26 : 3 = 8 2 -------> módulo es decir, 12 % 3 -----> 0 26 % 3 -----> 2 Ejercicio 4: - Hacer un programa en el cual se ingrese un número y como resultado se indique si es par o impar. Entonces, sabemos que un número par es aquel que cuando se divide por 2 su resto es 0, por lo tanto el diagrama sería:

Page 25: DiagrFlujo-ProgramacionC

Universidad de Magallanes - Departamento de Ingeniería en Computación

Profesor: Roberto Uribe P. Pág. Nº 25

Dentro de las aplicaciones típicas donde se utiliza el módulo están, determinar si un número es o no primo, obtención de los n primeros números perfectos, hacer que cualquier número sea convertido en otro dentro de un rango específico. Por ejemplo, para el último caso, suponga que se obtienen números entre 0 y 999, y se desea convertirlos al rango 0 a 10, el proceso principal sería: datonuevo= num % 11 Es decir, cualquier número módulo 11 daría un número entre 0 y 10 (y en este caso quedaría almacenado en la variable datonuevo). Generalmente se usa para que cualquier número obtenido de un proceso de generación de números al azar (números aleatorios) esté dentro del rango deseado.

Inicio

num=0

“Ingrese un Número”

num

(num % 2)=0 Si No

“El número es par”

“El número es impar”

Fin

Page 26: DiagrFlujo-ProgramacionC

Programación de Computadores y el Lenguaje C (versión 2003)

Profesor: Roberto Uribe P. Pág. Nº 26

Problemas Resueltos y Propuestos Los siguientes ejercicios son clásicos en los primeros cursos de programación. Se recomienda al alumno que resuelva los problemas y luego los compare con las soluciones planteadas en el libro, recuerde que un problema se puede resolver de muchas formas (demasiadas quizá!). Para cada solución propuesta en el libro, ejecute en papel el programa, de esa manera se dará cuenta del funcionamiento de este. Resueltos: 1.- Hacer el diagrama de flujo que calcule: xy , donde x e y son valores conocidos. 2.- Resolver :

∑=

=n

iix

1

; n : valor conocido

3.- Escribir el diagrama de flujo para la ecuación de 2º grado en la forma : ax2 + bx + c = 0 a, b, c constantes conocidas

con : xb b a c

a1

2 42

=− + − • •

xb b a c

a2

2 42

=− − − • •

4.- Hacer un diagrama de flujo que lea un archivo de datos numéricos : Condiciones : a) cuente los números leídos b) entregue el mayor número leído c) entregue el menor número leído Condición de fin de archivo es eof (eof : end of file), es decir cuando el dato leído es eof, entonces terminar el programa. 5.- Crear un programa que lea una serie de números y finalmente entregue una lista de cuantos eran pares y cuantos impares. La condición de término será cuando la cantidad de impares o pares supere 15. 6.- Escribir el diagrama de flujo para la sumatoria :

Page 27: DiagrFlujo-ProgramacionC

Universidad de Magallanes - Departamento de Ingeniería en Computación

Profesor: Roberto Uribe P. Pág. Nº 27

∑=

=n

i

ixy1

; n, x : valores conocidos

7.- Realizar el diagrama de flujo para el calculo de factorial de N. Tomar en cuenta : 0! = 1 1! = 1 N! = N*(N-1)! = N*(N-1)*(N-2)* .... *(N-N)! N : valor conocido 8.- Escribir el diagrama de flujo para ex donde : ex = 1 + x1 + x2 + x3 + ...... + xn 1! 2! 3! n! x : valor conocido, n=1000 Propuestos: 9.- Resolver : sen x = x - x3 + x5 - x7 ...... ((-1)n

*x2n+1) 3! 5! 7! (2n+1)! con x en radianes. x : valor conocido, n=10 10.- Se define "promedio geométrico" como : Prom = ( • •. ....•nota nota notaNn 1 2 Realizar diagrama de flujo para resolver dicho problema. Asuma que es posible realizar la operación de "raíz enésima de un número" ( n ); donde n valor conocido. 11.- Escriba un programa (diagrama de flujo) que calcule a sobre b.

ab

ab a b

=

−!

!( )! ; a,b,c: valores conocidos.

12.- Cree un diagrama de flujo que obtenga el resultado de la siguiente sumatoria:

Page 28: DiagrFlujo-ProgramacionC

Programación de Computadores y el Lenguaje C (versión 2003)

Profesor: Roberto Uribe P. Pág. Nº 28

( )∑=

−n

ii

1

212 (suponga n conocido)

13.- Resuelva la siguiente expresión : ∞

ΣΣΣΣ x(2n-1) /(2n-1)! i=1 - La condición de término es : x(2n-1) < 0.001 (2n-1)! 14.- Escriba un diagrama de flujo que transforme un número de cualquier base a decimal. Simplificación: asuma que la base no es mayor a 10. Debe indicar la base a la que pertenece el número y la cantidad de dígitos que contiene este. Ejemplo : Para transformar el número 143 en base 5 a decimal el programa debería funcionar así:

Programa que transforma números a decimal :

ingrese la base : 5

ingrese la cantidad de dígitos : 3

ingrese digito 1 : 1ingrese digito 2 : 4ingrese digito 3 : 3

El número en base 10 es : 48

Note que la base puede ser cualquiera ingresada por teclado.

Page 29: DiagrFlujo-ProgramacionC

Universidad de Magallanes - Departamento de Ingeniería en Computación

Profesor: Roberto Uribe P. Pág. Nº 29

Soluciones: 1.- x elevado a y es la multiplicatoria de x y veces, es decir, esto lo asociamos a un ciclo que nos permita ir de 1 hasta y. Usualmente al ciclo va ligado un contador que lleve en número de veces del ciclo. También, debemos tomar en cuenta una variable auxiliar que nos permita ir almacenando el valor anterior de la multiplicatoria. Lo último es que x e y son valores conocidos, entonces debemos ingresarlos, por ejemplo por teclado. Finalmente, debe tomar en cuenta que la variable auxiliar resultado se inicializa en 1, esto dado que 1 es el neutro multiplicativo, no puede ser 0, ya que el resultado final sería 0. El contador cont podría comenzar en 0, pero habría que modificar la condición del while por cont<y (menor absoluto), de otro modo el resultado sería erróneo (se multiplicaría una vez mas). Para un mejor entendimiento de la solución ejecute (en papel) el programa y modifique los valores de resultado y cont, de tal manera de comprobar los errores que se provocarían. Nuestro programa funciona para x distinto de 0 e y mayor o igual a 0, de otra manera el programa arrojaría un resultado erróneo. Modifique el diagrama para tomar en cuenta los casos anteriores. 2.- Para este ejercicio, la sumatoria va de 1 hasta n, con n conocido (ingresado al programa). Entonces, los elementos que necesitamos son: un ciclo (por ejemplo while), un contador y una variable auxiliar que almacena la suma sucesiva.

Inicio

x=0 , y=0 cont=1 resultado=1

cont = cont + 1

resultado=x*resultado

While

cont<= y

x , y

"x elevado a y es", resultado

Haga No

Fin

Page 30: DiagrFlujo-ProgramacionC

Programación de Computadores y el Lenguaje C (versión 2003)

Profesor: Roberto Uribe P. Pág. Nº 30

Observe que en este caso la sumatoria (variable x) comienza en 0. El programa podría modificarse de tal manera que si el valor ingresado es menor que 1, entonces enviar un mensaje de error al usuario. 3.- Para este caso no es necesario utilizar ciclos, ya que se conocen todos los valores de los datos y sólo se debe resolver la fórmula indicada. Lo único que hay que considerar es cuando el discriminante de la ecuación sea negativo, en este caso, como no podemos obtener la raíz de un número negativo, entonces lo que hacemos es que la salida por pantalla sólo represente al número imaginario. En este caso no se podría operar con un número imaginario, sin embargo, en un lenguaje de programación se podrían crear las estructuras necesarias para realizar las operaciones entre números imaginarios.

Inicio

i=1 ,x=0 n=1

i = i + 1

x=x + i

While

i<=n

"El resultado de la sumatoria es ",

x

n

Haga No

Fin

"Sumatoria de i, de 1 hasta n, Ingrese n mayor o igual a 1."

Page 31: DiagrFlujo-ProgramacionC

Universidad de Magallanes - Departamento de Ingeniería en Computación

Profesor: Roberto Uribe P. Pág. Nº 31

En la solución al problema se tomó en cuenta cuando el valor de a es 0, en ese caso la ecuación resulta de primer grado, por lo tanto la fórmula para resolverla es la trivial. Cuando el resultado son números imaginarios, la salida por pantalla es sólo representativa. Asuma que en este caso, lo que esta entre comillas dobles es texto y lo demás son valores. Un ejemplo de una solución imaginaria en nuestro diagrama podría ser (para a=2,b=4,c=5): x1 = -2 + 12 i x2 = -2 - 12 i

4.- Para este problema podemos tomar en cuenta los siguientes supuestos: se puede leer un dato numérico y preguntar si este es eof. A pesar de que los datos están en un archivo, se ocupa el mismo símbolo para la entrada.

Inicio

a=0,b=0 ,c=0 discr=0 x1=0,x2=0

a , b , c

"resolución de la ecuación cuadrática:

ax2+bx+c=0

Ingrese valores para: a, b, c"

x1=-c/b

discr=-discr

Si

a=0

"x = ", x1

Si No

discr=b*b-4*a*c

x1=(-b+ discr )/(2*a)

Si

discr>=0

"Soluciones Reales", "x1 = ", x1 "x2 = ", x2

Si No

x2=(-b - discr )/(2*a) "Soluciones Imaginarias" "x1= ", - b/(2*a), "+", discr /(2*a), "i" "x1= ", - b/(2*a), "-", discr /(2*a), "i"

Fin

Page 32: DiagrFlujo-ProgramacionC

Programación de Computadores y el Lenguaje C (versión 2003)

Profesor: Roberto Uribe P. Pág. Nº 32

En este caso debemos usar un ciclo while que termine cuando el dato sea igual a eof. No se puede usar for, ya que no se sabe la cantidad de veces que se repite el ciclo. No es necesario usar un contador. En nuestro programa se ha considerado el caso en que el archivo este vacío, es decir, el primer dato es EOF. Luego de haber leído el primer dato, se asigna como máximo y mínimo, esto resulta lógico dado que es el único dato leído. La primera iteración no afecta, sin embargo, al leer el segundo dato, este se compara con el máximo (el dato anterior), si resulta ser mayor, entonces se reemplaza, de igual manera con el mínimo. Así sucesivamente se va comparando el nuevo dato con el máximo y mínimo anterior. 5.- Para el siguiente diagrama, sabemos que tenemos que leer una serie de números, pero no sabemos cuantos. Entonces, necesitamos un ciclo cuya condición de término sea "la suma de pares o impares sea mayor o igual a 15", por lo tanto, se crean dos variables, una que cuente los pares y la otra los impares. Para terminar el ciclo una u otra debe ser mayor o igual a 15

Inicio

dato,max,min

"El máximo es:", max, "El mínimo es:", min

dato

Haga No

Fin

Si dato=EOF Si No

"No existen datos" max=dato

min=dato

max=dato

Si dato>max

min=dato

Si dato<min

dato

While dato<>EOF

Page 33: DiagrFlujo-ProgramacionC

Universidad de Magallanes - Departamento de Ingeniería en Computación

Profesor: Roberto Uribe P. Pág. Nº 33

(basta que uno llegue a 15 para terminar). Es esencial notar la importancia del o en la pregunta. Las dos condiciones del diagrama anterior se pueden reemplazar por una sola. Nota: La condición podría haber sido: Es decir, "si ambos son menores que 15, entonces lea datos". Note que están invertidos los símbolos y se reemplazó el o por el y. 6.- Este problema involucra una sumatoria y una multiplicatoria (potencia). Si utilizáramos funciones (módulos) podríamos resolver la sumatoria y llamar al módulo que calcula el xi cada vez (con un i diferente por vez). Sin embargo, por ahora resolveremos ambos problemas en un solo ciclo.

Inicio

cuenpar=0 cuenimpar=0 num=0

cuenpar=cuenpar+1

Si cuenpar>15

num

“Cantidad de” "Pares:”, cuenpar “Impares:”,cuenimpar

No

No

Si

Si

Fin

"Ingrese Datos”

Si cuenimpar>15

Si (num mod 2)=0

Si

cuenimpar=cuenimpar+1

No

No Si While cuenimpar>15 o cuenpar>15

Si No While cuenimpar<=15 y cuenpar<=15

Page 34: DiagrFlujo-ProgramacionC

Programación de Computadores y el Lenguaje C (versión 2003)

Profesor: Roberto Uribe P. Pág. Nº 34

Entonces, necesitamos una variable que almacena la sumatoria (suma) y otra la potencia (pot), con un contador (i) que vaya de 1 a n. Este ejercicio puede haber sido resuelto usando la estructura for. Modifique el programa para que arroje un error si el n ingresado es menor que 1. 7.- El factorial de un número es la multiplicatoria de 1 hasta el número, además, sabemos que si el número es 0 o 1, el factorial es 1. Observe que si n es igual a 0, la línea va directo hacia un símbolo de salida. Esto es correcto dado que fact se inicializó en 1 y por lo tanto la salida para n=0 será 1. 8.- El este ejercicio el ciclo iterará hasta 1000, sin embargo, esto es a modo de ejemplo, dado que al usar un compilador cualquiera el programa fallaría por un problema de overflow

Inicio

suma=0, pot=1, i=1

pot=pot * x

i=i + 1

“La sumatoria es :", suma

Si No

Fin

n

While i<=n

suma=suma + pot

Inicio

fact=1 i=1

fact=fact * i “El factorial es :", fact

Si

Si No

Fin

n

Si n=0

i : 1 n

Page 35: DiagrFlujo-ProgramacionC

Universidad de Magallanes - Departamento de Ingeniería en Computación

Profesor: Roberto Uribe P. Pág. Nº 35

(esto es lógico ya que 1000! es un valor excesivamente grande). Al traducir el diagrama a un lenguaje el valor 1000 debe modificarse por otro (ejemplo 10). Observe que la variable elevx comienza en 1, esto es para obtener el primer valor de la sumatoria. Usualmente, para el cálculo de este tipo de constantes se utiliza un rango, de tal manera que cuando este sea despreciable, entonces se termina el programa. Por ejemplo, cuando la variable div sea inferior a 0,001, entonces agregar el valor que se obtenga en el próximo ciclo no influirá demasiado en nuestra respuesta. Modifique el programa de tal manera que este termine cuando div sea menor o igual a 0,001.

Inicio

eelevx=1,x=0 pot=1,div=0 fact=1, i=1

pot=pot * x

fact=fact * i

i=i + 1

“e elevado a x es :",elevx

Si No

Fin

x

While i<=1000

eelevx=eelevx + div

div=pot / fact

Page 36: DiagrFlujo-ProgramacionC

Programación de Computadores y el Lenguaje C (versión 2003)

Profesor: Roberto Uribe P. Pág. Nº 36

Capítulo Nº 2 : Lenguaje C

Un poco de historia C es un lenguaje de propósito general, es decir, se pueden desarrollar aplicaciones de diversas áreas. Dentro de sus principales características podemos mencionar que, es un lenguaje estructurado, tiene una abundante cantidad de operadores y tipos de datos, es un lenguaje de nivel medio, pero se puede codificar a alto nivel, produce código objeto altamente optimizado, posee punteros y capacidad de aritmética de direcciones. C fue creado en los Laboratorios Bell de AT&T para correr originalmente en el sistema operativo Unix, sin embargo en la década de los 80 aparecieron versiones para computadores personales. Existe una íntima relación entre Unix y C, ya que el primero fue reescrito en C, así como el propio compilador de C y la mayoría de las herramientas de Unix, sin embargo, no esta ligado a ningún sistema operativo o máquina. C fue diseñado por Dennis Ritchie en 1972 a partir de un lenguaje llamado B escrito por Ken Thompson en 1970, que a su vez fue inspirado en uno llamado BCPL concebido por Martin Richard en 1967. Entre sus múltiples ventajas podemos mencionar que C es un lenguaje muy portable, es decir, es independiente de la arquitectura de la máquina y con alguna o ninguna modificación un programa puede correr en una amplia variedad de computadores. Es relativamente flexible en la conversión de datos (a veces se toma como desventaja). Su eficiencia y claridad han hecho que el lenguaje ensamblador casi no haya sido utilizado en Unix. El compilador de C es pequeño y tiene un gran poderío debido a sus múltiples bibliotecas. Sin embargo, tiene sus desventajas y entre ellas se puede mencionar: la excesiva libertad en la escritura del código fuente hace que muchas veces se cometan errores de programación, que, por ser correctos sintácticamente no se detectan en tiempo de compilación o a simple vista. Carece de instrucciones de entrada y salida, de manejo de strings (cadenas de caracteres), quedando el trabajo en manos de las bibliotecas provocando con esto algunos problemas de portabilidad. Hay múltiples ejemplos de aplicaciones construidas en C, como ser: construcción de sistemas operativos, procesadores de texto, administradores de bases de datos (por ejemplo Clipper), programas para Windows y Windows 95, las APIs (Application Program Interface) de las Librerías de Enlace Dinámico (DLL) de Windows están construidas mayoritariamente en C. Con algo de experiencia se pueden crear compiladores, sobre todo con ayuda de herramientas como Lex y Yacc que también están hechas en C.

Page 37: DiagrFlujo-ProgramacionC

Universidad de Magallanes - Departamento de Ingeniería en Computación

Profesor: Roberto Uribe P. Pág. Nº 37

Forma General del Lenguaje C. Para crear un programa en C, se escribe el código fuente (programa), luego se compila y finalmente se enlaza con las bibliotecas (se hace un link, en nuestra jerga diríamos “se linkea”). Código Fuente Compilar Enlazar (link) Todo lenguaje de programación posee palabras claves, estas son aquellas palabras que reserva el lenguaje para identificar ciclos, estructuras y en general cualquier cosa que sea parte de instrucciones, por ejemplo, las palabras while, if, struct son palabras claves en C. En C las palabras claves o reservadas deben escribirse en minúsculas, esto ya que C diferencia entre mayúsculas y minúsculas (a diferencia del lenguaje Pascal). El Lenguaje C se escribe a partir de un programa principal y de funciones, el programa principal también es una función (función principal), y se denomina main(), esta debe estar siempre presente y es la primera en ser llamada. Para hacer un programa en C, lo primero que se debe hacer es crear el programa fuente (con extensión ".c") en un editor de textos, en el caso de Unix, este puede ser el llamado vi que esta presente en la mayoría de las versiones de este sistema operativo, entonces se haría de la siguiente manera: vi <nombre_del_programa_c>

luego escribir el programa, salir del editor y compilarlo con el compilador cc u otro que posea el sistema: cc <nombre_del_programa_c>

Después de esto se genera el archivo ejecutable a.out, que se ejecuta: a.out

ó ./a.out (los símbolos "./" especifican en directorio actual) Luego se podría cambiar el nombre del ejecutable con el comando mv de unix, esto sería: mv a.out <nombre_nuevo>

o compilar el programa con la opción -o:

cc -o <nombre_archivo_ejecutable> <nombre_del_programa_C>

Page 38: DiagrFlujo-ProgramacionC

Programación de Computadores y el Lenguaje C (versión 2003)

Profesor: Roberto Uribe P. Pág. Nº 38

Ejemplo: 1) vi primero.c 1) vi primero.c 2) cc primero.c 2) cc -o primero primero.c quedaría a.out el ejecutable sería primero Para el caso de los compiladores para computadores personales, estos vienen en ambientes que integran un editor de texto y el compilador. En este caso para compilar y enlazar se usa el menú del editor o teclas de función (Hot keys), esto último depende del producto. En la actualidad, todos o la mayoría de los compiladores de C soportan el estándar ANSI de C. Dependiendo de la máquina donde se ejecute el compilador, se incluyen bibliotecas aparte para diferentes procesos, como ser, bibliotecas con funciones para aplicaciones gráficas, etc. Estructura de un Programa Cuando se escribe el programa se recomienda usar el siguiente formato:

llamadas a bibliotecasdeclaración de funciones (prototipos de funciones)declaración de variables globales

main(){

declaración de variables localessentencias

}

definición de funciones

Nota: Dentro de las sentencias también se pueden encontrar llamadas a funciones, las funciones son escritas del siguiente modo:

func(){

declaración de variables localessentencias

}

o también:

func(variables de paso)declaración de variables de paso{

declaración de variables localessentencias

}

A las variables de paso, se llamarán parámetros de la función. Sin embargo, las funciones se tratarán más adelante.

Page 39: DiagrFlujo-ProgramacionC

Universidad de Magallanes - Departamento de Ingeniería en Computación

Profesor: Roberto Uribe P. Pág. Nº 39

Bibliotecas Por lo general las bibliotecas contienen funciones creadas por el programador o entregadas con el producto. Por ejemplo las funciones de entrada y salida vienen especificadas en la biblioteca de entradas y salidas estándar stdio.h (Standar Input Ouput Header). Cuando desde el programa fuente se llama a una función que no esta escrita en dicho programa, al compilar se mantiene el nombre y luego al enlazar con las bibliotecas se busca el nombre de la función dentro de estas. Para poder utilizar una biblioteca o archivo de cabecera se usa el símbolo # seguido de la palabra reservada include, luego el nombre de la biblioteca, esto se hace al comienzo del programa. Por ejemplo, para llamar a la biblioteca stdio.h se hace de la siguiente manera: # include <stdio.h>

El nombre aparece entre menor y mayor (< >), esto significa que al enlazar buscará el archivo en el directorio especificado para los archivos de cabecera, si el archivo aparece entre comillas dobles (" "), entonces lo buscará en el directorio actual, es decir desde donde se ejecuta el programa, por ejemplo: # include "stdio.h"

sin embargo, stdio.h siempre se encuentra en el directorio donde están todas las bibliotecas, es decir se utiliza <>. Indentación Este concepto se refiere a mantener un orden dentro de la programación, es decir para que un programa sea ordenado y legible, entre otras cosas, este debe de ser modular y además ser indentado, para revisiones futuras y mantención de los programas. Indentar es simplemente ubicar en columnas diferentes los ciclos o estructuras internas a otras, por ejemplo:

#include <stdio.h>main(){. int i,suma=0;. i=0;. while(i<=10). {. . i=i+1;. .. . suma=suma+i;. }. printf("\n sumatoria=%d \n" ,suma);}

Identificadores

Estos son simplemente los nombres que pueden asumir variables o constantes dentro del programa. Existen algunas restricciones para los identificadores: - No deben ser iguales a nombres de palabras reservadas o bibliotecas.

Page 40: DiagrFlujo-ProgramacionC

Programación de Computadores y el Lenguaje C (versión 2003)

Profesor: Roberto Uribe P. Pág. Nº 40

- Deben comenzar con una letra o el caracter ‘_’. - No deben tener en medio el caracter espacio. Entonces, las variables o constantes comienzan con una letra o el caracter '_' y luego pueden ser letras, dígitos o '_'. Ejemplo de identificadores:

ContadorContavar1_variable3_Suma_total

Recuerde: en C se diferencia entre mayúsculas y minúsculas, por lo tanto, la variable var1 es distinta a VAR1. Comentarios Un comentario es una cadena de caracteres que no es tomada en cuenta por el compilador, esta va dentro de "/*" y "*/", Ejemplo:

/* esto es un comentario */

se utilizarán por lo general para documentar los programas, o sea para hacerlo más legible y ordenado, o simplemente para referencias personales del programador, por ejemplo: para indicar las últimas modificaciones hechas al programa, así el programador sabe que fue lo último que hizo en el programa. /* Esta es la ultima version, lo ultimo que hice fue:

Crear la funcion de ingreso de datos. La funcion de impresion deresultados funciona en forma correcta.

Ya son las 3 de la mañana y aun no termino mi tarea.*/

Puede parecer no necesario, pero generalmente los programadores tienen varias versiones de sus programas, las cuales van modificando, hasta que logran terminar una en forma completa y sin errores. Ocurre también que los programas pueden ser dejados de lado y vueltos a retomar tiempo después, típicamente uno no se acuerda de las últimas modificaciones hechas o de las cosas que funcionaban y de las que no.

Tipos de datos fundamentales Los tipos de datos son los tipos de valores que puede tomar una variable, ya sea numérico, caracter u otro. Por ejemplo, en C, el número 1 no es igual a 1,00; esto debido a que el primero es un número entero y el segundo es un flotante (o número real). Los tipos de datos fundamentales en C son:

Page 41: DiagrFlujo-ProgramacionC

Universidad de Magallanes - Departamento de Ingeniería en Computación

Profesor: Roberto Uribe P. Pág. Nº 41

Tipo largo bytes(MS-DOS) Nombre abreviado char 1 caracter short int 2 entero corto short int 2 entero long int 4 entero largo long unsigned char 1 caracter sin signo unsigned short int 2 entero corto sin signo unsigned short unsigned int 2 entero sin signo unsigned unsigned long int 4 entero largo sin signo unsigned long float 4 flotante long float 8 doble o flotante largo double void 0 vacío Por lo tanto, el nombre de cada tipo de dato es una palabra reservada.

Resumen Palabras clave auto break case char continue default do double else enum extern float for goto if int long register return short sizeof static struct switch typedef union unsigned void while. Cuando recién se comieza a programar en C, se ocupan sólo algunas de estas.

Operadores Son caracteres especiales que tienen un significado específico o determinado, estos indican al compilador realizar operaciones matemáticas o lógicas. Operadores Matemáticos

- menos unuario- resta+ suma* producto/ división-- Decremento++ Incremento% módulo= asignación

Precedencia (prioridad de los operadores matemáticos): Operadores Asociatividad

- (unuario) ++ -- derecha a izquierda* / % izq. a derecha+ - izq. a derecha= derecha a izq.

Page 42: DiagrFlujo-ProgramacionC

Programación de Computadores y el Lenguaje C (versión 2003)

Profesor: Roberto Uribe P. Pág. Nº 42

Operadores de Relación Estos se utilizan dentro de proposiciones del tipo while, if, for; es decir, en estructuras de control .

< : menor que> : mayor que<= : menor o igual que>= : mayor o igual que== : igual a!= : distinto de (no igual a)

Operadores Lógicos

! : negación (unuario) not&& : y lógico and|| : o lógico or

Prioridad:

!< <= > >=== !=&&||

Tabla de verdad para los operadores lógicos

! (not) && (and) || (or)x y !x x&&y x||y

__________________________________________________0 0 1 0 00 1 1 0 11 0 0 0 11 1 0 1 1

__________________________________________________ Existen estos mismos operadores (y algunos más) para trabajar a nivel de bit, pero estos se verán después en un apartado.

Comenzando a Programar

Cada vez que se escribe una línea, esta debe llevar ";" al final, con lo cual el compilador entiende que llega al final de una instrucción o proposición. Esto no ocurre en las llamadas a Bibliotecas (#include) y en las definiciones (#define) o cuando es una sentencia compuesta (una línea compuesta), es decir en el caso de un ciclo if, while, for, case, etc. Declaración de variables Antes de usar una variable dentro de un programa, es necesario indicarle al compilador que va a ser usada, para ello se declara:

Page 43: DiagrFlujo-ProgramacionC

Universidad de Magallanes - Departamento de Ingeniería en Computación

Profesor: Roberto Uribe P. Pág. Nº 43

Declaración:

<tipo> <lista_de_variables>;

tipo ::= es el tipo de dato a utilizar char, int, float, etc.lista_de_variables::=<variable1[,variable2[,variable3........]]]>

la coma se usa para separar dos variables, lo anterior indica que puede ir más de una variable. Ejemplo:

main(){

int conta; /* se declara una variable entera */char siono,primera_le; /*ambas variables son de tipo caracter*/int suma,promedio,i; /*se puede volver a repetir el tipo*/float exponencial; /*variable de punto flotante */...

}

Si una variable es de un tipo determinado, esto implica que puede tomar valores sólo del tipo indicado. Por ejemplo, si una variable es del tipo int no puede asignársele valores con decimales. Recuerde: es lo mismo que

int i; int i, j, k;int j;int k;

Asignación: Como ya es sabido, una asignación es almacenar en una variable el resultado de una operación o proceso. En "C" una asignación se representa como:

<variable> = <expresión>;

donde:

expresión ::= expresión operador_matemático expresión

o expresión ::= constante|variable

Ejemplo: a=5; /* a la variable a se le asigna el valor 5 (constante)*/

expo=max; /* a la variable expo se le asigna el contenido de lavariable max*/

expofin=ult+4; /* a expofin se le asigna el contenido de lavariable ult sumada a la constante 4*/

Page 44: DiagrFlujo-ProgramacionC

Programación de Computadores y el Lenguaje C (versión 2003)

Profesor: Roberto Uribe P. Pág. Nº 44

Inicialización Esta es una asignación que se utiliza para dar un valor de inicio a una variable. Ejemplo:

main(){

int i=0,j=0,k; /*inicializacion en la declaracion i, j*/int suma;k=1; /*inicializacion despues de ser declarada*/suma=(k+1)*200;/*la inicializacion puede ser hecha usando

variables*/..

}

Otros ejemplos de asignaciones:

sumatoria=sumatoria*nota; /*Expresion involucra multiplicación devariables*/

primela_le='A'; /*expresion es una constante de tipocaracter*/

resultado=factorial(dato); /*a resultado se le asigna el valorretornado por la funcion factorial*/

El último ejemplo es más complejo, en este caso el valor que retorna de la función factorial se asigna a la variable resultado. Funciones se verá en el próximo capítulo.

Entrada / Salida Estándar A diferencia de otros lenguajes, no existen instrucciones de entrada y salida, para ello existe una librería. La biblioteca (o archivo de cabecera) llamada stdio.h contiene todas las funciones de entrada y salida estándar, entre ellas las funciones printf() y scanf(). La f significa que es entrada o salida con formato. printf() Esta es la función de salida por pantalla, su formato es el siguiente:

printf(cadena_de_control,lista_de_argumentos)

La cadena de control comienza y termina con comillas dobles, dentro de estas va una cadena de caracteres que puede incluir caracteres de control o formato. La lista de argumentos se refiere a indicar las variables que serán mostradas o impresas. Ejemplo:

/* Mi primer programa */ /* primer.c */

#include <stdio.h>main(){

Page 45: DiagrFlujo-ProgramacionC

Universidad de Magallanes - Departamento de Ingeniería en Computación

Profesor: Roberto Uribe P. Pág. Nº 45

printf("Este es el primer programa que entiendo");return 0;

}

Nota: Este programa funciona correctamente, luego en Unix sería: $ cc primer.c se compila (el nombre es primer.c) $ a.out se ejecuta

Este es el primer programa que entiendo$ este es el resultado. El símbolo $ es el prompt del sistema operativo Unix. Notar que en el ejemplo no se utilizó lista de argumentos, la cadena de control sólo fue usada para imprimir una cadena de caracteres (palabras). Cuando se comienza a estudiar un nuevo lenguaje, lo primero que hay que aprender de él es como imprimir algo en pantalla, el segundo paso es leer desde teclado. Si se desea imprimir el valor de una variable se debe utilizar el formato adecuado. El formato comienza con el caracter "%", luego le sigue el caracter de conversión, entre estos no debe haber espacios en blanco. El caracter de conversión es aquel que indica cual es el tipo de dato que se imprimirá, es decir, si es entero, caracter, etc. Caracter de conversión tipo c caracter d entero e número flotante en notación científica f punto flotante g con el formato e o f , es más corto s cadena de caracteres (string) p puntero Si se le agrega la l o L a entero o flotante se indica que es long. Ejemplo:

#include <stdio.h>main(){

int a=20,b;int c=0 ;b=50;c=a*b;printf("a * b = %d", c );printf("\n a=%d y b=%d" , a, b );

return 0;}

Lista de argumentos

Cadena de Control

Lista de argumentos

Cadena de Control

Page 46: DiagrFlujo-ProgramacionC

Programación de Computadores y el Lenguaje C (versión 2003)

Profesor: Roberto Uribe P. Pág. Nº 46

Para a, b y c se uso formato entero. En Unix, la salida de este programa sería:

a * b = 1000a=20 y b=50

Los caracteres barra invertida (backslash) y n juntos, provocan un retorno de carro (return) en pantalla. Los espacios dentro del printf también aparecen en pantalla (note el primer espacio de la segunda línea). Dentro de la función printf se usan caracteres de control (o secuencia de escape), para ello se usa el caracter \ (barra invertida) seguida de algún otro caracter, por ejemplo, algunos caracteres especiales se representan como: caracter valor ascii nueva línea \n 10 tabulador \t 9 comillas \” 34 retorno de carro \r 13 apóstrofos \’ 39 barra invertida \\ 92 retroceso \b 8 alimentación de forma \f 12 En los strings se usa el caracter nulo \0 para indicar el fin de la cadena de caracteres. scanf() Esta es la función para entrada estándar, se utiliza de un modo parecido a la anterior. Ejemplo:

int numdato;printf("ingrese el número de datos:");scanf("%d",&numdato); /*ingreso de numdato*/

Entonces utiliza formato, en este caso %d indica que se lee con formato entero. En el ejemplo el caracter "&" se usa para indicar la dirección de memoria, es decir, "lee a un valor con formato entero y lo guarda en la dirección de memoria numdato". Por ahora no se le dará mucho énfasis al caracter &, hasta el capítulo de punteros, por ahora sólo es necesario saber que para leer un int, char o float se utiliza dicho caracter. tabla scanf() caracter de conversión tipo d Entero decimal o Entero Octal x Entero Hexadecimal u Entero Decimal sin signo

Page 47: DiagrFlujo-ProgramacionC

Universidad de Magallanes - Departamento de Ingeniería en Computación

Profesor: Roberto Uribe P. Pág. Nº 47

e nº de punto flotante f equivalente a e c caracter s cadena de caracteres (string) Ejercicio Nº 2.1: Escribir un programa que lea 2 datos numéricos y que realice las siguientes operaciones: suma, resta, multiplicación y división. Para desarrollar este problema, tenemos que tomar en cuenta que se pide?, luego, que necesitamos para hacer esto?. Entonces, sabemos que se nos pide leer dos datos de teclado e imprimirlos en pantalla, es decir, necesitamos las funciones de entrada/salida scanf y printf. Como sabemos, para utilizar dichas funciones debemos llamar a la biblioteca que las contiene, en este caso stdio.h. Por lo tanto la primera línea del programa debe ser:

# include <stdio.h>

luego de esto necesitamos escribir la función principal:

main(){

la llave indica el principio de la función main. Ahora, como tenemos que leer 2 datos y hacer cuatro operaciones, podemos crear 6 variables para guardar los datos y los resultados. Como dentro de las operaciones está la división, esta puede arrojar un resultado con decimales, para trabajar con decimales se ocupan los números reales que en C están representados por el tipo float, entonces:

float dato1,dato2,suma,resta,mult,div;

Note que el nombre de las variables es representativo de lo que son o de para que se usan, por ejemplo, la variable mult es para guardar la multiplicación, la div es para almacenar la división, etc.... esa es una idea para hacer entendible un programa. Después de esto, corresponde leer los datos. Para que el usuario del programa entienda que hace, este debe indicarle que hacer,

printf("Ingrese primer numero:");scanf("%f",&dato1);printf("\n ingrese segundo numero:");scanf("%f",&dato2);

Posteriormente se hacen las operaciones indicadas,

suma=dato1+dato2; /* en la variable suma se almacena la suma de a y b*/

resta=dato1-dato2;mult=dato1*dato2;div=dato1/dato2;

y finalmente la salida a pantalla.

printf("\n LA SUMA ES: %f", suma);printf("\n LA RESTA ES: %f", resta);printf("\n LA MULTIPLICACION ES: %f", mult);

Page 48: DiagrFlujo-ProgramacionC

Programación de Computadores y el Lenguaje C (versión 2003)

Profesor: Roberto Uribe P. Pág. Nº 48

printf("\n LA DIVISION ES: %f", div);}

Lo importante por ahora es notar que todas la variables se declararon de tipo flotante (float), esto para realizar la división con los mismos números ingresados. Sin embargo los datos podrían haber sido int, en este caso se podría haber utilizado una conversión en el momento de hacer la operación (cast) y convertir los datos enteros de entrada a flotante, por ejemplo.

int A=10,B=4;float f;f=(float)A/(float)B;

Es decir, A y B se convierten a flotante para hacer la división entre flotantes y asignarla a f. Esto es conocido como cast o conversión explícita. La construcción de un programa debe estar pensada en usuarios no expertos, y por lo tanto evitar cualquier ingreso erróneo, o estar preparados para ello. En nuestro programa, si la variable dato2 es cero, la división se indetermina y ocurriría un error en tiempo de ejecución, esto arrojaría un error como:

Arithmetic Execption

oDevide Cero - Error

Lo adecuado sería preguntar ..... Es dato2 distinto de Cero??...., para ello se utilizan las estructuras de control. En nuestro programa podríamos hacer dicha pregunta antes de calcular la división, se haría con un if. Esto quedaría de la siguiente forma: if (dato2==0) /* Se pregunta "es dato2 igual a cero" */

printf("\n LA DIVISION ES INDETERMINADA");else{

div=dato1/dato2; /*el calculo de la division se hace aca y no antes*/printf("\n LA DIVISION ES: %f", div);

}

El programa final sería: # include <stdio.h>

main(){

float dato1,dato2,suma,resta,mult,div;printf("Ingrese primer numero:");scanf("%f",&dato1);printf("\n ingrese segundo numero:");scanf("%f",&dato2);suma=dato1+dato2; /* en la variable suma se almacena la suma de a y b */resta=dato1-dato2;mult=dato1*dato2;printf("\n LA SUMA ES: %f", suma);printf("\n LA RESTA ES: %f", resta);printf("\n LA MULTIPLICACION ES: %f", mult);if (dato2==0) /* Se pregunta "es dato2 igual a cero" */

printf("\n LA DIVISION ES INDETERMINADA");else /* "de lo contrario" */

Page 49: DiagrFlujo-ProgramacionC

Universidad de Magallanes - Departamento de Ingeniería en Computación

Profesor: Roberto Uribe P. Pág. Nº 49

{div=dato1/dato2; /*el calculo de la division se hace aca y no antes*/printf("\n LA DIVISION ES: %f", div);

}return 0;

}

La sentencia if se verá en la siguiente sección. El lenguaje C permite en la salida realizar la operación, es decir, en los mismos printf podríamos haber realizados las operaciones, con esto nos evitamos declarar las variables que almacenan los resultados de las operaciones. Por ejemplo, se pueden reemplazar los printf anteriores por:

:printf("\n LA SUMA ES: %f", dato1+dato2);printf("\n LA RESTA ES: %f", dato1-dato2);printf("\n LA MULTIPLICACION ES: %f", dato1*dato2);::

printf("\n LA DIVISION ES: %f", dato1/dato2);:

Note que para los printf y scanf se utilizó el formato para flotantes %f.

Page 50: DiagrFlujo-ProgramacionC

Programación de Computadores y el Lenguaje C (versión 2003)

Profesor: Roberto Uribe P. Pág. Nº 50

Problemas Resueltos y Propuestos Acorde con lo introductorio del capítulo, los siguientes ejercicios permiten ejercitar la sintaxis y notación del lenguaje C más que programar. Resueltos: 1.- Escriba un programa en C que permita realizar el cálculo del área de un triángulo, para ello se deben ingresar los valores para la base y la altura. 2.- Haga un programa que obtenga la raíz cuadrada de un número ingresado por teclado. 3.- Cree un programa que calcule la derivada de un polinomio de máximo grado 4, asuma que los exponentes son positivos. Recuerde que para obtener la derivada de : C×xn es C×n×xn-1 Ejemplo : Para obtener la derivada de 2 x4 + x3 + 2 x el programa debería hacer:

Programa que calcula la derivada de un polinomio

ingrese constante de x4 : 2ingrese constante de x3 : 1ingrese constante de x2 : 0ingrese constante de x1 : 2ingrese constante de x0 : 0

La derivada es : 8x3 + 3x2 + 0x + 2 4.- Suponga un vehículo a una velocidad constante de 1.6 km./min. Haga un programa que exprese la velocidad en km./hora y en m/seg. Propuestos: 5.- Escriba un programa al cual se le ingrese una temperatura en grados Celcius y haga la conversión a grados Farenheit y Kelvin. ºF = (1.8 * ºC) + 32

ºK = ºC + 273.2

Page 51: DiagrFlujo-ProgramacionC

Universidad de Magallanes - Departamento de Ingeniería en Computación

Profesor: Roberto Uribe P. Pág. Nº 51

Soluciones: 1.- Como resulta obvio para este ejercicio, debemos utilizar la librería de entrada y salida estándar, además, declarar dos variables que almacenen la base y la altura del triángulo. Recordando que la fórmula para el cálculo del área es: (base * altura) / 2; el programa sería: #include <stdio.h>main(){

float base, altura;printf("\nPrograma que calcula el area de un triangulo\n");printf("Ingrese base del triangulo : ");scanf("%f",&base);printf("Ingrese altura del triangulo : ");scanf("%f",&altura);printf("El area del triangulo es : %f",(base*altura)/2);return 0;

} Note que las variables fueron declaradas como flotantes (reales), esto para permitir el uso de valores con decimales. 2.- En este problema nos encontramos con algo nuevo, el cálculo de la raíz cuadrada. La idea es utilizar todas la herramientas que nos provee el lenguaje, entonces, para este caso no se pretende realizar el cálculo manual de la raíz, sino utilizar la función raíz cuadrada que viene en la librería matemática de C. La función llamada sqrt (de square root), permite calcular la raíz cuadrada de un número, para ello recibe como parámetro un dato de tipo flotante o double. Además, hay que incluir la librería matemática math.h. Entonces: #include <stdio.h>#include <math.h>

main(){

double dato;printf("\nPrograma que calcula la Raiz Cuadrada de un numero\n");printf("Ingrese un numero : ");scanf("%lf",&dato);printf("La raiz cuadrada de %lf es %lf ",dato,sqrt(dato));return 0;

} La instrucción sqrt(dato), retorna la raíz cuadrada de dato, es decir, un valor numérico (float) que reemplaza la llamada a la función en la posición donde esta está. ¿Y que ocurre si el dato ingresado es negativo?. En este caso la función sqrt envía un error a pantalla y retorna un 0. Por ejemplo, si el valor ingresado fuera -4, el programa se comportaría de la siguiente manera: Programa que calcula la Raiz Cuadrada de un numero

Page 52: DiagrFlujo-ProgramacionC

Programación de Computadores y el Lenguaje C (versión 2003)

Profesor: Roberto Uribe P. Pág. Nº 52

Ingrese un numero : -4sqrt: DOMAIN errorLa raiz cuadrada de -4.000000 es 0.000000

sin embargo, habría que determinar antes si es que el dato es negativo, pero esto se puede resolver con las herramientas entregadas en el proximo capítulo (estructura if). Nota: En el caso de utilizar el compilador cc de unix, debe utilizarse un parámetro adicional para utilizar la librería matemática, si el programa fuente es raiz.c, el comando para compilar sería:

cc raiz.c -lm en el caso de los actuales compiladores para computadores personales, como ser Turbo C de la Borland, no es necesario hacer esto. 3.- Este problema podría parecernos difícil, sin embargo, está bastante acotado y por el contrario, es sumamente sencillo. Existen dos formas de hacerlo, una es utilizando un ciclo que nos permita leer las cinco constantes, otra es simplemente no utilizando ciclos, en ese caso debemos declarar todas las variables (una por cada constante que se lea). #include <stdio.h>main(){int c4,c3,c2,c1,c0;printf("\nPrograma que calcula la derivada de un polinomio de grado4.\n");printf("ingrese constante de x4 :");scanf("%d",&c4);printf("ingrese constante de x3 :");scanf("%d",&c3);printf("ingrese constante de x2 :");scanf("%d",&c2);printf("ingrese constante de x1 :");scanf("%d",&c1);printf("ingrese constante de x0 :");scanf("%d",&c0);printf("\nLa derivada es :");printf("%dx3 + %dx2 + %dx + %d",c4*4,c3*3,c2*2,c1);return 0;

}

4.- Para este problema, sabiendo que: 1 Kilómetro es igual a 1000 metros, 1 hora son 60 minutos y un minuto son 60 segundos, la operación es: 1.6 Km./min. = 1.6 * 60 Km./Hora 1.6 Km./min. = 1.6 *1000 m/min. = (1.6 * 1000) / 60 m/seg. Podríamos hacer el programa para que funciones en forma general, es decir, que se ingrese cualquier valor en Km./min. y convertirlo a lo solicitado:

Page 53: DiagrFlujo-ProgramacionC

Universidad de Magallanes - Departamento de Ingeniería en Computación

Profesor: Roberto Uribe P. Pág. Nº 53

#include <stdio.h>main(){

float dato;printf("\nConversion de Km./min. a Km./Hora y a m/seg. \n");printf("Ingrese un valor (en Km./min.) : ");scanf("%f",&dato);printf("Convertido a Km./Hora es : %f \n", dato*60);printf("Convertido a m/seg. es : %f \n",(dato*1000)/60);return 0;

}

Page 54: DiagrFlujo-ProgramacionC

Programación de Computadores y el Lenguaje C (versión 2003)

Profesor: Roberto Uribe P. Pág. Nº 54

Capítulo Nº 3: Estructuras de Control Las estructuras de control permiten cambiar la secuencia de las instrucciones, es decir tomar una alternativa o ruta diferente dentro de un programa. En un programa, el control fluye secuencialmente, es decir, toma una instrucción, la ejecuta y luego continua con la siguiente; esto siempre y cuando no exista un cambio explícito del control. En el último ejemplo del capítulo anterior, se vio como cambia el flujo de control por el uso de una proposición if. En este capítulo se mostrará también el modo de empleo de los operadores de relación y lógicos y su uso dentro de las proposiciones de control. Estructura if Esta estructura es de la siguiente forma:

if (expresión)sentencia_1

sentencia_siguiente

Si la expresión es verdadera entonces se ejecuta la sentencia_1, luego se ejecuta la sentencia_siguiente. Si la expresión resulta ser falsa se ejecuta inmediatamente la sentencia_siguiente. Recuerde: Una sentencia puede contener una sola línea, si tiene más de una se utiliza { y }, las llaves indican que lo que está dentro de estas es parte de la sentencia.

Proposición if - else

if (expresión)sentencia_1

elsesentencia_2

sentencia_siguiente

Si la expresión es verdadera se ejecuta la sentencia_1, de lo contrario se ejecuta la sentencia_2, luego se ejecuta la sentencia_siguiente (independiente cual de las dos sentencias se ejecute, siempre se ejecuta la sentencia_siguiente). Recuerde: una expresión dentro de una estructura de control puede contener operadores de relación, lógicos o matemáticos.

expresión::=expresión operador_de_relación expresiónexpresión::=expresión operador_lógico expresiónexpresión::=expresión operador_matemático expresiónexpresión::= variable|constante

La idea de lo anterior es indicar que dentro de la expresión pueden haber preguntas compuestas, observe los siguientes ejemplos.

expresión Si No

sentencia_1 sentencia_2

sentencia_siguiente

Es equivalente a

Page 55: DiagrFlujo-ProgramacionC

Universidad de Magallanes - Departamento de Ingeniería en Computación

Profesor: Roberto Uribe P. Pág. Nº 55

Ej.: if (dato>=50)

printf("\n APROBADO CON NOTA: %d", dato);else

printf("\n REPROBADO");printf("\n programa finalizado");

Ej.:

result=dato % 2; /*se asigna el valor obtenido a result*/if (result==0) /*luego se compara*/

printf("\n resultado par");

Otra forma sería:

if ((dato%2)!= 0) /*se hace dentro del if */printf("\n dato impar");

Aquí la expresión fue:

(variable operador_matematico constante) operador_relacion constante

Ej.:

if (promedio>=30 && promedio<50) /*condicion con dos preguntas*/printf("\n debe dar examen");

elseif (promedio>=50)

printf("\n Eximido");else

printf("\n Reprobado sin derecho a Examen");printf("\n Fin programa");

Para este ejemplo, si el promedio es menor que 50 y el promedio es mayor o igual a 30, entonces, se imprimen el mensaje en pantalla debe dar examen. De lo contrario (quedan dos alternativas que sea menor que 30 o mayor o igual a 50), vuelve a preguntar en otro if si promedio es mayor o igual a 50, de lo contrario, y la única alternativa que resta es que sea menor que 30. En este caso un if estaba dentro del else de otro if. Ej.:

scanf("%d",&x);if (x>=20 && x<=50 && x!=30) /*se uso el oper. logico y */{

printf("\n x=%d", x);printf("\n x esta entre 20 y 50, pero no es 30\n");

}

Observemos el comportamiento del if de este ejemplo, suponiendo que el dato ingresado es 25.

x>=20 && x<=50 && x!=30Verdadero && Verdadero && Verdadero

Es decir, el resultado sería verdadero y por lo tanto el control se mete dentro del if. En C, un if entrega un valor distinto de 0 para verdadero e igual a cero para falso. Por ejemplo, el siguiente if esta correcto, y siempre va a ser verdadero:

Page 56: DiagrFlujo-ProgramacionC

Programación de Computadores y el Lenguaje C (versión 2003)

Profesor: Roberto Uribe P. Pág. Nº 56

opcion=1;if (opcion)

printf("\nEste if es verdadero"); Como la variable opcion es 1, entonces el if va a ser verdadero, es como preguntar:

if (opcion!=0)

Igualmente correcto es preguntar:

if (dato%2)printf("\nEl dato es impar\n");

Es decir, si el resultado de dato%2 es distinto de 0, entonces el if es verdadero, lo que indicaría que la variable dato es impar. Note que % es un operador matemático. Estructura while (Mientras) La estructura while o ciclo while nos permite repetir un proceso una cantidad determinada de veces (mientras la expresión sea verdadera). Se escribe de la siguiente manera:

while (expresión)sentencia_1

sentencia_siguiente

La sentencia_1 se repite tantas veces como la condición (expresión) sea verdadera. Ejercicios: Los siguientes problemas son clásicos en programación, para resolverlos recuerde el modo de desarrollo de los ejercicios resueltos en el capítulo anterior. Ejercicio Nº 3.1: Escriba un programa en C para el cálculo de xy, con x e y datos conocidos. Resolución: La idea entonces es multiplicar x y veces, como no sabemos el valor de y no podemos hacer la operación en una sola instrucción. La solución a esto es usar un ciclo repetitivo (hasta ahora el único conocido es el while), de tal manera que se ejecute y veces.

Page 57: DiagrFlujo-ProgramacionC

Universidad de Magallanes - Departamento de Ingeniería en Computación

Profesor: Roberto Uribe P. Pág. Nº 57

# include <stdio.h>main(){

int cont=1,producto=1,x,y;printf("Este programa calcula x elevado a y (con y entero positivo)");printf("\n\nIngrese x (entero): ");scanf("%d",&x);printf("\n\nIngrese y mayor o igual a 0 : ");scanf("%d",&y);if (y<0)

printf("y es menor que 0 (aun no implementado)\n");else

if (y==0)printf("\n El resultado es: %d",producto); /* osea 1*/

else{

while (cont<=y){

producto=producto*x; /* se multiplica el valor anterior deproducto por x */

cont=cont+1;}printf ("/n El resultado es: %d",producto);

}return 0;

}

En este programa se utilizan todos los conocimientos adquiridos hasta ahora. Para simplificar el problema, por ahora el cálculo se hace para exponentes positivos, sin embargo, modificarlo para exponentes negativos no requiere mucho esfuerzo, al igual que para x real. Para entender la solución hay que tener en cuenta varias consideraciones. La primera es el uso de una variable auxiliar producto, la que mantendrá los valores acumulados de las multiplicaciones sucesivas. Este se inicializa en 1. Como norma general para aplicaciones matemáticas las variables que mantienen valores acumulados se inicializan, en el caso se multiplicatorias con el elemento neutro multiplicativo, es decir, 1; para el caso de sumatorias, el elemento neutro de la suma, osea, 0. El uso de la variable auxiliar permite que el valor de la variable x no se modifique, si esta se llegase a modificar, entonces el resultado del programa sería errado. El contador cont, es para llevar la cuenta y terminar el ciclo cuando el número de veces que se hizo la multiplicación sea y. Como última acotación, note el indentado aplicado al programa, recuerde que esto hace que el código fuente se vea ordenado y más fácil de entender (sobre todo los ciclos). Ejercicio Nº 3.2: Realizar un programa que calcule el factorial de un número ingresado por teclado. Recuerde que:

0! = 11! = 1n! = n*(n-1)! = n*(n-1)*(n-2)* ....... *(n-n)!

Page 58: DiagrFlujo-ProgramacionC

Programación de Computadores y el Lenguaje C (versión 2003)

Profesor: Roberto Uribe P. Pág. Nº 58

Resolución: Como primera cosa a recordar es incluir la biblioteca de entrada y salida estándar (ya que debemos ingresar y mostrar datos en pantalla), luego incluir la función main(). Otra cosa que debemos comenzar a mejorar es la interfaz con el usuario, es decir, que los mensajes arrojados a pantalla sean adecuados y claros, además, en este caso prepararnos para un ingreso erróneo, como ser, que el dato ingresado sea menor que 0. Una solución podría ser: # include <stdio.h>

main(){

int cont=1,fact=1,num;printf("Este programa calcula el factorial de un numero,");printf("\n\nIngrese un numero entero mayor que cero :");scanf("%d",&num);if (num<0)

printf("ERROR, no se puede calcular el factorial de %d\n",num);else

if (num==0)printf("\n El Factorial de %d es: %d",num,fact);

else{

while (cont<=num){

fact=fact*cont;cont=cont+1;

}printf("\n El factorial de %d es: %d",num,fact);

}return 0;

} Este programa podría fallar si el dato ingresado es muy grande, esto debido a que el resultado puede ser superior al máximo número representable por un entero (en PC, 2 bytes). Para ampliar el máximo valor se debe declarar la variable fact como long int o long. Si se comparan los ejercicios anteriores (2 y 3) debemos notar que las soluciones son similares, esto debido a que ambos cálculos se realizan de la misma forma. En el ejercicio 2 la multiplicación se va haciendo con el valor original (x) y en el 3 con el contador(cont). Muchos ejercicios matemáticos se resuelven de manera similar, sobre todo aquellos que involucran series y sucesiones. Revisemos el siguiente ejercicio. Ejercicio Nº 3.3: Escribir un programa que calcule ex. Recuerde que:

ex = 1 + x1 + x2 + x3 + ...... + xn

1! 2! 3! n!

x : valor conocido. Asuma n=10. /* Programa que calcula e elevado a x */

# include <stdio.h>main()

Page 59: DiagrFlujo-ProgramacionC

Universidad de Magallanes - Departamento de Ingeniería en Computación

Profesor: Roberto Uribe P. Pág. Nº 59

{float cont=1,fact=1,pot=1,exp=1,div;/* exp fue inicializado en 1 para obtener el primer valorde la sumatoria*/float n=10,x;printf("Calculo de e elevado a x");printf("Ingrese un numero:");scanf("%f",&x);if (x<0)

printf("Aun no implementado");else{

while(cont<=n){

fact=fact*cont;pot=pot*x;div=pot/fact;exp=exp+div;cont=cont+1;

}printf("\n El exponencial es: %f \n",exp);

}return 0;

}

Este ejercicio se resolvió según el diagrama de flujo del capítulo Nº 1. Recuerde que para verificar el comportamiento de un programa se puede ejecutar en papel, esto siempre y cuando el programa no sea extenso. Algo más del if Observe los siguientes ejemplos, ambos están correctos, 1) if (x==0)

if (y==0)printf("\n ERROR");

elseprintf("\n NO HAY ERROR");

2) if (x==0)

if (y==0)printf("\n ERROR");

elseprintf("\n NO HAY ERROR");

Ambos funcionan y compilan correctamente, sin embargo, lo que hay que determinar es a que if pertenece el else . Este es un problema de tipo semántico que se resuelve con la siguiente regla: "un else se asocia con el if más cercano". Por lo tanto, el indentado del caso 1 es el más adecuado (el indentado es sólo una norma, no es obligatorio). Existe una combinación para la estructura if, y es la siguiente:

if (expresión_1)sentencia_1

else if (expresión_2)sentencia_2

Page 60: DiagrFlujo-ProgramacionC

Programación de Computadores y el Lenguaje C (versión 2003)

Profesor: Roberto Uribe P. Pág. Nº 60

else if (expresión_3)sentencia_3

.

.

.

.else if (expresión_n)

sentencia_nelsesentencia_por_defecto

sentencia_siguiente

En este caso sólo se ejecuta la proposición que sea verdadera, ninguna otra. En caso que todas sean falsas, entonces se ejecuta la sentencia por defecto del else. Algo más del while Siendo C muy permisivo en algunas situaciones (por lo que muchas veces es o fue criticado por sus detractores), tome en cuenta lo siguiente para la estructura while: La función getchar() lee un caracter desde stdin (teclado). Ej.:

char c;c=getchar();/* getchar()-->lee un caracter y luego es asignado a c */

Entonces un ciclo while puede ser de la siguiente manera:

while ((c=getchar())==' ' ); /* sentencia nula */

Esto es lo mismo que:

while ((c=getchar( ))==' ');

En este caso primero se lee un caracter por teclado, luego este caracter se asigna a la variable c y finalmente c se compara con el espacio, si es igual (es verdadero) entonces se repite el ciclo, es decir, el while no termina hasta que encuentre un caracter distinto de espacio en blanco. Note que lo interesante es que la asignación se realizó en el while. Otra manera de escribirlo es:

c=' ';while (c==' '){

c=getchar();}

Para el siguiente caso vea que es lo que sucede:

while (c=getchar()==' ');

Page 61: DiagrFlujo-ProgramacionC

Universidad de Magallanes - Departamento de Ingeniería en Computación

Profesor: Roberto Uribe P. Pág. Nº 61

Nota: Como no existen los paréntesis, primero se hace la comparación y luego la asignación, es decir a c se le asigna 0 ó distinto de 0, según el resultado de la comparación. Esto ocurre debido a la prioridad de los operadores. La opción inversa de getchar() es:

putchar(c); /*envia a pantalla la variable que es de tipo caracter */

Para el caso de getchar y putchar, si c es declarado como int, también es válido, esto porque se hace la conversión del dato char a su representación entera. Por ejemplo, las siguientes sentencias están correctas:

putchar(‘m’);

Esta imprime el caracter m,

putchar(56);

imprimiría el caracter número 56 de la tabla o conjunto de caracteres en uso. Estructura for Cuando se conoce a priori la cantidad de veces que se tiene que repetir un proceso, entonces, el ciclo while puede ser reemplazado por un ciclo for. Este se denota por:

for (expresión_1; expresión_2; expresión_3)sentencia

sentencia_siguiente

La primera expresión es la inicialización de algún contador, la segunda es la condición, es decir, mientras sea verdadera se ejecuta la sentencia que esta dentro del for, de lo contrario se ejecuta la sentencia siguiente. La tercera corresponde al incremento del contador. Por ejemplo:

for (i=1;i<=10;i=i+1)printf("hola ");

printf("\nfueron muchos holas");

En este caso se imprime 10 veces hola, y al final fueron muchos holas. La estructura for se basa en la estructura while, y lo anterior es equivalente a:

expresión_1;while (expresión_2){

sentencia;expresión_3;

}sentencia_siguiente;

Page 62: DiagrFlujo-ProgramacionC

Programación de Computadores y el Lenguaje C (versión 2003)

Profesor: Roberto Uribe P. Pág. Nº 62

Ejercicio Nº 3.4: Escriba un programa que calcule la siguiente sumatoria, utilizando el ciclo for:

∑=

=n

iix

1

; n : valor conocido

#include <stdio.h>main(){

int sumatoria=0,i,n;printf ("\n Este programa calcula la sumatoria i de 1 a n");printf ("\n\n Ingrese n: ");scanf ("%d",&n);for (i=1;i<=n;i=i+1)

sumatoria=sumatoria+i;printf ("\n la sumatoria es: %d \n", sumatoria);return 0;

}

El for puede haber sido hecho como:

i=1;while (i<=n){

sumatoria=sumatoria+i; /* es lo mismo que sumatoria+=i; */i=i+1; /* es lo mismo que i++; */

}

Para los incrementos y decrementos se pueden utilizar los operadores ++ y --. Es igual escribir: i=i+1 que i++ Ejemplo del cálculo del factorial de n utilizando ciclo for.

for (cont=1;cont<=n;cont++)fact=fact*cont; /* fact*=cont; */

printf("\n El Factorial es: %d",fact);

Ciclo do-while Con los ciclos vistos hasta ahora (for y while), la condición se analiza al comienzo del bucle (otra forma de nombrar a un ciclo), para el caso del do-while, la condición se analiza al final del bucle. Esto implica, que el ciclo se ejecutará por lo menos una vez. La forma general de un ciclo do-while es

dosentencia

while (condicion); El ciclo do-while itera mientras la condición sea verdadera. En el siguiente ejemplo se leen caracteres de teclado mientras sean distinto de ‘a’, ‘b’ y ‘c’.

Page 63: DiagrFlujo-ProgramacionC

Universidad de Magallanes - Departamento de Ingeniería en Computación

Profesor: Roberto Uribe P. Pág. Nº 63

do{

car=getchar();printf("%d",car); /*imprimiria los valores ascii de los

caracteres leidos */}while (car!='a' && car!='b' && car!='c');

Proposición break Esta proposición rompe el flujo normal de control. Break termina un ciclo o una proposición switch.

while (1){

scanf("%d",&num);if ((num%2)==0){

printf("\n este era par");break;

}} /* break envia el control hasta esta linea */

En este ejemplo el while resulta ser siempre verdadero. Se va leyendo un número y se termina el ciclo cuando el dato leído es par, esto por causa de la sentencia break. Proposición Continue La palabra reservada continue se usa de manera similar al break en un ciclo, es decir, ignora todas las sentencias que siguen a la sentencia continue, sin embargo, no termina el ciclo, no evítale incremento del contador o que la condición sea evaluada. Si la condición sigue siendo verdadera, entonces el ciclo sigue iterando. Proposición switch La proposición switch tiene la función de un if anidado, esta es una sentencia de selección múltiple (es como la proposición case en Pascal). En el switch se va comparando el valor de la expresión con cada una de las constantes enteras que aparecen en las sentencias case. La forma de esta es:

switch(expresión_entera){

case constante_entera1: sentencia;[break;]

case constante_entera2: sentencia;[break;]

case constante_entera3: sentencia;[break;]

:::[default: sentencia] /* optativo */

}

Page 64: DiagrFlujo-ProgramacionC

Programación de Computadores y el Lenguaje C (versión 2003)

Profesor: Roberto Uribe P. Pág. Nº 64

Cuando la expresión resulta ser igual a una de las constantes, entonces se ejecuta la sentencia correspondiente hasta el break o hasta el final del switch. En caso contrario se ejecuta la sentencia default, que es opcional. Si existen más de una línea en la sentencia case no es necesario utilizar llaves. Por último la proposición break también es opcional. Revisemos los siguientes ejemplos: #include <stdio.h>main() /* cuenta cuantas a, e, i, o y u se han ingresado*/{

char letra;int conta=0,conte=0,conti=0,conto=0,contu=0,otros=0;do{

letra=getchar();switch(letra){

case 'a' : conta++;break;

case 'e' : conte++;break;

case 'i' : conti++;break;

case 'o' : conto++;break;

case 'u' : contu++;break;

default : otros++; /* es optativa */}

} while(letra!=‘\t’);return 0;

}

La expresión debe ser entera, sin embargo, si se utilizan constantes o variables de tipo caracter, estas se convierten a sus valores enteros. Al término de cada case se usa el break para terminar el switch, que lleva el control hasta la línea siguiente después del switch. El programa termina cuando el caracter leído es un tabulador. Se debe tomar en cuenta que a diferencia del if, en el switch sólo se puede comprobar igualdad. Además, no deben haber dos sentencias case con constantes iguales dentro de un mismo switch. Ejemplo: Si quisiéramos contar las vocales (en general) leídas, el programa podría ser: #include <stdio.h>main() /* cuenta cuantas vocales se han ingresado*/{

char letra;int contvocal=0,otros=0;while (1){

letra=getchar();switch(letra){

case 'a' :case 'e' :case 'i' :

Page 65: DiagrFlujo-ProgramacionC

Universidad de Magallanes - Departamento de Ingeniería en Computación

Profesor: Roberto Uribe P. Pág. Nº 65

case 'o' :case 'u' : contvocal++;

printf("%d",contvocal);break;

default : otros++;}

}return 0;

}

En este ejemplo, si se lee una letra a, como no existe break, no termina el switch, entonces el control pasa al case siguiente, y así hasta que llega a la sentencia donde se incrementa la variable contvocal, y se ejecuta el break.En este ejemplo no fue necesario colocar break en cada case. Es importante notar que el while se ejecuta mientras no sea el final de la entrada EOF (End of File). La entrada también podría haber sido un archivo. Por ejemplo, si crearamos un archivo llamado prueba.txt, podríamos haber ejecutado el programa de la siguiente manera : a.out < prueba.txt lo que hace que el while procese cada carácter contenido en prueba.txt.

Page 66: DiagrFlujo-ProgramacionC

Programación de Computadores y el Lenguaje C (versión 2003)

Profesor: Roberto Uribe P. Pág. Nº 66

Problemas Resueltos y Propuestos Con los conocimientos adquiridos en este capítulo Ud. debe ser capaz de resolver en C todos los ejercicios resueltos y propuestos del capítulo anterior. Resueltos: 1.- Escriba un programa en lenguaje C que verifique si un número ingresado por teclado es o no primo. Recuerde que un número primo es aquel que sólo es divisible por 1 y por el mismo. 2.- Escriba un programa en C que realice lo siguiente : - lea un número entero - lea un operador matemático - lea otro número entero. luego debe realizar la operación indicada y arrojar el resultado por pantalla. Los operadores matemáticos pueden ser de suma, resta, multiplicación y división. Ej.:

ingrese un número entero : 5ingrese operador : *ingrese el segundo número :10

El resultado de 5 * 10 es : 50

3.- Opinión personal: Con los conocimientos (herramientas) que Ud. tiene hasta ahora, hacer el siguiente programa lo considera: fácil, complicado, realmente difícil. Porqué? - leer cuatro números enteros - imprimirlos en orden ascendente (de menor a mayor). 4.- Realizar un programa que lea un número hexadecimal y lo convierta a su representación en base 10. - Restricciones : No usar ninguna función conocida para la conversión, sólo la supuesta más adelante. El dígito que se leerá tiene que ser de tipo char. - Recuerde que : 13C(16) ----> 316(10) - ya que : 1*162+3*161+12*160 256 + 48 + 12 316

Page 67: DiagrFlujo-ProgramacionC

Universidad de Magallanes - Departamento de Ingeniería en Computación

Profesor: Roberto Uribe P. Pág. Nº 67

- Para facilitar la solución: lea la cantidad de dígitos que posee el número. Luego lea los dígitos uno a uno. Ejemplo :

ingrese cantidad de dígitos: 3ingrese dígito 1 : 1ingrese dígito 2 : 3ingrese dígito 3 : CEl número en base 10 es : 316

- Supuesto : Existe una función llamada convcharaint(dato) que transforma un dato tipo char a int, ejemplo:

datochar='1';datoint=0;

datoint=convcharaint(datochar);

/* el datoint resulta igual a 1 */

5.- En C la entrada y salida estándar puede ser dirigida a archivo, esto simplemente usando los caracteres < o >. Por ejemplo, supongamos que se tiene un programa que lee 3 caracteres y los va imprimiendo en pantalla. El ingreso de los caracteres puede haber sido hecha con getchar() o scanf(). Si en vez de hacer la entrada por teclado, se crea un archivo de texto que contiene los tres caracteres (suponga “abc”) y luego el programa se ejecuta de la siguiente manera:

$a.out<archabc$

Cree un programa que lea caracteres desde stdin e imprima el número de vocales leídas y el número de líneas que tenía el archivo. Un archivo termina cuando el caracter leído es la constante EOF. Propuestos: 6.- Durante la última Gran Guerra, un grupo de ingenieros del área informática fue contratado por una de las partes en conflicto para investigar y decodificar los mensajes en clave que se enviaba el enemigo. Dichos mensajes, eran una serie de caracteres entre letras y números. Durante un periodo considerable de tiempo, estos investigaron la posible solución. Una vez que llegaron a ella, se dieron cuenta que los dígitos que contenía el mensaje correspondían a letras del mensaje real, esto último según la siguiente tabla:

M U R C I E L A G O0 1 2 3 4 5 6 7 8 9

Page 68: DiagrFlujo-ProgramacionC

Programación de Computadores y el Lenguaje C (versión 2003)

Profesor: Roberto Uribe P. Pág. Nº 68

Es decir, para la siguiente frase en clave: H9Y S5 39N934529N 69S P240529S 5X404D9S D56 2709 Su traducción es: Hoy se conocieron los primeros eximidos del ramo Entonces, se decidió crear un programa para hacer mas rápido el proceso de decodificación. Asuma que el ingreso es caracter a caracter, es decir, considerando lo explicado en el ejercicio anterior es posible leer los datos desde un archivo y direccionar la entrada con el símbolo menor. 7.- Para el siguiente programa:

# include <stdio.h>

main(){

int i=1;int n;float S=0.0,x;printf("\nIngrese n :");scanf("%d",&n);printf("\nIngrese x :");scanf("%f",&x);while(i<=n){

S=S+x/(float)i;i=i+1;

}printf("\n\nS es : %f",S);return 0;

} a.- Indique línea a línea, que proceso se va realizando, esto en forma clara. b.- Para n=10 y x=100, obtenga el valor final para S y i en cada ciclo (el valor puede quedar expresado). c.- Obtenga la fórmula matemática. 8.- Haga un programa que obtenga los 5 primeros números perfectos. Un número perfecto es aquel número positivo que es igual a la suma de los enteros positivos (excluyendo el mismo) que son divisores del número. Ejemplo : El primer número perfecto es el 6 ya que sus divisores son el 1, 2 y 3 y la suma de estos 1 + 2 + 3 = 6. Soluciones: 1.- Lo primero es recordar que un número primo es aquel que sólo es divisible por 1 y por el mismo. Entonces, una de las formas de resolver este problema, luego de ingresado el dato, es

Page 69: DiagrFlujo-ProgramacionC

Universidad de Magallanes - Departamento de Ingeniería en Computación

Profesor: Roberto Uribe P. Pág. Nº 69

recorrer con un contador desde 2 hasta el dato-1 y tener un otro contador que se vaya incrementando cada vez que la división de dato por el primer contador sea entera.. #include <stdio.h>main(){

int num,i=2,cont=0;printf("\nPrograma que verifica si un numero es o no primo.\n");printf("Ingrese un numero : ");scanf("%d",&num);if (num<=0)

printf("Error, el numero debe ser mayor que cero.\n");else

if (num==1)printf("El numero %d es primo.",num);

elseif (num==2)

printf("El numero %d es primo.",num);else{

for(i=2;i<num;i++)if ((num%i)==0)

cont++;if (cont>0)

printf("El numero %d no es primo.",num);else

printf("El numero %d es primo.",num);}

return 0;} Observe que el for va de 2 a num-1, esto ya que de otro modo cont se incrementaría al hacer el módulo por 1 y num. 2.-

#include <stdio.h>main(){

int num1,num2,oper;printf("\nPrograma que realiza las 4 operaciones basicas.\n");printf("ingrese un número entero :");scanf("%d",&num1);printf("ingrese operador :");scanf("%c",&oper);printf("ingrese el segundo número :");scanf("%d",&num2);printf("\nEl resultado de %d %c %d es :",num1,oper,num2);if (oper=='+')

printf("%d",num1+num2);else

if (oper=='-')printf("%d",num1-num2);

elseif (oper=='*')

printf("%d",num1*num2);else /* solo falta la division*/

if (num2!=0)printf("%d",num1/num2);

elseprintf("La division se indetermina);

return 0;}

Page 70: DiagrFlujo-ProgramacionC

Programación de Computadores y el Lenguaje C (versión 2003)

Profesor: Roberto Uribe P. Pág. Nº 70

Una forma más sencilla de construir este programa es usando la instrucción switch, la parte principal del programa quedaría:

switch(oper){

case '+': printf("%d",num1+num2);break;

case '-': printf("%d",num1-num2);break;

case '*': printf("%d",num1*num2);break;

case '/': if (num2!=0)printf("%d",num1/num2);

elseprintf("La division se indetermina);

break;default: printf("operacion no reconocida"); /* en caso de no

ser ninguna de las anteriores*/} /*fin switch*/

3.- A simple vista podría parecer fácil ordenar 4 números, sin embargo, no es tan sencillo. La dificultad radica principalmente en que la cantidad de comparaciones que se tienen que hacer son un mínimo de 4!, es decir, 24. Esto, ya que tendríamos que crear 4 variables e ir comparándolas entre ellas. En si, el problema resulta mas engorroso que difícil. En capítulos posteriores cuando se aprenda el concepto de arreglos podremos aprender como resolver este tipo de problemas sin tanta complicación. 4.- Los programas para cambio de base de un número son clásicos en programación, en este caso, como los números hexadecimales usan caracteres es necesario declarar de tipo char los dígitos que se ingresarán. También se indica que existe una función que permite convertir un char a int. Como aún no manejamos funciones sólo debemos recordar que un función va a retornar un valor, que se puede utilizar como un dato cualquiera (en este caso un dato int). Si el dato ingresado a la función es A, retorna 10, si es B, 11, etc. Otra cosa a tomar en cuenta, es que la cantidad de dígitos es variable, entonces, debemos utilizar un ciclo que nos permita leer todos los dígitos. #include <stdio.h>main(){

int i=0,numdig=0,valor=0,digint;char digchar;printf("\nPrograma que transforma un numero Hexadecimal a Decimal.\n");printf("Ingrese cantidad de digitos : ");scanf("%d",&numdig);if (numdig<=0)

printf("Error, el numero debe ser mayor que cero.\n");else

for(i=1;i<=numdig;i++){

printf("Ingrese digito %d :",i);scanf("%c",&digchar); /*se lee un char*/digint=convcharaint(digchar); /*convierte el char a int*/pot=1; /* Es necesario que pot se inicialice cada vez en 1*/for(j=1;j<=numdig-i;j++) /*calculo de la potencia de 16*/

pot=pot*16;valor=valor+digint*pot;

}

Page 71: DiagrFlujo-ProgramacionC

Universidad de Magallanes - Departamento de Ingeniería en Computación

Profesor: Roberto Uribe P. Pág. Nº 71

printf("El numero en base 10 es : %d.",valor);return 0;

} Observe que en el cálculo de la potencia, el for llega hasta numdig-1, esto ya que si son n dígitos, entonces el primero se multiplica por 16n-1 y el último por 160. Algunos cambios que se pueden hacer son:

for(i=1;i<=numdig;i++){

printf("Ingrese digito %d :",i);scanf("%c",&digchar); /*se lee un char*/for(j=1,pot=1;j<=numdig-i;j++)

pot=pot*16;valor=valor+convcharaint(digchar)*pot;

} En este caso dentro del for se inicializaron ambas variables. Además, la función se llamo en la misma operación, de esta manera se evita usar una variable (digint). Lo último que se tiene que considerar, es que este programa no compilaría dado que la función convcharaint no existe, en el próximo capítulo estaremos en condiciones de poder implementar dicha función. 5.- Como se mencionó en el encabezado de la pregunta, es posible que la entrada pueda ser a través de teclado o bien un archivo, para ello sólo hay que usar el símbolo menor al momento de ejecutar el programa. Para este ejercicio supongamos que creamos un archivo en un editor de texto cualquiera, en Unix podría ser el conocido vi, en DOS el edit o el mismo editor que trae el compilador de C con el que se esta trabajando (por ejemplo el Turbo C de la Borland trae su propio editor). #include <stdio.h>main(){

int car,cuentavoc=0,cuantalin=0,cuentatab=0;car=getchar();while (car!=EOF){

switch(car){

case 'a':case 'e':case 'i':case 'o':case 'u': cuentavoc++;

break;case '\t': cuentatab++;

break;case '\n': cuentalin++;

break;} /*fin switch*/car=getchar();

}printf("\nNumero de Vocales : %d",cuentavoc);printf("\nNumero de Tabuladores : %d",cuentatab);printf("\nNumero de Lineas : %d",cuentalin);return 0;

}

Page 72: DiagrFlujo-ProgramacionC

Programación de Computadores y el Lenguaje C (versión 2003)

Profesor: Roberto Uribe P. Pág. Nº 72

Capítulo Nº 4 : Funciones en C El lenguaje "C" esta creado a partir de funciones y su programación se hace a través de ellas, estas facilitan la estructuración y el entendimiento de los programas, "dividir para reinar". Una función puede realizar un proceso simple o complejo, como ser el cálculo de factorial, raíz cuadrada, determinante de una matriz, lectura de datos, procesos con punteros, etc... Una función consta de un encabezado y un cuerpo. Todas las funciones devuelven un valor o un valor vacío (void), este valor especifica el tipo de dato de la función, el cual se tiene que especificar, si no es así se asume entero. Ej.: mi_primera_func()

{printf("\n\n esta sera una de las muchas");printf("\n pero muchas, funciones que se");printf("\n haran en este entretenido");printf("\n ramo");/* la ausencia de acentos es porque la funcion esta hecha para

correr en Unix*/}

Para la función anterior el tipo de dato no se especificó por lo tanto es int, es decir el encabezado podría haber sido:

int mi_primera_func(){}

El cuerpo de la función va entre llaves. La función anterior no devuelve un valor sólo realiza un proceso. Una función puede ser llamada desde la función principal main() o de cualquier otra. La llamada se realiza colocando el nombre en el lugar donde, se desea que se ejecute la función. La función tiene que ir declarada antes del main y se puede escribir después de la función principal, esto último no es obligatorio, pero es una buena norma para programar. Ejemplo:

# include <stdio.h>int mi_segunda_func();main(){

printf("\n esto fue escrito antes de la funcion...");mi_segunda_func();printf (" \n la función ya se ejecuto");return 0;

}int mi_segunda_func(){

printf("\n\n Estoy dentro de la funcion");}

La salida de este programa sería:

Definición de la función

Declaración de la función

Page 73: DiagrFlujo-ProgramacionC

Universidad de Magallanes - Departamento de Ingeniería en Computación

Profesor: Roberto Uribe P. Pág. Nº 73

esto fue escrito antes de la función...

Estoy dentro de la funcionla función ya se ejecuto

A diferencia de otros lenguajes no existen procedimientos propiamente tal (procedure en Pascal o subroutine en Fortran). Sin embargo las funciones en C pueden cumplir este papel. Además existen funciones de tipo void (vacío) que no devuelven valores. Para el caso anterior la función podría haber sido void o sea:

void mi_segunda_func()

Instrucción return Esta es la expresión que permite devolver el valor de la función hacia donde es llamada, además de terminar la ejecución de dicha función. Ejemplo:

int suma(){

int A, B; /* declaraciones de variables locales */int sum;A=5;B=8;sum=A+B;return(sum);

}

Esta función nos permite ver el funcionamiento del return. El valor que retorna la función debe ser del mismo tipo con que fue declarada la función. El resto del programa sería:

#include <stdio.h>int suma(); /* Global */main(){

int result;result=suma();printf("\n suma es: %d",result) /*imprimiria 13*/return 0;

}

/* aqui deberia ir escrita la funcion */

En la sentencia result=suma(); a la variable result se le asigna el valor retornado de la función suma(). El nombre de la función es reemplazada por el valor que ella retorna, entonces también es válido hacer:

result=10+suma();

Lo que implicaría que result tendría el valor 23. Consideraciones :

Page 74: DiagrFlujo-ProgramacionC

Programación de Computadores y el Lenguaje C (versión 2003)

Profesor: Roberto Uribe P. Pág. Nº 74

-Las dos primeras funciones (mi_primera_func y mi_segunda_func) no contenían la intrucción return, esto no ocasiona problemas debido a que no retornan valor. Sin embargo, se acostumbra a colocar return. Algunos compiladores podrían enviar una advertencia al no encontrar return en una función. -La función principal main() tiene forma de función, sin embargo, técnicamente no lo es, por lo tanto es ilegal hacer llamadas a ella. Los programas que invocan a main() funcionan a veces, esto dependiendo de la implementación ya que algunas si tienen a main() como función normal.

Variables Globales y Locales Variables Globales: Estas se declaran fuera de las funciones, antes del main o en otros archivos y pueden utilizarse durante todo el programa y en cualquier parte de este, incluso en las funciones. Variables Locales: Estas se declaran dentro de bloques o funciones y permanecen activas solamente en ese ámbito. Ejemplo de variables locales:

float raizcua() /*funcion que retorna un valor flotante*/{

int i, j, /*variables locales*/float raizfi;::

}

Para este ejemplo, las variables i, j y raizfi son locales a la función raizcua, es decir, dichas variables comienzan a existir cuando se llama a la función y desaparecen cuando esta termina. Estas variables no pueden ser accesadas directamente desde otras funciones, así como desde la función raizcua no se puede tener acceso a variables declaradas en otras funciones. Además, es importante saber que dichas variables no conservan su valor, o sea, si la función es nuevamente llamada, entonces son de nuevo creada, y por lo tanto inicialmente contienen un valor basura. A estas variables se les llama automáticas. Tome en cuenta el siguiente ejemplo:

#include <stdio.h>int func1();float func2();int var1=0; /*variable global*/

main(){

int i=0; /*variable local a main*/::

}

Page 75: DiagrFlujo-ProgramacionC

Universidad de Magallanes - Departamento de Ingeniería en Computación

Profesor: Roberto Uribe P. Pág. Nº 75

int func1(){

int i=0; /*variable local a func1*/:

}float func2(){

int var1=0; /*variable local a func2*/:

}

Quizás con lo anterior podríamos tener alguna confusión, esto provocado porque tanto en la función main como en func1 existe una variable del mismo nombre, es más, existe una variable global llamada var1 que también esta declarada en func2. Como se dijo anteriormente las variables locales no pueden ser accesadas directamente desde otras funciones, por lo tanto, las variables i del main e i de func1 son distintas, si se llama a func1 desde el main se crea la variable i de func1 y la otra variable (la del main) no existe dentro de esta función. Un caso similar es con var1, como es global puede ser llamada de cualquier parte, por ejemplo desde func1 o main. Sin embargo, dentro de func2 la variable activa es la local, por lo tanto var1 global se desactiva dentro de esta función. Sin embargo, var1 global no pierde su valor, sólo se desactiva momentáneamente (por el periodo que dura func2). Es posible también encontrar declaraciones de variables locales en bloques internos dentro de alguna función, por ejemplo : :

main(){

int cont=0, suma=0;:{

float mult=1, div=0;::

}:

}

Para estos casos, el bloque interno se crea con { y se termina con }, las variables anteriores mult y div sólo existen dentro del bloque. Llamadas a Funciones Debemos recordar que una función puede ser llamada una o más veces y desde cualquier otra función, así como también se pueden llamar a muchas funciones dentro de una misma función. Una manera de ejemplificar esto, es a través del siguiente esquema:

Page 76: DiagrFlujo-ProgramacionC

Programación de Computadores y el Lenguaje C (versión 2003)

Profesor: Roberto Uribe P. Pág. Nº 76

Se asume que el programa posee cuatro funciones, una que calcula ex, otra el factorial de un número, otra un número cualquiera elevado a otro y otra (multinum) que realiza cualquier otra operación. Entonces, vemos que desde el programa principal (main) se llama a multinum, a factorial y a eelevx, sin embargo desde esta última también se llama a factorial, además de potencia.. La idea de las funciones es desarrollar programas aplicando el Modelo Top-Down, es decir, ir dividiendo el problema en partes más pequeñas y estas a su vez en otras más chicas, esto hasta que se encuentren subproblemas posibles de resolver, luego ir uniendo las soluciones a estos subproblemas, hasta permitir resolver el problema completo (problema original). Es por ello que se habla de dividir para reinar o dividir para conquistar, es más fácil resolver cualquier problema si este se divide en pequeñas partes que puedan ser manejables. Se debe tener cierto cuidado con las llamadas a funciones, ya que podrían ocurrir problemas tales como ciclos o loops infinitos, estos provocados porque no existe un adecuado término de la función. Observe el siguiente ejemplo:

func1()

main()

func2()

func3()

# include <stdio.h>double eelevx();long factorial();long potencia();int multinum();main(){

:::

}

long factorial()

int multinum()

long potencia()

double eelevx()

Page 77: DiagrFlujo-ProgramacionC

Universidad de Magallanes - Departamento de Ingeniería en Computación

Profesor: Roberto Uribe P. Pág. Nº 77

El esquema anterior podría provocar un loop infinito si no se determina correctamente el final de alguna de las funciones. Esto también podría ocurrir en las llamadas recursivas. Las funciones recursivas son aquellas donde existe una llamada a si misma. Las llamadas recursivas a funciones son muy utilizadas en programación y dan un aspecto mas entendible a esta. El siguiente ejemplo muestra una función que se llama a si misma,

int funrec(){

funrec();return 0;

} sin embargo para este ejemplo, como antes del return se llama a si misma, nunca alcanza la instrucción de término, por lo tanto queda en un ciclo infinito. Paso de Parámetros a funciones Una variable global permanece activa durante todo el periodo de ejecución de un programa, por lo tanto, el espacio de memoria que ocupa dicha variable permanece ocupada hasta que no termina el programa. Si se ocupan demasiadas variables de este tipo es posible que en algún momento se acabe la memoria RAM, sobre todo en computadores personales (a pesar que ahora estos traen la suficiente). El uso excesivo de variables globales atenta contra la modularidad del programa y lo hace más difícil de entender, esto último, ya que las funciones terminan amarradas a las variables del caso, lo que limita su reusabilidad. Una manera de evitar el exceso de variables globales es pasando parámetros a las funciones. Ejemplo: El siguiente programa incluye una función calcula el factorial de un número cualquiera:

func1()

main()

Page 78: DiagrFlujo-ProgramacionC

Programación de Computadores y el Lenguaje C (versión 2003)

Profesor: Roberto Uribe P. Pág. Nº 78

#include <stdio.h>long int factorial(int n);main(){

int datoin;long result;printf("\n ingrese número: ");scanf("%ld",&datoin);result=factorial(datoin);printf("\n el factorial es: %ld", result);

}

long int factorial(int n ){

long fact=1 ;int cont=1;for (cont=1;cont<=n;cont++)

fact=fact*(long)cont;return(fact);

}

En la definición de la función se incluyó un parámetro, entonces, este debe ser declarado, esto se puede hacer en la línea siguiente antes de la llave que indica el comienzo de la función. Como a la función se le pasa un parámetro, entonces, al llamarla también se debe incluir un parámetro. Lo que ocurre en el ejemplo es: desde el main se llama a la función factorial, a la cual se le pasa el parámetro datoin. En términos formales, se le pasa el valor (o contenido) de la variable datoin. La variable n de la función factorial asume el valor que fue ingresado a la función. Una vez que se realiza el proceso de cálculo del factorial, este retorna el resultado almacenado en fact hacia donde es llamada la función. En el main el valor retornado por la función es asignado a la variable result. La parte esencial del programa, el calculo del factorial, es igual a los ejemplos anteriores, sin embargo, ahora esta incluido dentro de una función, lo que le otorga una mayor modularidad al programa. De esta manera para calcular el factorial de otro número no se tiene que modificar la función. En el ejemplo, no es necesario asignar a una variable el resultado arrojado por la función, de esta manera nos ahorraríamos una variable,

printf("\n el factorial es: %ld",factorial(datoin));

Si se pasa más de un parámetro a la función se deben separar por coma y dentro de esta deben estar todos declarados. Existen dos formas de pasar parámetros a funciones, por valor (ejemplo anterior) o por referencia, esta última se verá más adelante en el capítulo de punteros. Paso de variables por valor Cuando se dice que una variable ha sido pasada por valor, quiere decir, que su contenido no se modifica dentro de la función. Para entender esto, estudiemos el siguiente ejemplo:

Parámetro de la función

Parámetro ingresado a la función en la llamada

Declaración del parámetro ingresado

Page 79: DiagrFlujo-ProgramacionC

Universidad de Magallanes - Departamento de Ingeniería en Computación

Profesor: Roberto Uribe P. Pág. Nº 79

#include <stdio.h>xxfun(int bli);main(){

int i;i=5;printf("\nAntes de la funcion i es igual a: %d",i);xxfun(i);printf("\nDespues de la funcion i es igual a: %d",i);

}

xxfun(int bli) /* la variable bli asume el valor con */{ /*la cual es llamada la función */

bli=bli+10;printf("\nDentro de la funcion bli es: %d",bli);

} /* función xxfun() no retorna valores */

La salida de este programa es:

Antes de la funcion i es igual a: 5Dentro de la funcion bli es: 15Despues de la funcion i es igual a: 5

La función xxfun() no logra modificar la variable de entrada i. El parámetro bli toma el valor de entrada (es decir 5), se incrementa en 10, pero es la variable bli la que se modifica y no i del main. Una vez que termina la función la variable bli deja de existir, si se llamara de nuevo, tomaría el nuevo valor de entrada a la función, cualquiera sea este. Para complicar un poco más el asunto, supongamos el siguiente ejemplo:

#include<stdio.h>int prueba(int dato);main(){

int dato=5,i=6,resultado=0;printf("\n dato=%d, i=%d, resultado=%d",dato,i,resultado);resultado=prueba(dato);printf("\n dato=%d, i=%d, resultado=%d",dato,i,resultado);

}

int prueba(int dato){

int i=1;dato++;printf("\n dato=%d, i=%d",dato,i);return(dato);

} La salida al programa es:

dato=5, i=6, resultado=0dato=6, i=1dato=5, i=6, resultado=6

¿Porqué?. En la primera línea no hay problema, son los valores iniciales de las variables declaradas en la función principal. En el segundo printf (el de la función), i es una variable local de la función prueba, que es distinta a el i de main. La variable dato de la

Page 80: DiagrFlujo-ProgramacionC

Programación de Computadores y el Lenguaje C (versión 2003)

Profesor: Roberto Uribe P. Pág. Nº 80

función prueba también es local (y por lo tanto distinta a dato de main), y en este caso no importa que tenga el mismo nombre del parámetro ingresado a la función, por lo tanto esta recibe el valor 5 y se incrementa en 1 (osea 6). Para el último printf las variables dato e i no se modifican, la variable resultado cambia su contenido ya que se le asigna el valor retornado de la función (6). Por último, dentro de la función prueba no se puede imprimir la variable resultado, ya que esta no esta declarada y tendríamos un error de compilación. Ejercicio Nº 4.1: Hacer una función que calcule el máximo entre 2 números enteros: La idea es que se le ingrese dos parámetros y la función retorne el mayor, por lo tanto no debemos ni leer de teclado ni imprimir en pantalla. Para saber cual de dos números es mayor, sólo debemos compararlos, entonces la parte principal del programa podría ser: declarar una variable que guarde el mayor

int num_max;

luego hacer la comparación entre los dos datos ingresados, supongamos dato1 y dato2, es decir,

if (dato1>dato2)num_max=dato1;

elsenum_max=dato2;

y listo!. Entonces, la función completa sería:

int maxnum(int dato1,int dato2){

int num_max;if (dato1>dato2)

num_max=dato1;else

num_max=dato2;return(num_max);

}

Para hacer más eficiente nuestra función, podemos ahorrarnos la variable num_max y haber hecho:

int maxnum(int dato1,int dato2){

if (dato1>dato2)return(dato1);

elsereturn(dato2);

}

Es importante indicar en este punto, que el objetivo de crear funciones es para que ellas realicen sólo lo necesario (ojalá una sola cosa), de esta manera se simplifican los problemas y se facilita la reutilización de dichas funciones.

Page 81: DiagrFlujo-ProgramacionC

Universidad de Magallanes - Departamento de Ingeniería en Computación

Profesor: Roberto Uribe P. Pág. Nº 81

Ejercicio Nº 4.2: Crear un programa que lea números enteros y que se obtenga el mayor de la lista ocupando la función maxnum del ejemplo anterior. El programa debe terminar cuando la suma de los valores absolutos de los datos leídos sea superior a 1000. Entonces, como ya es sabido debemos incluir la biblioteca de entrada y salida, declarar la función maxnum y comenzar con la función principal. Luego, debemos declarar una variable que mantenga el máximo, la suma, y una variable que contenga los datos que vamos a ir leyendo desde teclado. Como no sabemos la cantidad de datos a leer podemos utilizar un ciclo while e ir leyendo cada dato dentro de este. Para la suma, sabemos que debemos hacer suma igual a suma más el dato que leamos, pero si el dato es negativo, entonces, suma debe ser igual a suma menos el dato.

#include <stdio.h>int maxnum();main(){

int suma=0,num,max;printf("\nIngrese un dato: ");scanf("%d",&num);max=num;if (num<0) /*para el valor absoluto*/

suma=suma-num;else

suma=suma+num;while (suma<=1000){

scanf("%d",&num)max=maxnum(max,num);if (num<0)

suma=suma-num;else

suma=suma+num;}printf ("\nEl maximo numero leido fue: %d",max);

}

Tomar en cuenta que el primer número que leemos va a ser asignado al máximo, esto resulta ser lógico, ya que es el único leído. Posteriormente, a la función se le pasa como parámetros el dato que leemos y el anterior valor máximo y el mayor de ambos será asignado a max. Finalmente debemos incluir el código fuente de la función que calcula el máximo al final de programa. ¿Como podemos hacer mas eficiente el programa?, una forma sería que el calculo del valor absoluto de un número sea una función, esto sería: crear una función a la cual se le pase como parámetro un número y retorne el valor absoluto de este. Entonces nuestro programa completo quedaría:

#include <stdio.h>int maxnum(int abs);int abs(int dato);main(){

int suma=0,num,max;printf("\nIngrese un dato: ");scanf("%d",&num);max=num;

Page 82: DiagrFlujo-ProgramacionC

Programación de Computadores y el Lenguaje C (versión 2003)

Profesor: Roberto Uribe P. Pág. Nº 82

suma=suma+abs(num);while (suma<=1000){

scanf("%d",&num)max=maxnum(max,num);suma=suma+abs(num);

}printf ("\nEl maximo numero leido fue: %d",max);return 0;

}

int maxnum(int dato1,int dato2) /*funcion que calcula el mayor */{

if (dato1>dato2)return(dato1);

elsereturn(dato2);

}

int abs(int dato) /*funcion que calcula el valor absoluto */{

if (dato<0)return(-dato);

elsereturn(dato);

}

Por último, debemos saber que la notación usada hasta ahora para el paso de parámetros es conocida como Notación Moderna. En la Notación Clásica o Kernighan & Ritchie, los tipos de los parámetros se especifican fuera de los paréntesis de la cabecera de la función y antes del cuerpo de la función. Por ejemplo, la cabecera de las funciones anteriores serían:

int maxnum(dato1,dato2) /*funcion que calcula el mayor */int dato1,dato2;{

::

}

int abs(dato) /*funcion que calcula el valor absoluto */int dato;{

::

}

Esta notación es aún frecuente encontrarla, sobre todo en textos y código antiguo. En el estándar ANSI se mantiene únicamente por compatibilidad.

Page 83: DiagrFlujo-ProgramacionC

Universidad de Magallanes - Departamento de Ingeniería en Computación

Profesor: Roberto Uribe P. Pág. Nº 83

Problemas Resueltos y Propuestos Resueltos: 1.- Implemente la función potencia (xy). 2.- Escriba la función convcharaint() del capítulo anterior, recuerde que la cabecera de la función debe ser:

int convcharaint(char dato)

3.- Escriba un programa en el cual el usuario ingrese un número y este arroje una lista de todos los números primos menores o iguales a el número ingresado. Para lo anterior, haga una función que determine si el número es primo o no, el parámetro de entrada es el número y la función sólo debe retornar 0 si el número no es primo y distinto de 0 si lo es. Propuestos : 4.- Calcular la raíz cuadrada de un número utilizando la fórmula de Newton. Gn=(G0 + N/G0 )/2

N es el número para el cual se quiere obtener la raíz; G0 es la aproximación anterior (la cual se inicializa en N/2 ); Gn es la nueva aproximación.

El cálculo se realiza hasta que el valor absoluto de la diferencia entre la aproximación anterior y la nueva sea menor que cierto valor. Por ejemplo : |Gn-G0| < 10-6 (0.000001) Soluciones: 1.- El algoritmo para este problema esta resuelto, por lo tanto, al implementar la función debemos eliminar las salidas a pantalla, ya que lo único que deseamos es el resultado. float potencia(float x, int y){

float pot=1;int i=0;if (x==0)

return(0);for(i=1;i<=abs(y);i++)

pot=pot*x;if (y<0)

return(1/pot); /*exponente negativo*/else

return(pot); /*exponente positivo*/}

Page 84: DiagrFlujo-ProgramacionC

Programación de Computadores y el Lenguaje C (versión 2003)

Profesor: Roberto Uribe P. Pág. Nº 84

En la función, x puede ser cualquier número real, además, se implementó el caso de que el exponente fuese negativo, para ello en el for hay una llamada a la función abs, implementada anteriormente. 2.- En este caso se puede utilizar algunas de las características permisivas de C, como por ejemplo: hacer la resta entre caracteres. Suponiendo que la máquina donde se ejecuta el programa use la tabla de códigos ascii, lo que implica que los dígitos están en orden correlativo de '0' a '9', C nos permite hacer la operación '1' - '0' lo que nos entregaría como resultado 1 (int), ya que el caracter '1' se encuentra una posición mas allá del '0'. Por lo tanto, para obtener el valor numérico de una variable caracter que contenga un dígito sería: variable_char - '0'. Entonces: int convcharaint(char dato){

if (dato>='0' && dato<='9') /* es digito*/return(dato-'0');

elseswitch(dato){

case 'A' : return(10);case 'B' : return(11);case 'C' : return(12);case 'D' : return(13);case 'E' : return(14);case 'F' : return(15);default : return(-1); /*para indicar error*/

}}

Note que no se uso el break, esto ya que cualquier case que sea verdadero termina la función. Además, se retorna un -1 en caso de ingresar un caracter que no corresponde, suponiendo que el valor resultado va a ser mayor que 0. Una modificación interesante es, reemplazar el switch por : if (dato>=’A’ && dato<=’F’) /* es dígito */

return dato-‘A’+10;

3.- Para esta pregunta ya sabemos como determinar si un número es o no primo, lo que resta es ajustar el procedimiento para que haga lo que se nos pidió en el encabezado. #include <stdio.h>int esprimo(int dato);main(){

int num,i=2;printf("\nPrograma para primos.\n");printf("Ingrese un numero mayor que 0 : ");

Page 85: DiagrFlujo-ProgramacionC

Universidad de Magallanes - Departamento de Ingeniería en Computación

Profesor: Roberto Uribe P. Pág. Nº 85

scanf("%d",&num);if (num<=0)

printf("Error, el numero debe ser mayor que cero.\n");else{

printf("\nTodos los numeros primos hasta el %d son :\n",num);for (i=1;i<=num;i++)

if (esprimo(i))printf("%d\t",i);

}return 0;

}

int esprimo(int dato){

int cont=0;if (dato==1)

return 0; /*el 1 no es primo */if (dato==2)

return 1;else

for(i=2;i<dato;i++)if ((dato%i)==0)

cont++;if (cont>0)

return 0;else

return 1;}

La función cálcula si es primo de la misma manera que en el capítulo anterior, generalmente las funciones de este tipo no tienen salidas a pantalla y sólo hacen el proceso. Otra forma de construir la función es: int esprimo(int dato){

int i=0;if (dato==1)

return 0;if (dato==2)

return 1;else

for(i=2;i<dato;i++)if ((dato%i)==0)

return 0;return 1;

}

En este caso, la función termina a la primera ocurrencia de un módulo igual a 0, esto resulta lógico dado que no es necesario continuar con el for porque ya se sabe que el número no es primo. Esta modificación le otorga una mayor eficiencia y rapidez al algoritmo. Otra modificación que implicaría más eficiencia, sería que el for llegara sólo hasta la raíz cuadrada de dato. Un algoritmo más eficiente sería :

Page 86: DiagrFlujo-ProgramacionC

Programación de Computadores y el Lenguaje C (versión 2003)

Profesor: Roberto Uribe P. Pág. Nº 86

:else

for(i=sqrt(dato);i>=2;i--)if ((dato%i)==0)

return 0;return 1;:

Partir desde la raíz y decrementa el contador hasta 2, es muy diferente de partir de 2 y llegar hasta la raíz. Esto dado que en la primera alternativa la raíz se calcula una sola vez, en la inicialización. En el segundo caso, la raíz se evalúa en la condición, es decir, se calcula la raíz en cada ciclo, lo que involucra un costo adicional en tiempo. Note que sqrt(dato) es un valor double, sin embargo, se convierte en forma automática a int al asignárselo a y. Al usar la función raíz cuadrada debe agregar el archivo de cabecera math.h..

Page 87: DiagrFlujo-ProgramacionC

Universidad de Magallanes - Departamento de Ingeniería en Computación

Profesor: Roberto Uribe P. Pág. Nº 87

Capítulo Nº 5 : Arreglos o matrices Una matriz o arreglo es un conjunto de elementos (variables) todos del mismo tipo y que agrupados dan lugar a un área de memoria que puede ser tratada como una variable independiente y de una forma especial. Los elementos que componen el arreglo se encuentran ordenados por su posición relativa dentro de este. Las matrices pueden ser unidimensionales (vectores) o multidimensionales. Estos poseen un índice o subíndice asociado que va a indicar un componente específico, este índice es de tipo entero. La declaración de un arreglo unidimensional es:

tipo nombre_arreglo[tamaño];

donde tamaño es el largo del arreglo y tipo el tipo de dato del arreglo y por lo tanto de cada uno de sus componentes. En un arreglo sus componentes pueden ser de cualquier tipo, básico o creado por el usuario, pero todos del mismo tipo. Para identificar a un elemento se hace de la siguiente manera:

nombre_arreglo[posición]

donde posición es la ubicación del elemento dentro del arreglo. La posición va de 0 a tamaño-1. Ejemplo:

int numeros[10];

En términos reales, cuando se declara un arreglo se reservan espacios contiguos en memoria. En el ejemplo se declaró un arreglo de enteros de tamaño 10, es decir, los 10 elementos del arreglo son int. Entonces, gráficamente podríamos suponer que un arreglo es:

0 1 2 3 4 5 6 7 8 9

Si escribimos

numeros[6]

nos estamos refiriendo al séptimo elemento de la matriz, si hubiéramos elegido el sexto lo correcto sería: numeros[5]. Para asignar valores a un arreglo se debe hacer componente a componente:

numeros[6]=20;numeros[9]=10;

entonces, el arreglo quedaría:

Page 88: DiagrFlujo-ProgramacionC

Programación de Computadores y el Lenguaje C (versión 2003)

Profesor: Roberto Uribe P. Pág. Nº 88

0 1 2 3 4 5 6 7 8 9

20 10 Otra forma es inicializarlo en la declaración, de la siguiente manera:.

float numreal[4]={0.0,0.0,0.0,0.0};char frase[5]={'a','b','c','d','e'};

En este ejemplo declaramos un arreglo frase de tipo char, estos se conocen como cadenas de caracteres o string. La forma de imprimir una componente:

printf("\nLa componente 3 es %d",numeros[2]);

No se puede imprimir de una sola vez un arreglo de tipo distinto a char. Para el caso anterior no se puede hacer

printf("...%d %d %d %d...",numeros);

diez %d y luego el nombre del arreglo, si se puede diez %d y luego las diez componentes en la lista de argumentos. Para el caso de los string se puede utilizar el formato %s

printf("\n arreglo de caracteres es: %s",frase);/* imprime abcde */

Igualmente, para ingresar datos se debe hacer de a una componente a la vez:

scanf("%d",&numeros[0]); /*lee el dato de la primera componente*/

Para el caso de un arreglo de tipo caracter se puede hacer la lectura de todo el string, no debe llevar & porque el nombre del arreglo es un puntero al primer elemento de este.

scanf("%s",frase); /*no lleva & */

scanf("%c",&frase[0]); /*Lee solo una componente del string*/

Nos podemos dar cuenta lo engorroso que resulta leer componente a componente un arreglo (distinto de char), sobre todo si este es de tamaño grande (por ejemplo, 50). Sin embargo, para ello utilizamos el ciclo for. Por ejemplo, para crear un arreglo de tipo flotante de tamaño 50, ingresar los datos e imprimirlos en pantalla, el programa sería: #include <stdio.h>main(){

float reales[50];int i=0;printf("Ingrese 50 numeros reales\n");for(i=0;i<50;i++)

reales[i]=0.0; /*inicializacion del arreglo (no es necesario)*/for(i=0;i<50;i++)

scanf("%f",&reales[i]); /*Lee todas las componentes del arreglo.*/

Page 89: DiagrFlujo-ProgramacionC

Universidad de Magallanes - Departamento de Ingeniería en Computación

Profesor: Roberto Uribe P. Pág. Nº 89

printf("\El arreglo es\n");for(i=0;i<50;i++)

printf("%f\t",reales[i]); /*imprime las componentes del arreglo*/return 0;

}

Arreglos de n-dimensiones Para declarar arreglos de dos dimensiones se hace de la siguiente manera:

tipo nombre_arreglo[tamaño1][tamaño2];

Donde tamaño1 es la cantidad de filas y tamaño2 es la cantidad de columnas de la matriz. En general, para manipular vectores o matrices se hace de manera similar que en matemáticas, es decir, uno se refiere a una matriz a través de su nombre y a una componente específica por sus índices. Ejemplo:

int A[6][4];

Es decir, la matriz A es de 6x4, y para referirnos a A23, se hace A[1][2], tomando en cuenta que usualmente en matemáticas los subíndices van de 1 a n. La matriz anterior podríamos representarla como

0 1 2 3012345

Entonces, para inicializar una matriz de dos dimensiones con cero, la parte principal constaría de dos for (uno por cada dimensión) con dos índices. Para la matriz anterior

for (i=0;i<6;i++)for (j=0;j<4;j++)

A[i][j]=0; Ejercicio Nº 5.1 : Hacer un programa que calcule la matriz resultado de la multiplicación de una constante por una matriz de nxn. Solución: Como la matriz va a ser de n por n, se asume que el usuario ingresará el tamaño de la matriz, entonces, podemos definir una matriz de tamaño máximo, por ejemplo 100, esto se debe hacer ya que necesariamente debemos definir el tamaño. Sabemos que para multiplicar una constante por una matriz debemos multiplicar cada componente de la matriz por la constante. Por lo tanto, en nuestro programa debemos ingresar la cantidad de filas y

A[1][2]

A[3][1]

Page 90: DiagrFlujo-ProgramacionC

Programación de Computadores y el Lenguaje C (versión 2003)

Profesor: Roberto Uribe P. Pág. Nº 90

columnas de la matriz, los datos que esta contendrá y el valor de la constante. Una solución posible es: #include <stdio.h>main(){

int matriz[100][100],resul[100][100],const,i,j,fil,col;printf("Programa que calcula la multiplicacion de una matriz\n");printf("por una constante.\n");for (i=0;i<100;i++) /*se llena la matriz completa de ceros*/

for (j=0;j<100;j++)matriz[i][j]=0;

/*lectura de datos*/printf("Ingrese filas de la matriz");scanf("%d",&fil);printf("Ingrese Columnas de la matriz");scanf("%d",&col);printf("Ingrese datos de la Matriz\n");for (i=0;i<fil;i++) /* el rango ahora es fil x col*/

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

printf("(%d,%d)=",i,j); /*para que se vea una buena salida*/scanf("%d",&matriz[i][j]);

}printf("Ingrese Constante");scanf("%d",&const);

/* proceso de multiplicacion*/for (i=0;i<fil;i++) /* el rango ahora es fil x col*/

for (j=0;j<col;j++)result[i][j]=matriz[i][j]*const;

/* impresion de resultados*/printf("La matriz resultante es:");for (i=0;i<fil;i++){

for (j=0;j<col;j++)printf("%d",result[i][j]);

printf("\n"); /*para que cada fila salga en una linea*/}return 0;

}

Como se ve en este ejercicio, hay bastantes mensajes a pantalla, de esta manera se logra que el usuario final entienda de que se trata el programa, además de saber cuando ingresar los datos. Como antes se ha dicho, la inicialización no es necesaria, pero es una buena costumbre hacerlo. Los elementos de la matriz que están fuera del rango fil x col no se accesan, esto ya que los for no lo permiten. Constantes Simbólicas En nuestro ejemplo, si tuviéramos que modificar el tamaño máximo por cualquier motivo, tendríamos que rectificar algunas líneas de código, si el programa fuera mucho mas extenso, esto se complicaría. En C existe las llamadas constantes simbólicas, que son valores que se asignan a identificadores, los cuales no se pueden modificar. Esto se hace a través de la directiva #define. Por ejemplo: # define MAX 100

Page 91: DiagrFlujo-ProgramacionC

Universidad de Magallanes - Departamento de Ingeniería en Computación

Profesor: Roberto Uribe P. Pág. Nº 91

nos permitiría tener una constante llamada MAX con un valor de 100. La idea de esto es usar MAX en vez de 100, de esa manera, si se tiene que cambiar el valor 100 por otro, sólo se cambia MAX, de esa manera se modifica una sola línea del programa, esto nos simplifica el trabajo al momento de hacer mantenciones o depuraciones de nuestros programas. Los arreglos tienen múltiples aplicaciones, sobre todo en el arrea de las matemáticas, pero su espectro cubre mucho más que eso. Por ejemplo, un arreglo de dos dimensiones nos permitiría almacenar una lista con nombres, y si a esto agregáramos un arreglo de enteros, podríamos tener la lista de un curso y el promedio de cada alumno, o podría ser la lista de clientes y la deuda que poseen:

char NombresALum[20][30];int Notas[20];/* dos arreglos que nos permiten guardar 20 nombres y 20 notas*/

NombresAlum Notas0 1 2 3 .......... 29 0

0 01 12 23 34 :: ::::

19 19

Para leer el arreglo Notas se hace de la manera ya vista, para NombresAlum se puede hacer con dos ciclos for o bien con un ciclo for y utilizando el formato %s para string. Por ejemplo:

for (i=0,i<20; i++) /* filas */scanf("%s",NombresAlum[i]);

Esto leería las 20 filas, donde cada scanf estaría leyendo una cadena. Para que nuestro diseño funcione, se asume que cada alumno tiene su nota en la misma posición donde va su nombre, es decir, el alumno NombresAlum[11] tiene su nota en Notas[11].

Para el ejemplo anterior siempre es mejor usar estructuras (concepto nuevo), sin embargo, es una buena idea dado los conocimientos hasta ahora vistos.

B a r r i aC e c h iM a n d i o l aP e r e z:::::

...............

60514548:::::

Page 92: DiagrFlujo-ProgramacionC

Programación de Computadores y el Lenguaje C (versión 2003)

Profesor: Roberto Uribe P. Pág. Nº 92

Una de las precauciones que debemos tener con los arreglos es con sus rangos, ello porque C no comprueba los límites de los arreglos, por lo tanto queda en manos del programador comprobar que estos se respeten. Por ejemplo, en C lo siguiente es permitido:

int A[20],i;for (i=0;i<30;i++)

scanf("%d",&A[i]);

No hay error de compilación, sin embargo, esto podría provocar problemas, ya que alguna variable declarada con anterioridad puede ocupar los espacios de memoria posteriores al arreglo y por lo tanto ser sobrescrita. En general, para declarar arreglos de n-dimensiones, el formato es el siguiente:

tipo nombre_arreglo[tamaño1][tamaño2]....[tamañoN];

Ejercicio Nº 5.2: Una de las aplicaciones usuales de arreglos en programación es el ordenamiento de datos. Entonces, crear un programa al cual se le ingresen 5 números enteros y los imprima en orden ascendente. Solución: Una forma de resolver este problema es usando un arreglo que contenga los datos, luego ordenarlos dentro del arreglo e imprimirlos. La parte fundamental del problema es ordenar dichos datos. Hay variadas formas de hacerlo, una es ir comparando cada componente con la siguiente de tal manera de ir moviendo el menor hacia el extremo izquierdo del arreglo. Por ejemplo, supongamos los siguientes datos, 100,20,50,8,0. Entonces, tomamos la componente 0 (100) y lo comparamos con la componente 1 (20), luego si es mayor cambiamos el contenido, luego volvemos a comparar la componente 0 (que ahora sería 20) con la componente 2, y si es mayor lo intercambiamos, etc. El proceso sería: Ingresar los datos al arreglo, y luego

100 20 50 8 0

Como 100 es mayor que 20, se cambian

20 100 50 8 0

Como 20 no es mayor que 50, no se cambian

20 100 50 8 0

20 es mayor que 8, se cambian

8 100 50 20 0

0 100 50 20 8 El arreglo finalmente quedaría

8 es mayor que 0, se cambian

Page 93: DiagrFlujo-ProgramacionC

Universidad de Magallanes - Departamento de Ingeniería en Computación

Profesor: Roberto Uribe P. Pág. Nº 93

Hay que reconocer que en realidad lo que se compara son los contenidos de las componentes, ya que estas según los resultados de las comparaciones van a ir variando. Ahora, una vez que se recorrió el arreglo para la primera componente, se vuelve a hacer el mismo proceso para la segunda componente y con las restantes. Es suficiente llegar hasta la penúltima componente, ya que esta se compara con la siguiente. ¿Cual sería el algoritmo para esto?. Como es un arreglo, inmediatamente pensamos en un ciclo for para recorrer todo el vector. Ahora, el arreglo no se recorre una vez, sino, una por cada componente, es decir, se toma el componente 0 y se va comparando con las componentes 0+1 hasta n. Entonces una aproximación sería:

for(i=0;i<.....for(j=i+1;j<n;j++)

Como el for interno (j) recorre hasta el final y comienza en 1 mas que el externo, entonces i tiene que llegar hasta n-1. Osea:

for(i=0;i<(n-1);i++)for(j=i+1;j<n;j++)

Otra cosa importante que debemos saber, es como hacer el intercambio de datos entre dos variables. Para ello se utiliza una variable auxiliar, ejemplo:

aux=a;a=b;b=aux;

de esa manera no se pierde el contenido de la variable a. Entonces nuestro programa quedaría: #include <stdio.h>#define largo 5main(){

int datos[largo],i=0,j=0,aux;printf("Este programa ordenara los numeros que Ud. ingrese\n\n");printf("Ingrese %d numeros enteros\n",largo);for(i=0;i<largo;i++)

scanf("%d",&datos[i]);printf("\El arreglo sin ordenar es:\n");for(i=0;i<largo;i++)

printf("%d\t",datos[i]);for(i=0;i<(largo-1);i++) /*parte principal*/

for(j=i+1;j<largo;j++)if (datos[i]>datos[j]) /*para el intercambio de los contenidos*/{

aux=datos[i];datos[i]=datos[j];datos[j]=aux;

}printf("\El arreglo ordenado es:\n");for(i=0;i<largo;i++)

printf("%d\t",datos[i]);return 0;

}

Page 94: DiagrFlujo-ProgramacionC

Programación de Computadores y el Lenguaje C (versión 2003)

Profesor: Roberto Uribe P. Pág. Nº 94

La ejecución de la parte principal de este ejercicio incluyendo índices y los datos del arreglo es: i j datos[0] datos[1] datos[2] datos[3] datos[4] aux

100 20 50 8 00 1 20 100 50 8 0 1000 2 20 100 50 8 0 1000 3 8 100 50 20 0 200 4 0 100 50 20 8 81 2 0 50 100 20 8 1001 3 0 20 100 50 8 501 4 0 8 100 50 20 202 3 0 8 50 100 20 1002 4 0 8 20 100 50 503 4 0 8 20 50 100 100

Este es uno de los métodos de ordenamiento más conocidos y es denominado como Método de la Burbuja, existen otros métodos como el de Selección y el de Mezcla. Cadenas de Caracteres Los string o cadenas de caracteres se manipulan como un arreglo común, sin embargo, se pueden manejar de forma especial, como se pudo ver en los ejemplos anteriores de ingreso y salida a pantalla. Para trabajar con cadenas debemos primero tener claro ciertos conceptos respecto de ellas. - Un string es un arreglo de caracteres que termina con el caracter nulo ‘\0’. Es por ello que usualmente al declarar un string se le agrega un elemento más para que contenga dicho caracter. Sin embargo, C permite trabajar con constantes de caracteres, estas están encerradas entre comillas dobles, en este caso, si no se le incluye el caracter nulo al final, C lo hace de manera automática. Un ejemplo es:

"Aqui estamos otra vez" que sería lo mismo que

"Aqui estamos otra vez\0"

Por ejemplo, si se desea copiar un string en otro sin saber el largo del primero, podemos hacer:

for(i=0;str1[i]!=‘\0’;i++)str2[i]=str1[i];

str2[i]=‘\0’;

Con esto se copió str1 en str2, como dentro del for no se copio el caracter nulo, se hizo al final. Para manipular cadenas existe una biblioteca llamada string.h, en la cual se encuentran una serie de funciones que permiten entre otras cosas: comparar dos string, copiar

Guardó el valor anterior.

Datos ingresados

Page 95: DiagrFlujo-ProgramacionC

Universidad de Magallanes - Departamento de Ingeniería en Computación

Profesor: Roberto Uribe P. Pág. Nº 95

un string en otro, saber el tamaño de una cadena, ver si un caracter esta dentro del string, etc. Algunas de ellas son:

Nombre Función: Descripción strcpy(str2,str1); Copia str1 en str2. strcmp(str1,str2); Compara los dos string str1 y str2, devuelve

un valor entero que es : menor, mayor o igual a cero, dependiendo si str1 es lexicográficamente menor, mayor o igual a str2.

strlen(str1); Devuelve un entero que va a ser el largo de str1.

strcat(str1,str2); Concatena el string str2 al final de str1. strchr(str1,car1); Retorna un puntero a la primera aparición del

caracter car1 en str1. Ejemplos:

if (strcmp(nombre,string1)>0)printf("El string %s es mayor que %s",nombre,string1);

::strcat(nombre,string1);printf("Ahora %s tiene largo %d",nombre,strlen(nombre));:

Cadenas sin tamaño definido En C se puede hacer la inicialización de cadenas no delimitadas, para ello se declara el arreglo y se le asigna una constante string:

char normal[50]; /*declaracion tradicional*/char s1[]="Mensaje de Error";char s2[]="Ingrese de nuevo el dato";

Con esto nos evitamos saber a priori cuanto espacio debe tener el arreglo para contener todos los caracteres. C automáticamente crea un arreglo con el tamaño suficiente para contener el string mas el caracter nulo. Existe otra forma de declarar un string sin un tamaño específico, por ejemplo:

#include <stdio.h>#include <string.h>main(){

char normal[50]; /*declaracion tradicional*/char *s1;char *s2="Esto no tiene largo\0";char *s3="\0"; /*inicializada en nulo*/printf("El string %s tiene largo %d",s2,strlen(s2));strcpy(s3,s2);

Page 96: DiagrFlujo-ProgramacionC

Programación de Computadores y el Lenguaje C (versión 2003)

Profesor: Roberto Uribe P. Pág. Nº 96

::

En términos formales, en el ejemplo se declararon punteros a char, para ello se utilizó el operador *. Para el caso de s2 y s3 es idéntico al ejemplo anterior. Sin embargo, es ilegal hacer strcpy(s3,s2) y habría un error en tiempo de ejecución, de no ser así (depende del sistema operativo), puede ocurrir cualquier cosa. A veces se utilizan los punteros a char como arreglos y se realizan operaciones sobre ellos, como copiar, concatenar, etc. Cuando los programas son cortos no se provoca ningún error, sin embargo, cuando son más extensos, lo que implica mayor uso de memoria, usualmente ocurren errores de asignación de esta, esto provocado porque el arreglo no tiene la capacidad de almacenar todos los caracteres. Estos errores provocan la caída del programa o la mala ejecución de este. Para solucionar este problema, se hace una asignación dinámica de memoria al arreglo (dado que es un puntero a char). Esto en términos generales es asignar memoria a una variable en tiempo de ejecución, para ello se utiliza la función malloc(), esta se verá más adelante. Recuerde: Las variables globales obtienen memoria en tiempo de compilación. Las variables locales utilizan la pila de memoria. Como retornar un string En mucha ocasiones es necesario que una función retorne un arreglo, en este capítulo veremos el caso de retornar una cadena de caracteres. Para ello, hay que declarar la función como char y además agregar el caracter asterisco antes del nombre de la función. Para ilustrar lo anterior veamos el siguiente ejemplo:

#include <stdio.h>#include <string.h>char *agregahola(char *nombre);main(){

char nombre[50];printf("Ingresa un nombre :");scanf("%s",nombre);printf("%s",agregahola(nombre));

}

char *agregahola(char *nombre){

char s[50]strcpy(s,"Hola ");strcat(s,nombre);return s;

}

Page 97: DiagrFlujo-ProgramacionC

Universidad de Magallanes - Departamento de Ingeniería en Computación

Profesor: Roberto Uribe P. Pág. Nº 97

Recuerde que strcat concatena en este caso, el contenido de la variable nombre al final de s. Como lo que se retorna es un string, entonces, el segundo printf de la función principal es correcto. Este programa funciona sin problemas, pero lo ideal es declarar s con un tamaño capaz de contener el string que se le concatena. Además, es posible que al compilar aparezca una advertencia respecto de retornar una dirección de una variable local. Esto último se verá en el capitulo de punteros. Por último, el parámetro pasado a la función, fue declarado como: char *nombre lo que indica que lo que se pasa es un puntero a char. En este caso no hay error, ya que el string como tal es un puntero, y además, el tamaño de este parámetro es el tamaño de la variable que es pasada a la función.

Arreglos como parámetros Los arreglos como cualquier otra variable se pueden pasar como parámetros a las funciones y se hace de la misma manera. Supongamos:

#include <stdio.h>#define MAX 50void muestra(int arr[MAX]);main(){

int vector[MAX];:muestra(vector);::

}

void muestra(int arr[MAX]){

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

printf("%d\t",arr[i]);return;

}

En si el nombre de un arreglo es un puntero al primer elemento de este, entonces al pasarlo como parámetro a una función los valores de sus componentes pueden ser modificados. Por ejemplo, la siguiente función logra modificar el arreglo vector:

void modifica(int arr[MAX]){

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

arr[i]=arr[i]*10;return;

}

La llamada desde el main debería ser:

modifica(vector);

Page 98: DiagrFlujo-ProgramacionC

Programación de Computadores y el Lenguaje C (versión 2003)

Profesor: Roberto Uribe P. Pág. Nº 98

Ejercicio Nº 5.3 : Una de las aplicaciones frecuentes con arreglos es la búsqueda de datos en estos. Supongamos entonces que, se tiene un arreglo con N datos enteros. Cree una función que permita la búsqueda de un dato específico dentro del arreglo. Los datos no están ordenados dentro del arreglo. Solución : La primera forma de resolverlo es ir comparando uno a uno los datos del arreglo con el dato ingresado (dato que se busca).Entonces, debemos usar un ciclo que nos permita recorrer el arreglo, y dentro de este hacer la comparación : for (i=0;i<N;i++)

if(a[i]==dato)printf(“dato encontrado”);

elseprintf(“dato no encontrado”);

Este método se conoce jocosamente como el método carretero, ya que es el que podría demorarse más en buscar. Un ejemplo de programa completo sería : #include <stdio.h>#define N 15 /* N va a ser el máximo de datos */int busca(int a[N], int dato);main(){

int arr[N], dato, i=0;printf(“Ingrese datos para llenar el arreglo (%d)”,N);for(i=0;i<N;i++){

printf(“dato %d: ”,i);scanf(“%d”,&arr[i]);

}printf(“Ingrese un dato a buscar”);scanf(“%d”,&dato);if (busca(arr,dato))

printf(“Dato Encontrado”);else

printf(“Dato no existe en el arreglo”);return 0;

}

int busca (int a[N], int dato){

int i=0;for(i=0;i<N;i++)

if (a[i]==dato)return 1; /* encontrado */

return 0; /* no encontrado */}

Observe que se pasaron como parámetros tanto el arreglo como el dato que se busca. Esta función retorna 1 si el dato existe y después de recorrer todo el arreglo y comprobar que el dato no se encuentra retorna 0. Por último, si los datos dentro del arreglo se encuentran ordenados, se podría utilizar algún otro método más eficiente para la búsqueda, un método que se demorara menos.

Page 99: DiagrFlujo-ProgramacionC

Universidad de Magallanes - Departamento de Ingeniería en Computación

Profesor: Roberto Uribe P. Pág. Nº 99

Ejercicio Nº 5.4: Suponga que se tienen los dos arreglos mencionados anteriormente, NombresAlum y Notas, los que almacenan los nombres (y apellidos) de un curso y el promedio de cada alumno. En el problema planteado se asume que las notas están en las mismas posiciones de los nombres (pero en distintos arreglos). Entonces, se debe crear una función que permita ingresar un nombre y arroje el promedio de la persona.

Solución: El proceso sería, tomar el nombre ingresado por teclado y compararlo con toda la lista de nombres del arreglo NombresAlum, con esto obtenemos la posición en que se encuentra dicho nombre. Seguido, tomamos la posición obtenida y buscamos el promedio ubicado en esa posición en el arreglo Notas. Entre las cosas a considerar están: para comparar los nombres de la lista con el ingresado se debe usar la función strcmp() para comparar string, por lo tanto hay que incluir en el programa al archivo de cabecera string.h. Los arreglos están declarados como globales, de esa manera los podemos ocupar en cualquier función que tenga el programa. La primera parte del programa es: # include<stdio.h># include<string.h>char NombresALum[20][30];int Notas[20];/* dos arreglos que nos permiten guardar 20 nombres y 20 notas*/

La función sería: void consulta(){

char nombre[30]="\0";int posicion=-1, promedio=0;printf("Ingrese un nombre :");scanf("%s",nombre);posicion=buscanombre(nombre);if (posicion==-1){

printf("Error, Nombre no encontrado\n");return;

}else{

promedio=buscapromedio(posicion);printf("El promedio de %s es : %d\n",nombre,promedio);

}return;

} Entonces, tenemos la función que ingresa el nombre, para hacer más modular el programa creamos dos funciones más, una que busca el nombre y otra que busca el promedio. La primera función pasa como parámetro el nombre, si no es encontrado retorna -1, de esa manera evitamos errores. Una vez encontrada la posición, pasamos como parámetro a la segunda función este dato. Las funciones serían de la siguiente forma:

Page 100: DiagrFlujo-ProgramacionC

Programación de Computadores y el Lenguaje C (versión 2003)

Profesor: Roberto Uribe P. Pág. Nº 100

int buscanombre(char *s){

int i=0;for(i=0;i<20;i++)

if (strcmp(s,NombresAlum[i])==0) /*Si los nombres son iguales*/return(i); /*retorna la posicion*/

return(-1);} Con esto logramos ubicar la posición, si no la encuentra el for recorre todo el arreglo y por lo tanto retorna un -1. La función que busca el promedio sería: int buscapromedio(int pos){

return(Notas[pos])}/*No es necesario recorrer el arreglo ya que tenemos la posición. */

Page 101: DiagrFlujo-ProgramacionC

Universidad de Magallanes - Departamento de Ingeniería en Computación

Profesor: Roberto Uribe P. Pág. Nº 101

Problemas Resueltos y Propuestos Resueltos: 1.- Escribauna función a la que se ingresa una cadena de caracteres de largo indefinido y arroje como resultado la misma cadena, pero sin los caracteres espacio en blanco (' ') y tabulador ('\t'). Escriba el programa completo. Restricción: el programa debe hacerse utilizando un solo arreglo de caracteres. Ejemplo:

Ingrese tira de caracteres:

Espero que todos terminen a la hora.

La misma cadena es:

Esperoquetodosterminenalahora.

2.- Escriba un programa que lea una matriz de NxM, donde N y M son valores conocidos menores que 40. El programa debe convertir esta matriz en su traspuesta. Se entiende por matriz Traspuesta aquella que sus componentes j,i son iguales a las componentes i,j de la matriz original, es decir: a11 a12 a13 .... a1m a11 a21 a31 .... an1 A = a21 a22 a23 .... a2m AT= a12 a22 a32 .... an2 .................. .................... an1 an2 an3 .... anm a1m a2m a3m .... anm Condición: utilice el mismo arreglo para hacer el ejercicio. 3.- Escriba una función que realice una búsqueda en texto, para ello a la función debe ingresar dos string de tamaño indefinido, el primero es el texto en el que se va a buscar una palabra (segundo string). La función debe retornar 1 si es encontrada la palabra y 0 de lo contrario. 4.- Un grupo de expertos del área informática contratado específicamente para resolver los mensajes en clave tiene una nueva tarea. Cada vez que la otra parte en conflicto se da cuenta que sus mensajes son decodificados, esta inventa una nueva forma de codificarlos. Para el siguiente mensaje captado por el grupo de ingenieros:

Ehp o uoysare setnonru eeadbtapcssmoarmuofto reoenuusp t,s edoee tmrilrsnpouzo

Page 102: DiagrFlujo-ProgramacionC

Programación de Computadores y el Lenguaje C (versión 2003)

Profesor: Roberto Uribe P. Pág. Nº 102

u tio co.caedrqh

Finalmente se determinó que este texto correspondía a una matriz de caracteres, es decir, para el texto anterior la matriz es:

E h p o u o ys a r e s et n o n r u e ea d b t a p c s sm o a r m u o f to r e o e n u us p t , s e d

o e e t m r il r s n p o u z ou t i o c o .c a e d r q h

Para obtener el mensaje final, se tienen que leer la matriz columna a columna, es decir, es mensaje decodificado sería: Estamos luchando por aprobar este entretenido ramo, por supuesto que con mucho esfuerzo y estudio. Cree una función a la cual se le pasa como parámetro esta matriz (de 11x9) y que retorne un string con el texto decodificado. 5.- Diseñe un programa que simule una Base de Datos, para ello utilice sólo arreglos. Imagine que tiene una empresa con 20 trabajadores, a cada uno se le paga en forma diferente la hora de trabajo extra, además todos realizan horas extras de 0 a N siendo estos valores enteros positivos. El programa debe hacer lo siguiente: - Leer el nombre de cada trabajador. - Leer la cantidad de horas extras realizadas en el mes. - Leer el valor de la hora extra asignado para cada empleado. - Finalmente, debe entregar un resumen con todos los datos del empleado, además del dinero que recibirá de acuerdo a las horas trabajadas y al valor por hora, esto debe ser al finalizar el programa. Ejemplo:

Ingreso de Datos:

Nombre : Perico Perez PalotesHoras extra : 43Valor Hora : 1500

Nombre : Leonardo Da VinceHoras extra : 58Valor Hora : 2100Nombre : Cleopatra Contreras C.Horas extra : 0

Page 103: DiagrFlujo-ProgramacionC

Universidad de Magallanes - Departamento de Ingeniería en Computación

Profesor: Roberto Uribe P. Pág. Nº 103

Valor Hora : 1980. . . . . . . . . . . . .

Resumen:

Nombre Horas Valor TotalPerico Perez Palotes 43 1500 64500Leonardo Da Vince 58 2100 121800Cleopatra Contreras C. 0 1980 0. . . . . . . . . . . . . . . .

Propuestos: 6.- Escriba una función a la que se le ingrese como parámetro un string , dicha función debe retornar la cantidad de espacios en blanco, tabuladores, puntos y comas que tiene el arreglo. Condición: no se conoce el tamaño del string ingresado a la función. 7.- Haga un programa que determine si un palabra o frase ingresada vía teclado es o no palíndrome. Una palabra palíndrome es aquella se lee de la misma manera al derecho y al revés, ejemplo:

ingrese frase: alla

la frase es palíndrome.

8.- Escriba un programa que lea una matriz cuadrada de NxN donde N es valor conocido y menor que 40. El programa debe verificar que la matriz ingresada es simétrica o no. Se entiende por matriz simétrica aquella que el valor de sus componentes i,j es igual a j,i, es decir: a11 a12 a13 ... a1n a21 a22 a23 ... a2n ................ . an1 an2 an3 ... ann Donde: ai,j = aj,i 9.- Ud. pertenece a un equipo de expertos en inteligencia y se le ha encomendado crear un método para codificar mensajes. Después de pensar un poco en la posible fórmula, llego a la genial idea de utilizar el código ascii para hacer la codificación del mensaje. El método consiste en que a cada letra del mensaje original se le coloca en el mensaje que se enviará la letra siguiente, es decir, a la letra se le suma 1 lo que implicaría colocar la siguiente letra de la tabla ascii.

Page 104: DiagrFlujo-ProgramacionC

Programación de Computadores y el Lenguaje C (versión 2003)

Profesor: Roberto Uribe P. Pág. Nº 104

Por ejemplo, para el siguiente mensaje:

Oh! maestro, los que van a morir te saludan!!

El mensaje codificado sería:

Pi! nbftusp, mpt rvf wbo b npsjs uf tbmvebo!!

Cree un programa que lea el mensaje a codificar (mensaje real), luego codifíquelo utilizando un arreglo de tamaño no definido, y finalmente imprima este mensaje en pantalla. Para ello utilice una función a la cual se le pasa como parámetro el mensaje original y retorne un string con el mensaje codificado. Nota: si la letra es z, entonces se debe colocar la a en el mensaje codificado. 10.- Para el siguiente programa:

# include <stdio.h>main(){

int a[20],aux,n=0,y,j;printf("Ingrese el número de datos a leer");scanf("%d",&n);for(i=0;i<n;i++){

printf("\n Ingrese dato a[%d]:",i);scanf("%d",&a[i]);

}for(i=0;i<(n-1);i++)

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

if(a[i]>a[j]){

aux=a[i];a[i]=a[j];a[j]=aux;

}}

}

Suponiendo que los datos ingresados fueron 5 (10,-5,15,5,-20), muestre la ejecución del ciclo for principal, indicando el comportamiento de las variables y del arreglo. 11.- Implemente la función strcat, esta función concatena dos string. A dicha función se le ingresa como parámetros 2 cadenas de caracteres de tamaño no conocido. 12.- Implemente un programa que cuente las palabras que contiene un string: Utilice la función gets() para leer el string (esta función permite leer un string incluido los espacios en blanco y tabuladores). Las palabras pueden estar separadas por espacios, tabuladores, puntos, comas, punto y comas. Escriba una función que tenga como parámetro el string y retorne el número de palabras.

Page 105: DiagrFlujo-ProgramacionC

Universidad de Magallanes - Departamento de Ingeniería en Computación

Profesor: Roberto Uribe P. Pág. Nº 105

13.- Diseñe un programa que simule una Base de Datos, para ello utilice sólo arreglos. Suponga que se tiene un curso de 40 alumnos y que cada alumno rindió 3 pruebas en el semestre. El programa debe hacer lo siguiente (cada uno de los procesos debe ser una función): - Ingresar el Apellido y Nombre de todos los alumnos. - Leer las notas de cada alumno. - Calcular el promedio de cada alumno y dejarlo en un nuevo arreglo. - Finalmente debe entregar un listado con el apellido, nombre, notas y promedio. Al final de la lista debe entregar el promedio general del curso. Restricciones: Todas las operaciones deben ser hechas en funciones. Además, declare los arreglos en forma local, dentro del main. El programa debe funcionar de la siguiente manera:

Ingrese datos de los alumnos:

Nombre: Miguel AngelApellido: Garay

Nombre: AlejandraApellido: Cardenas

Nombre: PamelaApellido: Zuniga...

Ingrese Notas:

Miguel Angel Garay:Nota 1: 65Nota 2: 88Nota 3: 44Alejandra Cardenas:Nota 1: 50Nota 2: 29Nota 3: 60Pamela Zuniga:Nota 1: 58Nota 2: 100Nota 3: 15...Resumen:

Nombre 1 2 3 PromedioMiguel Angel Garay 65 88 44 66Alejandra Cardenas 50 29 60 46Pamela Zuniga 58 100 15 58.........

Promedio General: XX

Nota: Los nombre fueron cambiados para proteger a los inocentes ;-).

Page 106: DiagrFlujo-ProgramacionC

Programación de Computadores y el Lenguaje C (versión 2003)

Profesor: Roberto Uribe P. Pág. Nº 106

Soluciones: 1.- El objetivo de trabajar con la misma cadena es para aumentar la complejidad del problema, sin embargo, una solución más sencilla sería: ir copiando caracter a caracter de un arreglo a otro, salvo los espacios y tabuladores. La solución para el problema original es la siguiente:

# include <stdio.h>char *elim(char *st);main(){

char s[40]="\0";printf("Ahora :\n");gets(s);printf("%s",elim(s));return 0;

}

char *elim(char *st){

int i=0,j=0;for(i=0;st[i]!='\0';){

putchar(st[i]); /*para ver el proceso en pantalla*/if (st[i]==' ' || st[i]=='\t') /*encuentra espacio o tabulador*/{

putchar('<'); /*marca el inicio de lo que se mueve a la izquierda*/for(j=i+1;st[j]!='\0';j++){

st[j-1]=st[j]; /*copia caracter a caracter a la izq.*/putchar(st[j]); /*lo muestra en pantalla*/

}putchar('>'); /*fin de lo que se movio a la izq.*/st[j-1]='\0';

}else

i++;}printf("\n\n");return(st);

}

La idea de este programa es: al encontrar un espacio o tabulador, copia todo el arreglo una posición a la izquierda, luego, revisa nuevamente desde esa posición y repite el proceso al encontrar otro espacio o tabulador. Importante es notar que si se cumple la condición y una vez que se movió el resto del arreglo, se inserta un caracter '\0' al final menos 1, esto para que no quede basura al final del string. Las salidas a pantalla en la función es para ver como se va realizando el proceso, es decir, va mostrando lo que se esta moviendo a la izquierda. La función anterior tiene dos for, es decir, hace un recorrido al arreglo por cada espacio o tabulador. Analice el siguiente algoritmo : char *elim(char *st){

int i,dist=0;for (i=0;st[i]!=’\0’;i++)

if (st[i]==’ ’ || st[i]==’\t’) /* encuentra espacio o tabulador */dist++;

else

Page 107: DiagrFlujo-ProgramacionC

Universidad de Magallanes - Departamento de Ingeniería en Computación

Profesor: Roberto Uribe P. Pág. Nº 107

st[i-dist]=st[i];st[i-dist]=’\0’;return (st);

}

La nueva versión es mucho más eficiente, recorre sólo una vez el arreglo. 2.- Para resolver este ejercicio, lo que se hace es usar una variable auxiliar llamada aux para hacer el intercambio desde el elemento (i,j) al elemento (j,i) de la matriz en cuestión. Si se desea trabajar con otra matriz de diferente tamaño, basta con cambiar el valor de N y M en la cabecera del programa. #include <stdio.h>#define MAX 50#define N 3#define M 4

main(){int i=0,j=0,arr[MAX][MAX],aux,mayor;if (N>=M)mayor=N;

elsemayor=M; /* es necesario para poder decir cuantas veces se hara un

intercambio del elemento (i,j) al elemento (j,i) */for (i=0;i<N;i++)for(j=0;j<M;j++){printf("\nIngrese el elemento [%d],[%d] : ",i,j);scanf("%d",&arr[i][j]);

} /* Aquí se leyeron todos los elementos de la matriz */printf("\n\nSu Matriz original es : ");for (i=0;i<N;i++){printf("\n");for(j=0;j<M;j++)printf("%d\t",arr[i][j]);

} /* Aquí simplemente se imprimieron los elementos de la matriz */

for(i=0;i<mayor;i++)for(j=i+1;j<mayor;j++){aux=arr[j][i];arr[j][i]=arr[i][j];arr[i][j]=aux;

} /* Aquí la matriz se convirtio en su traspuesta */printf("\n\nSu Matriz Traspuesta es : ");for (i=0;i<M;i++){printf("\n");for(j=0;j<N;j++)printf("%d\t",arr[i][j]);

}return 0;

}

Page 108: DiagrFlujo-ProgramacionC

Programación de Computadores y el Lenguaje C (versión 2003)

Profesor: Roberto Uribe P. Pág. Nº 108

3.- En este ejercicio se usó una función encuentra que lo primero que hace es buscar el primer caracter del substring sub_str en el primer texto ingresado str y después que lo haya encontrado se comienza a comparar el resto del substring, contando el número de coincidencias (mediante un contador llamado flag)y si el número de coincidencias es igual al tamaño del substring entonces se retorna 1, y si no se encontró se retorna 0. #include <stdio.h>#include <string.h>#define MAX 100int encuentra(char primero[MAX],char segundo[MAX]);main(){int i;char arr1[MAX],arr2[MAX];printf("\nIngrese el primer texto : ");gets(arr1); /* La funcion gets() permite leer espacios y elimina el salto

de línea almacenado en el buffer del teclado al presionarla tecla enter para introducir el string arr1. Adiferencia de la función scanf() */

printf("\n\nIngrese el segundo texto : ");gets(arr2);printf("\n\n%d",encuentra(arr1,arr2));return 0;

}

int encuentra(char str[MAX],char sub_str[MAX]){int i=0,j=0,k=0,flag;for(i=0;str[i]!='\0';i++)if (str[i]==sub_str[0]) /* No se mete dentro del if hasta que */{ /* encuentre el primer elemento de sub_str */flag=1; /* en str */k=i;for(j=1;j<strlen(sub_str);j++){

k=k+1;if (str[k]!=sub_str[j])break;

flag++;}if (flag==strlen(sub_str))

return 1;}

return 0; }

4.- Aquí se recibe como parámetro un arreglo de dos dimensiones, y a cada elemento del string st se le asigna el elemento (j,i) de la matriz recibida. char *funcion_dec(char arr[11][9]){

int i=0,j=0,k=0;char st[100];for(i=0;i<11;i++)

for(j=0;j<9;j++){st[k]=arr[j][i];k++;

}st[k]='\0';return st;

}

Page 109: DiagrFlujo-ProgramacionC

Universidad de Magallanes - Departamento de Ingeniería en Computación

Profesor: Roberto Uribe P. Pág. Nº 109

Page 110: DiagrFlujo-ProgramacionC

Programación de Computadores y el Lenguaje C (versión 2003)

Profesor: Roberto Uribe P. Pág. Nº 110

Capítulo Nº 6 : Modos de Almacenamiento Existen cuatro formas de almacenamiento, estas determinan el alcance (Scope) y el tiempo que permanece la variable en memoria.

Variables Automáticas (auto) Todas las variables declaradas en una función son por defecto automáticas, es decir su alcance es local (a la función). Ej.: main()

{auto int suma=0;

}

En el ejemplo se declaró la variable suma explícitamente como automática. No es necesario poner la palabra reservada auto. Lo anterior es equivalente a:

main(){

int suma=0;}

La variable está activa en el par de llaves en la cual es declarada.

Variables Externas (extern) Si una variable se declara fuera de una función se dice que es externa (lo que conocemos como variable global). Su alcance va ha ser todo archivo o los archivos fuentes. Ej.: int parcial=1: /* variable definida externamente */

main(){

extern parcial; /* no es necesario */. /* se declara la variable definida */

} /* anteriormente */

Sumatoria(){

extern int parcial; /* tampoco es necesario */}

Para usar la variable externa no es necesario declararla como extern dentro de las funciones del mismo archivo, ya que si se omite la declaración se asume por defecto que es externa (o global). Por ejemplo, si se tiene un segundo archivo fuente se puede declarar en las primeras líneas de este.

Page 111: DiagrFlujo-ProgramacionC

Universidad de Magallanes - Departamento de Ingeniería en Computación

Profesor: Roberto Uribe P. Pág. Nº 111

Ej.: extern int parcial;void estructuras();{

::

}

Se asume que la variable esta definida en otro archivo. Por ejemplo, supongamos que tenemos dos archivos, princip.c y secundar.c: Entonces, la variable result utilizada en la función pot del archivo secundar.c es la declarada en princip.c. Si el programa hubiese sido hecho en Unix, habría que generar los códigos objetos de ambos archivos y luego el ejecutable. Para hacer esto se utiliza la opción -c al momento de compilar.

cc -c princip.c genera princip.o

cc -c secundar.c genera secundar.o

cc princip.o secundar.o genera a.out

Variables Registro (register) Las variables de tipo register nos permiten almacenar una variable en algún registro de la CPU en vez de la memoria RAM, esto con la finalidad de que dicha variable sea manipulada mucho más rápida que en memoria. Esto siempre y cuando sea posible ocupar algunos de los registros, de no ser así, la variable queda declarada como automática. El uso más común para este tipo de variable es en los contadores, ejemplo: Ej.: main()

{register int conta;::

}

# include<stdio.h>int fact();int result=0;main(){

::

}int fact(){

::

}

float pot(int x,int y){

int pot=1extern int result;:::result=pot;

}

princip.c secundar.c

Page 112: DiagrFlujo-ProgramacionC

Programación de Computadores y el Lenguaje C (versión 2003)

Profesor: Roberto Uribe P. Pág. Nº 112

Variables Estáticas (static)

Tienen el mismo alcance que las variables automáticas, pero no desaparecen cuando se termina la función, es decir, conservan su valor. El siguiente ejemplo demuestra lo anterior: Ej.: #include <stdio.h>

imprime()main(){ int i=0;

for (i=1; i<4; i++)imprime();

}

imprime(){

int local=1;static int varstatic=1;printf("\nlocal= %d varstatic=%d",local,varstatic);local++;varsta++;

}

Salida por pantalla =>

local=1 varstatic=1local=1 varstatic=2local=1 varstatic=3

Como ya sabemos la variable local local pierde su valor y por lo tanto cada vez que es llamada la función se inicializa. En el caso de varstatic, la primera vez se inicializa, pero posteriormente se pasa por alto dicha inicialización y mantiene el último valor asignado.

Variables estáticas externas Estas son variables globales declaradas como estáticas y su alcance es solamente el archivo donde fue declarada. Supongamos que tenemos dos archivos uno.c y dos.c, en el primero declaramos dos variables globales:

# include<stdio.h>int general;static int enarchivo;main(){

::

}

La variable general es conocida en todas las funciones del archivo uno.c y dos.c, la variable enarchivo es conocida solamente en las funciones del archivo uno.c.

Page 113: DiagrFlujo-ProgramacionC

Universidad de Magallanes - Departamento de Ingeniería en Computación

Profesor: Roberto Uribe P. Pág. Nº 113

Capítulo Nº 7 : Punteros en C Los punteros (o apuntadores) son una de las herramientas más poderosas de C, sin embargo en este caso, poder implica peligro. Es fácil cometer errores en el uso de punteros, y estos son los más difíciles de encontrar, una expresión típica usada en este caso por los programadores se refiere a la "perdida de un puntero", lo cual indica que ocurrió un problema en la asignación de algún puntero. La ventaja de la utilización de punteros en C es que mejora enormemente la eficiencia de algunos procesos, además , permite la modificación de los parámetros pasados a las funciones (paso de parámetros por referencia) y son usados en los procesos de asignación dinámica de memoria. Puntero es una representación simbólica de una dirección de memoria, es decir, contiene la dirección de un objeto o variable. Operador & Contiene la dirección o posición de memoria en la cual se ha almacenado una variable. El operador & es unuario, es decir, tiene un solo operando, y devuelve la dirección de memoria de dicho operando. Supongamos el siguiente ejemplo:

main(){

int auxiliar=5;printf("\nauxiliar=%d --->dirección=%p",auxiliar,&auxiliar);

}

por pantalla =>

auxiliar=5 ---> dirección=289926.

En este ejemplo se utilizó el modificador de formato %p que muestra la dirección según el formato del computador usado. Hasta ahora no había importado el lugar donde se almacenaban los datos. Los punteros se pueden declarar en los programas para después utilizarse y tomar las direcciones como valores. Por ejemplo:

int *punt; /* un apuntador a int */int j=5;

Se declaro un apuntador punt a int, es decir, el contenido del puntero es una dirección que apunta a un objeto de tipo entero. Si se hiciese la siguiente asignación:

punt=&j;

estaría correcta e implicaría que punt apunta a la dirección de memoria de la variable j. Gráficamente sería:

Page 114: DiagrFlujo-ProgramacionC

Programación de Computadores y el Lenguaje C (versión 2003)

Profesor: Roberto Uribe P. Pág. Nº 114

entonces, el valor de punt y &j sería 5006. Los punteros pueden ser inicializados, para ello existen varias formas:

punt=NULL;punt=&j;punt=(int *) 285395; /* asignación de dirección */

/* absoluta */

En la primera línea se le asigna un nulo, es como decir que apunta a ninguna parte. En la tercera línea se hace la asignación de una posición especifica de memoria. Entonces, para declarar un puntero, primero se especifica el tipo de dato (básico o creado por el usuario), luego el nombre del puntero precedido por el caracter *. Operador * El operador * es unuario y toma a su operando como una dirección de memoria, entonces el operador accesa al contenido de esa dirección. Por ejemplo, suponga las variables declaradas anteriormente y la asignación,

int *punt;int j=5;punt=&j;

entonces, si imprimimos en pantalla:

printf("El contenido de punt es: %d",*punt);

imprimiría:

El contenido de punt es: 5

con esto se imprimió el contenido de la dirección a la que apunta punt. Habría sido igual haber impreso la variable j. Si se declarara otra variable int, igualmente es valido hacer:

int *punt;int j=5,otro;punt=&j;::otro=*punt;

5006 5

5001 5002 5003 5004 5005 5006 5007 5008

punt (apunta ala dir. 5006que guarda unint)

j (variableint quecontiene eldato 5)

Direccinesde Memoria

Page 115: DiagrFlujo-ProgramacionC

Universidad de Magallanes - Departamento de Ingeniería en Computación

Profesor: Roberto Uribe P. Pág. Nº 115

que asignaría a otro el dato 5. Si se hace la siguiente asignación:

*punt=200;

se modifica el contenido de la dirección a la que apunta punt. Por lo tanto también se modificó la variable j, ya que la dirección de esta es a la que apunta punt. Hay ciertas restricciones en la asignación de punteros, por ejemplo:

int *p,*q,*r; /* 3 punteros a entero*/int array[20],var1;register int j;

p=&j;q=568200;r=&array;p=&(var1+5);

Todas las asignaciones anteriores están erróneas. A un puntero no se le puede asignar la dirección de una variable register, de hecho, porque esta está almacenada en un registro de la CPU. A un puntero no se le puede asignar un valor cualquiera si no existe una conversión explícita de tipo. No puede recibir la dirección de un nombre de un arreglo dado que este último es un puntero al primer elemento del arreglo. La última asignación también es ilegal ya que se pretende asignar la posición de var1 mas 5 posiciones de memoria. Las siguientes asignaciones están correctas:

int *p,*q;int array[20],var1;

p=NULL;q=&array[5];p=q;

Primero a p se le asigno el valor nulo, luego a q se le asigno la dirección del sexto elemento del arreglo y finalmente a p se le asigno la misma dirección que q, por lo tanto apuntan a la misma posición. Hasta ahora se ha ocupado implícitamente punteros en nuestros programas, ejemplo de ello es al ocupar scanf, donde pasamos como parámetro la dirección de la variable, también, en arreglos, como el caso de cuando es pasado a una función y sus valores resultan modificados. Paso de parámetros por referencia Sabemos que cuando se pasa como parámetro una variable a una función, esta no logra modificar el valor de la variable, eso es dado que fue pasada por valor. Para lograr que se modifique el contenido de una variable debe pasarse por referencia, para ello se pasa a la función la dirección de la variable y no el dato.

Page 116: DiagrFlujo-ProgramacionC

Programación de Computadores y el Lenguaje C (versión 2003)

Profesor: Roberto Uribe P. Pág. Nº 116

Ejercicio Nº 7.1: Supongamos que se desea hacer una función que haga el intercambio de datos entre dos variables (swap). Solución: Primero hagamos el programa principal, de tal manera de saber como se hace la llamada por referencia. Este podría ser:

#include <stdio.h>void swap(); /*funcion para el intercambio*/main(){

int a=100,b=200;printf("a=%d b=%d",a,b);swap(&a,&b);printf("a=%d b=%d",a,b);

}

Nuestro programa resulta bastante simple, lo importante es notar que ahora los parámetros a y b van precedidos de el operador &, es decir, no se pasan los datos 100 y 200, sino, las direcciones de las variables. Ahora, como a la función se le pasaron direcciones de variables int, entonces en la cabecera de la función deben declararse parámetros como punteros a int. Esto sería:

void swap(int *x,int *y)

Dentro de nuestra función lo que tenemos que hacer es intercambiar los valores. Como los parámetros son punteros, entonces debemos trabajar con los contenidos de dichos punteros (o los contenidos de las direcciones a la que apuntan los punteros), para ello utilizamos el operador asterisco. La función completa es:

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

int aux;aux=*x;*x=*y;*y=aux;

}

Con esto logramos nuestro objetivo, la salida al programa es:

a=100 b=200a=200 b=100

Ejercicio Nº 7.2: Cree un programa que lea dos números flotantes, luego una función que tenga dos parámetros y salga el mayor en el primer parámetro y el menor en el segundo. Solución: El programa principal sería:

#include <stdio.h>void maxymin(int *x,int *y);main(){

int i,j;scanf("%d",&i);

Page 117: DiagrFlujo-ProgramacionC

Universidad de Magallanes - Departamento de Ingeniería en Computación

Profesor: Roberto Uribe P. Pág. Nº 117

scanf("%d",&j);maxymin(&i,&j);printf("El Max es i=%d, El Min es j=%d",i,j);return 0;

}

La función no variaría mucho respecto al ejercicio anterior:

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

int aux;if (*x<*y){

aux=*x;*x=*y;*y=aux;

}return;

}

En el if se pregunto si "el contenido del puntero x es menor que el contenido del puntero y". En esta función podríamos utilizar la función anterior swap para hacer el cambio. En ese caso, aparte de incluir la declaración de dicha función antes del main, maxymin queda:

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

int aux;if (*x<*y)

swap(&*x,&*y);}

Hay que notar que al hacer &*x hablamos de la dirección del contenido del puntero x. Esto se hace utilizando la misma lógica de pasar la dirección. Sin embargo, hablar de &*x es hablar de x, en otras palabras, si decimos que "el dato 120 (por ejemplo) esta almacenado en la dirección x" ,es lo mismo que "el dato *x esta almacenado en x o en &*x". Entonces la llamada a swap puede ser hecha como:

if (*x<*y)swap(x,y);

Lo que cumple con pasar las direcciones de las variables a las funciones, esto ya en este caso los parámetros de por si son direcciones (punteros). Nota: Supongamos las siguientes sentencias:

int a=10,*p;p=&a;

es posible imprimir:

printf("%d",*&a);printf("%p",&*p);printf("%p",&p);

Page 118: DiagrFlujo-ProgramacionC

Programación de Computadores y el Lenguaje C (versión 2003)

Profesor: Roberto Uribe P. Pág. Nº 118

es decir, en la primera línea se imprime "el contenido de la dirección &a" que es lo mismo que imprimir a, pero, no se puede imprimir &*a, esto provocaría un error. En la segunda línea es igual a imprimir p. En la tercera se imprime la dirección donde esta almacenado el puntero p. Aritmética de Punteros Las operaciones matemáticas que se pueden usar con punteros es el decremento, incremento y resta entre dos punteros, es resto de las operaciones quedan prohibidas. La operatoria de punteros queda sujeta al tipo base de este, por ejemplo:

int *q,dato;q=&dato;q++;

En este caso si el puntero q apunta a la dirección 30000, el incremento apuntaría q a la dirección 30002. Esto ya que un entero tiene 2 bytes de largo y el incremento hace que se apunte al siguiente entero. Lo mismo ocurriría con q--. Si a q se le incrementa en 20 haría que q apuntara al vigésimo entero que esté despues de la posición original del puntero. Suponga el siguiente ejemplo:

int *p,arreglo[20],i;::p=&arreglo[0];for(i=0;i<20;i++){

printf("%d",*p);p++;

}

En este caso se imprime todo el arreglo arreglo utilizando el puntero p. Esto es efectivo dado que al declarar el arreglo, sus elemento se ubican en posiciones contiguas de memoria. También sería correcto utilizar

p=&arreglo[0];printf("El decimo elemento es : %d",*(p+9));

para referirnos al décimo elemento del arreglo. Como hay que tener cierto cuidado con el manejo de punteros, veamos este ejemplo:

int *p,arreglo[20],i;::p=&arreglo[0];for(i=0;i<20;i++){

printf("%d",*p);p++;

}printf("El decimo elemento es : %d",*(p+9));

Este es similar al anterior, sin embargo, no hace lo que supuestamente queremos, porque?, la razón es que el puntero fue incrementado 20 veces y despues del for ya no apunta al primer elemento del arreglo, por lo tanto puede imprimir cualquier valor (que sería aquel ubicado 9 variables enteras más allá).

Page 119: DiagrFlujo-ProgramacionC

Universidad de Magallanes - Departamento de Ingeniería en Computación

Profesor: Roberto Uribe P. Pág. Nº 119

También es posible hacer la resta entre dos punteros, esto con el objeto de saber cuantos datos del tipo base (de los punteros) se encuentran entre ambos. Igualmente se puede hacer la comparación entre punteros:

if (p>q)printf("\np es mayor que q\n");

En este caso indicaría que p esta ubicado en una posición de memoria superior a la de q. Algo más sobre punteros Los punteros no solamente pueden apuntar a tipos conocidos, sino que también a tipos creados por el usuario, como es el caso de punteros a estructuras. Podemos mencionar además que un uso frecuente de estos son en estructuras de datos para trabajar con listas enlazadas, arboles, etc., sin embargo esto último no corresponde verlo en este libro. Otro ejemplo de uso de punteros es cuando un arreglo es definido como un arreglo de punteros, por ejemplo, la siguiente declaración crea un arreglo de punteros de 5 elementos:

int *arrepunt[5];

donde cada una de las componentes del arreglo es un puntero a entero. Entonces, sentencias como las siguientes son correctas:

arrepunt[0]=&var1;printf("\n%d",*arrepunt[0]);

entonces, al primer elemento del arreglo se le asignó la dirección de la variable entera var1, posteriormente se imprimió el contenido de dicho puntero. malloc, free y sizeof Muchas veces en nuestros programas necesitaremos ocupar memoria en el momento en que este se está ejecutando, para ello existen dos funciones malloc y free. La primera, pide memoria del conjunto de memoria disponible, free hace lo contrario, devuelve la memoria pedida para que pueda ser ocupada posteriormente. Estas funciones se encuentran en la librería llamada stdlib.h y son la base de la asignación dinámica de memoria en C. Por ejemplo, si creamos un puntero a char y en algún momento necesitamos memoria para guardar algún dato, se puede hacer:

char *p1;:p1=malloc(20);

lo que nos permitirá crear 20 lugares de memoria para guardar datos, y p1 quedaría apuntando al primero de ellos. El parámetro pasado es un entero, pero en realidad el tipo

Page 120: DiagrFlujo-ProgramacionC

Programación de Computadores y el Lenguaje C (versión 2003)

Profesor: Roberto Uribe P. Pág. Nº 120

definido en stdlib.h es size_t que es parecido a un unsigned int. El prototipo de la función malloc es la siguiente:

void *malloc(size_t cantidad_de_bytes)

El valor devuelto por malloc es un puntero (void *) que se convierte a el tipo de puntero de la parte izquierda de la asignación. Sin embargo, para evitar problemas de portabilidad lo ideal es hacer la conversión explícita, de la forma:

p1=(char *)malloc(20); que convierte lo retornado por malloc a un puntero a char. Para devolver la memoria solicitada, la instrucción sería:

free(p1); En el ejemplo anterior un char ocupa un byte, no es problema pedir espacio para 20 char, sin embargo, si el puntero fuese entero, se tiene que multiplicar la cantidad pedida por el tamaño en bytes de un entero que es 2, entonces, si no sabemos el largo en bytes de un tipo o de una variable se nos complicaría pedir memoria, además que, dependiendo el computador los tipos podrían tener diferente largo. Para resolver estos problemas y para hacer nuestro programa más portable existe el operador en tiempo de compilación llamado sizeof (tamaño de) que retorna el tamaño en bytes del parámetro ingresado, que puede ser una variable o un identificador de tipo. Por ejemplo, si deseáramos imprimir el tamaño (en bytes) de un entero, lo correcto sería hacer:

printf("%d",sizeof(int)); Suponga lo siguiente:

int a,matriz[20][10];double var33;:printf("%d",sizeof(var33));printf("%d",sizeof(matriz));printf("%d\t\t%d",sizeof(a),sizeof(int));:

Imprimiría el tamaño de las variables var33, matriz (que sería 20*10*el largo de un entero) y el tamaño del tipo entero. En el ejemplo, sólo es obligatorio utilizar paréntesis cuando se pregunta el tamaño de un tipo. Con lo anterior podríamos crear memoria para un puntero a enteros usando

int *p2;p2=(int *)malloc(10*sizeof(int));

lo que crearía espacio para 10 enteros (y p2 apuntaría al primero de ellos).

Page 121: DiagrFlujo-ProgramacionC

Universidad de Magallanes - Departamento de Ingeniería en Computación

Profesor: Roberto Uribe P. Pág. Nº 121

Un ejercicio gráfico El siguiente ejercicio pretende mostrar gráficamente que ocurre con las variables y punteros durante la creación y asignación.

int *p;int z;

despues de la declaración anterior, es decir, crea una variable entera z la cual tiene su espacio en memoria, aunque su valor es desconocido (basura). Además, una variable puntero p (a int) la cual aún no tiene espacio asignado (para contener valores) y no apunta a ningura parte.

z=100;

p=&z; Despues de estas asignaciones, la variable z contiene el valor 100 y la variable p apunta a la dirección donde está z. Entonces, recién ahora se puede hablar del contenido de p, es decir, si se ejecuta:

printf(“%d”,*p);

Se imprime el valor 100. Si después,

p=NULL; Donde NULL es la forma de representar que el puntero apunta a nada. En la figura se muestra como barras paralelas decreciendo en tamaño. Si luego se hace:

p=(int *)malloc(sizeof(int));

z

¿??

p

¿??

z

100

p

¿??

z

100

p

z

100

p

NULL

Page 122: DiagrFlujo-ProgramacionC

Programación de Computadores y el Lenguaje C (versión 2003)

Profesor: Roberto Uribe P. Pág. Nº 122

se crea el espacio necesario en memoria para guarda un valor entero, a la que apuntará la variable p, a este valor sólo se puede accesar a través de p. Gráficamente:

*p=150; Después de esta asignación, el contenido de p es igual a 150, y z aún mantiene el valor 100. Si ahora hacemos nuevamente:

p=&z; Con esto, en nuestra jerga diriamos que: “se nos perdio un puntero”. En realidad, formalmente p no está perdido, si no que ahora apunta a z, pero nos es imposible recuperar el valor que tenía antes. Como se muestra en la figura, no hay ninguna forma de acceder al espacio de memoria que contiene el valor 150, siendo que ese espacio sigue ocupado. Si después de esto, se ejecuta la siguiente linea:

*p=200;

z

100

p

¿??

z

100

p

150

z

100

p

150

Page 123: DiagrFlujo-ProgramacionC

Universidad de Magallanes - Departamento de Ingeniería en Computación

Profesor: Roberto Uribe P. Pág. Nº 123

ocurre lo siguiente: Entonces, ahora, tanto z como el contenido de p tienen el valor 200. Finalmente, si:

printf(“%d %d”,z,*p); imprimiría en pantalla:

200 200

Un último comentario es recalcar que lo que almacena p, es una dirección de memoria, es decir, la dirección de memoria a la que está apuntando, en los esquemas anteriores esta se representa a través de la flecha

z

200

p

150

Page 124: DiagrFlujo-ProgramacionC

Programación de Computadores y el Lenguaje C (versión 2003)

Profesor: Roberto Uribe P. Pág. Nº 124

Problemas Propuestos 1.- Para el siguiente extracto de programa:

int *p,arreglo[6],i;:: /* datos del arreglo: 5 4 56 32 21 50 */p=arreglo;for(i=0;i<6;i++){

printf("%d",*p);printf("%p",p);p++;

}

a) Indique que hace el programa. b) Si el primer elemento del arreglo esta ubicado en la posición de memoria 13500, entonces, a que dirección de memoria queda apuntando p al finalizar el ciclo for?, escriba cada valor que va saliendo por pantalla. 2.- Escriba un programa que lea 100 números enteros y los almacene en un arreglo. Luego escriba una función que entregue a la función principal el valor máximo y mínimo, para ello ocupe parámetros pasados por referencia (a la función se le pasan tres parámetros: el arreglo y dos pasados por referencia que contendrán el máximo y mínimo). 3.- Escriba un programa que ingrese primero 2 datos (x,y) y que calcule la potencia de x elevado a y, luego un tercer dato (z) para el cual se calculará el factorial. La potencia y el factorial deben ser funciones a las cuales se le pasan parámetros por referencia. Los datos (x,y,z) deben ser leídos desde el programa principal.

Page 125: DiagrFlujo-ProgramacionC

Universidad de Magallanes - Departamento de Ingeniería en Computación

Profesor: Roberto Uribe P. Pág. Nº 125

Capítulo Nº 8 : typedef, struct y union

Typedef El lenguaje C dispone de un mecanismo denominado typedef que se utiliza para redefinir tipos otorgándoles nuevos nombres, esto permite entre otras cosas que las mantenciones futuras del programa sean más fáciles, también permite una mayor transportabilidad entre diferentes máquinas y evitar posibles problemas con los tipos de datos. El formato para typedef es:

typedef <tipo> <nuevo_tipo>; Ejemplo: typedef int enteros; En este caso se redefinió el tipo int, de esta manera el nuevo tipo enteros es más descriptivo. Entonces, con lo anterior podemos declarar variables usando enteros. Por ejemplo, las siguientes declaraciones

enteros a,b,c=5;enteros arra[20];

crean variables de tipo int, tres variables y un arreglo de 20 elementos. Ejemplo:

# define N 50 /* define una const. N=50 */

typedef float arrfloat[N];...main(){

arrfloat numeros,otronum[N];::numeros[i]=5.05; /*asignacion valida*/:

}

En el ejemplo anterior se definió un nuevo tipo que es un arreglo de flotantes, es decir, en la declaración que esta dentro del main, la variable numeros es un arreglo de 50 y la variable otronum es un arreglo de 50 por 50. Por ejemplo, para inicializar en 0 al arreglo otronum, se hace de la manera conocida:

for(i=0;i<N;i++)for(j=0;j<N;j++)

otronum[i][j]=0.0;

Page 126: DiagrFlujo-ProgramacionC

Programación de Computadores y el Lenguaje C (versión 2003)

Profesor: Roberto Uribe P. Pág. Nº 126

Estructuras Las estructuras son conjuntos de una o más variables que pueden ser de igual o distinto tipo (En Pascal se llaman register --> registro). La idea de agrupar datos en estructuras es para relacionar más adecuadamente esos datos. Por ejemplo, si deseáramos mantener en un programa los datos personales de un individuo, primero tendríamos que identificar los atributos de interés, como ser nombre, apellido, edad, fono, etc. Luego, crear una variable para cada atributo (eso con los conocimientos adquiridos hasta ahora). Finalmente hacer las operaciones. Las estructuras nos permiten trabajar dichos datos como una sola variable. Para definir una estructura se hace de la siguiente manera:

struct <nombre_estructura>{

<tipo> <nombre_campo1>;<tipo> <nombre_campo2>;::

}[lista de variables];

Entonces, para el ejemplo anterior podríamos definir la siguiente estructura:

struct persona{

char nombre[30];char apellido[30];int edad;long fono;

};

La palabra clave struct declara una estructura, el identificador persona es el nombre de la estructura y puede ser utilizado en declaraciones posteriores. Cada elemento de la estructura se conoce como "miembro" o "campo". Al final de la llave pueden ir identificadores que serán variables del tipo indicado (struct persona); esto reserva memoria para cada una de las variables. Ejemplo:

struct persona{

char nombre[30];char apellido[30];int edad;long fono;

}pers1,pers2;

También se pueden declarar variables locales como:

::main(){

struct persona persona3;

Page 127: DiagrFlujo-ProgramacionC

Universidad de Magallanes - Departamento de Ingeniería en Computación

Profesor: Roberto Uribe P. Pág. Nº 127

struct persona persona4={"Rene","Mancilla",18,222120L};...

}

En la segunda declaración (persona4), se inicializó con datos. En términos reales una estructura define un nuevo tipo de dato, y por lo tanto pueden aplicársele todo lo visto para los tipos de datos básicos, como ser: crear arreglos de estructuras, punteros a estructuras, estructuras como parámetros a funciones, etc. Para referirse a un miembro de una estructura se hace de la siguiente manera: <nombre_estructura>.<nombre_miembro>

Por ejemplo, para ingresar datos vía teclado a la variable persona3, sería:

::printf("\nIngrese datos");printf("\nNombre :");scanf("%s",persona3.nombre);printf("\nApellido:");scanf("%s",persona3.apellido);printf("\nEdad :");scanf("%d",&persona3.edad);printf("\nFono :");scanf("%ld",&persona3.fono);::

Es decir, de la misma manera que si fuese cualquier variable. Lo único a considerar es en el caso de leer el campo nombre (arreglos), si este se leyera caracter a caracter el subíndice se asocia a el miembro y no a la variable struct. Osea, la forma correcta sería:

for(i=0;i<30;i++)scanf("%c",persona3.nombre[i]);

y no,

scanf("%c",persona3[i].nombre);

que sería un error, ya que se estaría hablando del miembro nombre de la componente i de un arreglo llamado persona3.

Arreglos de estructuras Comúnmente las estructuras se llaman registros, en la vida real estos registros se agrupan, es decir, usualmente en nuestras aplicaciones necesitamos conjuntos de datos y no solamente datos independientes, ejemplo de esto es, una lista de empleados, una lista de clientes, una lista de cuentas corrientes, etc. Cada uno de estos conjuntos agrupan a n registros (estructuras) y cada registro contiene datos particulares que caracterizan al elemento.

Page 128: DiagrFlujo-ProgramacionC

Programación de Computadores y el Lenguaje C (versión 2003)

Profesor: Roberto Uribe P. Pág. Nº 128

Como cualquier otro tipo, se pueden crear arreglos donde cada una de sus componentes sea una estructura. Por ejemplo, se podría crear un arreglo para mantener una lista de nombres de personas. Usando la estructura anterior, esto podría ser:

:main(){

struct persona individuos[100]; /* para 100 personas */:

y luego referirnos a alguna componente como:

individuos[0].nombre para hablar del miembro nombre de la persona ubicada en la posición 0 del arreglo. Un arreglo de estructuras se maneja como cualquier otro arreglo. Estos conjuntos de datos los encontramos con el nombre de Bases de Datos, aunque formalmente cada conjunto de datos es una Tabla que forma parte de una Base de Datos. Nos podemos imaginar que una Tabla es un archivo en nuestro disco duro, sin embargo, como aún no poseemos los conocimientos para trabajar con archivos en C, podemos trabajar con los datos en memoria, a través de arreglos. Ejercicio Nº 8.1: Crear un programa que permita mantener una lista con los datos de los obreros de una empresa, para ello suponga que el máximo de obreros es de 40. Además, agregue los datos correspondientes a, número de horas extra que hace en el mes, el valor por hora extra que se le paga a cada obrero y el total de sobresueldo (horas extra por valor hora). Realice las siguientes operaciones: Ingresar el total de datos para los 40 obreros, luego imprima una lista resumida de los datos (sólo nombre, horas extra, valor por hora y total). Solución: Como se pide una lista de 40 obreros podemos crear una estructura que contenga los atributos importantes de los obreros, luego declarar un arreglo de 40 para contener todos los registros necesarios. #include <stdio.h>#define MAX 40struct obrero /* definicion de estructura */{

char nombre[30]; /*incluido apellido*/char direccion[30];int hextra; /*Horas Extras*/int hvalor; /*Valor por Hora*/long total; /*hextra*hvalor*/

};

main(){

struct obrero personal[MAX];int i=0;for (i=0;i<MAX;i++)

Page 129: DiagrFlujo-ProgramacionC

Universidad de Magallanes - Departamento de Ingeniería en Computación

Profesor: Roberto Uribe P. Pág. Nº 129

{printf("Nombre :");scanf("%s,personal[i].nombre);printf("\nDireccion :");scanf("%d",personal[i].dirección);printf("\nHoras Extras :");scanf("%d",&personal[i].hextra);printf("\nValor Hora :");scanf("%d",&personal[i].hvalor);printf("\n\n);personal[i].total=personal[i].hextra*personal[i].hvalor;

}system("clear") /*llamada al comando de 'borrar pantalla' en Unix*/printf("\nNombre\t\tHoras\t\tValor\t\tTotal\n")for (i=0;i<MAX;i++){

printf("%s",personal[i].nombre);printf("\t\t%d",personal[i].hextra);printf("\t\t%d",personal[i].hvalor);printf("\t\t%d",personal[i].total);

}return 0;

}

Una modificación a nuestro programa para reemplazar struct obrero en las declaraciones es usar typedef para renombrar el tipo. Para esto hay que agregar la sentencia despues de la definición de la estructura y colocar por ejemplo: typedef struct obrero empleados; y con esto se puede usar el tipo empleados en vez del tipo struct obrero. Entonces, en el main la declaración del arreglo personal puede ser reemplazada por:

empleados personal[MAX];

En este ejercicio, se podría haber utilizado funciones para las operaciones, de esta manera el programa es más modular. Por ejemplo, el llenado del arreglo podría ser: void llenar(empleados perso[MAX]){

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

printf("Nombre :");scanf("%s,perso[i].nombre);printf("\nDireccion :");scanf("%d",perso[i].dirección);printf("\nHoras Extras :");scanf("%d",&perso[i].hextra);printf("\nValor Hora :");scanf("%d",&perso[i].hvalor);printf("\n\n);perso[i].total=perso[i].hextra*perso[i].hvalor;

}}

nuevo tipo

tipo

Page 130: DiagrFlujo-ProgramacionC

Programación de Computadores y el Lenguaje C (versión 2003)

Profesor: Roberto Uribe P. Pág. Nº 130

entonces, la llamada a esta función desde el main es:

llenar(personal);

Note que el parámetro pasado a la función es todo el arreglo de estructuras, es por eso que cada uno de sus componentes sale modificado, si se hubiese pasado a la función cada componente tendríamos que haber usado parámetros pasados por referencia. Ejercicio Nº8.2: Para el ejercicio anterior, crear una función que permita responder la siguiente pregunta, dado el nombre de un obrero ¿Cual es el sobresueldo que obtiene este mes?. Supongamos que el nombre se ingresa fuera de esta función, entonces este es un parámetro que ingresa a dicha función. Solución: Con lo planteado en el ejercicio, entonces entendemos que los parámetros que se pasan a la función son el string que contiene el nombre ingresado y el arreglo personal, esto último ya que este es local (al main). Entonces la cabecera de nuestra función sería: void busca(empleados personal[MAX],char *nom)

El algoritmo principal que hay que implementar dentro de la función es el buscar un obrero específico, esto implica que cada obrero del arreglo debe compararse con el nombre ingresado. Lo anterior significa que hay que recorrer el arreglo, es decir, necesitamos un for. Para comparar dos string podemos usar la función strcmp de la librería string.h. Entonces, nuestra función completa quedaría:

void busca(empleados personal[MAX],char *nom){

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

if (strcmp(personal[i].nombre,nom)==0){

printf("\nEl obrero %s tiene un sobre sueldo de %ld\n",personal[i].nombre,personal[i].total);return; /*para terminar la funcion cuando el nombre es encontrado*/

}}printf("\nError, el nombre no existe\n");return;

}

Recuerde que el pasar el parámetro nom como char *nom, es decir como puntero, implica que el tamaño del string nom es el tamaño del string que fue pasado en la llamada a la función. Ejercicio Nº 8.3: Una aplicación que sería de interés realizar y que es muy común en los sistemas de bases de datos es el ordenamiento de los datos según algún atributo. Entonces, para el ejercicio 14 hacer una función a la cual se le pase como parámetro el arreglo de datos y esta realice el ordenamiento por nombre del obrero. Solución: Como la información esta contenida en un arreglo y conocemos el método de ordenamiento llamado burbuja, poseemos entonces lo necesario para implementar la función. La única diferencia con el método anteriormente visto, es que ahora las comparaciones de los

Page 131: DiagrFlujo-ProgramacionC

Universidad de Magallanes - Departamento de Ingeniería en Computación

Profesor: Roberto Uribe P. Pág. Nº 131

subíndices del arreglo se deben hacer utilizando las funciones de string.h, ya que los elementos a comparar son strings. void ordena(empleados personal[MAX]){

int i=0,j=0,auxext,auxval;long auxtot;char aux[30];for(i=0;i<(MAX-1);i++)

for(j=i+1;j<MAX;j++)if (strcmp(personal[i].nombre,personal[j].nombre)>0){

strcpy(aux,personal[i].nombre); /*intercambia nombre*/strcpy(personal[i].nombre,personal[j].nombre);strcpy(personal[j].nombre,aux);strcpy(aux,personal[i].direccion); /*intercambia direccion*/strcpy(personal[i].direccion,personal[j].direccion);strcpy(personal[j].direccion,aux);auxext=personal[i].hextra; /*intercambia hextra*/personal[i].hextra=personal[j].hextra;personal[j].hextra=auxext;auxval=personal[i].hvalor; /*intercambia hvalor*/personal[i].hvalor=personal[j].hvalor;personal[j].hvalor=auxval;auxtot=personal[i].total; /*intercambia total*/personal[i].total=personal[j].total;personal[j].total=auxtot;

}return;

}

Como en los casos anteriores el arreglo sale modificado. Note que se usaron las funciones strcmp para comparar y strcpy para copiar. Algo más sobre estructuras Cuando se declara una variable de un tipo estructura, sus miembros se ubican en posiciones contiguas de memoria. Sobre estructuras también se puede aplicar el operador tamaño de, los siguientes ejemplo son correctos:

printf("%d",sizeof(empleados));printf("%d",sizeof(personal));

En la primera sentencia se imprimiría el tamaño en bytes del tipo empleados (al menos 30+30+2+2+4). En la segunda sentencia se imprime el tamaño de todo el arreglo de estructuras. Estructuras Anidadas Se produce un anidamiento de estructuras cuando el miembro de una estructura es otra.

Page 132: DiagrFlujo-ProgramacionC

Programación de Computadores y el Lenguaje C (versión 2003)

Profesor: Roberto Uribe P. Pág. Nº 132

Ejemplo:

#include <stdio.h>#define N 50#define LARGO 30struct fecha {

int dia;int mes;int anho;};

struct identifica {char nombre[LARGO];char apellido[LARGO];struct fecha fnac;char direccion[LARGO];/*...*/char ciudad[LARGO];};

struct obrero{struct identifica datos;long sueldobase;};

main(){

struct fecha auxdia,otrodia={1,1,1990};struct identifica auxperson;struct obrero listado[N];::

}

En este caso se definieron tres estructuras, donde las dos últimas contienen un miembro que es una estructura. Para referirse al miembro de una estructura que a su vez es otra se usa igualmente el operador punto. Por ejemplo:

auxdia.anhoauxperson.fnac.dialistado[i].datos.fnac.dia

En el primer caso se habla del miembro anho de la variable auxdia, en el segundo del miembro dia del miembro fnac de la variable auxperson. En el último caso, como nos referimos a un arreglo hablamos del campo dia del miembro fnac del miembro datos de la componente i del arreglo listado. Entonces, para llenar de valores a la estructura auxperson se debe hacer:

scanf("%s",auxperson.nombre);scanf("%s",auxperson.apellido);scanf("%d",&auxperson.fnac.dia);scanf("%d",&auxperson.fnac.mes);scanf("%d",&auxperson.fnac.anho);::

Page 133: DiagrFlujo-ProgramacionC

Universidad de Magallanes - Departamento de Ingeniería en Computación

Profesor: Roberto Uribe P. Pág. Nº 133

Funciones y Estructuras En las actuales versiones de C se permiten realizar ciertas operaciones que en versiones anteriores no, como por ejemplo hacer la asignación directa de una estructura a otra, pasar como parámetro a una función una estructura completa y que una función retorne un tipo estructura. Para el caso de la asignación, la copia se realiza miembro a miembro. Suponga que existe una variable pers2 del tipo struct identifica, en el siguiente ejemplo se realiza la asignación de auxperson a pers2.

pers2=auxperson; También se puede hacer la copia de un solo miembro,

strcpy(pers2.nombre,auxperson.nombre); Para ver el paso de una estructura como parámetro y el retorno de una estructura, revisemos el siguiente ejemplo: #include <stdio.h>#include <string.h>

struct obrero{char nombre[30];int edad;long fono;};

void muestra(struct obrero x);struct obrero asigna();

main(){

struct obrero aa,bb={"Perico",28,226368L}; /* L al final indica que eslong*/

muestra(bb);aa=asigna();muestra(aa);return 0;

}

void muestra(struct obrero x){

printf("\nNombre : %s",x.nombre);printf("\nEdad : %d",x.edad);printf("\nFono : %ld\n",x.fono);return;

}struct obrero asigna(){

struct obrero x;scanf("%s",x.nombre);scanf("%d",&x.edad);scanf("%ld",&x.fono);return(x);

}

Page 134: DiagrFlujo-ProgramacionC

Programación de Computadores y el Lenguaje C (versión 2003)

Profesor: Roberto Uribe P. Pág. Nº 134

La novedad de este ejemplo es que una función retorna un variable de tipo structobrero que a su vez es asignada a la variable del mismo tipo aa. La función muestra sólo imprime los valores del parámetro en pantalla. Punteros a Estructuras Otra forma de asignarle valores a una estructura es que los valores se asignen dentro de la función, para ello debemos usar parámetros por referencia, es decir, pasar la dirección de memoria de la estructura, por ejemplo, nuestra función asigna puede ser llamada de la siguiente manera:

asigna(&aa); entonces, debería ser declarada anteriormente como void asigna(); En la cabecera de la función debemos declarar el parámetro como un puntero a una estructura, es decir, void asigna(struct obrero *est)

donde est es un puntero. Dentro de la función debemos utilizar el contenido de la variable. Para hacer más entendible declararemos una variable auxiliar y luego copiaremos los datos. void asigna(struct obrero *est){

struct obrero x;scanf("%s",x.nombre);scanf("%d",&x.edad);scanf("%ld",&x.fono);strcpy((*est).nombre,x.nombre);(*est).edad=x.edad;(*est).fono=x.fono;return;

}

Como sabemos, para hablar del contenido de un puntero utilizamos el operador *, sin embargo, no podemos hacer:

*est.edad ya que sería un error, esto debido a que el operador punto tiene mayor prioridad que el operador asterisco, por ello se utilizan los paréntesis. En el caso anterior, se estaría hablando del contenido del miembro edad de la variable est, es decir, edad debería ser un puntero, y eso no es así. Para simplificar nuestra función sólo bastaba hacerla de la siguiente manera:

Page 135: DiagrFlujo-ProgramacionC

Universidad de Magallanes - Departamento de Ingeniería en Computación

Profesor: Roberto Uribe P. Pág. Nº 135

void asigna(struct obrero *est){

scanf("%s",(*est).nombre);scanf("%d",&(*est).edad);scanf("%ld",&(*est).fono);return;

}

Es posible reemplazar

(*est).edad

por est->edad

donde, el operador -> reemplaza el formato anterior. Este operador es comúnmente llamado operador flecha y se construye con el signo menos seguido del signo mayor que. Algo más sobre punteros a estructuras Supongamos que se declara un puntero a la estructura fecha definida anteriormente, como fue indicado debe usarse el operador flecha para referirse a sus miembros:

:struct fecha *f;:f->dia=10;f->mes=06;f->anho=1968;:

Entonces, la siguiente sentencia:

++f->dia; incrementa el miembro dia, es decir, sería equivalente a escribir:

f->dia=f->dia+1;

o

++(f->dia); En cambio, la sentencia

(++f)->dia; incrementaría el puntero f, es decir, f apuntaría a la siguiente dirección de memoria donde exista una variable de tipo struct fecha, en la componente dia. Suponga ahora que tenemos las siguientes definiciones antes del main

Page 136: DiagrFlujo-ProgramacionC

Programación de Computadores y el Lenguaje C (versión 2003)

Profesor: Roberto Uribe P. Pág. Nº 136

::struct fecha {

int dia;int mes;int anho;};

typedef struct fecha *puntfec;:

si dentro del main declaramos una variable como: main{

puntfec f2;:

entonces f2 es un puntero a la estructura fecha, formalmente es una variable del tipo puntfec (que es un puntero), y por lo tanto debe manejarse como lo visto anteriormente. Si hiciésemos la siguiente declaración,

puntfec *f3; entonces, f3 es un puntero al tipo puntfec que a su vez en un puntero. En otras palabras f3 es un puntero a un puntero a fecha. Por lo tanto, para referirnos a el contenido de f3 debemos usar el operador *, esto tomando en cuenta la prioridad del operador flecha (que es mayor). Osea,

(*f3)->anho para referirnos al miembro año de la variable.

Uniones (unión) Una unión es una variable que permite almacenar tipos de datos distintos en el mismo espacio de memoria. La sintaxis de una variable unión es idéntica a una variable struct. Ej.:

union unica {int entero;float flotante;char caracter;};

Las declaraciones son de la siguiente forma:

union unica valores;union unica arrunion[5];union unica *ptrunion;

La idea es que el compilador guarda espacio para almacenar la mayor de las alternativas, es decir, el tamaño en bytes del miembro que ocupa más espacio, en este caso, el tamaño para un float. Sí

Page 137: DiagrFlujo-ProgramacionC

Universidad de Magallanes - Departamento de Ingeniería en Computación

Profesor: Roberto Uribe P. Pág. Nº 137

int es de 2 bytesfloat es de 4 byteschar es de 1 byte.

Para el caso de la variable valores, guarda un espacio de 4 bytes. Sí la variable valores hubiese sido un struct, entonces se hubiese podido almacenar los tres miembros, sin embargo, como es unión sólo almacena uno. Ejemplo: Asignación a uniones.

valores.entero=505;

Asigna 505 a la unión ocupando 2 bytes.

valores.flotante=0.6;

Borra el dato 505 y almacena 0.6 en 4 bytes. Se puede representar valores gráficamente como: Si fuese estructura: Si fuese union: Entonces, el tamaño es de 4 bytes, si fuese struct contendría 7 bytes.

int char

float

char

int

float

Page 138: DiagrFlujo-ProgramacionC

Programación de Computadores y el Lenguaje C (versión 2003)

Profesor: Roberto Uribe P. Pág. Nº 138

Problemas Resueltos y Propuestos Resueltos: Problema específico : Existe un programa escrito en C, dicho programa se encarga de manejar los datos de una empresa, esta puede tener hasta un máximo de 100 empleados. Para lo anterior se utilizan las siguientes estructuras:

struct obrero{int codigo;char apellido[30];char nombre[25];int Edad;char direccion[35];int coddepto;};

struct depto{int coddepto;char nomdepto[35];};

struct sueldo{int codigo;long sueldobase;long otros;};

struct aux{int codigo;char apellido[30];};

No se preocupe de la forma de lectura y almacenamiento en disco, sólo realice las funciones que se le piden. Al ejecutarse el programa, este carga los datos en las siguientes variables: struct obrero arobrero[100]; /*arreglo que contiene los datos de cada

obrero */struct depto ardepto[30]; /*arreglo que contiene el codigo y nombre de

cada depto. */struct sueldo arsueldo[100]; /*arreglo que contiene los sueldos de los

empleados */struct aux obreord[100]; /* arreglo de enteros */

Todas estas variables son globales. Cada obrero al ser ingresado al sistema se le asigna un número (código), este es en orden correlativo. Dicho código se utiliza para identificar al obrero, por ejemplo, en el arreglo arsueldo, se ocupa para indicar el sueldo de un obrero cuyo código esta indicado.

Page 139: DiagrFlujo-ProgramacionC

Universidad de Magallanes - Departamento de Ingeniería en Computación

Profesor: Roberto Uribe P. Pág. Nº 139

Ejemplo:

Arreglo arobrero:

codigo apellido nombreedad direccion coddepto

1 Alexander Nicolai 25 O'Higgins 54 32 Sanchez Alfredo 32 R. Correa 1050 43 Valencia Josefina 29 I. C. Pinto 33 34 Gonzalez Ramiro 25 Mardones 0166 25 Gonzalez Miguelina 31 Bories 1420 36: ...... ..... .. ..... ..: ...... ..... .. ..... ..: ...... ..... .. ..... ..-1 ...... ..... .. ..... ..-1 ......-1 ......

Arreglo ardepto:

coddepto nomdepto

1 Administración2 Mantención3 Informática. ...... ...... .....-1 .....-1 .....

Arreglo arsueldo:

codigosueldobase otros

1 230.000 51.0002 180.000 33.0003 200.000 40.0004 140.000 23.000...-1 .... ...-1 .... ...

Entonces Josefina Valencia del departamento de Informática gana 200.000 +

40.000 pesos. (los datos con -1 indican vacíos o nulos). Problemas: 1.- Se le pide crear un módulo (función) que realice lo siguiente: Ingrese un nombre de departamento y que imprima en pantalla el apellido, nombre, sueldo total (sueldobase+otros) de todos los empleados pertenecientes a ese depto. 2.- Usando las estructuras y arreglos anteriores, haga una función que lea por teclado un nombre de departamento y un valor numérico, luego imprima las personas que pertenecen a ese departamento (apellido, nombre, sueldototal), con la condición de que el sueldo total no

Page 140: DiagrFlujo-ProgramacionC

Programación de Computadores y el Lenguaje C (versión 2003)

Profesor: Roberto Uribe P. Pág. Nº 140

supere el valor numérico ingresado. Es decir todas las personas del depto .... que ganen menos de ..... 3.- Escriba una función que ordene los obreros alfabéticamente por apellido de menor a mayor (a-z). Para ello no modifique el arreglo original, duplique en otro arreglo (obreord), el código y apellido. Luego de duplicado y ordenado, imprima en pantalla el apellido, nombre y sueldo total, obviamente ordenado por apellido. 4.- La empresa a estimado aumentar el sueldo base de todos los empleados de un departamento determinado en un 10 %. Cree una función que realice este proceso. Es decir que lea el nombre de un depto. y que luego aumente en 10 % el sueldo base de todos los empleados de dicho depto. Propuestos: Problema específico Con los conocimientos que hasta ahora poseemos, suponga que se diseño una Base de Datos para llevar el control de las inscripciones de créditos de los alumnos de la carrera de Ingeniería de Ejecución en Computación e Informática. Dicha Base de Datos tiene las siguientes Tablas:

ALUMNOS(n_mat,rut,apellido,nombre)ASIGNATURAS(cod,nombre_ramo,tel,descripcion,semcar,annocar)INSCRIPCION(n_mat,cod,semestre,anno)NOTAS(n_mat,cod,opcion,semestre,anno,promedio)REQUISITOS(cod,cod_req)

Donde:

n_mat : número de matrícula del alumno,cod : código de la asignatura,tel : horas de teoría, ejercicios y laboratorio,semcar : semestre en que se dicta la asignatura,annocar : año en que se dicta la asignatura.semestre : semestre en que se inscribió el ramo(1 del segundo año),anno : año en que se inscribió el ramo (ej. 1 de 1996)cod_req : código del ramo requisito.

La representación de sus estructuras en C es:

struct alumnos{long n_mat ;long rut;char apellido[30];char nombre[30];}alum[200];

struct asignaturas{char cod[7];char nombre_ramo[30];int tel;char descripcion[40];

Page 141: DiagrFlujo-ProgramacionC

Universidad de Magallanes - Departamento de Ingeniería en Computación

Profesor: Roberto Uribe P. Pág. Nº 141

int semcar;int annocar;}asig[25];

struct inscripcion{long n_mat;char cod[7];int semestre;int anno;}insc[5000]; /* este arreglo es sólo un supuesto */

struct notas{long n_mat;char cod[7];int opcion;int semestre;int anno;float promedio;}not[5000];

struct requisitos{char cod[7];char cod_req[7];}req[100];

Explicación: Para la siguiente serie de problemas asuma que los arreglos ya contienen los datos, Ud. sólo debe responder las consultas que se le piden. Como ejemplo de los datos contenidos en los arreglos, suponga: :asignaturas[10]=("PRG2161","Programación de Computadores","402","Programación enC",1,2):asignaturas[15]=("LII3161","Lenguajes de Programación II","402","Orientación aObjetos",1,3)::inscripcion[405]=(9416035,"PRG2161",1,1996)inscripcion[406]=(9516010,"PRG2161",1,1996):

Nota: los datos dentro de los arreglos NO ESTÁN ORDENADOS!!, Problemas : 5.- Imprima una lista con los nombres y apellidos de todos los alumnos que inscribieron ramos en un semestre y año determinado, por ejemplo: el segundo semestre de 1995. Los parámetros semestre y año son ingresados a la función (no leídos por teclado). 6.- La alumna Carolina Bonacic puede inscribir Lenguajes de Programación I??, es decir, ingrese como parámetro a una función dos string, el primero un nombre de alumno y el segundo un nombre de un ramo. Verifique que el alumno puede o no inscribir el ramo, retorne 1 si puede y 0 si no. Recuerde que un ramo puede tener como requisito más de una asignatura. 7.- Imprimir la lista de los créditos tomados por el alumnos Juan Coñuecar el segundo semestre de 1995, indicando :

Page 142: DiagrFlujo-ProgramacionC

Programación de Computadores y el Lenguaje C (versión 2003)

Profesor: Roberto Uribe P. Pág. Nº 142

Nombre Ramo Opción TEL Promedio 8.- Suponga que en la cabecera de un programa aparecen las siguientes definiciones:

struct num{

int re; /*parte real*/int im; /*parte imaginaria*/

};

typedef num imagin; Es decir, se tiene un nuevo tipo de dato para el manejo de números imaginarios. Ud. debe de implementar las funciones que permitar realizar la suma, resta, multiplicación y división de dos numeros imaginarios. Las funciones deben ingresar ambos operandos y retornar el resultado. Sólo se pide las funciones. 9.- Uno de los problemas que hay al finalizar un Semestre Académico es preparar el horario para el semestre siguiente, este problema es sumamente complejo, a Ud. se le pide implementar una pequeña parte de la solución. Debe hacer un programa que permita ingresar el nombre de un ramo (string), el número de semestre (int) a el horario, para ello tome en cuenta el horario tiene 6 bloques y 5 días, donde cada bloque puede ser ocupado por una asignatura. Haga la implementación completa para el ingreso de los datos y validaciones. Es decir, el usuario ingresa un bloque y un día y el programa debe indicar si este ya esta ocupado por alguna asignatura, si no es así, entonces puede ingresar los datos correspondientes Simplificación del problema: Asuma que la solución es sólo para una carrera. No tome en cuenta el número de alumnos por sala. Recomendaciones: Primero resuelva el problema suponiendo que existe sólo una sala. Luego para n salas, por ejemplo 5. Para aumentar la complejidad del problema modifique el programa para que las asignaturas puedan ocupar máximo dos bloques a la semana.

Page 143: DiagrFlujo-ProgramacionC

Universidad de Magallanes - Departamento de Ingeniería en Computación

Profesor: Roberto Uribe P. Pág. Nº 143

Page 144: DiagrFlujo-ProgramacionC

Programación de Computadores y el Lenguaje C (versión 2003)

Profesor: Roberto Uribe P. Pág. Nº 144

Capítulo Nº9 : Archivos en C (en construcción)

Tipo usado en archivos

FILE

El tipo FILE es definido por C en el archivo de cabecera stdio.h para el manejo de archivos. Esta es una estructura, y se usará siempre con punteros a esta.

La definición de ésta estructura depende del compilador, pero en general mantienen un campo con la posición actual de lectura/escritura, un buffer para mejorar las prestaciones de acceso al archivo y algunos campos para uso interno.

Funciones para manejo de archivos A continuación se presenta un listado de las funciones más frecuentes para la administración de archivos en C.

Función fopen :

Sintaxis: FILE *fopen(char *nombre, char *modo);

Esta función sirve para abrir y crear archivos en disco. El valor de retorno es un puntero a una estructura FILE. Los parámetros de entrada son:

1. nombre: una cadena que contiene un nombre de archivo válido, esto depende del sistema operativo que estemos usando. El nombre puede incluir el camino completo.

2. modo: es una cadena que especifica el tipo de archivo que se abrirá o se creará y el tipo de datos que puede contener, de texto o binarios:

o r: sólo lectura. El archivo debe existir.

o w: se abre para escritura, se crea un archivo nuevo o se sobrescribe si ya existe.

o a: añadir, se abre para escritura, el cursor se sitúa al final del archivo. Si el archivo no existe, se crea.

o r+: lectura y escritura. El archivo debe existir.

o w+: lectura y escritura, se crea un archivo nuevo o se sobrescribe si ya existe.

o a+: añadir, lectura y escritura, el cursor se sitúa al final del archivo. Si el archivo no existe, se crea.

o t: tipo texto, si no se especifica t ni b, se asume por defecto que es t

o b: tipo binario.

Page 145: DiagrFlujo-ProgramacionC

Universidad de Magallanes - Departamento de Ingeniería en Computación

Profesor: Roberto Uribe P. Pág. Nº 145

Por ejemplo: el siguiente código hable un archivo para lectura y escritura de tipo binario. Al comienzo aparece declarada la variable fdata como puntero a FILE.

FILE *fdata;::if ((fdata=fopen("datos.dat","r+b"))==NULL){

printf("Error al abrir archivo");return 0;

}

Función fclose :

Sintaxis: int fclose(FILE *archivo);

Cierra un archivo y almacena los datos que aún están en el buffer de memoria, actualiza algunos datos de la cabecera del archivo que mantiene el sistema operativo. Además permite que otros programas puedan abrir el archivo para su uso.

Un valor de retorno cero indica que el archivo ha sido correctamente cerrado, si ha habido algún error, el valor de retorno es la constante EOF. El parámetro es un puntero a la estructura FILE del archivo que queremos cerrar.

Función fgetc :

Sintaxis: int fgetc(FILE *archivo);

Esta función lee un carácter desde un archivo.

El valor de retorno es el carácter leído como un unsigned char convertido a int. Si no hay ningún carácter disponible, el valor de retorno es EOF. El parámetro es un puntero a una estructura FILE del archivo del que se hará la lectura.

Función fputc :

Sintaxis: int fputc(int caracter, FILE *archivo);

Esta es la función inversa a fgetc y escribe un carácter a un archivo.

El valor de retorno es el carácter escrito, si la operación fue completada con éxito, en caso contrario será EOF. Los parámetros de entrada son el carácter a escribir, convertido a int y un puntero a una estructura FILE del archivo en el que se hará la escritura.

Page 146: DiagrFlujo-ProgramacionC

Programación de Computadores y el Lenguaje C (versión 2003)

Profesor: Roberto Uribe P. Pág. Nº 146

Función feof :

Sintaxis: int feof(FILE *archivo);

Esta función permite verificar si se ha llegado el fin del archivo. Muy frecuentemente

deberemos trabajar con todos los valores almacenados en un archivo de forma secuencial, la forma que suelen tener los cilos o bucles para leer todos los datos de un archivo es permanecer leyendo mientras no se detecte el fin de archivo. Por ejemplo:

while(!feof(fDB))

El valor de retorno es cero mientras no se haya alcanzado el fin de archivo (cuando llega al final del archivo retorna un número distinto de cero). El parámetro es un puntero a la estructura FILE.

Función rewind :

Sintaxis: void rewind(FILE *archivo);

La función rewind o rebobinar, ubica el control del archivo al principio de este. Esta función es heredada de los tiempos de las cintas magnéticas, donde literalmente había que rebobinar la cita hasta el principio. Función fgets :

Sintaxis: char *fgets(char *cadena, int n, FILE *archivo);

Esta función está diseñada para leer cadenas de caracteres. Leerá hasta n-1

caracteres o hasta que lea un retorno de línea. En este último caso, el carácter de retorno de línea también es leído.

El parámetro n nos permite limitar la lectura para no exceder el límite de la cadena.

El valor de retorno es un puntero a la cadena leída, si se leyó con éxito, y es NULL si se detecta el final del archivo o si hay un error. Los parámetros son: la cadena a leer, el número de caracteres máximo a leer y un puntero a una estructura FILE del archivo del que se leerá.

Función fputs :

Page 147: DiagrFlujo-ProgramacionC

Universidad de Magallanes - Departamento de Ingeniería en Computación

Profesor: Roberto Uribe P. Pág. Nº 147

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

La función fputs escribe una cadena en un archivo. No se añade el carácter de

retorno de línea ni el carácter nulo final.

El valor de retorno es un número no negativo o EOF en caso de error. Los parámetros de entrada son la cadena a escribir y un puntero a la estructura FILE del archivo donde se realizará la escritura. Función fread :

Sintaxis: size_t fread(void *varreg, size_t tamaño, size_t nreg, FILE*archivo);

Esta función está pensada para trabajar con registros de longitud constante. Es

capaz de leer desde un archivo uno o varios registros de la misma longitud y a partir de una dirección de memoria determinada. El usuario es responsable de asegurarse de que hay espacio suficiente para contener la información leída. El valor de retorno es el número de registros leídos, no el número de bytes. Los parámetros son: un puntero a la zona de memoria donde se almacenarán los datos leídos (varreg), el tamaño de cada registro, el número de registros a leer y un puntero a la estructura FILE del archivo del que se hará la lectura.

Función fwrite :

Sintaxis: size_t fwrite(void *varreg,size_t tamaño,size_t nreg, FILE *archivo);

Esta función es equivalente a fread, pero para escritura. Puede de escribir en un archivo uno o varios registros de la misma longitud almacenados a partir de una dirección de memoria determinada.

El valor de retorno es el número de registros escritos, no el número de bytes. Los parámetros son: un puntero a la zona de memoria donde se almacenarán los datos leídos, el tamaño de cada registro, el número de registros a leer y un puntero a la estructura FILE del archivo del que se hará la lectura.

Función fprintf :

Sintaxis: int fprintf(FILE *archivo, const char *formato, ...);

Es equivalente a printf, pero la salida se dirige a un archivo en lugar de la salida

estandar.

Page 148: DiagrFlujo-ProgramacionC

Programación de Computadores y el Lenguaje C (versión 2003)

Profesor: Roberto Uribe P. Pág. Nº 148

Función fscanf :

Sintaxis: int fscanf(FILE *archivo, const char *formato, ...);

Equivalente a scanf, pero la entrada se toma de un archivo en lugar del teclado (o

entrada estandar stdin).

Función fflush :

Sintaxis: int fflush(FILE *archivo);

fflush fuerza la salida de los datos acumulados en el buffer de salida del archivo.

Para mejorar las prestaciones del manejo de archivos se utilizan buffers, almacenes temporales de datos en memoria, las operaciones de salida se hacen a través del buffer, y sólo cuando el buffer se llena se realiza la escritura en el disco y se vacía el buffer. En ocasiones nos hace falta vaciar ese buffer de un modo manual, para eso sirve ésta función.

El valor de retorno es cero si la función se ejecutó con éxito, y EOF si hubo algún error. El parámetro de entrada es un puntero a la estructura FILE del archivo del que se quiere vaciar el buffer. Si es NULL se hará el vaciado de todos los archivos abiertos. Función fseek :

Sintaxis: int fseek(FILE *archivo,long int desplazamiento,int origen);

Situa el control de archivo (o cursor) para leer o escribir en un lugar especificado en

desplazamiento.

El valor de retorno es cero si la función tuvo éxito, y un valor distinto de cero si hubo algún error.

Los parámetros de entrada son: un puntero a una estructura FILE del archivo en el que queremos cambiar el cursor de lectura/escritura, el valor del desplazamiento(en bytes) y el punto de origen desde el que se calculará el desplazamiento.

El parámetro origen puede tener tres posibles valores:

1. SEEK_SET : el desplazamiento se cuenta desde el principio del archivo. El primer byte del archivo tiene un desplazamiento cero.

2. SEEK_CUR : el desplazamiento se cuenta desde la posición actual del cursor.

3. SEEK_END : el desplazamiento se cuenta desde el final del archivo.

Page 149: DiagrFlujo-ProgramacionC

Universidad de Magallanes - Departamento de Ingeniería en Computación

Profesor: Roberto Uribe P. Pág. Nº 149

Función ftell :

Sintaxis: long int ftell(FILE *archivo);

La función ftell sirve para averiguar la posición actual del cursor de lectura/escritura

de un archivo.

El valor de retorno será esa posición en bytes, o -1 si hay algún error.

Función remove :

Sintaxis : int remove(char *filename);

Esta función sirve para borrar permanentemente un archivo. El parámetro de entrada es la ruta absoluta o relativa del archivo a borrar.

Si la eliminación del archivo fue exitosa, el valor de retorno es cero, de lo contrario es un número distinto de cero. Un ejemplo interesante con fread Supongamos la siguiente estructura: struct _obrero{

char nombre[30];int edad;long fono;

};

typedef _obrero obrero;

supongamos ahora que se tiene un archivo llamado datos.dat, el cual fue escrito usando la misma estructura (con fwrite). Entonces para leer todo el archivo y mostrarlo en pantalla sería: void muestradatos(){

FILE *fdata;Int total=0;obrero a;if ((fdata=fopen("datos.dat","r+b"))==NULL){

printf("Error al abrir archivo");return;

}while(!feof(fdata)){

Page 150: DiagrFlujo-ProgramacionC

Programación de Computadores y el Lenguaje C (versión 2003)

Profesor: Roberto Uribe P. Pág. Nº 150

fread(&a, sizeof(obrero), 1, fdata);muestraobrero(a);total++;

}printf(“\ntotal de datos leidos:: %d\n”,total);

}

void muestraobrero(obrero a){

printf(“%s \t %d \t %ld \n”,a.nombre, a.edad, a.fono);return;

}

Observe que a fread se le paso la dirección de a, eso, dado que el parámetro requerido por la función es un puntero, en este caso un puntero a obrero. El parámetro 1, es que leemos sólo un dato de tipo obrero. Cada vez que se hace una lectura el control del archivo se mueve al dato siguiente, y el ciclo termina cual se llega al final de archivo. Si escribe y ejecuta este código, es posible que ocurra lo siguiente, la variable total con un dato más y en el listado, aparece repetido el último obrero, porque??. Este es un error típico y es producto de que despues de leer el último dato, aún no se alcanza el fin de archivo, por lo tanto se mete una vez más al while, se incrementa nuevamente total y el fread falla, asi que se muestra el último valor que teniala variable a. Si se reemplaza el while por: while(!feof(fdata)){

if (fread(a, sizeof(obrero), 1, fdata)<1)printf(“error al leer obrero”);

else{

muestraobrero(a);total++;

}}

Con esto se arregla el problema, y es porque la última lectura no alcanza a leer un dato de tipo obrero, por lo tanto el valor de fread es menor que 1.

Page 151: DiagrFlujo-ProgramacionC

Universidad de Magallanes - Departamento de Ingeniería en Computación

Profesor: Roberto Uribe P. Pág. Nº 151

Últimas Notas El siguiente paso a seguir en programación es el estudio de las estructuras de datos, este tema corresponde tanto a la construcción se complejas estructuras generalmente usando punteros como es el caso de listas enlazadas, listas doblemente enlazadas, arboles binarios y arboles en general, así como también recursividad y medición de la eficiencia de algoritmos para las estructuras mencionadas. No es necesario el conocimiento de un lenguaje específico para comprender estructuras de datos, basta usar seudolenguajes, sin embargo, es necesario para poder realizar la implementación de dichos algoritmos. En las carreras de Ingeniería en Computación o Informática estos tópicos se ven en las asignaturas que siguen la línea de Programación de Computadores como Estructuras de Datos y Algoritmos. Respecto del lenguaje C, lo visto en este curso es sólo una parte, queda mucho más por aprender ☺.

Page 152: DiagrFlujo-ProgramacionC

Programación de Computadores y el Lenguaje C (versión 2003)

Profesor: Roberto Uribe P. Pág. Nº 152

Anexo Nº 1 : Estructura de un Compilador

A nivel general un compilador verifica que el programa fuente cumpla con los requisitos que exige el lenguaje, es decir, primero verifica que los caracteres y símbolos usados sean los permitidos (análisis léxico), despues ve si estos símbolos están usados adecuadamente, osea, en el orden o lugar correcto (análisis sintáctico). La estructura fundamental de un compilador queda expuesto en el siguiente diagrama:

Programa Fuente

Analizador Léxico (lexer)

Tabla de Símbolos (Tokens)

Analizador Sintáctico (Parser)

Optimizaciónde

Código

Generaciónde

Código

Listado

Generación de Código Intermedio

(análisis Semántico)

Errores (Semánticos, Sintácticos) Código Objeto

Para máquinas particulares

Page 153: DiagrFlujo-ProgramacionC

Universidad de Magallanes - Departamento de Ingeniería en Computación

Profesor: Roberto Uribe P. Pág. Nº 153

Anexo Nº 2 : Algunos Conceptos Lenguajes de Programación: Son formas de indicarle al computador de manera detallada qué es lo que nosotros queremos que haga. Lenguaje de alto nivel: Es el lenguaje más próximo al hombre, el que nosotros entendemos. Lenguaje de bajo nivel o de máquina: Es el lenguaje más próximo a la máquina. C: Es un lenguaje de programación de nivel medio (compilado), es de propósito general. Pascal: Lenguaje de programación de alto nivel (compilado), de propósito general. Fortran: Lenguaje de alto nivel desarrollado para aplicaciones matemáticas y científicas, su nombre viene de FORmula TRANslation. En un principio era un lenguaje no estructurado, pero las nuevas versiones si lo son. Compilador: Permite traducir las instrucciones escritas en lenguaje de alto nivel en instrucciones de bajo nivel. Programación Estructurada: Método de Programación que pretende evitar al máximo los errores en los programas, su técnica es la de dividir el programa en bloques o etapas que se irán definiendo y concretando en forma descendente hasta llegar a completar el programa o resolver el problema. Las unidades básicas de Programación Estructurada son: Secuencia: Una serie de procesos, que va uno tras de otro. Selección: Proceso que permite elegir otros procesos. Iteración: Proceso que ocurre una cantidad de veces (Loop). Lenguaje Estructurado. Tipo de Lenguaje creado para facilitar la programación estructurada. Diagramas de Flujo: Es una representación gráfica de la definición, análisis o solución de un programa en el cual los símbolos se utilizan para representar operaciones, datos, etc. Permite visualizar el comportamiento de cada paso de un programa. Algoritmos: Son una secuencia de pasos muy detallados y ordenados lógicamente que nos permiten realizar una determinada acción.