miguel Á. toledo martÍnez contenido de la lecciÓn 10azul2.bnct.ipn.mx/c/funciones/archivos...

53
MIGUEL Á. TOLEDO MARTÍNEZ FUNCIONES - LECCIÓN - 10 10-1 CONTENIDO DE LA LECCIÓN 10 CONOCIMIENTOS BÁSICOS SOBRE FUNCIONES 1. Introducción 2 2. Funciones que regresan un solo valor: funciones sin void 3 2.1. Formato de funciones definidas por el usuario 4 2.2. Encabezado de la función 4 2.3. La clase de datos return 5 2.4. El nombre de la función 5 2.5. Lista de parámetros 6 2.6. Ejemplo 10.1 7 2.7. Sección de enunciado 7 2.8. Ejemplos 10.2, 10.3 8 2.9. Llamada a las funciones sin void 9 2.10. Argumentos reales en comparación con parámetros formales 10 2.11. Retorno de un resultado al programa que invoca a la función 11 2.12. Ejemplo 10.4 11 2.13. Examen breve 27 52 3. Funciones void 12 3.1. Parámetros de valor en comparación con parámetros de referencia 13 3.2. Parámetros de valor 13 3.3. Ejemplos 10.5, 10.6, 10.7, 10.8, 10.9 14 3.4. Parámetros de referencia 17 3.5. Ejemplos 10.10, 10.11 19 4. Localizaciones de las funciones dentro de su programa 20 5. Examen breve 28 52 6. Prototipo de funciones 22 6.1. Ejemplos 10.12, 10.13 23 7. Solución de problemas en acción: Programación estructurada, ley de Ohm 25 7.1. Problema 25 7.2. Definición del problema 25 7.3. Planeación de la solución 26 7.4. Codificación del programa 28 8. Solución de problemas en acción: Estado de cuenta de una cuenta de ahorros 36 8.1. Problema 36 8.2. Definición del problema 36 8.3. Planeación de la solución 36 8.4. Codificación del programa 40 9. Examen breve 29 52 10. Lo que necesita saber 42 11. Preguntas y problemas 44 11.1. Preguntas 44 11.2. Problemas 47

Upload: others

Post on 08-May-2020

5 views

Category:

Documents


0 download

TRANSCRIPT

MIGUEL Á. TOLEDO MARTÍNEZ

FUNCIONES - LECCIÓN - 10 10-1

CONTENIDO DE LA LECCIÓN 10

CONOCIMIENTOS BÁSICOS SOBRE FUNCIONES

1. Introducción 2 2. Funciones que regresan un solo valor: funciones sin void 3

2.1. Formato de funciones definidas por el usuario 4 2.2. Encabezado de la función 4 2.3. La clase de datos return 5 2.4. El nombre de la función 5 2.5. Lista de parámetros 6 2.6. Ejemplo 10.1 7 2.7. Sección de enunciado 7 2.8. Ejemplos 10.2, 10.3 8 2.9. Llamada a las funciones sin void 9 2.10. Argumentos reales en comparación con parámetros formales 10 2.11. Retorno de un resultado al programa que invoca a la función 11 2.12. Ejemplo 10.4 11 2.13. Examen breve 27 52

3. Funciones void 12

3.1. Parámetros de valor en comparación con parámetros de referencia 13 3.2. Parámetros de valor 13 3.3. Ejemplos 10.5, 10.6, 10.7, 10.8, 10.9 14 3.4. Parámetros de referencia 17 3.5. Ejemplos 10.10, 10.11 19

4. Localizaciones de las funciones dentro de su programa 20 5. Examen breve 28 52 6. Prototipo de funciones 22

6.1. Ejemplos 10.12, 10.13 23 7. Solución de problemas en acción: Programación estructurada, ley de Ohm 25

7.1. Problema 25 7.2. Definición del problema 25 7.3. Planeación de la solución 26 7.4. Codificación del programa 28

8. Solución de problemas en acción: Estado de cuenta de una cuenta de ahorros 36

8.1. Problema 36 8.2. Definición del problema 36 8.3. Planeación de la solución 36 8.4. Codificación del programa 40

9. Examen breve 29 52 10. Lo que necesita saber 42 11. Preguntas y problemas 44

11.1. Preguntas 44 11.2. Problemas 47

MIGUEL Á. TOLEDO MARTÍNEZ

FUNCIONES - LECCIÓN - 10 10-2

LECCIÓN 10

CONOCIMIENTOS BÁSICOS SOBRE FUNCIONES INTRODUCCIÓN Cuando los programas se vuelven más complejos, los programadores descomponen el programa en pequeños subprogramas llamados funciones. Cada función debe realizar una tarea específica. Cuando el programa requiere de la tarea, llama a la función, proporcionándole toda la información que requiera para llevar a cabo la tarea.

Objetivos de esta lección: •••• Aprender a construir programas de manera modular, a partir de piezas pequeñas llamadas

funciones. •••• Crear funciones nuevas. •••• Entender los mecanismos para pasar información entre funciones. •••• Comprender las clases de retorno de una función. •••• Crear prototipos de funciones.

En lecciones anteriores se ha presentado el diseño estructurado usando el refinamiento sucesivo (paso a paso) y las funciones estándar de C++. También se han desarrollado diseños descendentes de programas estructurados, pero se ha empleado una programación lineal cuando se codifican estos diseños. Como resultado, hemos estado codificando programas C++ que consisten en una sección principal definida por la función main() Es tiempo de aprender cómo codificar diseños estructurados usando un programa C++ estructurado que emplea funciones. Como se sabe, las funciones son la base de la programación estructurada en el lenguaje C++. Además, las funciones proporcionan el único método de comunicación con objetos en programación orientada a objetos. Por lo tanto, esta lección es importante para aprender la programación estructurada y la orientada a objetos en el lenguaje C++. Ya se han utilizado funciones estándar preconstruidas o predefinidas, en los programas. Todas estas funciones forman parte de los diversos archivos de encabezado incluidos en C++. Ahora es momento de desarrollar sus propias funciones y emplearlas para crear programas C++ bien estructurados y modulares, así como sus propias clases y objetos cuando estudie la programación orientada a objetos. Las funciones que se crean para uso propio en un programa se conocen con el nombre de funciones definidas por el usuario. En este sentido, el usuario es usted, el programador. Una función definida por el usuario es un bloque de enunciados o un subprograma, que se escribe para realizar una tarea específica requerida por el programador. Las funciones en C++ tienen la misma estructura del programa main() que ha utilizado a lo largo de estas lecciones:

claseRetorno nombreFuncion(listaParametros)

{ declaraciónVariables; enunciados; }

MIGUEL Á. TOLEDO MARTÍNEZ

FUNCIONES - LECCIÓN - 10 10-3

A una función se la da un nombre y se le llama o invoca usando su nombre cada vez que se va a realizar la tarea dentro del programa. El programa que llama o invoca una función suele conocerse con el nombre de programa llamador. Las funciones eliminan la necesidad de duplicar enunciados dentro de un programa. Dada una tarea que se va a realizar más de una vez, los enunciados, se escriben sólo una vez para la función. Entonces se llama a la función cada vez que se desee realizar la tarea. Además, el uso de las funciones mejora la claridad y la legibilidad de la lista del programa. Y lo que es más importante, el uso de las funciones dentro de un lenguaje estructurado, como C++, permite solucionar problemas complejos y muy largos usando un enfoque de diseño de programación de arriba-abajo, así como la construcción de clases y objetos en programación orientada a objetos. En C++ es posible hacer que una función sirva para dos propósitos. Puede hacerse una función para que regrese un solo valor al programa llamador. En C++, este tipo de función se conoce como sin void. También es posible escribir funciones que realicen tareas específicas sin que regrese valor alguno o regrese varios valores, a estas funciones se les conocen con el nombre de funciones void. FUNCIONES QUE REGRESAN UN SOLO VALOR: funciones sin void Ya se cuenta con alguna experiencia en funciones que regresan un valor en C++. Recuerde las funciones estándar que se utilizaron antes como sqrt(), sin() y cos(), por mencionar algunas. Vimos que C++ incluye funciones para el manejo de cadenas y funciones matemáticas, así como funciones para el manejo de E/S. Sin embargo, suponga que quiere realizar alguna operación que no es una función predefinida en C++, por ejemplo elevar al cubo. Debido a que C++ no incluye ninguna función estándar para tal operación, es posible codificar en el programa la operación con un enunciado como el siguiente:

cubo = x * x * x

Después, insertar este enunciado dentro del programa cada vez que se quiera elevar al cubo el valor de x. Sin embargo, ¿no sería más sencillo insertar el comando cubo(x) cada vez que se quiera elevar x al cubo, de tal manera que C++ sepa qué hacer al igual que sabe cómo ejecutar sqrt(x)? Puede hacer esto definiendo su propia función cubo(x). A esta función se le conoce con el nombre de función definida por el usuario, por razones obvias. Una función definida por el usuario es un subprograma que, cuando se invoca, realiza alguna tarea o regresa un solo valor que se asigna al nombre de la función en cualquier parte que se utilice el nombre en el programa llamador. De esta manera, si cubo(x) es una función definida por el usuario que calculará un valor, por decir, x, el enunciado cout << cubo(x); invocará la función y hará que se muestre el cubo de x. Ahora será necesario aprender a crear funciones definidas por el usuario.

MIGUEL Á. TOLEDO MARTÍNEZ

FUNCIONES - LECCIÓN - 10 10-4

FORMATO DE FUNCIONES DEFINIDAS POR EL USUARIO

// Encabezado de la función <claseRetorno> <nombreFunción> (<listaParámetros>) {// Empieza el bloque de enunciados de la función // Variables y constantes locales <los objetos variables y las constantes locales deberán ir aquí> // Enunciados de la función o cuerpo enunciado #1; enunciado #2; ....................................... enunciado #n; return <valor de regreso>; } // Final del bloque de enunciados de la función

El formato de la definición de una función consiste en tres secciones principales: una línea de encabezado de la función, cualquier variable local u objetos constantes requeridos por la función y una sección de enunciados. ENCABEZADO DE LA FUNCIÓN El encabezado de la función proporciona la interfaz de datos para la función. Una interfaz de función o encabezado de la función es un enunciado que forma un marco común entre la función y su programa llamador. Esta idea se muestra en la figura 10.1. Observe que el encabezado indica que datos de la función aceptarán desde el programa llamador y qué datos de la función regresarán al programa llamador. Cuando desarrolle los encabezados de las funciones, su perspectiva necesita ser relativa a la función. Deberá preguntarse dos cosas:

1. ¿Qué datos debe aceptar la función desde el programa llamador para realizar la tarea designada?

2. ¿Qué datos, si los hay, deberán regresar la función al programa llamador para cumplir la

tarea designada? Programa llamador Encabezado Cuerpo de la de la función función [proceso] [interfaz]

Figura 10.1. El encabezado de la función forma la interfaz entre el programa llamador y la función

Acepta

Regresa

MIGUEL Á. TOLEDO MARTÍNEZ

FUNCIONES - LECCIÓN - 10 10-5

En general, el encabezado de la función consiste en las tres partes siguientes: •••• La clase de dato que regresará la función, si lo hay. •••• El nombre de la función. •••• Una lista de parámetros.

LA CLASE DE DATOS return Lo primero que aparece en el encabezado de la función es la clase de datos del valor regresado. Recuerde que la función puede regresar un solo valor que reemplace el nombre de la función en donde aparece el nombre en el programa llamador. El valor que reemplaza el nombre de la función en el programa llamador se conoce con el nombre de valor de retorno o de regreso. Cuando se usa una función para este propósito, la clase de datos del valor de regreso se deberá especificar en el encabezado de la función. Por ejemplo, suponga que nuestra función cubo() regresa el cubo de un entero. Debido a que el cubo de un entero es un entero, la función regresará un valor entero. Como resultado, la clase de datos del valor regresado deberá ser int y especificarse en el encabezado de la función, como sigue:

int cubo(<listaParámetros>)

De otra manera, si nuestra función cubo() fuera el cubo de un valor de punto flotante, la clase regresada deberá ser float y el encabezado deberá verse como sigue:

float cubo(<listaParámetros>)

Cuando una función no regresa ningún valor al programa llamador deberá usar la palabra reservada void como la clase de regreso, como se muestra a continuación:

void funcionMuestra(<listaParámetros>)

Las funciones que no regresan ningún valor al programa llamador se usan para realizar tareas específicas, como E/S. EL NOMBRE DE LA FUNCIÓN El nombre de la función puede ser cualquier identificador legal en C++. Sin embargo, el nombre de la función no deberá empezar con un subguión (guión bajo), porque algunos depuradores colocan un subguión frente al nombre de la función si se encuentra un error en la función. El nombre de la función describirá la operación que la función realiza, al igual que cubo() describe el cubo de un valor. Cuando invoque la función dentro de un programa llamador, usará este nombre. Cada función que cree dentro de su programa debe de tener un nombre único. Como ocurre con los nombres de variables, los nombres de las funciones deben corresponder con la tarea que realiza. Por ejemplo:

MIGUEL Á. TOLEDO MARTÍNEZ

FUNCIONES - LECCIÓN - 10 10-6

Nombre de la función Propósito de la función imprimirCalificacion Imprimir calificación de los alumnos. cuentasPorPagar Procesa las deudas de una compañía. pedirNombre Solicita el nombre del usuario. imprimirDocumento Imprime el archivo correspondiente al documento. calcularImpuesto Calcula el impuesto del cliente. Recuerde dos cosas:

1. El nombre de la función nunca se puede usar dentro de la función. En otras palabras, el siguiente enunciado dentro de la función cubo() generará un error:

cubo = x * x * x;

Hay una excepción a esta regla que se llama recursividad, la cual se explicará en la siguiente lección.

2. El identificador de la función nunca se usará del lado izquierdo del símbolo de asignación

fuera de la función. Por tanto, el siguiente enunciado causará un error en el programa llamador:

cubo = x * x * x;

LISTA DE PARÁMETROS La lista de parámetros de la función incluye variables, llamadas parámetros, que pasarán desde el programa llamador y serán evaluados por la función. Piense en un parámetro como una variable de la función que espera recibir un valor desde el programa llamador cuando se invoca la función. Para determinar los parámetros de la función, pregúntese ¿qué datos deberá aceptar la función para realizar su tarea? Suponga que nuestra función cubo() elevará al cubo valores enteros. La función deberá aceptar un valor entero desde el programa llamador y regresará un valor entero al programa llamador. De esta manera, nuestra interfaz de función se puede describir como sigue:

Función cubo(): Un entero al cubo. Acepta: Un valor entero. Regresa: Un valor entero.

Vamos a diseñar x como un objeto entero que aceptará la función. En C++, un parámetro dado deberá especificarse en el encabezado de la función indicando su clase de datos seguido por su identificador. Como resultado, la lista de parámetros apropiados para la función cubo() deberá ser (int x). Colocando todo junto, el encabezado de la función deberá ser:

int cubo(int x)

Clase regreso Parámetro de la función

Nombre de la función Sí la función cubo() elevara al cubo valores de punto flotante, el encabezado de la función apropiado deberá ser:

MIGUEL Á. TOLEDO MARTÍNEZ

FUNCIONES - LECCIÓN - 10 10-7

float cubo(float x)

Clase regreso Parámetro de la función

Nombre de la función Ejemplo 10.1

Suponga que quiere escribir una función definida por el usuario para calcular el voltaje en un circuito de cd usando la ley de Ohm. Escriba el encabezado de la función:

