22 KiB
PEC 3
Índice
- PEC 3
5. Estructura de control alternativa
Estructuras de control de flujo.
5.1. Estructura alternativa
- Consulta sobre el estado actual o condición - Reacciona en función del valor obtenido. Tendrá como respuesta un valor lógico (verdadero o falso), pudiendo de esta forma tomar una decisión entre dos caminos distintos.
- Qué hacer cuando la condición es verdadera - Conjunto de instrucciones que se ejecutarán en caso verdadero.
- Qué hacer cuando la condición es falsa (opcional) - Conjunto de instrucciones que se ejecutarán en caso falso.
- Fin - Continua la ejecución del resto del programa.
5.2. Codificación de una estructura de control alternativa
Partes | Codificiación en lenguaje algorítmico |
---|---|
Consulta sobre el estado actual o condición | If estructura condicial then |
Qué hacer cuando la condición es verdadero | bloque de instrucciones |
Qué hacer cuando la condición es falso | else |
bloque de instrucciones | |
Marca de fin de estructura alternativa | end if |
Ejemplos:
Si un número es par mostrar un mensaje.
algorithm ex4
var
n: integer;
end var
n := readInteger();
if n mod 2 = 0 then
writeString("Es par");
end if
end algorithm
#include <stdio.h>
int main(int argc, char** argv) {
int n;
scanf("%d", &n);
if (n%2 == 0) {
printf("Es par");
}
return 0;
}
Si la temperatura es menor que 21ºC encender la calefacción. Si la temperatura es mayor o igual que 21ºC apagar la calefacción.
algorithm ex5
var
t: real;
end var
t := readReal();
if t < 21 then
switchOnHeating();
else
switchOffHeating();
end if
end algorithm
#include <stdio.h>
int main(int argc, char** argv) {
float t;
scanf("%f", &t);
if (t < 21) {
switchOnHeating();
} else {
switchOffHeating();
}
return 0;
}
Si la temperatura es menor que 18ºC encender la calefacción. Si la temperatura es mayor que 26ºC encender el aire acondicionado. En cualquier otro caso apagar la calefacción y el aire acondicionado.
algorithm ex6
var
t: real;
end var
t := readReal();
if t < 18 then
switchOnHeating():
else
if t > 26 then
switchOnAirConditioning();
else
switchOffHeating();
switchOffAirConditioning();
end if
end if
end algorithm
#include <stdio.h>
int main(int argc, char** argv) {
float t;
scanf("%f", &t);
if (t < 18) {
switchOnHeating();
} else {
if (t > 26) {
switchOnAirConditioning();
} else {
switchOffHeating();
switchOffAirConditioning();
}
}
return 0;
}
Si la edad es menor que 18 años mostrar un mensaje. Si la edad es mayor o igual que 18 años preguntar si tiene carnet de conducir. Si tiene carnet de conducir mostrar un mensaje. Si no tiene carnet de conducir mostrar otro mensaje.
algorithm carPooling
var
age: integer;
hasDrivingLicense: boolean;
end var
writeString("How old are you?");
age := readInteger();
if age < 18 then
writeString("You still cannot drive");
else
writeString("Do you have a driving licence?");
hasDrivingLicense := readBoolean();
if hasDrivingLicense then
writeString("Nice, we can share the car");
else
writeString("I do. If you want we can use my car");
end if
end if
end algorithm
#include <stdio.h>
#include <stdbool.h>
int main(int argc, char **argv) {
int age;
bool hasDrivingLicense;
int aux;
printf("How old are you?\n");
scanf("%d", &age);
if (age < 18){
printf("You still cannot drive\n");
} else {
printf("Do you have driving licence?\n");
scanf("%d", &aux);
hasDrivingLicense = aux;
if (hasDrivingLicense) {
printf("Nice, we can share the car\n");
} else {
printf("I do. If you want we can use my car\n");
}
}
return 0;
}
6. Tipos de datos: Vectores y matrices
Podemos definir variables que contienen varios elementos de un mismo tipo. Según estas variables tengan 1 o 2 dimensiones, las llamaremos vectores o matrices respectivamente.
6.1. Vectores
Los vectores o arrays, son estructuras de una sola dimensión, que contienen n valores de un mismo tipo.
var
t : vector[7] of real;
end var
/* Variable definition */
float t[7];
Para acceder a una posición de un vector se utiliza el índice de la posición deseada. El índice de un vector empieza en 0.
Es importante el valor que tiene la primera posición. En algunos lenguajes el valor 0 es el primer valor definido y en otros lenguajes el primer valor es 1.
En lenguaje algorítmico el primer valor es 1.
En lenguaje C el primer valor es 0.
Por ejemplo, para acceder a los valores de un vector de 7 posiciones en lenguaje algorítmico se haría de la siguiente forma:
algorithm tempAverage
var
t: vector[7] of real;
m: real;
end var
t[1] := readReal();
t[2] := readReal();
t[3] := readReal();
t[4] := readReal();
t[5] := readReal();
t[6] := readReal();
t[7] := readReal();
m := (t[1] + t[2] + t[3] + t[4] + t[5] + t[6] + t[7]) / 7.0;
writeReal(m);
end algorithm
Y en lenguaje C se haría de la siguiente forma:
#include <stdio.h>
int main(int argc, char** argv) {
float t[7];
float m;
scanf("%f", &t[0]);
scanf("%f", &t[1]);
scanf("%f", &t[2]);
scanf("%f", &t[3]);
scanf("%f", &t[4]);
scanf("%f", &t[5]);
scanf("%f", &t[6]);
m=(t[0] + t[1] + t[2] + t[3] + t[4] + t[5] + t[6]) / 7.0;
printf("%f", m);
return 0;
}
Dado que cuando se carga un programa para su ejecución se reserva la memoria para sus variables, cuando definimos un vector podemos utilizar constantes para su dimensión, pero nunca variables, ya que estas no tienen un valor conocido hasta que ya se está ejecutando el programa.
✅
#include <stdio.h>
#define N 3
int main(int argc, char** argv) {
float v[N];
}
❌
#include <stdio.h>
int main(int argc, char** argv) {
int m;
m := 3;
float v[m];
}x
Cuando la longitud de un vector es un dato conocido, es una buena práctica definirla como una constante. De esta forma, si en algún momento necesitamos cambiar la longitud del vector, solo tendremos que modificar el valor de la constante.
6.1.1. Cadenas de caracteres o strings
Un tipo muy utilizado de vector son las cadenas de caracteres o strings. En C, las cadenas de caracteres son vectores de caracteres terminados en el carácter nulo \0
(Valor ASCII 0).
Por lo tanto, si queremos guardar una cadena de caracteres será necesario reservar memoria para una posición adicional.
algorithm sayHello
var
name: string;
end var
writeString("What's your name?");
name := readString();
writeString("Hello");
writeString(name);
end algorithm
#include <stdio.h>
#define MAX_NAME_LEN 25
int main(int argc, char** argv) {
char name[MAX_NAME_LEN];
printf("What's your name?\n");
scanf("%s", name);
printf("Hello %s\n", name);
return 0;
}
Cuando utilizamos la instrucción en C scanf no ponemos el símbolo & delante.
Con los strings podemos utilizar los mismos operadores externos que con los caracteres =, ≠, <, >, ≤, ≥ para compararlos. La comparación se hace siguiendo el orden alfabético empezando por el primer caracter.
6.1.2. Representación de memoria
Todas las posiciones de un vector se guardan en memoria consecutivamente. La dirección de memoria de la primera posición del vector es la dirección de memoria de la variable que lo contiene.
6.1.3. Inicialización
Como buena práctica, primero definimos el vector especificando su tamaño máximo y después lo inicializamos.
algorithm vectors
var
v1 : vector[3] of integer;
v2 : vector[4] of integer;
end var
v1[1] := 3;
v1[2] := 5;
v1[3] := -1;
v2[1] := 3;
v2[2] := 5;
v2[3] := -1;
end algorithm
#include <stdio.h>
int main(int argc, char** argv) {
/* Variable definition*/
int v1[3];
int v2[4];
v1[0] = 3;
v1[1] = 5;
v1[2] = -1;
v2[0] = 3;
v2[1] = 5;
v2[2] = -1;
return 0;
}
En el caso de v2, es un vector de 4 posiciones del que solo inicializamos las 3 primeras.
En el caso de las cadenas de caracteres, podemos utilizar las comillas dobles ("), o tratarlo igual que un vector normal y asignar carácter a carácter. A continuación se muestra un ejemplo donde, para cada string, se muestra su contenido, el tamaño en bytes y la cantidad de caracteres (se contabilizan hasta encontrar el símbolo de final de cadena '\0')
#include <stdio.h>
#include <string.h>
int main(int argc, char** argv) {
char a[4];
char b[5];
char c[4];
char d[5];
a[0] = 'a';
a[1] = 'b';
a[2] = 'c';
a[3] = '\0';
b[0] = 'a';
b[1] = 'b';
b[2] = 'c';
b[3] = '\0';
strcpy(c, "abc");
strcpy(d, "abc");
printf("a - %s --> (%d) bytes, (%d) chars\n", a, sizeof(a), strlen(a));
printf("b - %s --> (%d) bytes, (%d) chars\n", b, sizeof(b), strlen(b));
printf("c - %s --> (%d) bytes, (%d) chars\n", c, sizeof(c), strlen(c));
printf("d - %s --> (%d) bytes, (%d) chars\n", d, sizeof(d), strlen(d));
return 0;
}
6.2. Matrices
Las matrices son vectores de dos dimensiones. Se pueden definir como un vector de vectores.
algorithm matrix
var
m : vector[2][3] of integer;
end var
m[1][1] := 1;
m[1][2] := 2;
m[1][3] := 3;
m[2][1] := 4;
m[2][2] := 5;
m[2][3] := 6;
end algorithm
#include <stdio.h>
int main(int argc, char** argv) {
/* Variable definition*/
int m[2][3];
m[0][0] = 1;
m[0][1] = 2;
m[0][2] = 3;
m[1][0] = 4;
m[1][1] = 5;
m[1][2] = 6;
return 0;
}
Primero se declara el número de filas y luego el de columnas.
Igual que en el caso de los vectores unidimensionales, con los de n-dimensiones el índice de las posiciones empieza por 1 con lenguaje algorítmico, y por 0 con lenguaje C.
Ejemplo que calcula el valor promedio de todos los valores de la matriz anterior:
algorithm matAverage
var
m : vector[2][3] of integer;
r : real;
end var
m[1][1] := 1;
m[1][2] := 2;
m[1][3] := 3;
m[2][1] := 4;
m[2][2] := 5;
m[2][3] := 6;
r := integerToReal(m[1][1] + m[1][2] + m[1][3] + m[2][1] + m[2][2] + m[2][3]);
r := r/6.0;
writeReal(r);
end algorithm
#include <stdio.h>
int main(int argc, char** argv) {
/* Variable definition*/
int m[2][3];
float r;
m[0][0] = 1;
m[0][1] = 2;
m[0][2] = 3;
m[1][0] = 4;
m[1][1] = 5;
m[1][2] = 6;
r = (m[0][0] + m[0][1] + m[0][2] + m[1][0] + m[1][1] + m[1][2]) / 6.0;
printf("%f\n", r);
return 0;
}
Cuando se guarda en memoria, se hace por filas. Por tanto, una matriz como la anterior en memoria es equivalente a un vector de 2x3 = 6 posiciones y por tanto de 6x4 = 24 bytes.
6.3. Ejemplos
Algoritmo que calcule el producto escalar de dos vectores en un espacio de dos dimensiones (2D)
algorithm dotProduct
var
a : vector[2] of integer;
b : vector[2] of integer;
c : integer;
end var
a[1] := readInteger();
a[2] := readInteger();
b[1] := readInteger();
b[2] := readInteger();
c := a[1] * b[1] + a[2] * b[2];
writeInteger(c);
end algorithm
#include <stdio.h>
int main(int argc, char** argv) {
/* Variable definition*/
int a[2], b[2];
int c;
scanf("%d", &a[0]);
scanf("%d", &a[1]);
scanf("%d", &b[0]);
scanf("%d", &b[1]);
c = a[0] * b[0] + a[1] * b[1];
printf("(%d,%d) * (%d,%d) = %d\n", a[0], a[1], b[0], b[1], c);
return 0;
}
Algoritmo que calcule el producto entre la matriz identidad de 3x3 y un valor entero introducido por teclado.
algorithm dotProduct
var
id : vector[3][3] of integer;
s : integer;
r : vector[3][3] of integer;
end var
id[1][1] := 1;
id[1][2] := 0;
id[1][3] := 0;
id[2][1] := 0;
id[2][2] := 1;
id[2][3] := 0;
id[3][1] := 0;
id[3][2] := 0;
id[3][3] := 1;
s := readInteger();
r[1][1] := id[1][1] * s;
r[1][2] := id[1][2] * s;
r[1][3] := id[1][3] * s;
r[2][1] := id[2][1] * s;
r[2][2] := id[2][2] * s;
r[2][3] := id[2][3] * s;
r[3][1] := id[3][1] * s;
r[3][2] := id[3][2] * s;
r[3][3] := id[3][3] * s;
end algorithm
#include <stdio.h>
int main(int argc, char** argv) {
/* Variable definition*/
int id[3][3];
int s;
int r[3][3];
id[0][0] = 1;
id[0][1] = 0;
id[0][2] = 0;
id[1][0] = 0;
id[1][1] = 1;
id[1][2] = 0;
id[2][0] = 0;
id[2][1] = 0;
id[2][2] = 1;
scanf("%d", &s);
r[0][0] = id[0][0] * s;
r[0][1] = id[0][1] * s;
r[0][2] = id[0][2] * s;
r[1][0] = id[1][0] * s;
r[1][1] = id[1][1] * s;
r[1][2] = id[1][2] * s;
r[2][0] = id[2][0] * s;
r[2][1] = id[2][1] * s;
r[2][2] = id[2][2] * s;
return 0;
}
7. Cambio de tipos de datos en C
En la mayoría de casos, el compilador de C es capaz de realizar conversiones implícitas de tipos de datos, automáticas y transparentes. Sin embargo, en ocasiones es necesario realizar conversiones explícitas. El estándar del lenguaje C (ISO/IEC 9899) define las reglas utilizadas para hacer estos cambios.
7.1. Reglas de coneversión
Lista simplificada de las normas del estándar:
- Si una operación involucra dos operandos y uno de ellos es de tipo
long double
, el otro se convierte enlong double
. - Si una operación involucra dos operandos y uno de ellos es de tipo
double
, el otro se convierte endouble
. - Si una operación involucra dos operandos y uno de ellos es de tipo
float
, el otro se convierte enfloat
. - En la mayoría de los casos, los valores de tipo
char
yshort int
se convierten enint
inmediatamente. - Si una operación involucra dos operandos y uno de ellos es de tipo
long int
, el otro se convierte enlong int
. - Si una expresión involucra tanto enteros con signo como sin signo, la situación se complica. Si el operando sin signo tiene una capacidad de representación menor (por ejemplo, tenemos un
unsigned int
y unlong int
) por lo que el tipo con signo tiene capacidad para representar todos los valores del tipo sin signo, el valor sin signo se convierte en el tipo con signo y el resultado adopta este tipo con signo. En caso contrario (es decir, si el tipo con signo no puede representar todos los valores del tipo sin signo, como por ejemplounsigned short int
y unint
), ambos valores se convierten en un tipo sin signo común y el resultado tiene este tipo sin signo. - Finalmente, cuando se asigna un valor a una variable mediante el operador de asignación, este se convierte automáticamente al tipo de la variable, si tanto el valor como la variable tienen tipo aritmético (es decir, número entero o punto flotante).
7.2. Cambio explícito de tipos de datos
En casos no cubiertos por estas reglas o si nos interesa que no se apliquen, habrá que hacer el cambio de datos de forma explícita. En la regla 6 es aconsejable hacer la conversión explícita para evitar resultados inesperados.
En los casos que nos interese hacer un cambio de tipo de datos o cast
, lo haremos poniendo el tipo destino entre paréntesis, actuando como un operador unario, lo que significa que afectará a lo que tenga justo a su derecha.
#include <stdio.h>
int main(int argc, char** argv) {
int a = 1;
int b = 2;
float r;
r = a / b;
printf("%f", r);
return 0
}
Esperaríamos que la variable r
tuviera el valor 0.5, pero realmente tendrá el valor 0. La expresión a/b
, tanto a
como b
son enteros y, por tanto, la operación es una división entera que tiene como resultado 0. Después tenemos una asignación a una variable real que, siguiendo la regla 7, se convierte en real y se guarda en la variable r
.
Si queremos que la división no sea entera deberemos hacer un cambio explícito:
#include <stdio.h>
int main(int argc, char** argv) {
int a = 1;
int b = 2;
float r;
r = (float)a / (float)b;
printf("%f", r);
return 0;
}
Teniendo en cuenta la regla 3, solo con que cambiemos uno de los tipos ya sería suficiente.
#include <stdio.h>
int main(int argc, char** argv) {
int a = 1;
int b = 2;
float r;
r = (float)a / b;
}
8. Cadenas de carácteres en C
8.1. Cadenas de caracteres o strings
Una cadena de caracteres o string
, es una secuencia de caracteres finalizada por el carácter '\0'
. Esta secuencia de caracteres se almacena dentro de un vector de caracteres. Por tanto, hay que diferenciar entre el tamaño del vector y el tamaño de la cadena de caracteres.
Para saber la longitud de un string, tenemos el método strlen
.
Necesitamos la librería string.h
.
#include <stdio.h>
#include <string.h>
int main(int argc, char **argv) {
char c[3]="ab";
int len;
/* Get the string length */
len=strlen(c);
/* Show the length */
printf("The length is %d\n",len);
return 0;
}
8.1.1. Asignación
Cuando trabajamos con vectores y las cadenas de caracteres no son una excepción, no podemos hacer una asignación directa.
#include <stdio.h>
int main(int argc, char **argv) {
char s1[3]="ab";
char s2[3];
/* BAD assignation */
s2=s1;
/* Show the values */
printf("s1=%s, s2=%s\n", s1, s2);
/* Change the first char in s2 */
s2[0]='k';
/* Show the values */
printf("s1=%s, s2=%s\n", s1, s2);
return 0;
}
Pueden pasar dos cosas.
- Que el compilador detecte esta situación y nos dé un error de compilación.
- Que queden asignadas a la misma zona de memoria, por lo que el segundo
printf
nos mostrará dos cadenas de caracteres con los valoreskb
, ya que al modificars2
también habremos modificados1
. (Como un puntero)
No se puede hacer una asignación directa entre vectores ni cadenas de caracteres.
Con el método strcpy
se puede copiar el contenido de una string
a otra.
#include <stdio.h>
#include <string.h>
int main(int argc, char **argv) {
char s1[3]="ab";
char s2[3];
/* *** Correct assignation *** */
strcpy(s2,s1);
/* Show the values */
printf("s1=%s, s2=%s\n", s1, s2);
/* Change the first char in s2 */
s2[0]='k';
/* Show the values */
printf("s1=%s, s2=%s\n", s1, s2);
return 0;
}
Los vectores no deben tener obligadamente la misma longitud, pero el vector s2
debe tener suficiente espacio para s1
.
8.1.2. Comparación
De la misma manera que en el caso de la asignación, la comparación entre dos vectores no se puede hacer con los operadores habituales (==, <, >, ...). Siempre diría que no son iguales aunque tengan las mismas cadenas de caracteres.
#include <stdio.h>
int main(int argc, char **argv) {
char s1[3]="ab";
char s2[3]="ab";
/* Compare the two strings (ERROR)*/
if (s1 == s2) {
printf("EQUALS\n");
} else {
printf("NOT EQUALS\n");
}
return 0;
}
Para comparar cadenas de caracteres, tenemos el método strcmp(s1, s2)
. Recibe como parámetros las dos cadenas de caracteres y nos devuelve un valor entero:
0
sis1
ys2
son iguales,- menor a
0
sis1 < s2
(en orden alfabético), - mayor a
0
sis1 > s2
(en orden alfabético).
8.2. Programación segura
No debemos pasarnos nunca del espacio disponible en el vector.
#include <stdio.h>
#include <string.h>
int main(int argc, char **argv) {
int a=3;
char b='a';
char c[3]="ab";
int d=3;
/* Show the initial values */
printf("a=%d, b=%c, c=%s, d=%d\n",a,b,c,d);
/* Assign all the space. No '\0' is stored */
strcpy(c,"abc");
printf("a=%d, b=%c, c=%s, d=%d\n",a,b,c,d);
/* Assign more space. Memory of other variables is used */
strcpy(c,"abcd");
printf("a=%d, b=%c, c=%s, d=%d\n",a,b,c,d);
return 0;
}
El resultado es:
a=3, b=a, c=ab, d=3
a=3, b=a, c=abc, d=3
a=3, b=a, c=abcd, d=0
Se ha modificado el valor de la variable d.
Para evitar este tipo de situaciones, existen versiones de la mayoría de métodos que tratan con cadenas de caracteres y que permiten limitar la longitud a copiar. Estos métodos suelen añadir una «n» al nombre de la función. El siguiente código es robusto a este tipo de problemas, utiliza el método strncpy
que le indica el tamaño del vector de destino:
#include <stdio.h>
#include <string.h>
int main(int argc, char **argv) {
int a=3;
char b='a';
char c[3]="ab";
int d=3;
/* Show the initial values */
printf("a=%d, b=%c, c=%s, d=%d\n",a,b,c,d);
/* Assign all the space. No '\0' is stored */
strncpy(c,"abc",3);
printf("a=%d, b=%c, c=%s, d=%d\n",a,b,c,d);
/* Assign more space. Memory of other variables is NOT used */
strncpy(c,"abcd",3);
printf("a=%d, b=%c, c=%s, d=%d\n",a,b,c,d);
return 0;
}
Siempre se deben tomar precauciones, pero en especial cuando las cadenas de caracteres se crean dentro del programa, por ejemplo concatenando valores o distintas cadenas de caracteres, o cuando las introduce el usuario.