Update PEC3
This commit is contained in:
parent
bcb7577058
commit
de6fb8fa4d
@ -17,9 +17,8 @@
|
|||||||
- [7.2. Cambio explícito de tipos de datos](#72-cambio-explícito-de-tipos-de-datos)
|
- [7.2. Cambio explícito de tipos de datos](#72-cambio-explícito-de-tipos-de-datos)
|
||||||
- [8. Cadenas de carácteres en C](#8-cadenas-de-carácteres-en-c)
|
- [8. Cadenas de carácteres en C](#8-cadenas-de-carácteres-en-c)
|
||||||
- [8.1. Cadenas de caracteres o strings](#81-cadenas-de-caracteres-o-strings)
|
- [8.1. Cadenas de caracteres o strings](#81-cadenas-de-caracteres-o-strings)
|
||||||
- [8.1.1. Definición](#811-definición)
|
- [8.1.1. Asignación](#811-asignación)
|
||||||
- [8.1.2. Asignación](#812-asignación)
|
- [8.1.2. Comparación](#812-comparación)
|
||||||
- [8.1.3. Comparación](#813-comparación)
|
|
||||||
- [8.2. Programación segura](#82-programación-segura)
|
- [8.2. Programación segura](#82-programación-segura)
|
||||||
|
|
||||||
|
|
||||||
@ -557,34 +556,266 @@ int main(int argc, char** argv) {
|
|||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## 7. Cambio de tipos de datos en C
|
## 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](http://www.open-std.org/JTC1/SC22/WG14/www/standards.html#9899)) define las reglas utilizadas para hacer estos cambios.
|
||||||
|
|
||||||
|
|
||||||
### 7.1. Reglas de coneversión
|
### 7.1. Reglas de coneversión
|
||||||
|
|
||||||
|
Lista simplificada de las normas del estándar:
|
||||||
|
|
||||||
|
1. Si una operación involucra dos operandos y uno de ellos es de tipo `long double`, el otro se convierte en `long double`.
|
||||||
|
2. Si una operación involucra dos operandos y uno de ellos es de tipo `double`, el otro se convierte en `double`.
|
||||||
|
3. Si una operación involucra dos operandos y uno de ellos es de tipo `float`, el otro se convierte en `float`.
|
||||||
|
4. En la mayoría de los casos, los valores de tipo `char` y `short int` se convierten en `int` inmediatamente.
|
||||||
|
5. Si una operación involucra dos operandos y uno de ellos es de tipo `long int`, el otro se convierte en `long int`.
|
||||||
|
6. 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 un `long 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 ejemplo `unsigned short int` y un `int`), ambos valores se convierten en un tipo sin signo común y el resultado tiene este tipo sin signo.
|
||||||
|
7. 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
|
### 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.
|
||||||
|
|
||||||
|
```c
|
||||||
|
#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:
|
||||||
|
|
||||||
|
```c
|
||||||
|
#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.
|
||||||
|
|
||||||
|
```c
|
||||||
|
#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. Cadenas de carácteres en C
|
||||||
|
|
||||||
### 8.1. Cadenas de caracteres o strings
|
### 8.1. Cadenas de caracteres o strings
|
||||||
|
|
||||||
#### 8.1.1. Definición
|
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`.
|
||||||
|
|
||||||
|
```c
|
||||||
|
#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
|
||||||
|
|
||||||
#### 8.1.2. Asignación
|
Cuando trabajamos con vectores y las cadenas de caracteres no son una excepción, no podemos hacer una asignación directa.
|
||||||
|
|
||||||
|
```c
|
||||||
|
#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.
|
||||||
|
1. Que el compilador detecte esta situación y nos dé un error de compilación.
|
||||||
|
2. Que queden asignadas a la misma zona de memoria, por lo que el segundo `printf` nos mostrará dos cadenas de caracteres con los valores `kb`, ya que al modificar `s2` también habremos modificado `s1`. (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.
|
||||||
|
|
||||||
|
```c
|
||||||
|
#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
|
||||||
|
|
||||||
#### 8.1.3. 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.
|
||||||
|
|
||||||
|
```c
|
||||||
|
#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` si `s1` y `s2` son iguales,
|
||||||
|
- menor a `0` si `s1 < s2` (en orden alfabético),
|
||||||
|
- mayor a `0` si `s1 > s2` (en orden alfabético).
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### 8.2. Programación segura
|
### 8.2. Programación segura
|
||||||
|
|
||||||
|
No debemos pasarnos nunca del espacio disponible en el vector.
|
||||||
|
|
||||||
|
|
||||||
|
```c
|
||||||
|
#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:
|
||||||
|
```c
|
||||||
|
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:
|
||||||
|
|
||||||
|
```c
|
||||||
|
#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.
|
||||||
|
Loading…
Reference in New Issue
Block a user