Para desarrollar el encabezado de la función, trataremos a la función como a una caja negra y nos haremos las siguientes preguntas: (1) ¿Qué dato deberá aceptar la función desde el programa llamador para realizar la tarea de aplicación? (2) ¿Qué deberá regresar al programador llamador? Las respuestas a estas preguntas dictarán el encabezado de la función o interfaz. Para contestar la primera pregunta, piense en qué deberá evaluar la función. Para calcular el voltaje usando la ley de Ohm, la función deberá evaluar dos cosas: corriente y resistencia. Así que utilicemos las palabras corriente y resistencia como nuestros parámetros. ¿De esta manera, la función deberá aceptar un valor de punto flotante de corriente y un valor de punto flotante de resistencia, Después, deberá decidir qué datos regresará la función al programa llamador. Debido a que la función evalúa valores de punto flotante, tiene sentido matemático que el valor regresado también deba ser un valor de punto flotante.

Ahora, la interfaz de función se puede describir como sigue:

Función voltaje(): Calcula el voltaje usando la ley de Ohm. Acepta: Un valor de punto flotante para corriente y un valor de punto

flotante para resistencia. Regresa: Un valor de punto flotante para voltaje.

Una vez que se ha decidido lo que acepta y lo que regresa la función, el encabezado de la función se construye fácilmente con la sintaxis de C++ como sigue:

float voltaje(float corriente, float resistencia)

SECCIÓN DE ENUNCIADO La sección de enunciado de la función incluye aquellas operaciones que la función deberá realizar para regresar un valor al programa llamador. Observe otra vez el formato general para la función definida por el usuario. Como puede observar, la sección de enunciado completa está enmarcada dentro de llaves. Después de abrir una llave, deberá empezar la sección de enunciado declarando cualquier objeto constante y definiendo objetos variables que se usarán dentro de la función. Cualquier objeto constante o variable listado aquí se llama local, porque se definen sólo para uso local dentro de la misma función. Las constantes y variables locales no tienen significado fuera de la función en la cual se definen. No duplique ninguno de sus parámetros de función aquí. Liste solamente constantes o variables adicionales que la función requiera durante su ejecución. Un ejemplo común de una variable local es un contador de ciclo que se emplea como parte de un ciclo while, do/while o for dentro de la función. En realidad, puede declarar constantes locales y definir variables locales en cualquier lugar dentro de la función siempre y cuando se listen antes de usarlas. Sin embargo, el buen estilo dicta que éstas sean declaradas o definidas al principio de la sección de enunciado de la función.

MIGUEL Á. TOLEDO MARTÍNEZ

FUNCIONES - LECCIÓN - 10 10-8

No confunda las variables locales con los parámetros de la función. Una variable local se define después de abrir la llave de una función para usarle dentro de ésta. Un parámetro de función se define en el encabezado de la función como un lugar que mantiene el espacio para los valores del argumento que pasan a la función cuando se llama a la misma. También se usará dentro de la función para suministrar datos desde el programa llamador a la función. Los enunciados ejecutables de la función siguen a cualquier enunciado o definición local. El último enunciado en una función sin void es el return. Este se usa cuando se regrese un solo valor al programa llamador. Así, si nuestra función cubo() regresa el cubo de x, un enunciado de regreso apropiado deberá ser:

return (x * x * x);

Combinando el encabezado de la función y la sección de enunciado para la función cubo(), dará la función completa como sigue:

int cubo(int x) { return(x * x * x); } // Fin de cubo()

Obviamente, ésta es una función relativamente sencilla que no requiere ningún enunciado de definición local o ejecutable que no sea el enunciado return. Ejemplo 10.2 Complete la función voltaje() cuyo encabezado se desarrolló en el ejemplo 10.1 Solución

La ley de Ohm requiere la función para multiplicar la corriente por la resistencia para obtener el voltaje. De esta manera, el único enunciado requerido en la función es un enunciado return que regresará el producto de corriente y resistencia. Colocando todo junto, la función completa se convierte en:

float voltaje(float corriente, float resistencia) { return (corriente * resistencia); } // Fin de voltaje()

Una fuente común de error durante la codificación de una función sin void es hacer la asignación al nombre de la función dentro de ésta, como sigue:

float voltaje(float corriente, float resistencia) { return voltaje = corriente * resistencia; } // Fin de voltaje()

Esto siempre causará un error de compilación, porque se intenta regresar el nombre de la función. Deberá regresar un valor que, en este caso, es el producto de corriente y resistencia.

Ejemplo 10.3

Escriba una función que regrese la suma de todos los enteros desde 1 a algún valor máximo entero, llamado maximo. La función deberá obtener el valor de maximo desde el programa llamador.

MIGUEL Á. TOLEDO MARTÍNEZ

FUNCIONES - LECCIÓN - 10 10-9

Solución

Vamos a llamar a esta función suma(). Ahora, la función deberá aceptar un valor entero llamado maximo desde el programa llamador. Debido a que la función es para sumar todos los enteros desde 1 hasta maximo, ésta regresará un valor entero. De esta manera nuestra interfaz de función se puede describir como sigue:

Función suma(): Suma todos los enteros desde 1

hasta maximo. Acepta: Un valor entero: maximo. Regresa: Un valor entero.

Utilizando esta información, el encabezado de la función se convierte en:

int suma(int maximo)

El siguiente paso es determinar si hay alguna variable local requerida por los enunciados de la función. Se puede usar un ciclo for para calcular la suma de enteros desde 1 hasta maximo. Sin embargo, el enunciado for requiere una variable contador. Esta es una aplicación clásica para una variable local. Vamos a llamar a esta variable utilizada como contador local contador. Después, también necesita una variable temporal dentro del ciclo for para mantener un subtotal de la suma cada vez que el ciclo se ejecuta. Denominaremos a esta variable local subTotal. De acuerdo con lo anterior, la función es:

int suma(int maximo) {

int subTotal = 0; for(int contador = 1; contador <= maximo; ++ contador)

subTotal = subTotal + contador; return subTotal;

} // Fin de suma()

LLAMADA A LAS FUNCIONES sin void Se llama o invoca a una función sin void en cualquier parte de su programa de la misma manera que llama las funciones estándar en C++. Por ejemplo, puede llamar una función usando un operador de asignación o un enunciado cout como el siguiente:

y = cubo(2); o

cout << cubo(2);

En ambos casos, el valor 2 se pasa a la función que se va a elevar al cubo. De esta manera, en nuestra función cubo(), el parámetro x toma el valor 2. Y la función regresará el cubo de 2, que es 8. Con el enunciado de asignación, la variable y asignará el valor 8, y el enunciado cout hará que el valor 8 se muestre en pantalla. Aquí están otras dos formas en que nuestra función cubo() se puede llamar:

int a = 2; y = cubo(a);

o

cout << cubo(a);

MIGUEL Á. TOLEDO MARTÍNEZ

FUNCIONES - LECCIÓN - 10 10-10

En estos casos, la función es la variable a elevada al cubo, donde a a se le ha asignado previamente el valor 2. De esta manera el valor de a o 2, pasa a la función. En nuestra función cubo() el parámetro x toma el valor de a.

Las funciones también pueden ser llamadas como parte de expresiones aritméticas o enunciados relacionales. Por ejemplo, nuestra función cubo() puede ser llamada como parte de una expresión aritmética, como ésta:

int a = 1; y = 1 + cubo(a) * 2;

¿Qué se asignará a y? Bien, primero C++ evalúe la función cubo() para obtener 8,

después realiza la operación de multiplicación para obtener 16 y por último suma 1 a 16 para obtener 17.

También puede usar funciones como parte de operaciones relacionales, como ésta:

if(cubo(a) >= 27) ¿Cuándo será verdadera la relación? Cuando a sea mayor o igual a 3, ¿correcto? Cuando

a es mayor o igual a 3, el cubo(a) es mayor o igual a 27. Recuerde que cuando una función se diseña para regresar un solo valor al programa llamador, el valor regresado reemplaza el nombre de la función donde quiera que se use el nombre en el programa llamador.

Generalmente se llamarán las funciones sin void dentro de su programa usando un

enunciado de asignación, un objeto cout o como parte de una operación aritmética. Recuerde pensar en la llamada de función como un valor. Esto es, un valor reemplaza la llamada de función donde ésta aparece en el programa. Pregúntese: ¿Tiene sentido un valor aquí? Por ejemplo, todos los siguientes enunciados tienen sentido, porque un valor puede sustituirse fácilmente por la llamada de función:

resultado = cubo(a): cout << cubo(a); solucion = x * cubo(a) + 5;

Por otra parte, el siguiente enunciado no tendrá sentido y causará un error de

compilación.

cubo(a);

ARGUMENTOS REALES EN COMPARACIÓN CON PARÁMETROS FORMALES En el ejemplo anterior del cubo(), la variable a que se usa en el programa llamador se llama argumento real. Por otra parte, la variable correspondiente x que se usa en el encabezado de la función se llama parámetro formal. Los argumentos reales son valores o variables que se utilizan dentro de la llamada de la función y los parámetros formales son variables que se utilizan dentro del encabezado de la función y que reciben valores del argumento real. De esta manera, decimos que el parámetro formal en nuestra función cubo(), x, toma el valor del argumento real a, utilizado en la llamada de función. Estas son algunas cosas que querrá recordar de los argumentos reales y los parámetros formales:

MIGUEL Á. TOLEDO MARTÍNEZ

FUNCIONES - LECCIÓN - 10 10-11

•••• Las variables de argumento real se deberán definir en el programa llamador. Estará en la función

main(), a menos que las funciones sean llamadas por otras funciones. •••• La clase de datos de los argumentos reales correspondientes y los parámetros formales deberán ser

los mismos. •••• Los parámetros formales mantienen el espacio para los valores del argumento real durante la

ejecución de la función. Los parámetros formales siempre se listan en la sección de parámetros del encabezado de la función.

•••• El número de argumentos reales usado durante la llamada de función deberá ser el mismo que el número de parámetros formales listados en el encabezado de la función, excepto cuando se usan parámetros predeterminados. Los parámetros predeterminados se explicarán posteriormente.

•••• La correspondencia entre argumentos reales y parámetros formales se establece sobre las bases de uno a uno de acuerdo con el orden de la lista respectiva.

•••• Aunque las variables del argumento real y del parámetro formal a menudo tienen diferentes nombres de variable, pueden ser las mismas. Cuando éste es el caso, los objetos variables respectivos deberán definirse en el programa llamador y también tendrán que aparecer en la lista de parámetros de la función.

RETORNO DE UN RESULTADO AL PROGRAMA QUE INVOCA A LA FUNCIÓN En algunas ocasiones la función ejecutará cierto tipo de cálculos. La función posteriormente devolverá este valor a quién la llama. Cuando una función regresa un valor, debe indicarle a C++ la clase del valor que devuelve, tal como int, float, char y así sucesivamente. Para informarle a C++ de la clase de retorno de la función, simplemente preceda al nombre de la función con la clase correspondiente. Por ejemplo, la siguiente función sumaValores suma dos números enteros y regresa un resultado entero. Ejemplo 10.4

El siguiente programa SUMAVAL.CPP, utiliza la función sumaValores, para sumar valores diferentes:

/* El siguiente programa: SUMAVAL.CPP, llama a una función llamada sumaValores, la cual calcula la suma de dos números enteros. */ #include <iostream.h> //Para cout int sumaValores(int a, int b) { return (a+b); } //Fin de sumaValores() void main(void) { cout << "100 + 200 = " << sumaValores(100, 200) << endl; cout << "500 + 501 = " << sumaValores(500, 501) << endl; cout << "-1 + 1 = " << sumaValores(-1, 1) << endl; } //Fin de main()

EXAMEN BREVE 27

MIGUEL Á. TOLEDO MARTÍNEZ

FUNCIONES - LECCIÓN - 10 10-12

FUNCIONES void Las funciones que no regresan un solo valor al programa llamador a menudo se escriben para realizar alguna tarea específica. Estas se llaman funciones void. Cuando una función no regresa un solo valor al programa llamador, se deberá usar la palabra reservada void como el regreso de la clase de datos. Además, estas funciones pueden o no requerir parámetros. Cuando no se requieren parámetros, simplemente deje la lista de parámetros en blanco para indicar al compilador que la función no necesita recibir ningún valor desde el programa llamador. Las funciones que no regresen un valor o no requieran ningún parámetro son del tipo de funciones más simples en C++. Por ejemplo, suponga que quiere escribir una función que mostrará el siguiente encabezado en la pantalla cada vez que se le llama:

NOMBRE CALLE DIRECCIÓN ESTADO CIUDAD CÓDIGO POSTAL Vamos a llamar a esta función muestraEncabezado() Para desarrollar el encabezado de la función, pregúntese que deberá aceptar la función para realizar la tarea diseñada y que deberá regresar. En este caso, la función simplemente muestra la información constante de la línea de encabezado y no necesita aceptar ni regresar ningún dato. De esta manera, nuestra interfaz de función puede escribirse como sigue:

Función muestraEncabezado(): Muestra la información fija del encabezado. Acepta: Nada. Regresa: Nada.

Usando esta información, el encabezado de la función se convierte en:

void muestraEncabezado(void)

Observe el encabezado de la función y verá que se usa la palabra void como la clase de regreso. La palabra reservada void se emplea aquí para indicar al compilador que no hay valor de regreso. También, note que no hay parámetros requeridos para esta función, porque la lista de parámetros también contiene void o se deja en blanco. En otras palabras, la función no regresa un valor y no requiere ningún argumento para evaluar. Simplemente realiza una tarea determinada, en este caso mostrando un encabezado. Para mostrar el encabezado, lo que necesita es un enunciado cout en el cuerpo de la función. Colocando todo junto, la función se convierte en:

void muestraEncabezado(void) {

cout << “\tNOMBRE\tCALLE\tDIRECCION\tESTADO\tCIUDAD\tCODIGO POSTAL” “\n\t______\t_____\t_________\t______\t______\t______ ______” << endl;

} // Fin de muestraEncabezado()

Por último, no verá un enunciado return al final de la función, porque la función no regresa ningún valor. ¿Cómo puede llamar a esta función en su programa? Simple, sólo use el nombre de la función como un enunciado dentro del programa llamador cada vez que el encabezado se muestre, por ejemplo:

muestraEncabezado();

MIGUEL Á. TOLEDO MARTÍNEZ

FUNCIONES - LECCIÓN - 10 10-13

Ningún argumento real se lista en la llamada de la función, porque la función no evalúa ningún argumento.

Cuando llame una función void, simplemente liste el nombre de la función y los

argumentos requeridos como un enunciado sencillo dentro de su programa. No llame una función void con un operador de asignación u objeto cout como lo hace para las funciones sin void. La siguiente llamada muestraEncabezado() deberá causar un error de compilación.

encabezado = muestraEncabezado(); // Error cout << muestraEncabezado(); // Error

La llamada correcta es simplemente:

muestraEncabezado();

PARÁMETROS DE VALOR EN COMPARACIÓN CON PARÁMETROS DE REFERENCIA

La función anterior muestraEncabezado() no requiere ninguna lista de parámetros formal, porque para evaluar no necesita recibir ningún argumento desde el programa llamador. Cuando se requieren evaluar parámetros formales por la función, éstos deberán listarse en el encabezado de la función en una de dos formas: como parámetros de valor o como parámetros de referencia. PARÁMETROS DE VALOR Hasta el momento, se han usado parámetros de valor en esta lección. Los parámetros de valor permiten una comunicación de datos en un sentido desde el programa llamador a la función. Una primera aproximación de este concepto se muestra en la figura 10.2a. Programa llamador Función

