7 PEC5
7.1 Cuándo utilizar & dentro de funciones/acciones
A veces cuando hacemos llamadas a acciones dentro de acciones, vemos que si pasamos un parámetro con &
los resultados no son correctos, pero en cambio el parámetro sin el &
sí que funciona. Y al revés.
Para aclarar estas dudas usaremos el siguiente ejemplo: es un programa sencillo que trabaja con tipo tPec
. Un elemento tPec
contiene dos atributos: un nombre
(cadena de caracteres) y una nota
(decimal). El programa hace la lectura de valores desde teclado con la acción pecRead(...)
, la modificación del nombre de la PEC con nombreToUpperCase (...)
, y su posterior impresión por pantalla mediante pecWrite(...)
.
Con el objetivo de dejarlo todo lo más claro posible se han añadido comentarios extensos dentro del programa, explicando en cada situación qué se hace y por qué.
/* Ejemplo ES0701 */
#include <stdio.h>
#include <string.h>
/* Definición de constantes */
#define MAX_NOMBRE 5+1
/* Definición de la tupla tPec */
typedef struct {
char nombre[MAX_NOMBRE];
float nota;
} tPec;
/* Predeclaración de funciones y acciones */
/* Acción que lee por teclado los atributos de un
* tipo tPec y le asigna los valores. En este
* caso el parámetro pec es de clase 'out', dado que
* su valor antes y después de ejecutar la acción
* Hhabrá cambiado. Además, dado que el valor inicial
* de pec no nos interesa para nada (recordemos que
* el objetivo de esta acción es leer de teclado
* y dar valor a pec, por lo tanto sobrescribir
* cualquier valor previo que tenga), podemos
* descartar que sea de clase 'inout'.
* Cuando un parámetro es de clase out/inout, lo
* pasamos por referencia o, lo que es lo mismo,
* pasamos un puntero a un tipo de elemento; como
* se puede ver, aquí pec es un puntero a un
* elemento de tipo tPec, ya que va precedido por *.
* Utilizamos un puntero porque es la única manera
* que tenemos de modificar un elemento definido
* fuera del ámbito de la acción, desde dentro de
* la propia acción.
*/
void pecRead(tPec *pec);
/* Acción que, dado un tipo tPec, muestra su
* contenido (nombre y nota) por pantalla. Aquí el
* parámetro pec es de clase 'in': su valor
* antes y después de ejecutar la acción no variará.
* Un parámetro de clase 'in' lo pasamos por valor:
* en este caso es un elemento de tipo tPec.
* Como se puede ver, no tiene que ir precedido por *.
*/
void pecWrite(tPec pec);
/* Acción que, dado un tipo tPec, coge su
* nombre y lo pasa a mayúsculas. Por ejemplo, si
* el nombre es "pEc01" lo modificará por "PEC01".
* El parámetro es de clase 'inout': su valor
* antes y después de ejecutar la acción habrá cambiado
* y además, el valor inicial que tiene es
* importante, ya que lo necesitamos para calcular el
* valor final ( "pEc01" -> "PEC01"). Al ser un
* parámetro de clase 'inout', pasaremos su
* valor por referencia: necesitamos que pueda ser
* modificado desde dentro de la acción, por lo que
* es necesario trabajar con un puntero al elemento
* pec, de ahí que vaya precedido con *.
*/
void nombreToUpperCase(tPec *pec);
/* Programa principal */
int main(int argc, char **argv) {
/* Definimos las variables */
tPec pec1, pec2, pec3;
/* Damos valor a los atributos de cada una
* de las 3 PEC. Tened en cuenta que estamos pasando
* punteros a elementos de tipo tPec: el &
* previo indica que tomamos la dirección de
* memoria donde reside el elemento de tipo
* tPec. Como pasamos punteros a memoria,
* desde dentro de la acción podremos modificar
* el contenido de pec1, pec2 y pec3, aunque
* estas tres variables hayan sido
* definidas fuera del ámbito de la acción.
*/
pecRead(&pec1);
pecRead(&pec2);
pecRead(&pec3);
/* Mostramos por pantalla los atributos de
* cada una de las 3 PEC. En este caso
* el paso de parámetros se hace por valor:
* pasamos directamente los elementos de tipo
* tPec, ya que estos no serán modificados
* desde dentro de la acción (son de clase 'in').
*/
pecWrite(pec1);
pecWrite(pec2);
pecWrite(pec3);
return 0;
}
/* Implementación de funciones y acciones */
void pecRead(tPec *pec) {
/* Leemos desde teclado el valor
* correspondiente al nombre de la PEC
*/
printf("Introduce nombre : ");
scanf("%s", pec->nombre);
/* Leemos desde teclado el valor
* correspondiente a la nota de la PEC
*/
printf("Introduce nota: ");
scanf("%f", &pec->nota);
/* Ahora llegamos a un punto donde, dentro de una
* acción, llamamos a otra acción. ¿Debemos
* pasar como atributo pec, o &pec?
* Ante esta duda, debemos preguntarnos
* qué tipo de valor contiene ahora
* mismo (antes de ejecutar la siguiente acción)
* el parámetro pec. Si recordamos cómo está
* definida la acción pecRead (tPec *pec), pec
* es un puntero a memoria, ya que va precedido
* de *. Esto significa que en este preciso
* momento pec sigue siendo un puntero.
* Por otro lado, si nos fijamos en la definición
* de la acción nombreToUpperCase(tPec *pec), vemos
* que también espera recibir un puntero. Dado que
* pec es un puntero y nomToUpperCase(...)
* espera recibir un puntero, simplemente le pasamos
* pec como parámetro (¡y no &pec!)
*/
nombreToUpperCase(pec);
printf("----------------------\n");
}
void pecWrite(tPec pec) {
/* Mostramos por pantalla los valores
* de los atributos de la PEC
*/
printf(">> %s con nota %.1f \n", pec.nombre, pec.nota);
}
void nombreToUpperCase(tPec *pec) {
/* Hay librerías que ya implementan los cambios
* entre mayúsculas y minúsculas. En este caso
* hemos optado por no utilizar ninguno e implementarlo
* nosotros mismos, tratando el string nombre de pec
* como un recorrido carácter a carácter.
*/
int i;
for (i = 0; pec->nombre[i] != '\0'; i++) {
if (pec->nombre[i] >= 'a' && pec->nombre[i] <= 'z') {
pec->nombre[i] = pec->nombre[i] + ('A' - 'a');
}
}
}
7.2 Multiple definition of NOMBRE_CONSTANTE
Cuando usamos constantes definidas mediante const
en un programa modulado se puede obtener el error multiple definition of NOMBRE_CONSTANTE
.
Por ejemplo: tenemos el siguiente programa modulado en tres ficheros, dni.h
dni.c
main.c
, que calcula la letra que le corresponde a un DNI.
El contenido de dni.h
es:
const char CODIGO[] = "TRWAGMYFPDXBNJZSQVHLCKE";
const int DENOMINADOR = 23;
/* Predeclaración de las funciones/acciones */
int leer();
char calcularLetra(int numero);
void mostrar(int numero, char lletra);
El contenido de dni.c
es:
#include <stdio.h>
#include "dni.h"
/* Implementación de las funciones/acciones */
int leer() {
int dni;
printf("Teclea un DNI (sin letra): ");
scanf("%d", &dni);
return dni;
}
char calcularLetra(int numero) {
int resultado;
resultado = numero % DENOMINADOR;
return CODIGO[resultado];
}
void mostrar(int numero, char letra) {
printf("El DNI con letra es: %d-%c \n", numero, letra);
}
El contenido de main.c
es:
/* Ejemplo ES0702 */
#include "dni.h"
/* Código principal */
int main(int argc, char **argv) {
int dni;
char letra;
dni = leer();
letra = calcularLetra(dni);
mostrar(dni, letra);
return 0;
}
Si ejecutamos el programa, devolverá los siguientes errores:
#include "dni.h"
... dni.h:3: multiple definition of `CODI'
... dni.h:3: first definition here
... dni.h:4: multiple definition of `DENOMINADOR'
... dni.h:4: first definition here
El error está causado porque se realiza el include de dni.h
en dos puntos distintos del programa:
- En el fichero
dni.c
, para poder usar las constantes dedni.h
. - En el fichero
main.c
, para poder usar las acciones y funciones predeclaradas endni.h
.
Este hecho intenta definir en memoria dos veces la constante CODIGO
y dos más la constante DENOMINADOR
, una situación que no es posible: no se permite tener múltiples constantes definidas con el mismo nombre en un mismo ámbito.
Para solucionar el error tenemos dos opciones:
Opción 1:
Usar #define
en vez de const
. Cambiando de esta forma la definición de las constantes en el fichero dni.h
se soluciona l’error:
/* Ejemplo ES0703 */
#define CODIGO "TRWAGMYFPDXBNJZSQVHLCKE"
#define DENOMINADOR 23
/* Predeclaración de las funciones/acciones */
int leer();
char calcularLetra(int numero);
void mostrar(int numero, char letra);
Opción 2:
La opción static
permite modificar el ámbito de la constante, pasando de ser una constante de clase a una constante de fichero, impidiendo de esta forma que haya duplicados en el ámbito global del programa. Por tanto, podemos solucionar el error añadiendo static
en la definición de las constantes con const
dentro del fichero dni.h
.
/* Ejemplo ES0704 */
static const char CODIGO[] = "TRWAGMYFPDXBNJZSQVHLCKE";
static const int DENOMINADOR = 23;
/* Predeclaración de las funciones/acciones */
int leer();
char calcularLetra(int numero);
void mostrar(int numero, char letra);
7.3 Tipos de parámetros en acciones y funciones
En las acciones los parámetros pueden ser de clase in
, out
o inout
. Debe especificarse en el momento de definir la acción a nuestro algoritmo.
Ejemplo: tres formas de implementar una suma de dos enteros con diferentes acciones según las clases in
, out
o inout
de los parámetros:
action suma1(in num1: integer, in num2: integer)
var
resultado: integer;
end var
resultat := num1 + num2;
writeString("Resultado de la suma = ");
writeInteger(resultado);
end action
action suma2(in num1: integer, in num2: integer, out resultado: integer)
resultado := num1 + num2;
end action
action suma3(inout num1: integer, in num2: integer)
num1 := num1 + num2;
end action
En las funciones, en cambio, todos los parámetros son de entrada, con lo que no es necesario indicar el in
.
Ejemplo:
function suma4(num1: integer, num2: integer): integer
var
resultado: integer;
end var
resultado := num1 + num2;
return resultado;
end function
7.4 Paso por valor vs paso por referencia
El clásico paso por valor corresponde a los parámetros de tipo in
, en los que pasamos la variable/valor.
Por otra parte, el paso por referencia consiste en pasar como parámetro de tipo out
o inout
la dirección de memoria de la variable (puntero).
7.5 Ejemplo: invertirPalabra
Imaginemos que tenemos la tupla tPalabra
que tiene dos campos:
cadena
: contiene el string con el valor de latPalabra
.numeroCaracteresCadena
: contiene el número de caracteres de la cadena.
Implementamos la acción invertir()
, que hace dos operaciones:
- Invierte (gira) la
cadena
detPalabra
; por ejemplo, si introducimos “Fundamentos” el resultado será “stnemanoF”. - Calcula el valor de
numeroCaracteresCadena
; si tenemos “Fundamentos” como cadena, el valor será 9.
Queremos que por teclado se pida el valor para el campo cadena
de dos tPalabra
, y en ambas queremos aplicar la acción invertir()
. Una posible forma de implementarla sería la siguiente:
/* Ejemplo ES0705 */
#include <stdio.h>
#include <string.h>
#include <stdbool.h>
#define MAX_CHAR 20+1
#define MAX_PALABRAS 2
typedef struct {
char cadena[MAX_CHAR];
int numeroCaracteresCadena;
} tPalabra;
/* Predeclaración de la acción.
* Esta acción recibe un parámetro inout de tipo tPalabra,
* invierte (gira) el campo cadena y calcula el valor
* correspondiente para el campo numeroCaractersCadena
*/
void invertir(tPalabra *palabra);
int main(int argc, char **argv) {
int i;
/* Introducimos por teclado un total de MAX_PALABRAS */
for (i = 0; i < MAX_PALABRAS; i++) {
tPalabra palabra;
printf("Introduce una palabra : ");
scanf("%s", palabra.cadena);
invertir(&palabra);
printf("La palabra invertida es : %s, de %d letras.\n",
palabra.cadena, palabra.numeroCaracteresCadena);
}
}
/* Implementación de la acción */
void invertir(tPalabra *palabra) {
tPalabra palabraInvertida;
int longitudPalabra;
int i;
longitudPalabra = strlen(palabra->cadena);
for (i=0; i<longitudPalabra; i++ ) {
palabraInvertida.cadena[(longitudPalabra-1)-i] = palabra->cadena[i];
}
/* Indicamos el finalizador del string */
palabraInvertida.cadena[longitudPalabra] ='\0';
strcpy(palabra->cadena, palabraInvertida.cadena);
palabra->numeroCaracteresCadena = longitudPalabra;
}
La acción solo recibe un parámetro tPalabra
, ya que únicamente se ejecuta sobre una tPalabra
. Como la queremos ejecutar para cada una de las dos tPalabra
, repetimos la llamada dos veces dentro del bucle (una por cadatPalabra
).
7.6 Ejemplo: isPar
Ejemplo de función que devuelve un booleano:
/* Ejemplo ES0706 */
#include <stdio.h>
#include <stdbool.h>
/* Predeclaración de la función isPar, la cual devuelve un
* booleano que indica si el número pasado por parámetro es
* par (true) o impar (false).
*/
bool isPar(int numero);
int main(int argc, char **argv) {
int numero;
printf("Teclea un número : ");
scanf("%d", &numero);
if (!isPar(numero)) {
printf("El número %d es impar.\n", numero);
} else {
printf("El número %d es par.\n", numero);
}
}
/* Implementación de la función */
bool isPar(int numero) {
bool resultado;
if (numero % 2 == 0) {
resultado = true;
} else {
resultado = false;
}
return resultado;
/* La estructura condicional anterior se puede
* sustituir por la siguiente expresión equivalente:
* return (numero % 2 == 0);
*/
}
7.7 Ejemplo: pivoteDefensivaTirosLibres
Se añade al ejemplo previo pivoteDefensiva un nuevo factor de comparación: en caso de empate de las comparaciones anteriores, escogeremos la que tenga un mejor porcentaje de tiros libres.
/* Ejemplo ES0707 */
#include <stdio.h>
#include <string.h>
#include <stdbool.h>
/* Recibimos una petición de un equipo femenino de baloncesto,
* en la que nos piden un programa que les permita
* seleccionar la mejor pivote defensiva entre una
* serie de candidatas.
* La mejor pivote defensiva es aquella que captura
* más rebotes; en caso de empate, se elegirá la que
* haga más tapones. En caso de empate nos interesará
* escoger la que tenga mejor porcentaje en tiros libres.
* Habrá que implementar 3 acciones y 2 funciones:
* - acción leerJugadora(j): lee de teclado y
* guarda todos los atributos de la jugadora a la
* en la tupla j.
* - acción mostrarJugadora(j): muestra por pantalla
* el valor de los atributos de la tupla j.
* - acción copiarJugadoras(j1, j2): copia el valor
* de todos los atributos de j2 hacia j1.
* - función compararJugadoras(j1, j2): devuelve -1 en
* caso de que la mejor pivote sea j1, y 1 en caso
* de que la mejor sea j2.
* - función porcentajeTirosLibres(intentados, anotados):
* devuelve el porcentaje de acierto en tiros libres
* en función de los valores pasados por parámetro.
*/
#define MAX_NOMBRE 20+1
#define MAX_APELLIDO 20+1
#define MAX_JUGADORAS 3
typedef struct {
char nombre[MAX_NOMBRE];
char apellido[MAX_APELLIDO];
float rebotes;
float tapones;
float tirosLibres; /* en porcentaje */
} tJugadora;
/* Predeclaraciones de funciones/acciones */
void leerJugadora(tJugadora *j);
void mostrarJugadora(tJugadora j);
void copiarJugadora(tJugadora *destino, tJugadora origen);
int compararJugadoras(tJugadora j1, tJugadora j2);
float porcentajeTirosLibres(int intentados, int anotados);
/* Programa principal */
int main(int argc, char **argv) {
tJugadora vJugadoras[MAX_JUGADORAS];
int i, resultado;
/* Se crea la tJugadora ficticia
* mejorPivote que nos ayudará a encontrar
* la mejor opción de entre todas las
* candidatas
*/
tJugadora mejorPivote;
mejorPivote.rebotes = 0;
mejorPivote.tapones = 0;
mejorPivote.tirosLibres = 0.0;
/* Leemos todas las jugadoras con
* la acción leerJugadora(). Esta
* acción recibe un parámetro de salida
* (out), el cual contendrá la
* jugadora leída por teclado. Como
* se trata de un parámetro de tipo
* out, se realizará un paso por
* referencia (= pasaremos un puntero)
*/
for (i=0; i<MAX_JUGADORAS; i++) {
leerJugadora(&vJugadoras[i]);
}
/* Mostramos por pantalla cuál
* es la mejor jugadora de perfil
* pivote defensivo. La idea es ir
* recorriendo una a una las jugadoras
* del vector y compararlas con mejorPivote:
* 1. Si la jugadora del vector es mejor
* que mejorPivote, copiaremos los datos
* de la jugadora hacia mejorPivote.
* 2. Si mejorPivote es mejor que la
* jugadora del vector, no haremos nada.
* Al finalizar el recorrido de todas
* las jugadoras del vector, tendremos que
* mejorPivote contendrá la jugadora
* que estamos buscando.
*/
for (i=0; i<MAX_JUGADORAS; i++) {
resultado = compararJugadoras(mejorPivote, vJugadoras[i]);
if (resultado == 1) {
copiarJugadora(&mejorPivote, vJugadoras[i]);
}
}
printf("\nMejor opción como pivote defensiva : ");
mostrarJugadora(mejorPivote);
return 0;
}
/* Implementación de las funciones/acciones */
void leerJugadora(tJugadora *j) {
int intentados;
int anotados;
printf("Introduce los datos de la nueva jugadora: \n");
printf("\tNombre: ");
scanf("%s", j->nombre);
printf("\tApellido: ");
scanf("%s", j->apellido);
printf("\t>> Promedios por partido:\n");
printf("\tRebotes: ");
scanf("%f", &j->rebotes);
printf("\tTapones: ");
scanf("%f", &j->tapones);
printf("\tTiros libres intentados: ");
scanf("%d", &intentados);
printf("\tTiros libres anotados: ");
scanf("%d", &anotados);
j->tirosLibres = porcentajeTirosLibres(intentados, anotados);
}
void mostrarJugadora(tJugadora j) {
printf("\n%s, %s: %.1f rebotes, %.1f tapones, %.1f%% tiros libres \n",
j.apellido, j.nombre, j.rebotes, j.tapones, j.tirosLibres);
}
void copiarJugadora(tJugadora *destino, tJugadora origen) {
/* Recordemos:
* - si el parámetro es un puntero, accederemos a los
* atributos con '->'
* - si el parámetro es un valor, accederemos a los
* atributos con '.'
*/
strcpy(destino->nombre, origen.nombre);
strcpy(destino->apellido, origen.apellido);
destino->rebotes = origen.rebotes;
destino->tapones = origen.tapones;
destino->tirosLibres = origen.tirosLibres;
}
int compararJugadoras(tJugadora j1, tJugadora j2) {
/* Estamos buscando una jugadora que
* tenga un perfil de pivote defensivo,
* Con lo que seleccionaremos:
* 1. Aquella que tenga más rebotes por partido
* 2. En caso de empate en rebotes, aquella que
* haga más tapones por partido
* 3. En caso de empate, la que tenga mejor
* porcentaje de tiros libres
*/
int resultado;
resultado = 0;
if (j1.rebotes > j2.rebotes) {
resultado = -1;
} else {
if (j1.rebotes < j2.rebotes) {
resultado = 1;
} else {
/* En este punto tenemos que
* j1.rebotes == j2.rebotes,
* con lo que vamos a comparar el siguiente
* atributo según la prioridad definida
* para la posición de pivote defensiva
*/
if (j1.tapones > j2.tapones) {
resultado = -1;
} else {
if (j1.tapones < j2.tapones) {
resultado = 1;
} else {
/* Añadimos la variante de valorar
* el porcentaje de acierto en tiros libres
*/
if (j1.tirosLibres >= j2.tirosLibres) {
resultado = -1;
} else {
resultado = 1;
}
}
}
}
}
return resultado;
}
float porcentajeTirosLibres(int intentados, int anotados) {
return ((float)anotados/intentados)*100.0;
}
7.8 Ejemplo: IMC
Imaginemos que queremos un programa que calcule el IMC (índice de masa corporal) de una persona. El programa tendrá dos acciones y una función:
- leerDatos(): acción con 2 parámetros de clase
out
correspondientes al pes y la altura de la persona. - calcularIMC(): funció con 2 parámetros de clase
in
, el pes y la altura, que devolverá un valor decimal. - mostrarIMC(): acción con 1 parámetro de clase
in
, el valor del IMC, que será mostrado por pantalla.
En primer lugar, diseñamos el algoritmo:
action leerDatos(out peso: real, out altura: real)
writeString("Introduce el peso (kg): ");
peso := readReal();
writeString("Introduce la altura (m): ");
altura := readReal();
end action
function calcularIMC(peso: real, altura: real): real
var
imc: real;
end var
imc := peso / (altura * altura);
return imc;
end function
action mostrarIMC(in imc: real)
writeString("IMC = ");
writeReal(imc);
end action
algorithm IMC
var
peso: real;
altura: real;
resultado: real;
end var
leerDatos(peso, altura);
resultado := calcularIMC(peso, altura);
mostrarIMC(resultado);
end algorithm
A continuación hacemos la conversión de algoritmo a lenguaje C. Se han añadido explicaciones en forma de comentarios para que se vea con claridad el tipo de parámetros usados (puntero o valor) según su clase:
/* Ejemplo ES0708 */
#include <stdio.h>
/* Predeclaración de funciones y acciones */
/* Los parámetros peso y algura de la acción
* leerDatos son de clase out, por lo que
* los precedemos con * (nos indica que son
* punteros).
*/
void leerDatos(float *peso, float *altura);
/* Los parámetros peso y altura de la función
* calcularIMC son de clase in, como lo son
* todos los parámtros de las funciones.
*/
float calcularIMC(float peso, float altura);
/* El parámetro imc de la acción mostrarIMC
* es de clase in.
*/
void mostrarIMC(float imc);
int main(int argc, char **argv) {
/* Definición de las variables */
float peso;
float altura;
float resultado;
/* En la acción leerDatos() le pasamos los punteros
* de las variables peso y altura,
* para que su valor pueda ser modificado
* desde dentro de la acción. Son parámetros de
* clase out.
*/
leerDatos(&peso, &altura);
/* En el caso de la función calcularIMC() y la acción
* mostrarIMC(), al ser todos los parámetros de
* clase in los pasamos por valor.
*/
resultado = calcularIMC(peso, altura);
mostrarIMC(resultado);
return 0;
}
/* Implementación de funciones y acciones */
/* La acción leerDatos se ocupa de leer desde
* teclado el peso y la altura de una persona,
* y asigna el valor a los parámetros peso y
* altura. Fijáos que tanto el peso como la
* altura son punteros (van precedidos de *),
* por lo que al ejecutar el scanf ya no es
* necesario añadir el prefijo &.
*/
void leerDatos(float *peso, float *altura) {
printf("Introduce el peso (kg): ");
scanf("%f", peso);
printf("Introduce la altura (m): ");
scanf("%f", altura);
}
/* Función que calcula el IMC */
float calcularIMC(float peso, float altura) {
float imc;
imc = peso / (altura*altura);
return imc;
}
/* Acción que muestra por pantalla el valor
* que contiene el parámetro imc, de clase in.
*/
void mostrarIMC(float imc) {
printf("IMC = %.1f \n", imc);
}
A continuación se muestra un ejemplo de ejecución del programa:
Introduce el peso (kg): 56
Introduce la altura (m): 1.69
IMC = 19.6
7.9 Ejemplo: mediaNotas
Queremos un programa que calcule la media de 3 PEC. La nota de cada PEC la entramos desde teclado y tiene que ser un valor entre [0..10]. En caso que alguna de las notas no pertenezca a este rango, queremos que el programa muestre un menesaje de error y finalice.
En este caso no queremos que el bucle se limite a realizar las n-iteraciones correspondientes: queremos que, dada una situación concreta, se pueda salir del bucle. Lo podemos conseguir añadiendo una variable booleana a la condición de entrada del bucle (en el caso de este ejemplo, isValorCorrecto
).
Una posible codificación del programa en lenguaje C es la siguiente:
/* Ejemplo ES0709 */
#include <stdio.h>
#include <stdbool.h>
#define NUM_PECS 3
int main(int argc, char **argv) {
float nota;
float sumaNotas;
bool isValorCorrecto;
int i;
isValorCorrecto = true;
sumaNotas = 0.0;
i = 0;
/* Se usa un bucle while que tiene dos
* condiciones de finalización:
* - que haya realizado todas las iteraciones
* posibles (valor indicado por NUM_PECS).
* - que detecte que la nota tecleada
* no pertenezca al rango [0..10].
*/
while (i<NUM_PECS && isValorCorrecto) {
printf("Nota PEC%d: ", i + 1);
scanf("%f", ¬a);
/* Si la nota tecleada está fuera del
* rango [0..10], modificamos el valor
* de la variable booleana isValorCorrecto
* a false. Esto provocará que la condición
* de entrada al bucle sea false y, por lo
* tanto, saldremos de él.
*/
if (nota < 0.0 || nota > 10.0) {
isValorCorrecto = false;
/* Si el valor de la nota es correcto,
* continuamos con las operaciones correspondientes.
*/
} else {
sumaNotas = sumaNotas + nota;
i = i + 1;
}
}
/* Si no se ha detectado ningún error en las notas
* significará que el valor de isValorCorrecto continua
* siendo true, por lo que mostraremos el resultado de la
* media por pantalla.
*/
if (isValorCorrecto) {
printf("Media = %.2f \n", sumaNotas / (float)NUM_PECS);
/* En caso contrario, mostramos el mensaje de error. */
} else {
printf("Error: ¡el valor de la nota es incorrecto! \n");
}
return 0;
}
Un ejemplo de ejecución sin errores:
Nota PEC1: 9.0
Nota PEC2: 10.0
Nota PEC3: 8.4
Media = 9.13
Un ejemplo de ejecución con error:
Nota PEC1: 9.3
Nota PEC2: 12
Error: ¡el valor de la nota es incorrecto!