3 PAC3
3.1 Com declarar un vector en llenguatge algorísmic
Quan es declara un vector en llenguatge algorísmic no cal declarar una variable per cadascuna de les posicions d’aquest vector.
Per exemple, si volem un algorisme que sumi tres enters continguts dins d’un vector, podem fer el següent:
const
NUM_ENTERS: integer = 3;
end const
algorithm sumaEnters
var
enters: vector[NUM_ENTERS] of integer;
suma: integer;
end var
enters[1] := 13;
enters[2] := 24;
enters[3] := 2;
{ Com es pot veure, accedim directament a les posicions }
{ del vector en comptes de definir una variable }
{ per cada posició. }
suma := enters[1] + enters[2] + enters[3];
{ Cal recordar també que en llenguatge algorísmic, }
{ en un vector de mida n la primera posició del vector }
{ és la 1 i la darrera la n; en canvi en llenguatge C }
{ la primera sempre és la 0 i l'última la n-1. }
writeString("La suma dels 3 enters del vector és: ");
writeInteger(suma);
end algorithm
3.2 Significat dels arguments del main
La principal diferència entre la definició main(int argc, char **argv)
i main()
és que la primera opció està preparada per rebre arguments quan s’executa el programa i la segona no.
Per exemple, si tenim el següent programa compilat en C i li passem una sèrie d’arguments des de la línia de comandes:
$> programa a1 a2 a3
Amb el main definit com a main(int argc, char **argv)
podem accedir des de dins del programa a tots els arguments passats; així haurem de:
argc = 4
argv[0] = "programa"
argv[1] = "a1"
argv[2] = "a2"
argv[3] = "a3"
El mateix sistema operatiu s’ocupa de donar-li el valor a l’argument int argc
(número total d’arguments inclòs el nom del programa), amb el que únicament ens hem de preocupar de passar els arguments. D’altra banda, argv
és un array de punters on cadascun d’ells apunta a un argument format per una cadena de caràcters; així argv
contindrà a cadascuna de les seves posicions els arguments passats des de línia de comandes, i en la posició 0 el nom del programa.
Si, en canvi, tenim definit el programa com a main()
, simplement no hi ha forma d’accedir als arguments que se li puguin arribar a passar. Moltes vegades podem tenir les dades ja definides dins del mateix programa o pot ser que les anem a consultar a una font externa, per la qual cosa, no tenir la capacitat de processar arguments no suposa cap impediment a l’hora d’executar el nostre programa.
3.3 Assignar valors a un vector
La lectura i assignació de valors a un vector es realitza de la següent forma en llenguatge algorísmic:
const
MAX_TEMP: integer = 2;
end const
algorithm lecturaTemperatures
var
temperatures: vector[MAX_TEMP] of float;
end var
writeString("Introdueix la lectura 1 : ");
temperatures[1] := readReal();
writeString("Introdueix la lectura 2 : ");
temperatures[2] := readReal();
writeString("Els valors introduïts han estat : ");
writeString("> Valor de la posició ");
writeInteger(1);
writeString(" : ");
writeReal(temperatures[1]);
writeString("> Valor de la posició ");
writeInteger(2);
writeString(" : ");
writeReal(temperatures[2]);
end algorithm
I en llenguatge C:
/* Exemple CA0301 */
#include <stdio.h>
#define MAX_TEMP 2
int main(int argc, char **argv) {
float temperatures[MAX_TEMP];
printf("Introdueix la lectura 1 : ");
scanf("%f", &temperatures[0]);
printf("Introdueix la lectura 2 : ");
scanf("%f", &temperatures[1]);
printf("> Valor de la posició %d : %.1f \n", 0, temperatures[0]);
printf("> Valor de la posició %d : %.1f \n", 1, temperatures[1]);
return 0;
}
Cal remarcar una diferència important:
- En llenguatge algorísmic les posicions del vector van des de la 1 fins a la N.
- En llenguatge C, van des de la 0 fins a la N-1.
En tots dos casos N fa referència al nombre total d’elements del vector.
3.4 Stack smashing detected
L’error stack smashing detected (code dumped)
es produeix quan s’intenta accedir o operar amb una posició d’un vector que no hem definit prèviament. Es pot donar per diferents situacions que acaben generant el mateix problema.
- Cas 1: es defineix un vector de N-posicions, però en comptes de començar per la posició 0 ho fem per la 1. Això és incorrecte: recordem que en C la posició inicial d’un vector sempre és la 0, i la final sempre és N-1. Exemple, per un vector de 3 posicions tindrem:
int vector1[3];
vector1[1] = 13; /* Posició del vector1 vàlida */
vector1[2] = 24; /* Posició del vector1 vàlida */
vector1[3] = 48; /* Posició del vector1 no vàlida! */
/* En canvi la posició 0 del vector,
* que tenim disponible no l'hem utilitzat!
*/
- Cas 2: es defineix un vector amb menys posicions de les que necessitem. Per exemple, si tenim:
int vector2[2];
Significa que les posicions reservades en memòria per aquest vector són:
vector2[0] = 10; /* Posició del vector2 vàlida */
vector2[1] = 13; /* Posició del vector2 vàlida */
vector2[2] = 24; /* Posició del vector2 no vàlida! */
Per tant, qualsevol operació amb vector2[2]
ens generarà l’error indicat. Si volem que el vector contingui 3 elements només cal definir-ne correctament la mida:
int vector2[3];
3.5 Concatenació en llenguatge algorísmic
A diferència del llenguatge C, en notació algorísmica no es preveu l’ús d’especificadors que permetin fer concatenacions entre cadenes de caràcters, enters, decimals, etc.
Per tant, en llenguatge algorísmic cal trencar els strings amb fragments més petits i que facin referència únicament a un tipus de dades. Per exemple, si es vol mostrar per pantalla el missatge “L’empleat que cobra més és Marta, i la seva nòmina és de 4675.30 €.”, ho farem de la següent forma:
writeString("L'empleat que cobra més és ");
writeString(nomEmpleat);
writeString(", i la seva nòmina és ");
writeReal(nomina);
writeString(" €.");
En llenguatge C equivaldria a:
printf("L'empleat que cobra més és %s, i la seva nòmina és de %.2f €.",
nomEmpleat, nomina);
3.6 Importància dels tipus utilitzats en llenguatge C
El resultat de les operacions en llenguatge C depèn del tipus de variable definit i dels tipus de valors utilitzats. A continuació s’exposen tres casos que, segons el tipus que s’hagi definit en les variables utilitzades, donarà un resultat o un altre:
Cas 1:
/* Exemple CA0302 */
#include <stdio.h>
int main(int argc, char **argv) {
int numero1;
int numero2;
int numero3;
int mitjana;
printf("Tecleja el primer número: ");
scanf("%d", &numero1);
printf("Tecleja el segon número: ");
scanf("%d", &numero2);
printf("Tecleja el tercer número: ");
scanf("%d", &numero3);
mitjana = (numero1 + numero2 + numero3) / 3;
printf("La mitjana és %d", mitjana);
return 0;
}
En aquest cas, si donem els valors number1 = 1
, number2 = 3
, number3 = 4
, el resultat és average = 2
. El resultat de la divisió serà un enter, ja que tant numerador com denominador estan formats per enters. El resultat enter es desa en una variable entera, i per pantalla obtindrem: 2
.
Cas 2:
/* Exemple CA0303 */
#include <stdio.h>
int main(int argc, char **argv) {
int numero1;
int numero2;
int numero3;
float mitjana;
printf("Tecleja el primer número: ");
scanf("%d", &numero1);
printf("Tecleja el segon número: ");
scanf("%d", &numero2);
printf("Tecleja el tercer número: ");
scanf("%d", &numero3);
mitjana = (numero1 + numero2 + numero3) / 3;
printf("La mitjana és %f", mitjana);
return 0;
}
Igual que en el cas anterior, el numerador i el denominador de la divisió estan formats per enters, amb la qual cosa el resultat serà un enter. En aquest cas, el resultat enter el desem en una variable de tipus float
, i per això C mostrarà el resultat amb decimals: 2.000000
Cas 3:
/* Exemple CA0304 */
#include <stdio.h>
int main(int argc, char **argv) {
int numero1;
int numero2;
int numero3;
float mitjana;
printf("Tecleja el primer número: ");
scanf("%d", &numero1);
printf("Tecleja el segon número: ");
scanf("%d", &numero2);
printf("Tecleja el tercer número: ");
scanf("%d", &numero3);
mitjana = (numero1 + numero2 + numero3) / 3.0;
printf("La mitjana és %f", mitjana);
return 0;
}
En aquest cas, el resultat de la divisió serà un decimal, ja que el denominador conté un decimal (en aquest cas 3.0
). El resultat amb decimals es guarda en una variable de tipus float
, i per pantalla es mostrarà : 2.666667
.
3.7 Exemple: notaFinal
El següent exemple calcula la nota final d’una assignatura en funció d’una sèrie de condicionals i d’operacions:
/* Exemple CA0305 */
#include <stdio.h>
#include <string.h>
/* Volem un programa que calculi la nota final
* d'una assignatura. La nota final es calcula a partir
* de l'AC (Avaluació continua) i la nota de la Pràctica:
*
* Nota final = 30% AC + 70% Pràctica
*
* Una vegada entrades totes les notes, si se'n
* detecta alguna que sigui incorrecta (fora del
* rang [0.0 a 10.0]), es mostrarà un missatge
* informatiu per pantalla i no es realitzarà cap
* més operació.
*
* L'AC està formada per 3 PAC: PAC1, PAC2, PAC3.
* La nota de l'AC es calcula mitjançant la
* mitjana de les 3 PAC.
*
* Si la nota de l'AC és inferior a 4, no cal
* realitzar cap càlcul: l'assignatura queda suspesa.
*
* Si la nota de l'AC és superior o igual a 4, es
* calcula la nota final juntament amb la nota de
* la Pràctica.
*
* La nota final es mostrarà en format "grade letters",
* segons la següent relació:
*
* MH: 10
* A: de 9.0 a 9.9
* B: de 7.0 a 8.9
* C+: de 5.0 a 6.9
* C-: de 3.0 a 4.9
* D: de 0.0 a 2.9
*
* Els punts que tracta aquest exemple:
* - definició de vectors
* - utilització del condicional if-else
* - condicionals if-else niats
* - assignació d'un string a una variable amb strcpy
* - reserva espai pel finalitzador '\0'
*/
#define PAC1 0
#define PAC2 1
#define PAC3 2
#define PRA 3
#define MAX_ACTIVITATS 4
#define MAX_CHARS 2+1 /* el +1 correspon al finalitzador '\0' */
#define PES_AC 0.3 /* AC 30% pes de la nota final */
#define PES_PRA 0.7 /* PRA 70% pes de la nota final */
int main(int argc, char **argv) {
/* Vector que conté les notes de
* totes les activitats del curs
*/
float notes[MAX_ACTIVITATS];
/* Nota d'avaluació continua: equival
* a la mitjana de les 3 PAC
*/
float notaAC;
float notaFinalNumerica;
/* String que conté la nota final
* de l'assignatura (MH, A, B...)
*/
char notaFinal[MAX_CHARS];
/* Es demana des de teclat les notes
* de les 3 PAC i de la PRA
*/
printf("Nota PAC1: ");
/* L'assignació d'un valor es pot
* fer directament sobre una posició
* del vector
*/
scanf("%f", ¬es[PAC1]);
/* Idem per la resta d'activiats */
printf("Nota PAC2: ");
scanf("%f", ¬es[PAC2]);
printf("Nota PAC3: ");
scanf("%f", ¬es[PAC3]);
printf("Nota PRA: ");
scanf("%f", ¬es[PRA]);
/* Primer de tot, comprovem que
* totes les notes del vector estiguin
* dins del rang [0.0 .. 10.0]
*/
if (notes[PAC1] > 10.0 || notes[PAC2] > 10.0 ||
notes[PAC3] > 10.0 || notes[PRA] > 10.0 ||
notes[PAC1] < 0.0 || notes[PAC2] < 0.0 ||
notes[PAC3] < 0.0 || notes[PRA] < 0.0) {
printf("\n>> Error detectat en una o més notes:");
printf("\n>> S'atura el càlcul de la nota final.\n");
} else {
/* En aquest punt sabem que totes les notes
* estan dins del rang [0.0 .. 10.0]
*/
/* Comprovem ara que la mitjana de les 3 PAC
* no sigui inferior a 4
*/
notaAC = (notes[PAC1] + notes[PAC2] + notes[PAC3]) / 3.0;
if (notaAC < 4) {
/* Per donar millor visibilitat, mostrem només
* el primer decimal de les notes numèriques
*/
printf("\n>> Nota mínima d'AC insuficient: %.1f", notaAC);
printf("\n>> S'atura el càlcul de la nota final.\n");
} else {
/* En aquest punt totes les notes són correctes,
* per tant es pot començar amb el càlcul de la
* nota final
*/
notaFinalNumerica = notaAC * PES_AC + notes[PRA] * PES_PRA;
/* Ara falta saber quina "grade letter" correspon
* a la notaFinalNumerica calculada; ho solucionem
* amb nous if-else niats
*/
if (notaFinalNumerica <= 2.9) {
/* Per assignar un string a una variable
* de tipus string, utilitzem la comanda
* strcpy, no pas '='
*/
strcpy(notaFinal, "D");
} else {
if (notaFinalNumerica <= 4.9) {
strcpy(notaFinal, "C-");
} else {
if (notaFinalNumerica <= 6.9) {
strcpy(notaFinal, "C+");
} else {
if (notaFinalNumerica <= 8.9) {
strcpy(notaFinal, "B");
} else {
if (notaFinalNumerica <= 9.9) {
strcpy(notaFinal, "A");
} else {
strcpy(notaFinal, "MH");
}
}
}
}
}
/* Per finalitar, mostrem tots els resultats
* calculats per pantalla
*/
printf("\n>> Nota AC: %.1f", notaAC);
printf("\n>> Nota PRA: %.1f", notes[PRA]);
printf("\n>> Nota final: %s (%.1f)\n", notaFinal, notaFinalNumerica);
}
}
return 0;
}
3.8 Exemple: lloguerFurgoneta
Imaginem que tenim una empresa de lloguer de furgonetes i volem oferir als nostres possibles clients un comparador de preus. Aquest comparador tindrà en compte el número de kilòmetres estimat que farà el client amb la nostra furgoneta, i el número de dies que la tindrà en lloguer.
Per calcular l’import del lloguer utilitzarem dos valors: d’una banda el consum per kilòmetre de la furgoneta, i d’altra banda el preu de lloguer per dia. Aquests valors variaran en funció de la furgoneta que vulguin llogar, amb el que cadascun d’ells el desarem en un vector diferent. La posició (índex) del vector ens indicarà quina furgoneta estem tractant.
El programa demanarà des del canal estàndard d’entrada el número de dies que es vol llogar i els kilòmetres estimats. A continuació calcularà el cost total de lloguer per cadascuna de les furgonetes i desarà el resultat en un altre vector (l’índex continuarà sent l’identificador de la furgoneta).
Degut a la gran quantitat de demanda de furgonetes, només ens en queden tres per llogar. El programa s’ha de resoldre únicament amb vectors i blocs condicionals if-else
(no es poden utilitzar bucles). El programa ha de retornar com a resultat quina és la furgoneta més econòmica de les tres en funció de les dades entrades, desglossant tots els elements que s’han tingut en compte per realitzar el càlcul.
Primer de tot, ens plantejarem mentalment què ha de fer el nostre algorisme:
- Definir les constants.
- Definir les variables.
- Llegir els valors requerits des del canal d’entrada estàndard.
- Calcular el cost total i desar-lo al vector de resultats.
- Mirar quina de les furgonetes és la més econòmica.
- Mostrar per pantalla el desglòs detallat de la més econòmica.
Una possible forma de codificar l’algorisme pot ser la següent:
const
NUM_FURGONETES: integer = 3;
PREU_LITRE_COMBUSTIBLE: real = 1.912;
end const
algorithm lloguerFurgoneta
var
consumsPerKm: vector[NUM_FURGONETES] of real;
preusLloguerPerDia: vector[NUM_FURGONETES] of real;
preusFinals: vector[NUM_FURGONETES] of real;
{ Valors d'entrada }
numDiesLloguer: integer;
previsioKm: integer;
{ Utilitzarem la següent variable per saber quina d'elles
és la més econòmica de totes, i així poder mostrar el desglòs
final. }
furgonetaMesEconomica: integer;
end var
{ Inicialitzem els vectors amb les dades de cada furgoneta.
Els següents valors formen part de la nostra aplicació
de lloguer, no es demanen pel canal d'entrada. }
consumsPerKm[1] := 8.25/100.0;
consumsPerKm[2] := 12.0/100.0;
consumsPerKm[3] := 9.5/100.0;
preusLloguerPerDia[1] := 80.5;
preusLloguerPerDia[2] := 69.9;
preusLloguerPerDia[3] := 72.8;
{ Lectura de valors des del canal d’entrada. }
writeString("Quants dies vols llogar la furgoneta? ");
numDiesLloguer := readInteger();
writeString("Quants kilòmetres faràs? ");
previsioKm := readInteger();
{ Càlcul de costos totals per furgoneta. }
preusFinals[1] := preusLloguerPerDia[1] * integerToReal(numDiesLloguer) +
consumsPerKm[1] * integerToReal(previsioKm) * PREU_LITRE_COMBUSTIBLE;
preusFinals[2] := preusLloguerPerDia[2] * integerToReal(numDiesLloguer) +
consumsPerKm[2] * integerToReal(previsioKm) * PREU_LITRE_COMBUSTIBLE;
preusFinals[3] := preusLloguerPerDia[3] * integerToReal(numDiesLloguer) +
consumsPerKm[3] * integerToReal(previsioKm) * PREU_LITRE_COMBUSTIBLE;
{ Mirem quina de les tres és la més econòmica, comparant els corresponents
costos totals de les furgonetes. Fixeu-vos que s'utilitza una estructura de
condicionals niats (un dins de l'altre). Guardem l'identificador de la opció
més econòmica dins de la variable furgonetaMesEconomica. }
if preusFinals[1] ≤ preusFinals[2] and
preusFinals[1] ≤ preusFinals[3] then
furgonetaMesEconomica := 1;
else
if preusFinals[2] ≤ preusFinals[1] and
preusFinals[2] ≤ preusFinals[3] then
furgonetaMesEconomica := 2;
else
furgonetaMesEconomica := 3;
end if
end if
{ Es mostra el detall de valors de la furgoneta més econòmica
de totes, així com el seu import final. }
writeString("La furgoneta més econòmica és la número: ");
writeInteger(furgonetaMesEconomica);
writeString(">> Consum litres per kilòmetre: ");
writeReal(consumsPerKm[furgonetaMesEconomica]);
writeString(">> Preu per litre de combustible: ");
writeReal(PREU_LITRE_COMBUSTIBLE);
writeString("€");
writeString(">> Previsió kilòmetres: ");
writeInteger(previsioKm);
writeString(">> Preu de lloguer diari: ");
writeReal(preusLloguerPerDia[furgonetaMesEconomica]);
writeString("€");
writeString(">> Dies de lloguer: ");
writeInteger(numDiesLloguer);
writeString(">> Import total: ");
writeReal(preusFinals[furgonetaMesEconomica]);
writeString(" €");
end algorithm
Una vegada tenim l’algorisme definit, només queda que traduïr-lo al llenguatge C:
/* Exemple CA0306 */
#include <stdio.h>
#define NUM_FURGONETES 3
#define PREU_LITRE_COMBUSTIBLE 1.912
int main(int argc, char **argv) {
float consumsPerKm[NUM_FURGONETES];
float preusLloguerPerDia[NUM_FURGONETES];
/* El cost total de cada furgoneta es desarà en el vector
* preusFinals.
*/
float preusFinals[NUM_FURGONETES];
/* Valors d'entrada per teclat */
int numDiesLloguer;
int previsioKm;
/* Utilitzarem la següent variable per saber quina d'elles
* és la més econòmica de totes, i així poder mostrar el desglòs
* final.
*/
int furgonetaMesEconomica;
/* Inicialitzem els vectors amb les dades de cada furgoneta.
* Els següents valors formen part de la nostra aplicació
* de lloguer, no es demanen per teclat.
*/
consumsPerKm[0] = 8.25/100.0;
consumsPerKm[1] = 12.0/100.0;
consumsPerKm[2] = 9.5/100.0;
preusLloguerPerDia[0] = 80.5;
preusLloguerPerDia[1] = 69.9;
preusLloguerPerDia[2] = 72.8;
/* Lectura de valors des de teclat. */
printf("Quants dies vols llogar la furgoneta? ");
scanf("%d", &numDiesLloguer);
printf("Quants kilòmetres faràs? ");
scanf("%d", &previsioKm);
/* Càlcul de costos totals per furgoneta */
preusFinals[0] = preusLloguerPerDia[0] * (float)numDiesLloguer +
consumsPerKm[0] * (float)previsioKm * PREU_LITRE_COMBUSTIBLE;
preusFinals[1] = preusLloguerPerDia[1] * (float)numDiesLloguer +
consumsPerKm[1] * (float)previsioKm * PREU_LITRE_COMBUSTIBLE;
preusFinals[2] = preusLloguerPerDia[2] * (float)numDiesLloguer +
consumsPerKm[2] * (float)previsioKm * PREU_LITRE_COMBUSTIBLE;
/* Mirem quina de les tres és la més econòmica,
* comparant els corresponents costos totals
* de les furgonetes. Fixeu-vos que s'utilitza
* una estructura de condicionals niats (un dins de l'altre).
* Guardem l'identificador de la opció més econòmica
* dins de la variable furgonetaMesEconomica.
*/
if (preusFinals[0] <= preusFinals[1] && preusFinals[0] <= preusFinals[2]) {
furgonetaMesEconomica = 0;
} else {
if (preusFinals[1] <= preusFinals[0] && preusFinals[1] <= preusFinals[2]) {
furgonetaMesEconomica = 1;
} else {
furgonetaMesEconomica = 2;
}
}
/* Es mostra el detall de valors de la furgoneta més econòmica
* de totes, així com el seu import final.
*/
printf("\nLa furgoneta més econòmica és la número %d: \n",
furgonetaMesEconomica);
printf(">> Consum litres per kilòmetre: %.2f \n",
consumsPerKm[furgonetaMesEconomica]);
printf(">> Preu per litre de combustible: %.2f € \n",
PREU_LITRE_COMBUSTIBLE);
printf(">> Previsió kilòmetres: %d \n",
previsioKm);
printf(">> Preu de lloguer diari: %.2f €\n",
preusLloguerPerDia[furgonetaMesEconomica]);
printf(">> Dies de lloguer: %d \n",
numDiesLloguer);
printf(">> Import total: %.2f € \n",
preusFinals[furgonetaMesEconomica]);
return 0;
}
Quan mirem quina de les furgonetes és la més econòmica, utilitzem condicionals niats (un dins de l’altre). Això ens permet fer més òptim el nostre programa: en cas que detectem que es compleix la primera de les condicions (la furgoneta més econòmica és la 0), ja no fem cap més comprovació de les que quedarien pendents (la més econòmica és la 1? la més econòmica és la 2?). És molt important tenir sempre present aquest plantejament, per tal que els vostres programes no realitzin més comprovacions de les necessàries.
A més, hem utilitzat la variable furgonetaMesEconomica
per saber quina d’elles és la més econòmica de les 3. El valor que conté aquesta variable és realment un índex, de forma que quan es mostrin les dades de la més econòmica per pantalla, simplement l’utilitzarem sobre els vectors i anirem recuperant els valors de la posició indicada per l’índex. Així ens estalviem d’anar desant tots els valors dels vectors en variables auxiliars de tipus consumPerKmFurgonetaEconomica
, preuLloguerPerDiaFurgonetaEconomica
, costTotalFurgonetaEconomica
. Utilitzant una única variable, furgonetaMesEconomica
, obviem la creació de múltiples variables innecessàries.
3.9 Exemple: aprovarAssignatura
A continuació es planteja un exemple que ens permetrà saber com podem superar una assignatura. L’aproximació inicial, poc òptima, avalua totes les condicions possibles utilitzant condicionals niats:
/* Exemple CA0307 */
#include <stdio.h>
#include <stdbool.h>
/* Volem un programa que ens indiqui si hem superat l'assignatura
* a partir dels resultats obtinguts en les 4 àrees d'avaluació:
* l'avaluació contínua (AC), les pràctiques (PR), la prova de
* síntesi (PS) i l'examen final (EX).
* A partir del pla docent, veiem que l'assignatura es pot superar
* en els següents casos
* 1. Si se supera l'AC, la PR i la PS.
* 2. Si se supera l'AC, la PR i l'EX.
* 3. Si se supera la PR i l'EX.
* Una possible solució (GENS ÒPTIMA I MOLT MILLORABLE!!) seria...
*/
int main(int argc, char **argv) {
bool hasAC;
bool hasPR;
bool hasPS;
bool hasEX;
bool assignSuperada;
int intToBool;
printf("Has superat l'avaluació contínua? (0-false, 1-true) :");
scanf("%d", &intToBool);
hasAC = intToBool;
printf("Has superat les pràctiques? (0-false, 1-true) :");
scanf("%d", &intToBool);
hasPR = intToBool;
printf("Has superat la prova de síntesi? (0-false, 1-true) :");
scanf("%d", &intToBool);
hasPS = intToBool;
printf("Has superat l'examen? (0-false, 1-true) :");
scanf("%d", &intToBool);
hasEX = intToBool;
if (hasAC) {
if (hasPR) {
if (hasPS) {
if (hasEX) {
assignSuperada = true;
} else {
assignSuperada = true;
}
} else {
if (hasEX) {
assignSuperada = true;
} else {
assignSuperada = false;
}
}
} else {
if (hasPS) {
if (hasEX) {
assignSuperada = false;
} else {
assignSuperada = false;
}
} else {
if (hasEX) {
assignSuperada = false;
} else {
assignSuperada = false;
}
}
}
} else {
if (hasPR) {
if (hasPS) {
if (hasEX) {
assignSuperada = true;
} else {
assignSuperada = false;
}
} else {
if (hasEX) {
assignSuperada = true;
} else {
assignSuperada = false;
}
}
} else {
if (hasPS) {
if (hasEX) {
assignSuperada = false;
} else {
assignSuperada = false;
}
} else {
if (hasEX) {
assignSuperada = false;
} else {
assignSuperada = false;
}
}
}
}
if (assignSuperada) {
printf(">> Assignatura superada. Enhorabona! \n");
} else {
printf(">> Assignatura no superada. \n");
}
return 0;
}
Aquesta gran estructura de condicionals niats es pot simplificar. Si mostrem tots els possibles valors que poden prendre les variables booleanes en una taula, tenim que:
hasAC | hasPR | hasPS | hasEX | assignSuperada |
---|---|---|---|---|
false | false | false | false | false |
false | false | false | true | false |
false | false | true | false | false |
false | false | true | true | false |
false | true | false | false | false |
false | true | false | true | true |
false | true | true | false | false |
false | true | true | true | true |
true | false | false | false | false |
true | false | false | true | false |
true | false | true | false | false |
true | false | true | true | false |
true | true | false | false | false |
true | true | false | true | true |
true | true | true | false | true |
true | true | true | true | true |
Com es pot veure, únicament s’aprovarà l’assignatura en dos casos:
- Si s’ha superat la pràctica (hasPR), l’avaluació contínua (hasAC) i la prova de síntesi (hasPS).
- Si s’ha superat la pràctica (hasPR) i l’examen (hasEX).
Per tant, podem substituir tots els condicionals niats per una única expressió:
assignSuperada = (hasPR && hasAC && hasPS) || (hasPR && hasEX)
Que també equival a:
assignSuperada = hasPR && ((hasAC && hasPS) || hasEX)
Així, una solució molt més òptima i correcta és la següent:
/* Exemple CA0308 */
#include <stdio.h>
#include <stdbool.h>
/* Codificació alternativa del mateix exercici: més eficient i òptima. */
int main(int argc, char **argv) {
bool hasAC;
bool hasPR;
bool hasPS;
bool hasEX;
bool assignSuperada;
int aux;
printf("Has superat l'avaluació contínua? (0-false, 1-true) :");
scanf("%d", &aux);
hasAC = aux;
printf("Has superat les pràctiques? (0-false, 1-true) :");
scanf("%d", &aux);
hasPR = aux;
printf("Has superat la prova de síntesi? (0-false, 1-true) :");
scanf("%d", &aux);
hasPS = aux;
printf("Has superat l'examen? (0-false, 1-true) :");
scanf("%d", &aux);
hasEX = aux;
/* Expressió equivalent */
assignSuperada = hasPR && ((hasAC && hasPS) || hasEX);
if (assignSuperada) {
printf(">> Assignatura superada. Enhorabona! \n");
} else {
printf(">> Assignatura no superada. \n");
}
return 0;
}
3.10 Errors més freqüents
3.10.1 Strings: declaració com a vectors de caràcters en llenguatge algorísmic
Pseudocodi incorrecte:
var
name: vector[MAX_CHAR] of char;
end var
En l’algorisme anterior, s’intenta emular el llenguatge C a l’hora de declarar una variable de tipus string
, declarant-la com un vector de caràcters. Això és incorrecte i absolutament innecessari, perquè en llenguatge algorísmic sí que existeix el tipus string
i, per tant, la seva declaració és molt més senzilla.
Pseudocodi correcte:
var
name: string;
end var
3.10.2 Sintaxi pròpia de C: funcions complexes
Pseudocodi incorrecte:
function hotelCmp(hotel1: tHotel, hotel2: tHotel): boolean;
if strcmp(hotel1.brand, hotel2.brand) = 0 then
{...}
end if
end function
La intenció de l’algorisme és comparar dos camps de tipus string
de les variables hotel1
i hotel2
. No obstant això, es fa ús de la funció strcmp()
, que és pròpia de C i no existeix en llenguatge algorísmic.
En llenguatge algorísmic, la comparació de dues cadenes de caràcters és molt més senzilla: es fa directament amb =
.
Pseudocodi correcte:
function hotelCmp(hotel1: tHotel, hotel2: tHotel): boolean;
if (hotel1.brand = hotel2.brand) then
{ ... }
end if
3.10.3 Estructura alternativa: condicionals consecutius
Aquest no és un error sintàctic o semàntic, sinó una mala pràctica de disseny.
Pseudocodi incorrecte:
if discountHotel ≥ 0 and discountHotel ≤ 10 then
writeString("Invalid data");
end if
if discountHotel > 10 and discountHotel ≤ 20 then
writeString("Not bad");
end if
if discountHotel > 20 and discountHotel ≤ 50 then
writeString("Good!");
end if
L’algorisme anterior vol mostrar un missatge en pantalla en funció del valor de la variable discountData
. Per aquest propòsit, res millor que una estructura alternativa, però no de la manera com està dissenyada.
Fixeu-vos que s’han construit tres blocs if
…end
if independents i consecutius. Això vol dir que durant l’execució, tots els blocs seran avaluats de forma consecutiva per decidir si cal executar el codi interior o no. Això no és necessari ni desitjable, ja que augmenta el temps d’execució.
Sempre que puguem, cal construir estructures alternatives niades i excloents, de forma que només s’avaluïn les condicions imprescindibles.
Pseudocodi correcte:
if discountHotel ≥ 0 and discountHotel ≤ 10 then
writeString("Invalid data");
else
if discountHotel > 10 and discountHotel ≤ 20 then
writeString("Not bad");
else
if discountHotel > 20 and discountHotel ≤ 50 then
writeString("Good!");
end if
end if
end if
3.10.4 Estructura alternativa: condicionals buits
Aquest no és un error sintàctic o semàntic, sinó una mala pràctica de disseny.
Pseudocodi incorrecte:
if discountHotel ≥ 0 then
else
writeString("Invalid data");
end if
L’algorisme anterior vol mostrar un missatge d’error en pantalla si el valor de la variable discountHotel
és negatiu, però l’estructura per fer-ho no és apropiada. En cas que es compleixi la condició discountHotel ≥ 0
, l’estructura alternativa no executa res. Recordem que és possible una estructura if
sense else
, i de fet sembla que en aquest cas s’hagi afegit únicament perquè es creia el contrari.
Pseudocodi correcte:
if discountHotel < 0 then
writeString("Invalid data");
end if
3.10.5 Estructura alternativa: interrompre un algorisme (I)
Pseudocodi incorrecte:
algorithm nameAlgorithm
if discountHotel < 0 then
writeString("Invalid data");
end algorithm;
else
writeString("Continue...");
{ ... }
end if
Sovint ens demanen d’interrompre l’execució d’un algorisme en cas que es doni alguna situació, per exemple, un error en l’entrada de dades. Pel que fa al disseny algorísmic, no hi ha cap funció ni sentència pensada per efectuar aquesta acció de forma explícita. En l’agorisme de l’exemple, s’intenta fer-ho utiltzant la sentència end algorithm
, però això no és correcte.
Senzillament cal muntar l’estructura alternativa de forma que quan es produeixi l’error, no s’executi cap més sentència, per exemple, posant tot el codi a executar dins el bloc de l’else
. Ens hem d’assegurar, això sí, que un cop sortim del bloc if
…else
, l’algorisme no té res pendent per executar.
Un disseny molt més elegant seria amb les condicions inverses, deixant sempre els errors i la sortida de l’algoritme dins el bloc else
:
Pseudocodi correcte:
algorithm nameAlgorithm
if discountHotel ≥ 0 then
writeString("Continue...");
{ Do something }
else
writeString("Invalid data");
{ Nothing else to do }
end if
end algorithm;
3.10.6 Estructura alternativa: interrompre un algorisme (II)
Pseudocodi incorrecte:
algorithm nameAlgorithm
if discountHotel < 0 then
writeString("Invalid data");
exit();
else
writeString("Continue...");
{ ... }
end if
end algorithm
Hi ha vegades en les que es vol emular en llenguatge algorísmic algunes funcions de C que interrompen l’execució del codi, com per exemple exit()
. Això és incorrecte ja que aquesta funció no existeix en llenguatge algorísmic; de fet, en llenguatge C, encara que existeixi, també cal evitar-la, ja que el seu ús no és una bona pràctica de programació.
Pseudocodi correcte:
algorithm nameAlgorithm
if discountHotel ≥ 0 then
writeString("Continue...");
{ Do something }
else
writeString("Invalid data");
{ Nothing else to do }
end if
end algorithm;
3.10.7 Constants i nombres: Valors numèrics en el codi (hardcode)
Pseudocodi incorrecte:
const
NUM_SEATS1: integer = 34;
NUM_RIDES: integer = 3;
A1: integer = 1;
A2: integer = 2;
A3: integer = 3;
end const
var
emptySeats: vector[3] of integer;
end var
{ input values }
writeString("EMPTY SEATS ");
writeString(NAME_RIDE1);
writeString(" >> ");
emptySeats[1] := readInteger();
En l’algoritme anterior, es declara el vector d’enters emptySeats
, amb una longitud igual a 3 posicions. Posteriorment, es llegeix un enter i es guarda a la primera posició del vector. A l’enunciat de l’exercici es donaven unes constants ja declarades, i es demanava explícitament utilitzar-les per operar amb els vectors.
El motiu no és altre que introduir el (bon) costum de fer servir constants i evitar al màxim els valors numèrics directes al codi (tècnica coneguda com hardcode). D’aquesta manera, és molt més fàcil mantenir el codi posteriorment i fer-hi modificacions.
En cas que volguéssim modificar la longitud del vector, o guardar els valors en posicions diferents, només hauríem de modificar la constant al principi del codi, en comptes d’haver de modificar totes les línies on apareixen aquests valors numèrics.
Pseudocodi correcte:
const
NUM_SEATS1: integer = 34;
NUM_RIDES: integer = 3;
A1: integer = 1;
A2: integer = 2;
A3: integer = 3;
end const
var
emptySeats: vector[NUM_RIDES] of integer;
end var
{input values}
writeString("EMPTY SEATS ");
writeString(NAME_RIDE1);
writeString(" >> ");
emptySeats[A1] := readInteger();
3.10.8 Constants: declaració de constants mal ubicada
Codi incorrecte:
#include <stdio.h>
int main(int argc, char **argv) {
#define MAX_LEN 15
char brand[MAX_LEN];
return 0;
}
Les constants s’han de declarar al principi del bloc de codi, i fora de qualsevol funció (sigui el main
o una altra), ja que igual que els tipus de dades, les constants es defineixen de forma global per a tot el programa. No és correcte obrir un bloc de declaració de constants dins un bloc central de codi, i encara menys obrir diversos blocs de constants a mesura que les necessitem.
Codi correcte:
#include <stdio.h>
#define MAX_LEN 15
int main(int argc, char **argv) {
char brand[MAX_LEN];
/* ... */
return 0;
}
3.10.9 Strings: comparació directa
Codi incorrecte:
#include <stdio.h>
#define MAX_LEN 15
int main(int argc, char **argv) {
char name1[MAX_LEN];
char name2[MAX_LEN];
if (name1 == name2) {
/* ... */
}
return 0;
}
En C les cadenes de caràcters (així com la resta de vectors) no es poden comparar directament amb l’operador ==
. En el seu lloc tenim dues opcions:
- En el cas dels vectors, es poden comparar element a element, de forma individual.
- En el cas dels strings, el C disposa de funcions específiques, com ara
strcmp()
.
Codi correcte:
#include <stdio.h>
#define MAX_LEN 15
int main(int argc, char **argv) {
char name1[MAX_LEN];
char name2[MAX_LEN];
if (strcmp(name1, name2) == 0) {
/* ... */
}
return 0;
}