Figura 10.2a. Paso de parámetros mediante un valor Observe que los valores de los argumentos reales en el programa llamador se pasan (por medio de un valor) a parámetros formales en la función. Otra forma de pensar con respecto a esto es que el parámetro formal recibe una copia del valor del argumento real. Cuando la función opera sobre un parámetro de valor, está operando en una copia, más que en el valor original en el programa llamador. De esta manera, el valor del argumento real en el programa llamador se protege de un cambio accidental por la función. Algo que se debe recordar cuando se usan parámetros de valor es que cualquier manejo de los parámetros formales dentro de la función no afecta el valor del argumento real usado por la llamada de la función. Por ejemplo, considere la siguiente función:

Parámetros formales

Argumentos reales “Copia”

MIGUEL Á. TOLEDO MARTÍNEZ

FUNCIONES - LECCIÓN - 10 10-14

void pasaValor(int x, int y); {

// Incrementa y decrementa parámetros formales ++x; --y; // Muestra los valores del parámetro formal cout << “x = ” << x << endl; cout << “y = ” << y << endl;

} // Fin de pasaValor()

Aquí, los parámetros x y y son parámetros de valor. Observe que, dentro de la función, el valor de x se incrementa y el de y se decrementa. Así, los valores resultantes se muestran usando un enunciado cout. Ahora, suponga que la función anterior se llama por medio del programa siguiente:

void main(void)

{ // Se definen las variables de argumento real int a = 0; int b = 0; // Se llama a la función pasaValor(a, b); // Muestra los valores de argumento real cout << “a = ” << a << endl; cout << “b = ” << b << endl;

} // Fin del main()

Primero, observe cómo se hace el llamado a la función. Es simplemente un enunciado en el programa llamador. El nombre de la función se lista seguido por los argumentos reales requeridos dentro de paréntesis. Los argumentos reales son a y b, porque éstos se listan en la llamada de la función. Cuando se ejecuta la llamada de la función, el valor de a pasa al parámetro de valor x y el de b pasa al de y. Otra forma de decir esto es que x recibe una copia de a y y recibe una copia de b. Observe que antes de la llamada de la función, el programa llamador inicializa ambos valores a y b a 0. Como resultado, x y y reciben el valor de 0 desde el programa llamador. La función después incrementa el valor de x, decrementa el valor de y y muestra los nuevos valores de x y y. Sin embargo, las operaciones sobre x y y no tienen efecto en los argumentos reales (a y b) en el programa llamador. Los valores de a y b permanecen en 0. Observe que después de la llamada de la función, el programa llamador muestra los valores de a y b. ¿Qué se verá en la pantalla al ejecutar el programa? Bien, la función pasaValor() muestra x y y, después main() muestra a y b. De esta manera, el resultado que se exhibe es:

x = 1 y = -1 a = 0 b = 0

A continuación veamos algunos ejemplos en los que se muestren los conceptos antes aprendido. Ejemplo 10.5 El siguiente programa, MSTRMSJ.CPP utiliza una función para enviar un mensaje:

MIGUEL Á. TOLEDO MARTÍNEZ

FUNCIONES - LECCIÓN - 10 10-15

Ejemplo 10.6 El siguiente programa, DOSMSJ.CPP, utiliza las funciones muestraTitulo() y muestraLeccion() para visualizar información acerca de las lecciones:

Para incrementar la capacidad de las funciones, C++ le permite a sus programas pasar información (argumentos) a sus funciones. Cuando una función utiliza parámetros, debe indicarle a C++ la clase de cada uno de los parámetros, tales como int, float, char y así sucesivamente.

/* El siguiente programa: MSTRMSJ.CPP, crea una función muestraMensaje(), para visualizar un mensaje. */ #include <iostream.h> //Para cout void muestraMensaje(void) { cout << "Hola, he sido rescatado por C++" << endl; } //Fin de muestraMensaje() void main(void) { cout << "Llamado a una función" << endl << endl; muestraMensaje(); cout << endl << "Regreso del llamado a la función" << endl; } //Fin de main()

/* El siguiente programa: DOSMSJ.CPP, utiliza dos funciones para enviar cada una un mensaje. */ #include <iostream.h> //Para cout void muestraTitulo(void) { cout << "APUNTES: Apuntes de COMPUTACION I" << endl; } //Fin de muestraTitulo() void muestraLeccion(void) { cout << "LECCION: Conocimientos básicos sobre funciones" << endl; } //Fin de muestraLeccion() void main(void) { muestraTitulo(); muestraLeccion(); } //Fin de main()

MIGUEL Á. TOLEDO MARTÍNEZ

FUNCIONES - LECCIÓN - 10 10-16

Ejemplo 10.7

El siguiente programa: USOPAR.CPP, utiliza la función mostrarNumero, varias veces, en cada ocasión se le pasa un valor diferente.

En la mayoría de los casos, sus programas pueden pasar varios valores a la función. Para cada valor pasado, su función debe declarar su nombre y clase. Ejemplo 10.8

El siguiente programa: GDEPEQ.CPP, utiliza la función mostrarGdeYPeq para visualizar el número más grande y el más pequeño de tres enteros que recibe.

/* El siguiente programa: USOPAR.CPP, elabora una función que muestra los diferentes parámetros que le son enviados. */ #include <iostream.h> //Para cout void mostrarNumero(int valor) { cout << "El valor del parámetros es: " << valor << endl; } //Fin de mostrarNumero() void main(void) { mostrarNumero(1); mostrarNumero(1001); mostrarNumero(-532); } //Fin de main()

/* El siguiente programa: GDEPEQ.CPP, crea una función que determina y muestra cual es el mayor y el menor de tres enteros que le son enviados. */ #include <iostream.h> //Para cout void mostrarGdeYPeq (int a, int b, int c) { int pequeno = a; int grande = a; if (b > grande) grande = b; if (b < pequeno) pequeno = b; if (c > grande) grande = c; if (c < pequeno) pequeno = c; cout << "El valor más grande es: " << grande << endl; cout << "El valor más pequeño es: " << pequeno << endl; } //Fin de mostrarGdeYPeq()

MIGUEL Á. TOLEDO MARTÍNEZ

FUNCIONES - LECCIÓN - 10 10-17

Ejemplo 10.9

El siguiente programa: MSTEMP.CPP, utiliza la función mostrarEmpleado para visualizar la edad de un empleado (clase int) y su salario (clase float)

PARÁMETROS DE REFERENCIA Los parámetros de referencia, algunas veces llamados parámetros de variable, difiere de los de valor en que éstos proporcionan una combinación en dos sentidos entre el programa llamador y la función, como se ilustra en la figura 10.2b. Programa llamador Función

Figura 10.2b. Paso de parámetros mediante referencia

void main (void) { mostrarGdeYPeq (1, 2, 3); cout << endl; mostrarGdeYPeq (500, 0, -500); cout << endl; mostrarGdeYPeq (1001, 1001, 1001); } //Fin de main()

/* El siguiente programa: MSTEMP.CPP, muestra una función que visualiza la edad, y el salario de un empleado. */ #include <iostream.h> //Para cout void mostrarEmpleado(int edad, float salario) { cout << "El empleado tiene " << edad << " años" << endl; cout << "El empleado gana $" << salario << endl; } //Fin de mostrarEmpleado() void main(void) { mostrarEmpleado(32, 25000.00); } //Fin de main()

Parámetros formales

Argumentos reales “Dirección”

MIGUEL Á. TOLEDO MARTÍNEZ

FUNCIONES - LECCIÓN - 10 10-18

Un parámetro de referencia proporciona una comunicación de ida y vuelta de datos entre el programa llamador y la función. Observe el camino de comunicación de ida y vuelta: los valores de los argumento reales pasan a los parámetros formales en la función y después los valores de parámetros formales regresan a los argumentos reales. Esto permite a la función cambiar los valores de argumento real en el programa llamador. Recuerde que un parámetro de valor es simplemente una copia del valor del argumento real; por lo tanto, cualquier operación sobre el parámetro dentro de la función no tiene efecto en el valor del argumento original. De otra manera, un parámetro de referencia representa la dirección del valor de argumento real en la memoria. Como resultado, cualquier cambio que se haga al parámetro de referencia dentro de la función cambiará lo que está almacenado en esa dirección. Esto obviamente modifica el valor original del argumento real en el programa llamador. Para crear un parámetro de referencia, simplemente inserte un ampersand (&) antes de los identificadores de parámetro apropiados en el encabezado de la función. Vamos a cambiar nuestro ejemplo pasaValor() para usar parámetros de referencia, como sigue:

void pasaReferencia(int &x, int &y);

{ // Incrementa y decrementa parámetros formales ++x; --y; // Muestra los valores del parámetro formal cout << “x = ” << x << endl; cout << “y = ” << y << endl;

} // Fin de pasaReferencia()

El cambio principal aquí es insertar un ampersand antes de x y y en el encabezado de la función. Seguro, el nombre de la función también se ha cambiado para reflejar la nueva aplicación. ¿Qué deberá ver en la pantalla como resultado de la ejecución del siguiente programa?

void main(void)

{ // Se definen las variables de argumento real int a = 0; int b = 0; // Se llama a la función pasaReferencia(a, b); // Muestra los valores de argumento real cout << “a = ” << a << endl; cout << “b = ” << b << endl;

} // Fin del main()

Debido a que x y y son ahora parámetros de referencia y no parámetros de valor, cualquier operación que afecta a x y y dentro de la función también afectará los valores de los argumentos reales a y b que se utilizan en la llamada de la función. Estos son los valores que muestra el programa:

x = 1 y = -1 a = 1 b = -1

MIGUEL Á. TOLEDO MARTÍNEZ

FUNCIONES - LECCIÓN - 10 10-19

Como puede ver, los nuevos valores de x y y pasan de regreso a a y b respectivamente. Usamos la expresión pasan de regreso para describir la acción. Sin embargo, recuerde que en realidad nada pasa de regreso, porque la función está simplemente operando sobre las direcciones de los argumentos reales a y b.

Ejemplo 10.10

¿Qué se mostrará como resultado del siguiente programa? void main(void)

{ // Define variables de argumento real int a = 0; int b = 0; // Llama a la función muestraParametros(a,b); // Muestra los valores de argumento real cout << “a = ” << a << endl; cout << “b = ” << b << endl;

} // Fin de main() /********************************************************** Esta función muestra el uso de valores en comparación con parámetros de referencia. **********************************************************/

void muestraParametros(int &x, int y) {

// Incremente y decrementa parámetros formales ++x; --y; //Muestra valores de parámetro formal cout << “x = ” << x << endl; cout << “y = ” << y << endl;

} // Fin de muestraParametro()

Solución

Aquí se ve un programa completo que incorpora una función. La función está localizada inmediatamente después de la llave que encierra la función main() La función se llama entonces dentro de la sección de enunciado main() simplemente listando su nombre seguido por una relación de los argumentos reales requeridos. Observe que los argumentos reales (a y b) se definen como objetos enteros en main() Ahora vea el encabezado de la función. Los parámetros formales son x y y. Ambos son enteros; sin embargo, x es un parámetro de referencia mientras que y es un parámetro de valor. Observe el uso de ampersand antes de x. Esto define x como un parámetro de referencia. Sigue una coma después de x finalizando esta definición. Después y se define por separado como un parámetro de valor (sin ampersand) Como resultado, el valor de x pasa de regreso a main() pero no el valor de y. Esto es lo que se verá en la pantalla:

x = 1 y = -1 a = 1 b = 0

MIGUEL Á. TOLEDO MARTÍNEZ

FUNCIONES - LECCIÓN - 10 10-20

Ejemplo 10.11

Escriba una función llamada intercambio() que acepte dos objetos enteros desde el programa llamador y regrese los objetos con sus valores intercambiados.

Solución

Esta es una aplicación ideal para parámetros de referencia porque, para intercambiar los valores, el proceso de intercambio dentro de la función deberá tener un efecto sobre los valores original en el programa llamador. Así, la función deberá aceptar dos objetos enteros y regresar los mismos dos objetos con los valores intercambiados. A continuación se muestra una descripción de la interfaz de la función:

Función intercambio(): Intercambia los valores de dos objetos enteros. Acepta: Dos objetos enteros. Regresa: Los mismos dos objetos enteros. Debido a que la función deberá regresar las mismas dos variables enteras que los que acepta, ambos serán parámetros de referencia. Vamos a etiquetar los parámetros como objeto1 y objeto2. El encabezado de la función entonces se convierte en:

void intercambio(int &objeto1, int &objeto2)

Observe el uso de ampersand para indicar que los parámetros son parámetros de referencia. Debido a que objeto1 y objeto2 son parámetros de referencia, se intercambiarán los valores de los objetos de argumento real usados en la llamada de la función. Ahora, para intercambiar los dos valores dentro de la función, deberá crear un objeto variable local temporal para que no se pierda uno de los valores. De acuerdo con esta idea, la función completa es:

void intercambio(int &objeto1, int &objeto2)

{ // Define como local la variable temporal int temp; // Intercambia los valores temp = objeto1; objeto1 = objeto2; objeto2 = temp;

} // Fin de intercambio()

Para llamar a esta función en un programa, simplemente liste el nombre de la función y proporcione dos objetos variables para intercambiar, por ejemplo:

intercambio(a, b)

Por supuesto, a y b deberán definirse e inicializarse con valores dentro del programa llamador, en algún lugar antes de esta llamada de la función.

LOCALIZACIÓN DE LAS FUNCIONES DENTRO DE SU PROGRAMA Antes de que su programa pueda llamar a una función, C++ debe saber la clase de valor que la función regresa y el número y clase de parámetros que la función utiliza. En los ejemplos antes presentados las definiciones de las funciones preceden a las llamadas que se hacen a las mismas.

Sin embargo, en muchos casos, las funciones pueden aparecer en diversos lugares de su programa fuente, por ejemplo, después de cerrar la llave de la función main() No hay límite en el

MIGUEL Á. TOLEDO MARTÍNEZ

FUNCIONES - LECCIÓN - 10 10-21

número de funciones definidas por el usuario que se puedan usar en un programa. Para llamar una función que regresa un valor, deberá insertar el nombre de la función donde se quiera que regrese el valor. Para llamar una función void, simplemente liste su nombre como un enunciado dentro del programa llamador. En ambos casos, los argumentos reales requeridos por la función deberá listarse dentro de paréntesis después del nombre de la función cuando ésta se llama. Además, el número de argumentos reales que se utilizan en la llamada de función deberá ser el mismo que el número de parámetros formales definidos en el encabezado de la función respectiva, a menos que se usen parámetros predeterminados.

La colocación de las funciones en un programa C++ se resume en la figura 10.3.

Observe la estructura en bloques de un programa general. La función main() forma el bloque externo general del programa y las funciones definidas por el usuario forman los bloques internos que se anidan dentro de la función main() por medio de las llamadas de las funciones. Esta es la razón de que C++ sea conocido como lenguaje estructurado en bloques. De ahora en adelante, cuando desarrollemos programas C++, intentaremos dividir el problema de programación general en un grupo de subproblemas más sencillos cuya solución combinada soluciona el problema original. ¿Cómo se codificarán estos subproblemas? Ya sabe, ¡Cómo funciones! Esta es la esencia de la programación estructurada y diseño descendente de software.

Figura 10.3. Las funciones por lo general se colocan después de main() en un programa C++

prototipo funcion1(); prototipo funcion2(); void main(void)

{ Llamada a la funcion1(); Llamada a la funcion2();

} // Fin de main()

funcion1() {

enunciado1; enunciado2; ...................; enunciadoN;

} // Fin de la funcion1()

funcion2() {

enunciado1; enunciado2; ...................; enunciadoN;

} // Fin de la funcion2()

EXAMEN BREVE 28

MIGUEL Á. TOLEDO MARTÍNEZ

FUNCIONES - LECCIÓN - 10 10-22

PROTOTIPO DE FUNCIONES Para asegurarse de que C++ conoce las características de cada función que utiliza su

programa, debe colocar prototipos de las funciones al principio de su programa fuente, algunas veces, se conoce como un enunciado de función; es un modelo de interfaz para la función que usa el compilador para verificar el número apropiado de argumentos y las clases de datos de los argumentos en las llamadas de la función.

Los prototipos fuerzan al compilador a realizar verificaciones adicionales a sus llamadas

de función, de esta manera ayudan en la detección de errores de programación asociados con llamadas de función. Por ejemplo, si una función espera recibir un valor entero y el programador trata de pasar una cadena de caracteres, el compilador puede detectar el error porque C++ requiere que la función prototipo se especifique antes de la llamada de la función. Dado que los prototipos obligan al compilador a verificar errores durante la compilación, esto no afecta el tamaño ni la velocidad del tiempo de ejecución del programa. Aunque tome al compilador ligeramente más tiempo en realizar esta tarea de verificación de errores, cualquier error detectado por medio de los prototipos puede ahorrar horas de depuración, que serían necesarias si no se emplearan los prototipos. Por estas razones, el lenguaje C++ requiere las funciones prototipo. Sin embargo, conviene saber que la función prototipo es opcional en el lenguaje C.

En la definición anterior de prototipo, se infiere que éste proporciona un modelo de

interfaz para la función. Bien, la interfaz de la función es el encabezado de la función; por lo tanto, el prototipo de la función es sólo una copia del encabezado de la función que utiliza el compilador para verificar las llamadas a la función. Así el prototipo dicta las clases de datos que aceptará la función desde el programa llamador y las que regresará la función al programa llamador.

Los prototipos de función se localizan antes de la función main(). El prototipo de

función puede ser sólo una copia del encabezado de la función seguida por un punto y como, como lo que sigue:

void estudiante(int numero, float promedio, char calificacion);

Aquí, el prototipo le dice al compilador que la función estudiante() no regresará un valor

al programa llamador. Además, estudiante() espera recibir tres parámetros cuando se le llama. El primer parámetro se interpretará como un entero, el segundo como un valor de punto flotante y el tercero como un carácter. Si se hace un llamado de la función con más o menos el número de parámetros listados en el prototipo, el compilador generará un error, a menos que se especifiquen parámetros predeterminados. Si se llama a la función con parámetros que pertenecen a clases de datos diferentes que las listadas en el prototipo, los parámetros se tratarán como si éstos fueran la lista de clases de datos respectivos.

En general un prototipo de función, contiene información acerca del tipo de retorno y de

sus parámetros. Los siguientes enunciados ilustran prototipo de función para varias de las funciones ya utilizadas:

MIGUEL Á. TOLEDO MARTÍNEZ

FUNCIONES - LECCIÓN - 10 10-23

void muestraMensaje(void); void mostrarNumero(int); void mostrarEmpleado(int, float); int sumaValores(int, int); float valorPromedio(int, int);

Ejemplo 10.12

El siguiente programa: ESTUDIANTE.CPP, utiliza la función estudiante() cuyo prototipo ya se mostró:

La salida generada por este programa es:

Hay 5 exámenes, que dan como resultado un promedio de 85.6 y una calificación alfabética de B.

Cómo puede observar, los argumentos reales en la llamada de la función pasaron a estudiante() y se utilizaron para construir el enunciado cout. Si se han utilizado más o menos parámetros en la llamada de la función, el resultado será un error de compilación. Pero, ¿qué pasaría si las clases de datos del argumento no fueran las mismas que las listadas en el prototipo? Considere la siguiente llamada a la función:

estudiante(‘B’, ‘A’, 67);

Esta llamada no deberá producir un error de compilación, porque el número de parámetros es correcto. Sin embargo, el compilador interpretará el primer parámetro como un entero, el segundo como un valor de punto flotante y el tercero como un valor carácter. El resultado que genera la llamada de función será:

Hay 66 exámenes, que dan como resultado un promedio de 65.0 y una calificación alfabética de C.

¿Advierte lo que sucede? La función usó el equivalente entero del carácter ‘B’ para el primer parámetro, el equivalente a punto flotante del carácter ‘A’ para el segundo parámetro y el equivalente del carácter del

/* El siguiente programa: ESTUDIANTE.CPP, ilustra una función que muestra el número de exámenes realizados, el promedio y la calificación alfabética del estudiante. */ #include <iostream.h> //Para cout // Prototipo de la función void estudiante(int numeroExamenes, float promedio, char calificacion); void main(void) { estudiante(5, 85.6, 'B'); } // Fin del main() /*********************************************************************************** Esta función mostrará un promedio del estudiante y su calificación. ***********************************************************************************/ void estudiante(int numeroExamenes, float promedio, char calificacion) { cout.setf(ios::fixed); cout.precision(1); cout << "Hay " << numeroExamenes << " exámenes, que dan como resultado " << "un promedio de " << promedio << endl; cout << "y una calificación alfabética de " << calificacion << '.' << endl; } // Fin de estudiante()

MIGUEL Á. TOLEDO MARTÍNEZ

FUNCIONES - LECCIÓN - 10 10-24

entero 67 para el tercer parámetro. Desde luego, estas equivalencias se derivan del código de caracteres ASCII. Así, la lección aquí es asegurarse que la clase de datos de los argumentos coincida con las clases de datos de los parámetros formales, de lo contrario obtendrá resultados impredecibles. Observe nuevamente el prototipo de función estudiante() y verá que sólo es una copia del encabezado de la función. Debido a esto, los prototipos son fácilmente codificados en su programa usando la característica de copia de bloque de su editor. Una vez que ha codificado una función, simplemente marque el encabezado de la función y cópiela al área prototipo justo antes de la función main() No olvide añadir un punto y coma al final del prototipo, porque el encabezado de la función copiado no la tiene. También puede listar sus prototipo de funciones sin ningún identificador de parámetros, como el siguiente:

void estudiante(int, float, char)

Después de todo, al compilador no le interesan los nombres de los parámetros, sólo el número de parámetros y sus clases de datos. Un punto final: si olvida incluir un prototipo para una función obtendrá un error familiar: prototype expected cuando compile su programa. También obtendrá este error si olvida incluir un archivo de encabezado para una función estándar en su programa debido a que los prototipos para las funciones estándar en C++ están incluidos en los archivos de encabezado.

Ejemplo 10.13

El siguiente programa, PROTO.CPP, ilustra el uso de prototipo de función.

Como hemos dicho una de las características de los prototipos de función es forzar a que

los argumentos sean del tipo adecuado. Estas conversiones pueden producir resultados incorrectos si no se siguen las reglas de asignación de tipos de C++. Dichas reglas especifican la manera de convertir un tipo de datos a otro sin perder información. Las reglas de asignación se aplican a expresiones que contienen valores de dos o más clases de datos; tales expresiones también se conocen como expresiones de tipo mixto. El tipo de cada valor de una expresión de tipo mixto se promueve al tipo más alto de la expresión (de hecho, se crea una versión temporal de cada valor que se utiliza para la expresión; los valores originales permanecen sin cambio) Otro uso común de la promoción es cuando el tipo de un argumento para una función no es igual al tipo de parámetro especificado en la definición de la función. En la tabla 10.1 se listan en los tipos de datos integrados, desde el tipo mas alto al tipo más bajo.

/* El siguiente programa: PROTO.CPP, ilustra el uso de prototipos */ #include <iostream.h> //Para cout float valorPromedio(int, int); // Prototipo función void main(void) { cout << "El promedio de 2000 y 2 es " << valorPromedio(2000, 2) << endl; } //Fin de main() float valorPromedio(int a, int b) { return ((a + b) / 2.0); } //Fin de valorPromedio()

MIGUEL Á. TOLEDO MARTÍNEZ

FUNCIONES - LECCIÓN - 10 10-25

La conversión de valores hacia tipos menores puede dar como resultado valores incorrectos. Por lo tanto, sólo puede convertirse un valor de un tipo menor asignando específicamente dicho valor a una variable de menor tipo o empleando un operador de conversión mediante cast. Los valores de los argumentos de las funciones se convierten a los tipos de parámetros de un prototipo de función como si se estuvieran asignando directamente a variables dichos tipos.

Clase de datos

long double double float unsigned long int (sinónimo de unsigned long) long int (sinónimo de long) unsigned int (sinónimo de unsigned) Int unsigned short int (sinónimo de unsigned short) short int (sinónimo de short) unsigned char Short Char

Tabla 10.1. Jerarquía de promociones de las clases de datos estándar de C++ SOLUCIÓN DE PROBLEMAS EN ACCIÓN: Programación estructurada LEY DE OHM PROBLEMA

En lecciones anteriores, hemos desarrollado un programa de la ley de Ohm que maneja menús, controlado por un ciclo. Sin embargo, aunque desarrollamos un diseño estructurado, empleamos una implementación lineal para codificar el diseño. Es tiempo de hacer lo correcto y emplear programación estructurada por medio de las funciones para instrumentar el diseño estructurado. Esta es la definición del problema que desarrollamos anteriormente:

DEFINICIÓN DEL PROBLEMA

Salida: El menú que pida al usuario seleccionar una opción de cálculo de

voltaje, corriente o resistencia. Se necesita un mensaje para entradas no válidas. Un valor de voltaje, corriente o resistencia, dependiendo de la opción del

programa que el usuario seleccione. Entrada: Una respuesta del usuario al menú (V, C, R o S) Si selecciona V: el usuario escribe los valores para corriente y

resistencia. Si selecciona C: el usuario escribe los valores para voltaje y resistencia. Si selecciona R: el usuario escribe los valores para voltaje y corriente. Si selecciona S: el programa termina. Procesamiento: Calcula la opción seleccionada. Case V: Voltaje = Corriente x Resistencia. Case C: Corriente = Voltaje / Resistencia. Case R: Resistencia = Voltaje / Corriente. Case S: Termina el programa. El menú se repite hasta que el usuario decide terminar el programa

MIGUEL Á. TOLEDO MARTÍNEZ

FUNCIONES - LECCIÓN - 10 10-26

PLANEACIÓN DE LA SOLUCIÓN

Con el uso del diseño de programación estructurada, dividiremos el problema en subproblemas individuales para solucionar el problema general. Hay dos tareas importantes que surgen directamente a partir de la definición del problema:

•••• Mostrar el menú y leer la opción del usuario. •••• Realizar los cálculos seleccionados y mostrar los resultados.

Primero, el programa deberá mostrar un menú de selecciones al usuario. El usuario escribirá su elección desde el menú y dependiendo de su opción, se hará uno de los tres cálculos para determinar la cantidad solicitada. El diagrama de estructura en la figura 10.4 muestra el diseño descendente.

opción

opción

Figura 10.4. Diagrama de estructura para el problema de la ley de Ohm

Observe que hay ahora tres niveles de solución del problema. En el primer nivel ohm(), llama a una función para mostrar el menú y obtener la selección del usuario. La función muestraMenu() realiza esta tarea y envía la selección del usuario de regreso a main() como se indica en el diagrama. La función ohm () enviará la selección a la función llamada leyDeOhm(), que llamará una de las tres funciones, dependiendo de la selección, para realizar los cálculos requeridos. Ahora, la descripción del proceso requiere que agreguemos al programa una característica de control del ciclo. Aquí se muestra el conjunto de algoritmos revisados a través del primer nivel de refinamiento

ALGORITMO INICIAL

ohm () INICIO

hacer Llama a la función muestraMenu(). Llama a la función leyDeOhm().

mientras (opción != ‘s’ Y opción != ‘S’) FIN.

El primer nivel de refinamiento muestra el contenido de muestraMenu() y leyDeOhm(), como sigue:

ohm() Llama a muestraMenu() Llama a leyDeOhm()

muestraMenu() Muestra el menú y obtiene la opción del usuario

leyDeOhm() Realiza el cálculo seleccionado y muestra los resultados

calculaVoltaje() si opción V: calcula y muestra el voltaje.

calculaCorriente() si opción C: calcula y muestra la corriente

calculaResistencia() si opción R: calcula y muestra la resistencia

MIGUEL Á. TOLEDO MARTÍNEZ

FUNCIONES - LECCIÓN - 10 10-27

PRIMER NIVEL DE REFINAMIENTO

muestraMenu() Inicio

Muestra el menú del programa que pide al usuario seleccionar la opción de voltaje (V), corriente (I), resistencia (R) o salir (S). Leer(opcion).

Fin..

leyDeOhm() Inicio

Caso V: Llama a la función calculaVoltaje(). Caso C: Llama a la función calculaCorriente(). Caso R: Llama a la función calculaResistencia(). Case S: Termina el programa. sino: escribir(“Entrada no válida, seleccionar de nuevo”).

Fin.

Observe que la característica de control del ciclo se adiciona al nivel del algoritmo inicial ohm () Los algoritmos muestraMenu() y leyDeOhm() del primer nivel de refinamiento no han cambiado respecto de nuestra solución anterior. Así es que, enfoquémonos en el control del ciclo en ohm () Recordemos que la función muestraMenu() obtiene la opción del menú del usuario. Si el usuario introduce una s o S para salir del programa, se romperá el ciclo y el programa termina. Pero, ¿hay algún error aquí? La seudocodificación dice si el usuario introduce una s o S el programa se termina, sin embargo, la verificación del ciclo emplea la operación Y. Entonces, ¿por qué se utiliza la operación Y en lugar de la operación O para realizar esta verificación? Este es un candidato clásico para explicar en el escritorio la lógica del algoritmo antes de codificar el programa. Pregúntese ¿cuándo se romperá el ciclo? Un ciclo do/while se rompe cuando el estado verificado es falso, ¿correcto? Recuerde que el resultado de una operación Y es falso cuando alguno de sus estados es falso. Como resultado, el ciclo se romperá cuando opcion sea una s o una S. El ciclo continuará cuando sean verdaderos ambos lados de la operación Y. Por tanto, el ciclo continuará siempre y cuando opcion no sea una s y opcion no sea S. ¿No es esto lo que queremos hacer? ¿Qué pasaría si erróneamente usara la operación O en la prueba del ciclo anterior? Una operación O produce un resultado verdadero cuando alguna de sus condiciones es verdadera. Una de estas condiciones podría ser siempre verdadera, dado que opción no puede ser s y S. El resultado de este descuido será un ciclo infinito. Necesitamos hacer otra aclaración aquí. Esta aplicación es un candidato clásico para el ciclo do/while en vez de un ciclo while, porque siempre querrá que el menú se muestre por lo menos una vez para permitirle al usuario la selección de una opción. Ahora, necesitamos un segundo nivel de refinamiento para mostrar el contenido de las funciones de cálculo: SEGUNDO NIVEL DE REFINAMIENTO

calculaVoltaje()

Inicio Escribir(“Favor de introducir un valor para la corriente”). Leer(corriente). Escribir(“Favor de introducir un valor para la resistencia”). Leer(resistencia). si resistencia < 0

escribir(“Entrada no válida, ejecutar el programa de nuevo”). sino

Calcula voltaje = corriente x resistencia. escribir(voltaje).

Fin.

calculaCorriente() Inicio

MIGUEL Á. TOLEDO MARTÍNEZ

FUNCIONES - LECCIÓN - 10 10-28

Escribir(“Favor de introducir un valor para el voltaje”). Leer(voltaje). Escribir(“Favor de introducir un valor para la resistencia”). Leer(resistencia). si resistencia <= 0

escribir(“Entrada no válida, ejecutar el programa de nuevo”). sino

Calcula corriente = voltaje / resistencia. escribir(corriente).

Fin. calculaResistencia()

Inicio Escribir(“Favor de introducir un valor para el voltaje”). Leer(voltaje). Escribir(“Favor de introducir un valor para la corriente”). Leer(corriente). si corriente == 0

escribir(“Entrada no válida, ejecutar el programa de nuevo”). sino

Calcula resistencia = voltaje / corriente. escribir(resistencia).

Fin.

Cada función de cálculo obtiene los datos necesarios para los cálculos respectivos de la ley de Ohm. La función hace los cálculos y muestra el resultado. Observando más de cerca estos algoritmos, encontrará algunas características de protección. Los enunciados si/sino dentro de cada función protegen contra entradas de datos no válidos. Un valor de resistencia negativa no es válido porque no hay tal cosa. Además, no podrá dividir entre 0. Como resultado, las funciones calculaCorriente() y calculaResistencia() cada una verifica las entradas cero para los valores que se usan como divisor en cálculo de la ley de Ohm.

CODIFICACIÓN DEL PROGRAMA

Ahora, para instrumentar el diseño, necesitamos construir las funciones. La tarea principal es desarrollar las interfaces de la función. Para hacerlo, debemos desarrollar la definición del problema para cada función. La definición del problema desarrollado anteriormente dirige la salida, entrada y procesamiento del programa general, ohm (). Esto es todavía necesario para definir el problema para el programa como un todo. Sin embargo, para construir las interfaces de la función, debemos dirigir la definición del problema desde la perspectiva de la función. En otras palabras, debemos considerar la salida, entrada y procesamiento de cada función individual. La entrada a la función es lo que la función debe aceptar para realizar su tarea designada, la salida de la función es lo que la función regresa, y el procesamiento es la tarea que realizará la función. Empezaremos con la función muestraMenu(). Es necesario preguntar dos cosas 1) ¿Qué necesita aceptar (entrada) la función para realizar su tarea designada? y 2) ¿Qué necesita regresar (salida) la función al programa llamador? La tarea de la función muestraMenu() es mostrar el menú y regresar la opción del usuario a su función de llamada, ohm (). De esta manera, esta función no necesita aceptar nada desde ohm () para realizar su tarea, pero requiere regresar un valor sencillo para ohm (), que es la opción del menú del usuario. Esto se muestra en el diagrama de estructura por medio de la variable opción que viene de muestraMenu(). Observe que nada va dentro de esta función. Aquí está la definición del problema desde la perspectiva de esta función:

Función muestraMenu(): Muestra el menú y obtiene la entrada del usuario. Acepta: Nada. Regresa: La opción del menú obtenida del usuario. La definición de la función describe la interfaz de la función. La primera decisión que necesita tomar es si se usará una función void o una sin void. Emplee una función sin void si la función produce un valor sencillo que regrese al programa llamador. Use una función void si la función no acepta o regresa nada o

MIGUEL Á. TOLEDO MARTÍNEZ

FUNCIONES - LECCIÓN - 10 10-29

acepta una(s) variable(s), cambia y regresa la(s) variable(s) modificada(s) al programa llamador. En otras palabras, si el diagrama de estructura indica que la función produce un valor sencillo, utilice una función sin void. Si el diagrama de estructura indica que la función no acepta ni regresa nada, o si la función acepta y regresa una variable determinada, utilice una función void. Estos lineamientos se resumen en la tabla 10.1. Debido a que nuestra función muestraMenu() no acepta nada, pero regresa la opción del menú del usuario, deberá ser una función sin void. Así, la clase de regreso deberá ser char, porque la opción del usuario será un carácter desde el menú.

TABLA 10.1 CLASE DE REGRESO DE LA FUNCIÓN

Regresa Clase de datos return Un valor sencillo Sin void Nada void Valores múltiples void

Después, decidiremos los parámetros de la función. La tabla 10.2 proporciona las pautas para determinar si un parámetro determinado deberá ser un valor o un parámetro de referencia.

TABLA 10.2 PARÁMETROS DE FUNCIONES

Acepto o regresa Parámetro

No acepta Ninguno Acepta sin regresar Valor (en un sentido) Acepta y regresa Referencia (en dos sentidos) Ningún parámetro se requiere para muestraMenu(), porque no acepta nada, de esta manera la interfaz de la función es solamente:

char muestraMenu(void)

Ahora, simplemente colocamos el código muestraMenu(void) desarrollado al principio dentro de la función. Aquí está la función completa:

char muestraMenu()

{ // Define la variable local opcion char opcion = ´S´; // Genera el menú y obtiene la opción del usuario cout << "\n\n\t\t\tEscriba V para encontrar el voltaje." << endl << endl

<< "\t\t\tEscriba C para encontrar la corriente." << endl << endl << "\t\t\tEscriba R para encontrar la resistencia." << endl << endl << "\t\t\tEscriba S para salir." << endl << endl << "\tPor favor escriba su opción: "; cin >> opcion;

// Regresa la opción return opcion;

} // Fin de muestraMenu()

MIGUEL Á. TOLEDO MARTÍNEZ

FUNCIONES - LECCIÓN - 10 10-30

Observe que nuestra variable opcion ahora está definida como una variable local dentro de la función. Además, un enunciado return se coloca al final de la función para regresar opcion a su función de llamada ohm (). Después, vamos a desarrollar la función leyDeOhm(). Empecemos a desarrollar la interfaz de la función. En el diagrama de estructura es fácil de ver lo siguiente:

Función leyDeOhm(): Realiza el cálculo de la opción y muestra los resultados. Acepta: La entrada opción del menú, obtenida desde main(). Regresa: Nada. A partir de la descripción de la interfaz, es fácil ver que se deberá usar una función void porque la función no regresa nada. Aunque la función muestre los resultados, no está regresando ningún valor a su función de llamada ohm (). Sin embargo, esta función necesita aceptar la opción del menú desde ohm (). Esto significa que necesitamos un parámetro de función. Ahora la pregunta es: ¿El parámetro es de valor o de referencia?. Bien, si es de un sentido dentro de la función, el parámetro deberá ser de valor. Si es en dos sentidos, entrada y salida de la función, deberá ser un parámetro de referencia. Debido a que acepta este parámetro, pero no lo regresa, deberá ser un parámetro de valor, ¿correcto? El parámetro necesario es un carácter, así la interfaz de la función se convierte en:

void leyDeOhm(char opcion)

Ahora, al insertar nuestro código de enunciado de intercambio en el cuerpo de la función, se obtendrá:

void leyDeOhm(char opcion)

{ // Establecer la precisión de la salida. cout.setf(ios::fixed); cout.precision(2);

switch(opcion)

{ case 'v'

case 'V': cout << "\n\nEl valor del voltaje es: “ << calculaVoltaje() << “ volts.” << endl; break;

case ‘c’: case ‘C’:

cout << “\n\nEl valor de la corriente es: “ << calculaCorriente() << “ miliamperes.” << endl; break;

case ‘r’: case ‘R’:

cout << “\n\nEl valor de la resistencia es: “ << calculaResistencia() << “ kilohms.” << endl; break;

case ‘s’: // Termina el programa case ‘S’:

cout << “El programa terminado” << endl; break;

// Muestra el mensaje de entrada no válida default:

cout << “\n\nEsta es una entrada no válida.” << endl; } // Fin de switch()

} // Fin de leyDeOhm()

MIGUEL Á. TOLEDO MARTÍNEZ

FUNCIONES - LECCIÓN - 10 10-31

Observe que la función emplea el enunciado switch, que actúa en la selección del usuario, opcion, que se recibe como un parámetro desde la función de llamada, ohm (). Después, veremos que cada caso de la ley de Ohm llama a otra función como parte de un enunciado cout para calcular el voltaje, corriente o resistencia requeridos, dependiendo del valor de opcion. Cada función de la ley de Ohm regresará un valor sencillo de voltaje, corriente o resistencia, dependiendo del case que se ejecute. ¿Esto le sugiere el tipo de funciones (void o sin void)? Por último, observe que no hay enunciado return en nuestra función leyDeOhm() porque es una función void.

Enseguida se presentan las descripciones de interfaz para cada una de las funciones de la ley de Ohm:

Función calculaVoltaje(): Obtiene del usuario la corriente y resistencia y usa le ley de Ohm para

calcular el voltaje. Acepta: Nada. Regresa: Voltaje. Función calculaCorriente(): Obtiene del usuario el voltaje y resistencia y usa la ley de Ohm para

calcular la corriente. Acepta: Nada. Regresa: Corriente. Función calculaResistencia(): Obtiene del usuario el voltaje y corriente y usa la ley de Ohm para

calcular la resistencia. Acepta: Nada. Regresa: Resistencia. Cada una de estas funciones tendrá la misma interfaz, excepto, desde luego, para el nombre de la función. Cada función será una función sin void, porque la función produce y regresa un solo valor. Ningún parámetro se requiere, porque ninguna de las funciones necesita aceptar ningún dato de la función de llamada leyDeOhm(). Probablemente piense que cada una de las funciones necesita las dos cantidades desconocidas en la ecuación de la ley de Ohm para hacer el cálculo necesario. Sin embargo, estas cantidades se obtienen del usuario dentro de cada una de las funciones y no desde la función de llamada leyDeOhm(). A continuación se dan las interfaces de la función resultante:

float calculaVoltaje(void); float calculaCorriente(void); float calculaResistencia(void);

Ahora, todas las funciones completas:

float calculaVoltaje() {

// Define variables locales float corriente = 0.0; // Corriente en miliamperes float resistencia = 0.0; // Resistencia en Kilo-ohms. // Obtiene la corriente y la resistencia cout << “\nEscriba el valor de la corriente en miliamperes\tC = ”; cin >> corriente; cout << “\nEscriba el valor de la resistencia en kilo-ohms\t\R = ”; cin >> resistencia; // Verifica que la resistencia sea < 0 y calcula el voltaje if(resistencia < 0)

{ cout << "\n\nEsta es una entrada no válida. Por favor"

"seleccione de nuevo." << endl; return 0.0;

} // Fin del if else

MIGUEL Á. TOLEDO MARTÍNEZ

FUNCIONES - LECCIÓN - 10 10-32

return corriente * resistencia;

} // Fin de calculaVoltaje()

float calculaCorriente() {

// Define variables locales float voltaje = 0.0; // Voltaje en volts float resistencia = 0.0; // Resistencia en kilo-ohms // Obtiene el voltaje y resistencia cout << "\nEscriba el valor del voltaje en volts\tV = ";

cin >> voltaje; cout << "\nEscriba el valor de la resistencia en KiloOhms\tR = "; cin >> resistencia; // Verifica que la resistencia sea <= 0 y calcula la corriente if(resistencia <= 0)

{ cout << "\n\nEsta es una entrada no válida. Por favor" " seleccione de nuevo." << endl;

return 0.0; } // Fin del if

else return voltaje / resistencia;

} // Fin de calculaCorriente()

float calculaResistencia() {

// Define variables locales float voltaje = 0.0; // Voltaje en volts float corriente = 0.0; // Corriente en miliamperes // Obtiene el voltaje y la corriente cout << "\nEscriba el valor del voltaje en volts\tV = ";

cin >> voltaje; cout << "\nEscriba el valor de la corriente en miliamperes\tI = "; cin >> corriente;

// Verifica que la corriente sea == 0 y calcula la resistencia if(corriente == 0)

{ cout << "\n\nEsta es una entrada no válida. Por favor" " seleccione de nuevo." << endl;

return 0.0; } // Final del if

else return voltaje / corriente;

} // Fin de calculaResistencia()

Observe que cada una de las funciones obtiene del usuario los datos necesarios para el cálculo. Los datos de entrada son verificados. Si no son válidos, se genera un mensaje apropiado; de otra manera, la función regresa el valor requerido. Observe que hay dos enunciados return en cada función. Dado que hemos definido las funciones como sin void, éstas deben regresar un valor, sin importar ninguna condición de terminación. Esto es la razón por la que hay un enunciado return 0.0 después del mensaje de entrada no válida en cada función. Se ha elegido regresar el valor 0.0 en esta situación, porque debe regresar un valor de punto flotante.

MIGUEL Á. TOLEDO MARTÍNEZ

FUNCIONES - LECCIÓN - 10 10-33

Estamos listos para combinar todo en un programa completo. Es el siguiente:

/* El siguiente programa: MENU.CPP, muestra el concepto de programación estructurada */ Salida: Un menú del programa le pide al usuario seleccionar una opción para calcular el Voltaje, Corriente o Resistencia. Se debe enviar un mensaje en el caso de hacer una selección inválida. Mostrar un valor del Voltaje, Corriente o resistencia dependiendo de la opción que el usuario seleccione. Entrada: Una respuesta del usuario al menú (V, C, R o S). Si se selecciona V: escribe un valor para la corriente y la resistencia. Si selecciona C: escribe un valor para voltaje y resistencia. si selecciona R: escribe un valor para voltaje y corriente. Si selecciona S: el programa termina. Procesamiento: Cálculo de la opción seleccionada Case V: Voltaje = Corriente x Resistencia. Case C: Corriente = Voltaje / Resistencia. Case R: Resistencia = Voltaje / Corriente. Case S: Termina el programa. */ #include <iostream.h> // Para cin y cout. // Funciones prototipo char muestraMenu(void); // Muestra el menú de la ley de Ohm void leyDeOhm(char opcion); // Obtiene la opción del menú y llama la función // de la ley de Ohm necesaria float calculaVoltaje(void); // Calcula el voltaje float calculaCorriente(void); // Calcula la corriente float calculaResistencia(void); // Calcula la resistencia void main(void) { // Definición de variables argumentos de la función. char opcion = 'S'; // Entrada del usuario desde el menú. // Muestra el mensaje de la descripción del programa. cout << "Este programa calculará el voltaje de cd, corriente, o\n" "resistencia proporcionando los otros dos valores." << endl << endl; do { opcion = muestraMenu(); // Llama a muestraMenu() leyDeOhm(opcion); // Llama a la leyDeOhm() } // Fin del do/while while((opcion != 's') && (opcion != 'S')); } // Fin del main() // Función para mostrar el menú y regresar la opción del usuario char muestraMenu() { // Define la variable local char opcion = 'S';

MIGUEL Á. TOLEDO MARTÍNEZ

FUNCIONES - LECCIÓN - 10 10-34

// Genera el menú y obtiene la opción del usuario cout << "\n\n\t\t\tEscriba V para encontrar el voltaje." << endl << endl << "\t\t\tEscriba C para encontrar la corriente." << endl << endl << "\t\t\tEscriba R para encontrar la resistencia." << endl << endl << "\t\t\tEscriba S para salir." << endl << endl << "\tPor favor escriba su opción: "; cin >> opcion; // Regresa la opcion return opcion; } // Fin de muestraMenu() // Funcion para llamar la funcion de la ley de Ohm requerida void leyDeOhm(char opcion) { // Establecer la precisión de la salida. cout.setf(ios::fixed); cout.precision(2); switch(opcion) { case 'v': case 'V': cout << "\n\nEl valor del voltaje es: " << calculaVoltaje() << " volts." << endl; break; case 'c': case 'C': cout << "\n\nEl valor de la corriente es: " << calculaCorriente() << " miliamperes." << endl; break;

case 'r': case 'R': cout << "\n\nEl valor de la resistencia es: " << calculaResistencia() << " KiloOhms." << endl; break; case 's': // Termina el programa case 'S': cout << "Programa terminado" << endl; break; // Muestra el mensaje de entrada no válida. default: cout << "\n\nEsta es una entrada no válida. Por favor" " seleccione de nuevo." << endl; } // Fin del switch } // Fin de leyDeOhm() // Función para el cálculo del voltaje float calculaVoltaje(void) { // Define variables locales float corriente = 0.0; // Corriente en miliamperes float resistencia = 0.0; // Resistencia en KiloOhms

MIGUEL Á. TOLEDO MARTÍNEZ

FUNCIONES - LECCIÓN - 10 10-35

// Obtiene la corriente y resistencia cout << "\nEscriba el valor de la corriente en miliamperes\tC = "; cin >> corriente; cout << "\nEscriba el valor de la resistencia en KiloOhms\tR = "; cin >> resistencia; // Verifica que la resistencia sea < 0 y calcula el voltaje if(resistencia < 0) { cout << "\n\nEsta es una entrada no válida. Por favor" " seleccione de nuevo." << endl; return 0.0; } // Fin de if else return corriente * resistencia; } // Fin de calculaVoltaje // Función para el cálculo de la corriente float calculaCorriente() { // Define variables locales float voltaje = 0.0; // Voltaje en volts float resistencia = 0.0; // Resistencia en KiloOhms // Obtiene el voltaje y resistencia cout << "\nEscriba el valor del voltaje en volts\tV = "; cin >> voltaje; cout << "\nEscriba el valor de la resistencia en KiloOhms\tR = "; cin >> resistencia; // Verifica que la resistencia sea <= cero y calcula la corriente if(resistencia <= 0) { cout << "\n\nEsta es una entrada no válida. Por favor" " seleccione de nuevo." << endl; return 0.0; } // Fin del if else return voltaje / resistencia; } // Fin de calculaCorriente() // Función para calcular la resistencia float calculaResistencia() { // Define variables locales float voltaje = 0.0; // Voltaje en volts float corriente = 0.0; // Corriente en miliamperes // Obtiene el voltaje y la corriente cout << "\nEscriba el valor de voltaje en volts\tV = "; cin >> voltaje; cout << "\nEscriba el valor de la corriente en miliamperes\tI = "; cin >> corriente;

MIGUEL Á. TOLEDO MARTÍNEZ

FUNCIONES - LECCIÓN - 10 10-36

Lo primero que se ve en la parte superior del programa son las funciones prototipo. Estas sólo se copiaron del encabezado de la función agregando un punto y coma al final de cada una. El buen estilo dice que cada prototipo deberá comentarse de acuerdo con el propósito de la función. Recuerde que C++ requiere un prototipo para cada función para que pueda verificar la precisión de las llamadas de la función. A continuación, se sorprenderá de la simplicidad de la función main(). Todo lo que hace es escribir un mensaje de descripción del programa y llamar nuestras dos funciones principales dentro de un ciclo de control del programa. La mayor parte del trabajo real del programa se hace dentro de las funciones. ¡Esto es la programación estructurada! El resto del programa contiene cada una de las funciones que hemos explicado anteriormente.

SOLUCIÓN DE PROBLEMAS EN ACCIÓN: Estado de cuenta de una cuenta de ahorros.

En la lección 4, empleamos el refinamiento sucesivo (paso a paso) para diseñar una solución para una aplicación de cuentas de ahorro. Codificamos la solución usando implementación lineal porque, en ese momento, no se conocía cómo aplicar un diseño estructurado usando las funciones C++. Vamos a revisar esta aplicación con nuestro reciente conocimiento de funciones. Aquí está el enunciado del problema otra vez:

PROBLEMA

Su banco local lo contrató para diseñar un programa estructurado que procese datos de las cuentas de ahorro. Desarrolle una serie de algoritmos relacionados con el uso del método de diseño estructurado de arriba-abajo (descendente) que se pueda codificar en un lenguaje de programación estructurada.

DEFINICIÓN DEL PROBLEMA

Salida El programa debe generar un reporte que muestre las transacciones (balance

del mes anterior, depósitos y retiros del mes actual) de la cuenta y el balance actual para una cuenta de ahorro en un mes determinado.

Entrada Supondremos que el usuario ingresará la información de las transacciones

mensualmente e incluirá el balance del mes anterior, los depósitos y retiros del mes actual.

Procesamiento El programa debe agregar al balance del mes anterior los depósitos, restar los

retiros, calcular y agregar el interés del mes para determinar el balance actual.

PLANEACIÓN DE LA SOLUCIÓN

Con el uso del diseño estructurado, empezaremos en dividir el problema en subproblemas para solucionar el problema bancario. Trate de identificar las tareas individuales para solucionar el problema. Primero, el usuario debe ingresar los datos de la transacción requerida, las cuales incluirán el balance del mes anterior, depósitos y retiros del mes actual. Una vez que se ingresan los datos de la transacción, el programa debe adicionar los depósitos al balance de la cuenta, restar los retiros de la cuenta, calcular el interés y generar el reporte necesario. Como resultado, podemos identificar cinco tareas del programa como sigue:

// Verifica que la corriente sea == 0 y calcula la resistencia if(corriente == 0) { cout << "\n\nEsta es una entrada no válida. Por favor" " seleccione de nuevo." << endl; return 0.0; } // Fin de if else return voltaje / corriente; } // Fin de calculaResistencia()

MIGUEL Á. TOLEDO MARTÍNEZ

FUNCIONES - LECCIÓN - 10 10-37

•••• Obtener del usuario los datos de la transacción. •••• Agregar los depósitos al balance anterior de la cuenta. •••• Restar los retiros del balance anterior de la cuenta. •••• Calcular el interés de la cuenta. •••• Generar el reporte mensual de la cuenta.

La figura 10.5 muestra el diagrama de la estructura en donde se ven los bloques necesarios para el programa:

Figura 10.5. Diagrama de estructura para el problema bancario.

Debido a que usaremos la técnica estructurada de bloques para diseñar el programa, debemos emplear el refinamiento paso a paso para desarrollar el algoritmo. El nivel de algoritmo inicial, main(), reflejará la definición del problema y llama a los módulos de los subprogramas individuales o funciones como sigue:

ALGORITMO INICIAL

estadoCuenta ()

INICIO Llamar a la función obtenerDatos() para obtener los datos de la transacción.

Llamar a la función agregarDepositos() para adicionar los depósitos en la cuenta.

Llamar a la función restarRetiros() para restar los retiros de la cuenta. Llamar a la función calcularInteres para calcular el interés de la cuenta. Llamar a la función generarReporte() para mostrar el reporte de la cuenta.

FIN. El primer nivel de refinamiento requiere que mostremos un algoritmo detallado para cada módulo del subprograma o función. Estos son como sigue: PRIMER NIVEL DE REFINAMIENTO obtenerDatos() Inicio

Escribir (“Introducir balance del mes anterior”). Leer(balanceAnterior). Escribir (“Introducir los depósitos del mes actual”). Leer(depositos). Escribir (“Introducir los retiros del mes actual”). Leer(retiros).

Fin.

estadoCuenta() Determinación del balance mensual de una cuenta de ahorros

obtenerDatos()

Obtención de los datos de las transacciones

agregarDepositos()

Adición de los depósitos

restarRetiros()

Substracción de retiros

calcularInteres()

Cálculo del interes

generarReporte()

Generar el reporte

MIGUEL Á. TOLEDO MARTÍNEZ

FUNCIONES - LECCIÓN - 10 10-38

agregarDepositos() Inicio

Calcular balanceActual = balanceAnterior + depositos. Fin. restarRetiros() Inicio

Calcular balanceActual = balanceActual – retiros. Fin. calcularInteres() Inicio

Calcular balanceActual = balanceActual + (balanceActual * interes). Fin. generarReporte() Inicio

Escribir(balanceActual). Escribir(depositos). Escribir(retiros).

Fin. Con nuestro conocimiento de funciones podemos modificar el diagrama de estructura, como se muestra en la figura 10.6. Observe lo que se ha adicionado. Primero, el cuadro en la parte superior está etiquetado como estadoCuenta () y simplemente llama a las funciones individuales en el orden en que éstas se necesitan para solucionar el problema. Segundo, los objetos de datos que pasan a las funciones, o desde éstas, se muestran en las líneas que conectan a estadoCuenta () con las funciones. Los objetos que acepta la función se muestran a la izquierda de la línea de conexión y los objetos que regresa la función se muestran a la derecha de la línea como sigue:

•••• Si un objeto de datos va en un sentido dentro de la función, es un parámetro de valor. •••• Si un objeto de datos va en un sentido fuera de la función, es un valor de regreso. •••• Si un objeto de datos va en dos sentidos dentro y fuera de la función, es un parámetro de

referencia.

Para construir el diagrama estructurado extendido, deberá considerar una definición de problema para cada función describiendo la interfaz de función en términos de lo que acepta y regresa. Vamos a empezar con la función obtenerDatos(). Función obtenerDatos(): Obtiene del usuario el balance actual de la cuenta, depositos y retiros. Acepta: Conserva el lugar para un objeto variable para balance, depositos y retiros. Regresa: Balance, depositos y retiros. La función obtenerDatos() deberá obtener entradas del usuario y regresarlas al programa llamador estadoCuenta (). Como resultado, la función deberá aceptar un lugar para un objeto variable colocado para cada entrada del usuario. La función obtendrá del usuario un valor para llenar cada lugar para la variable y después regresará la variable a estadoCuenta (). Esta descripción de interfaz se representa en el diagrama de estructura de la figura 10.6, mediante la muestra de las variables balance, depositos y retiros que van dentro y fuera del módulo de la función obtenerDatos(). Esto significa que los tres deberán ser parámetros de referencia, dando origen a una interfaz de función de:

void obtenerDatos(float &balance, float &depositos, float &retiros)

MIGUEL Á. TOLEDO MARTÍNEZ

FUNCIONES - LECCIÓN - 10 10-39

Figura 10.6. Diagrama de estructura extendido que muestra los elementos de datos fluyendo hacia o desde las

funciones de la cuenta bancaria. La interfaz de función agregarDepositos() se puede describir como sigue:

Función agregarDepositos(): Añade los depositos al balance actual. Acepta: El balance y los depositos de la cuenta

actual. Regresa: Un nuevo balance de cuenta.

Esta función necesita que reciba el balance y los depositos actuales de la cuenta para calcular y regresar el nuevo balance. Como resultado, la función acepta balance y depositos; balance entonces cambia por la adición de los depositos y balance regresa a estadoCuenta (), como se muestra en el diagrama de estructura. Observe que balance va en dos sentidos, dentro y fuera de la función, mientras que depositos va solamente en un sentido, dentro de la función. Esto significa que balance deberá ser un parámetro de referencia y depositos, un parámetro de valor. Por lo tanto, la función interfaz resultante será:

void agregarDepositos(float &balance, float depositos)

La función restarRetiros() recibe el balance y el retiro actual de la cuenta, resta los retiros y regresa el nuevo balance de la cuenta. Con esta información, esta interfaz se puede describir como sigue:

Función restarRetiros(): Resta los retiros del balance actual. Acepta: El balance y los retiros actuales de la cuenta. Regresa: Un nuevo balance de la cuenta.

El diagrama de estructura ejemplifica esta información mostrando balance que va en dos sentidos, dentro y fuera de la función, mientras retiros va solamente en un sentido, dentro de la función. De esta manera, balance será un parámetro de referencia, y retiros un parámetro de valor. La interfaz de la función resultante es:

void restarRetiros(float &balance, float retiros) Después, la función calcularInteres() recibe el balance de la cuenta, calcula el interés mensual lo adiciona al balance y regresa el balance. Esta función se puede describir como sigue:

Función calcularInteres(): Adiciona el interés mensual al balance de la cuenta

Acepta: El balance actual de la cuenta. Regresa: Un nuevo balance de la cuenta.

obtenerDatos()

Obtención de los datos de transacciones

agregarDepositos()

Adición de los depósitos

restarRetiros()

Substracción de retiros

calcularInteres()

Cálculo del interes

generarReporte()

Generar el reporte

estadoCuenta ()

Determinación del balance mensual de una cuenta de ahorros

balance depositos retiros

balance Depositos Retiros

balance depositos

balance

balance retiros

balance balance balance balance Depositos retiros

MIGUEL Á. TOLEDO MARTÍNEZ

FUNCIONES - LECCIÓN - 10 10-40

Aquí, la función acepta el balance de la cuenta, que ha cambiado por la adición del interés mensual y regresa a estadoCuenta () por medio de la función. Transcribiendo esta información al diagrama de estructura, se encuentra que balance va dentro y fuera del módulo de la función calcularInteres(). La interfaz resultante es:

void calcularInteres(float &balance)

Por último, la función generarReporte() recibe el balance, depositos y retiros de la cuenta para generar el informe de la cuenta. Por lo tanto, esta función se describe como sigue:

Función generarReporte(): Genera el informe de la cuenta. Acepta: El balance, depositos y retiros de la cuenta. Regresa: Nada.

Para generar el informe de la cuenta, la función debe recibir el balance, depositos y retiros de la cuenta desde estadoCuenta (). Sin embargo, estos valores no se modifican ni regresan; sólo se muestran como parte del informe por medio de la función. De esta manera, el diagrama de estructura indica balance, depositos y retiros en un solo sentido, dentro del módulo de la función. Esto significa que éstos deberán ser parámetros de valor, ¿correcto? La interfaz de la función resultante es:

void generarReporte(float balance, float depositos, float retiros) Cerciórese de ver cómo se muestra la descripción en el diagrama de estructura de la interfaz para cada función y cómo se traduce el diagrama de estructura directamente al código interfaz C++. Si un objeto de datos solamente se presenta fuera de la función, es un valor de regreso. Observe que no hay variables que sólo estén presentes fuera de cualquiera de las funciones. Como resultado, todas las funciones tienen clases de regreso void. Si un objeto sólo va dentro de la función, es un parámetro de valor. Si un objeto está dentro y fuera de la función, es un parámetro de referencia y debe estar precedido de un símbolo ampersand en el prototipo.

Por último, empleando las interfaces de la función, los algoritmos y combinando todo, obtenemos el siguiente código:

CODIFICACIÓN DEL PROGRAMA

La solución del problema anterior se puede codificar fácilmente en cualquier lenguaje de programación estructurada. A continuación los algoritmos anteriores se codifican en C++.

/* El siguiente programa: ESTRUC1.CPP, muestra la naturaleza estructurada en bloques de C++. */ #include <iostream.h> // Para cin y cout. // Define INTERES como una constante global. const float INTERES = 0.001; // Impuesto mensual actual. // Prototipo de funciones. void obtenerDatos(float &, float &, float &); void agregarDepositos(float &, float); void restarRetiros(float &, float); void calcularInteres(float &); void generarReporte(float, float, float);

MIGUEL Á. TOLEDO MARTÍNEZ

FUNCIONES - LECCIÓN - 10 10-41

void main(void) {

// Define las variables del argumento de la función float balance = 0.0; // Balance de la cuenta. float depositos = 0.0; // Depósitos mensuales. float retiros = 0.0; // Retiros mensuales. // Muestra el mensaje de descripción del programa. cout << "Este programa generará un reporte de la cuenta bancaria con\n"

"base en la información proporcionada por el usuario" << endl << endl;

// Funciones llamada obtenerDatos(balance, depositos, retiros); agregarDepositos(balance, depositos); restarRetiros(balance, retiros); calcularInteres(balance); generarReporte(balance, depositos, retiros); } // Final de main()

// Esta función obtiene la información mensual de la cuenta por el usuario. void obtenerDatos(float &balance, float &depositos, float &retiros) {

cout << "Ingrese el balance de la cuenta : $"; cin >> balance; cout << "Ingrese los depósitos de este mes: $"; cin >> depositos; cout << "Ingrese los retiros de este mes : $"; cin >> retiros; } // Fin obtenerDatos()

// Esta función adiciona los depósitos mensuales al balance de la cuenta. void agregarDepositos(float &balance, float depositos) {

balance = balance + depositos; } // fin agregarDepositos()

/ Esta función sustrae los retiros mensuales desde el balance de la cuenta. void restarRetiros(float &balance, float retiros) { balance = balance - retiros; } // Fin restarRetiros() // Esta función adiciona los intereses mensuales al balance de la cuenta. void calcularInteres(float &balance) { balance = balance + (balance * INTERES); } // Fin calcularInteres() // Esta función muestra el reporte mensual de la cuenta. void generarReporte(float balance, float depositos, float retiros) { cout << endl; cout << "El balance de la cuenta actualmente es: $" << balance << endl; cout << "Los depósitos fueron : $" << depositos << endl; cout << "Los retiros fueron : $" << retiros << endl;

} // Fin generarReporte()

MIGUEL Á. TOLEDO MARTÍNEZ

FUNCIONES - LECCIÓN - 10 10-42

¡Eso es todo! Ahora debe tener el conocimiento para comprender todas las facetas de este problema, desde el diseño estructurado al programa estructurado C++.

LO QUE NECESITA SABER Antes de continuar con la siguiente lección, asegúrese de haber comprendido los siguientes conceptos:

!!!!""""La mejor manera de desarrollar y dar mantenimiento a un programa grande es dividirlo en varios módulos más pequeños, que son más fáciles de manejar que el programa completo. Los módulos se escriben en C++ como clases y funciones.

!!!!""""Conforme sus programas se hacen más complejos, puede dividirlos en partes más pequeñas llamadas funciones.

!!!!""""Una función se invoca por medio de una llamada de función. Dicha llamada menciona la función por su nombre y proporciona la información (en forma de argumentos) que necesita para realizar su tarea.

!!!!""""El propósito de la ocultación de información es que las funciones tengan acceso únicamente a la información que necesitan para completar su tarea. Esta es una manera de implementar el principio de menor privilegio, uno de los más importantes para la buena ingeniería de software.

!!!!""""Las funciones se invocan normalmente en los programas escribiendo su nombre seguido de sus argumentos entre paréntesis.

!!!!""""Todas las funciones definidas por el usuario que se usan en main() se definen después de cerrar la llave de main().

!!!!""""La función se define escribiendo un encabezado de función, que incluye la clase de regreso, el nombre de la función y una lista de parámetros. El cuerpo o sección de enunciado de la función sigue al encabezado de la función.

!!!!""""Cada función debe de tener un nombre único. !!!!""""Las funciones pueden regresar un valor al programa llamador y pueden recibir información llamadas

parámetros. !!!!""""Si una función regresa un valor, debe definir el tipo del valor regresado (int, char, etc.) antes del

nombre de la función; en caso contrario tiene que preceder al nombre de la función con void. !!!!""""Si la función recibe parámetros, debe definir un nombre único para cada parámetro así como su tipo.

Si la función no recibe parámetros, debe colocar la palabra reservada void dentro de los paréntesis que siguen al nombre de la función.

!!!!""""C++ debe saber el tipo de valor que regresa la función y el número y tipo de parámetros que la función recibe. Si la definición de la función se encuentra posteriormente a la utilización de la misma, debe de colocar un prototipo de función al principio de su programa fuente.

!!!!""""A diferencia del lenguaje C, el lenguaje C++ necesita que cada función tenga un prototipo. Los prototipos de las funciones deben listarse antes que main() e incluir las clases de datos de regreso de la función, el nombre de la función y una lista de las clases de datos de parámetro.

!!!!""""Los prototipos se usan por el compilador para verificar el número apropiado de argumentos cuando se llama a la función.

!!!!""""Las funciones en C++ se pueden hacer para regresar un valor sencillo para el programa llamador o realizar alguna tarea específica.

!!!!""""Cuando se diseña una función para regresar un valor sencillo al programa llamador, el valor regresado reemplaza el nombre de la función en donde se use el nombre en el programa llamador. De esta manera, el nombre de la función puede aparecer como parte de un operador de asignación, un enunciado cout, un operador aritmético o una verificación de enunciado.

EXAMEN BREVE 29

MIGUEL Á. TOLEDO MARTÍNEZ

FUNCIONES - LECCIÓN - 10 10-43

!!!!""""Cuando se diseña una función para realizar una tarea específica, se llama a la función empleando el nombre de la función (seguido por una lista de argumentos reales) como un enunciado en el programa.

!!!!""""Los argumentos reales son datos que pasan a la función cuando se llama a la función. Los parámetros formales se definen dentro del encabezado de la función y toman el (los) valor (valores) de los argumentos reales cuando se llama a la función.

!!!!""""Los parámetros pueden pasar entre el programa llamador y la función por medio de valor o referencia. Cuando pasan parámetros por medio de valor, los argumentos reales en el programa llamador no se afectan por las operaciones sobre los parámetros formales dentro de la función. Cuando pasan los parámetros por medio de referencia, los argumentos reales en el programa llamador reflejarán cualquier cambio a los parámetros formales dentro de la función.

!!!!""""De esta manera, el paso de los parámetros por medio de valor es una comunicación de datos en un sentido desde el programa llamador a la función. El paso de los parámetros por referencia es una comunicación de datos en dos sentidos desde el programa llamador a la función y de regreso al programa llamador.

!!!!""""Los argumentos de una función pueden ser constantes, variables y expresiones. !!!!""""Las variables locales sólo son conocidas dentro de la definición de una función. No se permite que las

funciones conozcan los detalles de implementación de las demás funciones (incluyendo sus variables locales).

!!!!""""El formato general de una definición de función es: claseValorDevuelto nombreFuncion(listaParametros)

{ cuerpoFuncion;

}

La claseValorDevuelto es la clase del valor que se le devuelve al invocador. Si una función no devuelve un valor, claseValorDevuelto se declara void. El nombreFuncion es cualquier identificador válido. La listaParametros es una lista separada por comas que contiene las declaraciones de las variables que se pasarán a la función. Si la función no recibe valores, listaParametros se declara como void. El cuerpoFuncion es el conjunto de declaraciones e instrucciones que constituyen la función.

!!!!""""El número, clase y orden de los argumentos pasados a una función debe ser iguales a los parámetros de la definición de la función.

!!!!""""Cuando un programa llega a una función, el control se transfiere desde el punto de invocación a la función llamada, se ejecuta dicha función y el control se devuelve al indicador.

!!!!""""Las funciones llamadas pueden devolverle el control al llamador de cualquiera de tres formas. Si la función no devuelve un valor, el control se devuelve al llegar a la llave derecha de terminación de la función, o ejecutando la instrucción:

return; Si la función devuelve un valor, la instrucción:

return expresión; devuelve el valor de expresión.

!!!!""""Las funciones que no devuelven un valor se declaran con una clase de devolución void. Cualquier intento por devolver un valor desde la función o por emplear el resultado de la invocación de la función en la función invocadora genera un error de sintaxis.

!!!!""""Las listas de parámetros vacías se especifican con paréntesis vacíos o con void entre ellos. !!!!""""Los prototipos de función declaran la clase de devolución de la función y el número, clase y orden de

los parámetros que espera recibir la función. !!!!""""Los prototipos de función le permiten al compilador verificar que las funciones se llamen

correctamente. !!!!""""El compilador ignora los nombres de variables que se mencionen en el prototipo de función.

MIGUEL Á. TOLEDO MARTÍNEZ

FUNCIONES - LECCIÓN - 10 10-44

!!!!""""Cada biblioteca estándar tiene un archivo de encabezado correspondiente, el cual contiene los prototipos de función de todas sus funciones, así como las definiciones de varias constantes simbólicas requeridas por dichas funciones.

!!!!""""Los programas en C++ no se compilan a menos que se haya indicado un prototipo de función para cada función o que se defina la función antes de utilizarla.

!!!!""""Los programadores pueden y deben crear e incluir sus propios archivos de encabezado. !!!!""""Al pasar un argumento utilizando una llamada por valor, se copia el valor de la variable para pasarla

a la función llamada. Los cambios a la copia que sucedan en dicha función no afectan el valor de la variable original.

!!!!""""En C++, los parámetros de referencia se definen usando un signo ampersand (&), antes del nombre del parámetro en el encabezado de la función.

!!!!""""C++ ofrece una forma directa de llamada por referencia, empleando parámetros de referencia. Para indicar que el parámetro de una función se pasa por referencia, ponga en el prototipo de función, después del tipo de parámetro, un &. En la llamada de la función, mencione por nombre a la variable y será pasada como una llamada por referencia. En la función llamada, la mención de la variable con su nombre local en realidad hace referencia a la variable original del invocador. Por lo tanto, la función llamada puede modificar la variable original.

!!!!""""También se pueden crear parámetros de referencia para utilizarlos localmente como alias de otras variables dentro de una función. Las variables de referencia deben inicializarse en sus declaraciones y no se pueden reasignar como alias de otras variables. Una vez declarada una variable de referencia como alias de otra variable, todas las operaciones supuestamente efectuadas sobre el alias en realidad se llevan a cabo en la variable.

PREGUNTAS Y PROBLEMAS PREGUNTAS 1. ¿Cuáles son las tres cosas que se deben especificar en el encabezado de una función? 2. Explique la diferencia entre un argumento real y un parámetro formal. 3. Llene los siguientes espacios en blanco:

a) Los componentes de los programas C++ se llaman ______________ y _____________. b) Las funciones se llaman por medio de _____________________. c) Las variables que sólo son conocidas dentro de la función en la que se definen se llaman

______________________. d) La instrucción ________________ en una función llamada sirve para devolverle el valor de una

expresión al invocador. e) La palabra clave ___________ sirve en los encabezados de función para indicar que la función no

devuelve un valor que ésta no contiene parámetros. f) El _________________ de un identificador es la parte del programa en la que dicho identificador puede

utilizarse. g) Las tres maneras de devolverle el control al invocador desde una función llamada son ____________,

_________________ y __________________. h) Un ____________ le permite al compilador revisar el número, clase y orden de los argumentos pasados a la

función. 4. ¿Cuáles de las siguientes encabezado de funciones no son válidas? Explique por qué no son válidas.

a. float promedio(num1, num2) b. int masGrande(x, y: int) c. float masPequeno(float a, b) d. string resultado (char carácter)

5. Escribir el encabezado apropiado para las siguientes funciones: a. inverso de x: 1/x

MIGUEL Á. TOLEDO MARTÍNEZ

FUNCIONES - LECCIÓN - 10 10-45

b. tan(x) c. Convierta un valor entero a una letra de calificación. d. Convierta grados Fahrenheit a grados Celsius. e. Calcule el factorial de cualquier entero N (N!). f. Calcule el promedio de tres enteros. g. La función hipotenusa, que toma dos argumentos de punto flotante de doble precisión, lado1 y lado2, y

devuelve un resultado de punto flotante de doble precisión. h. La función masPequeña(), que toma tres enteros, x, y y z, y devuelve un entero. i. La función instrucciones(), que no recibe ningún argumento y no devuelve ningún valor. (Nota: tales

funciones sirven por lo general para desplegarle instrucciones al usuario). j. La función flotante(), que toma un argumento entero, numero, y devuelve un resultado de punto flotante.

6. Dé los prototipos de función de la pregunta 4. 7. Verdadero o falso: Cuando una función no tiene una clase de datos de regreso, deberá indicar esto con la

palabra reservada null. 8. Explique la diferencia entre un parámetro de valor y un parámetro de referencia. 9. Cuando pasa un parámetro por referencia a una función.

a. El argumento real toma el valor del parámetro formal. b. El parámetro formal toma el valor del argumento real. c. El argumento real refleja cualquier cambio al parámetro formal después de la ejecución de la función. d. a y b e. b y c f. a y c

10. Cuándo usa un operador de asignación para llamar a una función, la función debe incluir un enunciado ________________.

11. ¿Cuáles de los siguientes prototipo de funciones no son válidas? Explique por qué no son válidas. a. void imprimeEncabezado(); b. int error(float num1, char num2); c. void obtenDato(&int cantidad, char fecha); d. float promedio(int numero, float total); e. char muestra(int, char, float);

12. Verdadero o falso: Un objeto variable definido en main() es visible en todas las funciones llamadas por main().

13. ¿Cuáles de los siguientes son parámetros de valores y cuáles son parámetros de referencia? a. char probA(char &A, char &B, float X, int Y); b. int probB(int num1, int num2, int num3, float &prom); c. float probC(int, float, char &);

14. Escriba el encabezado apropiado para las siguientes funciones: a. Una llamada de función muestra() que deberá regresar un valor de punto flotante y recibir un entero, un

valor de punto flotante y un carácter (en ese orden) cuando se llama. b. Una llamada a la función saltar() que hará que la impresora salte un determinado número de líneas,

donde el número de líneas para saltar se obtiene desde el programa llamador. c. Una llamada a la función intercambiar() que intercambiará los valores de dos variables enteras obtenidas

desde el programa llamador y regresará los valores intercambiados al programa llamador. d. Una llamada de función hipot() que regresará la hipotenusa de un triángulo rectángulo, dados los

valores de los catetos desde el programa llamador. 15. Escriba prototipos para las funciones en la pregunta 12. 16. Escriba enunciados C++ que llamarán las cuatro funciones de la pregunta 12. 17. Dada la siguiente función:

void intercambia(int &x, int &y) {

int temp; temp = x; x = y;

MIGUEL Á. TOLEDO MARTÍNEZ

FUNCIONES - LECCIÓN - 10 10-46

y = temp; } // Fin de intercambia() Determine la salida para cada uno de los siguientes segmentos de código que llama a la función intercambia(). a. A = 2;

B = 10; cout << “A = ” << A << “ B = ” << B << endl; intercambia(A,B); cout << “A = ” << A << “ B = ” << B << endl;

b. A = 20; B = -5; cout << “A = ” << A << “ B = ” << B << endl; if (A < B)

intercambia(A,B); else

intercambia(B,A); cout << “A = ” << A << “B = ” << B << endl;

c. num1 = 1; num2 = 5; for(int cuenta = 5; cuenta; -- cuenta)

{ intercambia(num1, num2); cout << “Num1 = ” << num1 << “ Num2 = ” << num2 << endl; ++ num1; --num2;

} // Fin del for

18. Encuentre los errores en los siguientes segmentos de programa y explique cómo pueden corregirse.

a) int g(void) {

cout << “Dentro de la función g” << endl; int h(void)

{ cout << “Dentro de la función h” << endl;

}//Fin de h() }//Fin de g()

b) int sum( int x, int y) {

int resultado;

resultado = x + y; }

c) int sum( int n ) {

if ( n == 0 ) return 0

else n + sum ( n – 1 ):

}

d) void f( float a ); {

float a;

MIGUEL Á. TOLEDO MARTÍNEZ

FUNCIONES - LECCIÓN - 10 10-47

cout << a << endl; }

e) void producto ( void ) {

int a, b, c, resultado; cout << “Introduzca tres enteros: ”; cin >> a >> b >> c; resultado = a * b * c; cout << “El resultado es ” << resultado; return resultado;

}

f) float cubo( float ); /* prototipo de función */ .......... cubo( float numero ) /* definición de la función */

{ return numero * numero * numero;

} g) float y = 123.45678;

int x; x = y; cout << static_cast<float> (x) << endl;

h) double cuadrado(double numero) {

double numero; return numero * numero;

}

i) void producto ( void ) }

19. ¿Por qué un prototipo de función contendría una declaración de tipo de parámetro como float &? 20. (Verdadero/falso) Todas las llamadas de C++ se realizan mediante llamadas por valor.

PROBLEMAS Escriba las funciones para realizar las siguientes tareas: 1. Convierta una temperatura en grados Fahrenheit a grados Celsius. 2. Busque xy, donde x es un valor real e y un valor entero. 3. Calcule la tan(θθθθ), para un ángulo θθθθ en grados. 4. Busque el inverso (1/x) de cualquier valor real x. 5. Busque el máximo de dos valores enteros. 6. Busque el mínimo de dos valores enteros. 7. Calcule n! usando iteración. 8. Examine un rango de valores y regrese el valor booleano verdadero si un valor está dentro del rango y

falso si está fuera del rango. 9. Encuentre el balance bancario al final de un mes determinado para un valor inicial de depósito y tasa de

interés. Defina el número de meses, depósito y tasa de interés variables en main() y páselos a su función. 10. Muestre su nombre, clase, instructor y hora. Nota: Para mostrar su nombre e instructor la función deberá

aceptar un arreglo de caracteres. Para hacer que la función acepte un arreglo, simplemente proporcione una

MIGUEL Á. TOLEDO MARTÍNEZ

FUNCIONES - LECCIÓN - 10 10-48

definición de arreglo en el encabezado de la función. Por ejemplo, suponga que define un arreglo en main() para contener su nombre, como sigue:

char nombre[MAX];

Para recibir este arreglo desde main(), simplemente repita la definición de arreglo en su encabezado de función, como sigue:

muestra(char nombre[MAX], ...)

11. ¿Qué hace que la impresora salte un determinado número de líneas, en donde el número de líneas que se va a saltar pasa a la función?

12. Intercambie dos valores de punto flotante cualesquiera. 13. Un estacionamiento público cobra una tarifa mínima de $2.00 por estacionarse hasta tres horas; luego cobra

$0.50 adicionales por hora o fracción a partir de las tres horas. La tarifa máxima por periodo de 24 horas es de $10.00. Suponga que ningún automóvil se estaciona por más de 24 horas a la vez. Escriba un programa que calcule e imprima las cuentas por estacionamiento de tres clientes que dejaron allí sus autos ayer. Usted introduce las horas de estacionamiento de cada cliente. Su programa imprimirá los resultados en forma tabular y con orden, y calculará e imprimirá el total de los recibos de ayer. También determinará el cargo por cliente utilizando la función calculaCargo(). Sus salidas deberán aparecer en el siguiente formato:

14. 15. 16. 17. 18. 14. Compare algún nuevo valor de punto flotante a un valor máximo obtenido desde el programa llamador.

Reemplace el valor máximo con el nuevo valor si el nuevo valor es más grande que el valor máximo. Use la función que desarrolló en el problema 12 para la operación de intercambio.

En los problemas 15 al 17, escriba tres funciones independientes para cada problema, como sigue:

•••• Una función para leer los valores de entrada necesarios. •••• Una segunda función para realizar los cálculos necesarios usando los valores de entrada desde la

primera función. •••• Una tercera función para mostrar los resultados de la segunda función.

15. Revise el programa de nómina que desarrolló para ACME, S.A. DE C.V. en el problema 12 de la lección 6

para emplear funciones. Recuerde que el programa de nómina calculará el pago neto de José dada la siguiente información:

Nombre del empleado Número de horas trabajadas a la semana Salario por hora ISR (7.15%) Retención federal (16%) Retención estatal (4.75%)

Suponga que ACME, S.A. DE C.V. solamente requieren escribir los primeros tres artículos cuando se ejecuta el programa. Genere un informe usando el siguiente formato:

Nombre del empleado: XXXXXXXXXXXXXXXXXXXXXXXXXXXXX

Salario por hora: $XXX.XX Número de horas trabajadas a la semana: XX.XX Pago bruto: $XXXX.XX

Carro Horas Cargo 1 1.5 2.00 2 4.0 2.50 3 24.0 10.00 TOTAL 29.5 14.50

MIGUEL Á. TOLEDO MARTÍNEZ

FUNCIONES - LECCIÓN - 10 10-49

Deducciones: ISR: $XXX.XX Retención federal: XXX.XX Retención estatal: XXX.XX -------------- Total de deducciones: $XXX.XX

Pago neto: $XXXX.XX Nota: Para obtener el nombre del empleado, la función deberá aceptar y regresar un arreglo de caracteres. Para

hacer que la función acepte y regrese un arreglo, simplemente proporcione una definición de arreglo en el encabezado de la función. Por ejemplo, suponga que define un arreglo en main() para obtener su nombre, como sigue:

char nombre[MAX];

Para aceptar este arreglo desde main() y regresarlo a main(), simplemente repita la definición del arreglo en el encabezado de la función, como ésta:

muestra(char nombre[MAX], ...)

16. Encuentre la altura a la cual la escalera en la figura 10.10 hace contacto con el muro, dada la longitud de la escalera y la distancia de la base de la escalera al muro.

Altura de la escalera Longitud de la escalera

Distancia al muro

Figura 10.10. Una escalera para el problema 17

17. Una secuencia de números Fibonacci se define como sigue:

F0 = 0 F1 = 1 ......... Fn = Fn-1 + Fn-2 para n ≥≥≥≥ 2

Esto quiere decir que los primeros dos números en la secuencia son 0 y 1. Entonces, cada número Fibonacci adicional es la suma de los dos números anteriores en la secuencia. De esta manera, los primeros 10 números Fibonacci son:

MIGUEL Á. TOLEDO MARTÍNEZ

FUNCIONES - LECCIÓN - 10 10-50

0, 1, 1, 2, 3, 5, 8, 13, 21, 34

Aquí, decimos que el primer número ocupa la posición 0 en la secuencia, el segundo número la posición 1 en la secuencia, y así sucesivamente. Así, la última posición en una secuencia de 10 números es la posición 9. Desarrolle un programa que emplee una función iterativa para generar una secuencia Fibonacci de todos los números hasta alguna posición n escrita por el usuario.

18. Escriba una función potenciaEntera(base, exponente) que devuelva el valor de:

baseexponente

Por ejemplo, potenciaEntera(3, 4) = 3 * 3 * 3 * 3. Suponga que exponente es un entero positivo diferente de cero y que base es un entero. La función potenciaEntera() debería utilizar for o while para controlar el cálculo. No utilice funciones de la biblioteca matemática.

19. Defina una función hipotenusa que calcule la longitud de la hipotenusa de un triángulo rectángulo cuando se dan los otros dos lados. Emplee esta función en un programa para determinar la longitud de la hipotenusa de los siguientes triángulos. La función deberá tomar dos argumentos del tipo double y devolver la hipotenusa como double.

20. Escriba una función múltiplo() que tome dos enteros y determine si el segundo es un múltiplo del primero. La

función deberá tomar dos argumentos enteros y devolver verdadero si el segundo es un múltiplo del primero y falso si no. Utilice esta función en un programa que acepte como entrada una serie de pares de enteros.

21. Escriba un programa que acepte como entrada una serie de enteros y los pase, uno a la vez, a la función par(), que se vale del operador de módulo para determinar si un entero es par. La función deberá tomar un argumento entero y devolver verdadero si es par y falso si es non.

22. Escriba una función que presente en el margen izquierdo de la pantalla un cuadrado de asteriscos cuyo lado esté especificado en el parámetro entero lado. Por ejemplo, si lado es 4, la función presentará: **** **** **** ****

23. Modifique la función del problema 22 para formar el cuadrado con el carácter contenido en el parámetro de carácter llenaCaracter. Por lo tanto, si lado es 5 y llenaCaracter es #, entonces la función imprimirá: #### #### #### ####

24. Utilice técnicas parecidas a las desarrolladas en los problemas 22 y 23 para generar un programa que trace una amplia gama de formas.

25. Escriba segmentos de programa que realicen lo siguiente: a. Calcule la parte entera del cociente cuando el entero a se divida entre el entero b. b. Calcule el residuo entero cuando el entero a se divida entre el entero b. c. Utilice las partes de programa desarrolladas en a) y b) para escribir una función que acepte un entero

entre 1 y 32767 y lo imprima como una serie de dígitos, separando cada uno con dos espacios. Por ejemplo, el entero 4563 deberá imprimirse como:

Triángulo Lado 1 Lado2 1 3.0 4.0 2 5.0 12.0 3 8.0 15.0

MIGUEL Á. TOLEDO MARTÍNEZ

FUNCIONES - LECCIÓN - 10 10-51

4 5 6 2 26. Escriba una función que tome el tiempo como tres argumentos enteros (horas, minutos y segundos) y

devuelva el número de segundos transcurridos desde que el reloj dio las 12. Con esta función, calcule el tiempo en segundos entre dos horas, ambas comprendidas dentro de un ciclo de 12 horas del reloj.

27. Implemente las siguientes funciones enteras: a. La función celsius() devuelve el equivalente en grados centígrados de una temperatura eh Fahrenheit. b. La función fahrenheit() devuelve el equivalente en grados Fahrenheit de una temperatura en grados

centígrados. c. Con estas funciones, escriba un programa para imprimir gráficos que muestren el equivalente en grados

centígrados de todas las temperaturas Fahrenheit entre 32 y 212 grados. Imprima las salidas de forma tabular, para minimizar la cantidad de líneas de salida y simplificar la lectura.

28. Escriba una función que devuelva el menor de tres números de punto flotante. 29. Se dice que un número entero es un número perfecto si la suma de sus factores, incluyendo el 1 (pero no el

número mismo), da tal número. Por ejemplo, el 6 es un número perfecto porque 6 = 1 + 2 + 3. Escriba una función perfecto() que determine si el parámetro numero es un número perfecto. Utilice esta función en un programa que determine e imprima todos los números perfectos entre 1 y 1000. Imprima los divisores de todos los números perfectos, para confirmar que, de hecho, se trata de un número perfecto. Pruebe el poder de su computadora probando con números mucho mayores que 1000.

30. Se dice que un número es primo si sólo se puede dividir entre 1 y entre sí mismo. Por ejemplo, 2, 3, 5 y 7 son primos, pero 4, 6, 8 y 9 no. a. Escriba una función que determine si un número es primo. b. Con esta función, haga un programa que determine e imprima todos los números primos entre 1 y 10,000.

¿Cuántos de estos 10,000 números debe probar en realidad antes de tener la seguridad de haber encontrado todos los primos?

c. Inicialmente podría pensar que n/2 es el límite superior que debería probar para ver si se trata de un número primo, pero sólo necesita llegas hasta la raíz cuadrada de n. ¿Por qué? Rescriba el programa y ejecútelo de ambas manera. Estime la mejora de desempeño.

31. Escriba una función que tome un valor entero y devuelva el número con sus dígitos en reversa. Por ejemplo, dado el número 7631, la función deberá devolver 1367.

32. El máximo común divisor de dos enteros es el mayor entero que divide ambos números. Escriba una función mcd() que devuelva el máximo común divisor de dos enteros.

33. Escriba una función puntosCalidad() que acepte el promedio de un estudiante y devuelva 4 si su promedio está entre 90 y 100, 3 si está entre 80 y 89, 2 si está entre 70 y 79, 1 si está entre 60 y 69 y 0 si es menor que 60.

34. Escriba una función distancia() que calcule la distancia entre dos puntos (x1, y1) y (x2, y2). Todos los números y valores devueltos deben ser de clase float.

MIGUEL Á. TOLEDO MARTÍNEZ

FUNCIONES - LECCIÓN - 10 10-52

EXAMEN BREVE 27

1. ¿Cuál es el papel de una función en un programa C++? 2. Las tres secciones principales de una función son las secciones________________,

________________ y ________________________. 3. ¿Cuál es el propósito del encabezado de la función en un programa C++? 4. Liste las tres partes del encabezado de una función. 5. A una variable de función que espera recibir un valor del programa llamador, se conoce como

___________________________. 6. ¿Cuál es el propósito de un enunciado return en una función? 7. Explique la diferencia entre un argumento real en un programa llamador y un parámetro formal en

el encabezado de una función.

EXAMEN BREVE 28

1. ¿Qué deberá usar como clase de datos de regreso cuando una función no regresa un solo valor al programa llamador?

2. ¿Cuáles son las dos cosas que deben considerarse cuando se desarrolle un encabezado de función? 3. La comunicación de datos en un sentido desde el programa llamador a la función se proporciona por

medio de los parámetros de ______________________________. 4. La comunicación de datos de ida y vuelta entre el programa llamador y una función se proporciona

por medio de los parámetros de ______________________________. 5. Para especificar un parámetro de referencia en un encabezado de función, debe usarse el símbolo

_____________ antes del identificador del parámetro. 6. ¿Dónde se localiza el cuerpo de una función en un programa C++?

EXAMEN BREVE 29

1. ¿Cuál es el propósito principal de una función prototipo? 2. ¿Dónde se localiza normalmente una función prototipo en un programa C++? Verdadero o falso: Los parámetros listados en un prototipo de función se pueden integrar en la relación solamente por las clases de datos, sin ningún identificador correspondiente.

RESPUESTA EXAMEN BREVE 27 1. El papel de una función en un programa C++ es eliminar la necesidad de duplicar los enunciados. El

uso de las funciones permite resolver problemas grandes y complicados dividiendo el problema en subproblemas más pequeños, que se manejan con más facilidad (método descendente)

2. Las tres secciones principales de una función son las secciones encabezado de la función, variables o constantes locales y enunciado.

3. En C++, el encabezado de la función en un programa es la interfaz de datos para la función. Este forma una frontera común entre la función y el programa llamador.

4. Las tres partes del encabezado de una función son las siguientes:

MIGUEL Á. TOLEDO MARTÍNEZ

FUNCIONES - LECCIÓN - 10 10-53

•••• La clase de datos del valor que regresará la función, si hay alguno. •••• El nombre de la función. •••• Una lista de parámetros. 5. A una variable de función que espera recibir un valor del programa llamador, se conoce con el

nombre de parámetro. 6. El enunciado return en una función se usa cuando se va a regresar un solo valor al programa

llamador. 7. La diferencia entre un argumento real en un programa llamador y un parámetro formal en un

encabezado de función es que el argumento real contiene el valor que pasa al parámetro formal en el encabezado de la función. En otras palabras, el parámetro formal recibe el valor del argumento real en el programa llamador.

RESPUESTA EXAMEN BREVE 28 1. void se debe usar como la clase de datos de regreso cuando una función no regresa un valor al

programa llamador. 2. Las dos cosas que se deben considerar cuando se desarrolla el encabezado de la función son las

siguientes: •••• Qué aceptará la función para realizar su tarea. •••• Qué regresará la función. 3. La comunicación de datos en un sentido desde el programa llamador a la función se proporciona por

medio de los parámetros de valor. 4. La comunicación de datos de ida y vuelta entre el programa llamador y una función se proporciona

por medio de los parámetros de referencia. 5. Para especificar un parámetro de referencia en un encabezado de función, debe usarse el símbolo

ampersand (&) antes del identificador del parámetro. 6. El cuerpo de una función normalmente se localiza después de cerrar la llave de la función main()

RESPUESTA EXAMEN BREVE 29 1. El propósito principal de un prototipo de función es permitir que el compilador verifique la

correspondencia entre los argumentos reales en una llamada de función y los parámetros formales que la función espera recibir.

2. Un prototipo de función normalmente se localiza después de las instrucciones del preprocesador y antes de la función main() en un programa de C++.

Verdadero: Los parámetros que se listan en un prototipo de función se pueden integrar en la relación solamente por las clases de datos, sin ningún identificador correspondiente.