CONVERSIÓN DE TIPOS.







Introducción: conversión y refundición.
El problema de la conversión de tipos ya se ha abordado anteriormente mediante la función scanf() , que nos permite leer con formato alfanumérico y efectuar una conversión al formato de la variable de destino, en función del carácter de especificación de formato empleado. La función scanf() , junto con sus equivalentes para cadenas ( sscanf() ) y para archivos de texto ( fscanf() ) permite leer directamente una gama muy amplia de informaciones. Al margen de esta función, el lenguaje C proporciona funciones que poseen la misión específica de efectuar conversiones de formato. Estas funciones se recogen en la tabla de la sección siguiente. Existen otras funciones, del tipo de strtol() , que permiten especificar la base en que está escrito el número almacenado en la cadena transformada. El proceso de conversión consiste en traducir información de un formato a otro. El formato original va a ser de tipo alfanumérico, esto es, una colección de bytes, cada uno de los cuales contiene un carácter que representa parte de una cadena alfanumérica. Esa cadena es la representación de un número en alguna base. El formato de destino es de tipo numérico ( int , long , float , double ) y por tanto estará formado por una colección de bytes codificados en binario. Este proceso de conversión es relativamente costoso, porque la colección de operaciones necesarias para efectuar la traducción no es trivial. Este tipo de conversión de tipo es un caso particular de otro proceso, el de refundición o casting. En este segundo caso, se traduce el valor de una variable dada al valor equivalente para algún otro tipo. Por ejemplo, se puede traducir un número de coma flotante a un entero, o viceversa:

#include<stdio.h>
#include<stdlib.h>

int main(int argc, char * argv[])
{
 int entero;
 float real;

 entero = 7;
 real = (float)entero;
 printf("El numero 7 traducido a float es %6.3f\n", real);

 real = 3.141592;
 entero = (int)real;
 printf("El numero 3.141592 traducido a entero es %d\n", entero);

 return 0;
}



Comentarios .- Obsérvese que realmente hay un cambio de tipo, esto es, que se crea un nuevo valor con una disposición de bits que se ajusta a las convenciones del nuevo tipo. No se trata, simplemente, de reinterpretar el tipo de una variable como si fuera otro. Sin embargo, en el caso de la reinterpretación de punteros, éso es precisamente lo que se hace. A fin de cuentas, una dirección (un puntero) son 4 bytes codificados de igual modo sea cual fuere el tipo de variable que señala el puntero. Por esta razón, la refundición de tipos de puntero no necesita introducir cambios en los bits del valor original. Véanse las secciones dedicadas a punteros .


man strtol

La biblioteca de funciones de C ofrece una colección de funciones destinadas a efectuar traducciones entre formatos, especialmente entre formatos numéricos y alfanuméricos. La sintaxis de estas funciones se puede hallar fácilmente en las páginas man , mediante expresiones similares a man strtol . Las páginas del manual son una herramienta indispensable para el desarrollo en C, y pueden consultarse tanto localmente como en Internet.

Tabla de funciones de conversión de tipos.
Véanse a continuación algunas de las funciones de conversión de tipos más habituales:

long strtol(const char *ptro_cadena, char ** ptr, int base) Esta función proporciona el resultado de traducir al formato long el comienzo de la cadena señalada por ptro_cadena . Si la cadena no corresponde a un formato válido, se proporciona el valor 0 .
float strtof(const char * restrict nptr, char ** restrict endptr) Esta función proporciona el resultado de traducir al formato double el comienzo de la cadena señalada por ptro_cadena . Si la cadena no corresponde a un formato válido, se proporciona el valor 0.0 .

Estas funciones son suficientes para traducir del formato alfanumérico a los formatos numéricos habituales.
Téngase en cuenta que estas funciones producen un valor nulo cuando la cadena proporcionada no se puede traducir correctamente al formato numérico deseado. Esto podría producir errores, al confundirse el valor 0 con un valor realmente introducido por el usuario. C proporciona distintas formas de abordar este problema, y una de ellas consiste en analizar directamente el valor de la cadena, descartándola si contiene caracteres que manifiestamente no puedan formar parte del tipo de número que deseamos. Para ello se cuenta con las funciones de sección siguiente.

Funciones de análisis de valor ( isxxxx )
El archivo de encabezado ctype.h contiene, entre otras, funciones que permiten determinar si el carácter que se les pasa como argumento pertenece o no a determinados grupos de caracteres, como son las letras, los dígitos, las minúsculas, las mayúsculas, etc. Estas funciones nos permiten determinar si una cadena tiene o no sentido como valor numérico. Estas funciones devuelven el valor cero (falso) si el carácter proporcionado como parámetro no pertenece al grupo indicado, o un valor no nulo si, por el contrario, el carácter pertenece al tipo indicado. La tabla de funciones y comprobaciones que efectúan puede verse a continuación.

int isalnum(int c) Comprueba si el carácter proporcionado es una letra o un dígito.
int isalpha(int c) Comprueba si el carácter proporcionado es una letra.
int iscntrl(int c) Comprueba si el carácter proporcionado es de control (retorno de carro, salto de línea, etc).
int isdigit(int c) Comprueba si el carácter proporcionado es un dígito decimal (de 0 a 9).
int isgraph(int c) Comprueba si el carácter proporcionado es un carácter imprimible, pero no un espacio.
int islower(int c) Comprueba si el carácter proporcionado es una letra minúscula.
int isprint(int c) Comprueba si el carácter proporcionado es un carácter imprimible, incluyendo los espacios.
int ispunct(int c) Compueba si el carácter proporcionado es un signo de puntuación. Se considera signos de puntuación aquellos caracteres que, siendo imprimibles, no son espacios ni tampoco caracteres alfanuméricos.
int isspace(int c) Comprueba si el carácter es un "espacio en blanco", esto es, uno de los siguientes:
' ' Espacio
\f Salto de página
\n Nueva línea
\r Retorno de carro
\t Tabulador horizontal.
\v Tabulador vertical.
Si es cualquiera de ellos, devuelve un valor verdadero; en caso contrario proporciona un valor falso.
int isupper(int c) Determina si un carácter es una letra mayúscula. Esta función es aplicable para aquellos caracteres en que las funciones iscntrl() , isdigit() , ispunct() y isspace() tienen todas ellas el valor falso.
int isxdigit(int c) Esta función toma el valor verdadero si el carácter proporcionado es un dígito hexadecimal (de 0 a 9 o alguna de las letras A-F incluyendo minúsculas.


La mejor manera de entender el comportamiento de estas funciones es estudiar los resultados del programa siguiente, que hace uso de ellas con la intención de mostrar su valor aplicado a distintos caracteres del código ASCII.
/* Programa prueba_is.c */
#include<stdio.h>
#include<ctype.h>

int main(int argc, char * argv[]) {
 int i;
 char nombre[][8] = { "isalnum", "isalpha", "iscntrl", "isdigit", "isgraph",
        "islower", "isprint", "ispunct", "isspace", "isupper",
        "isxdigit"};
 char si[] ="   Si  |";
 char no[] ="   No  |";
 printf("\n\nTabla ASCII - desglose mediante funciones isxxx\n\n");
 printf("     ");
 for(i=0;i<9;i++)
  printf("%s|",nombre[i]);
 printf("\n");
 for(i=0;i<128;i++)
  {
   printf("%3d %c", i, isprint(i)?i:' ');
   printf("%s%s%s%s%s%s%s%s%s",
       isalnum(i)?si:no,
       isalpha(i)?si:no,
       iscntrl(i)?si:no,
       isdigit(i)?si:no,
       isgraph(i)?si:no,
       islower(i)?si:no,
       isprint(i)?si:no,
       ispunct(i)?si:no,
       isspace(i)?si:no,
       isupper(i)?si:no,
       isxdigit(i)?si:no);
   printf("\n");
  };
 printf("     ");
 for(i=0;i<9;i++)
  printf("%s|",nombre[i]);
 printf("\n\n");
 return 0;
}

Lo prometido es deuda, y el resultado es como se puede ver a continuación. Lamentablemente, estas funciones no son aplicables a caracteres de valor ASCII superior a 127, aun cuando todos los caracteres con signos diacrítios ocupan, precisamente, lugares posteriores al último valor admisible.

Tabla ASCII - desglose mediante funciones isxxx isalnum|isalpha|iscntrl|isdigit|isgraph|islower|isprint|ispunct|isspace| 0 No | No | Si | No | No | No | No | No | No | 1 No | No | Si | No | No | No | No | No | No | 2 No | No | Si | No | No | No | No | No | No | 3 No | No | Si | No | No | No | No | No | No | 4 No | No | Si | No | No | No | No | No | No | 5 No | No | Si | No | No | No | No | No | No | 6 No | No | Si | No | No | No | No | No | No | 7 No | No | Si | No | No | No | No | No | No | 8 No | No | Si | No | No | No | No | No | No | 9 No | No | Si | No | No | No | No | No | Si | 10 No | No | Si | No | No | No | No | No | Si | 11 No | No | Si | No | No | No | No | No | Si | 12 No | No | Si | No | No | No | No | No | Si | 13 No | No | Si | No | No | No | No | No | Si | 14 No | No | Si | No | No | No | No | No | No | 15 No | No | Si | No | No | No | No | No | No | 16 No | No | Si | No | No | No | No | No | No | 17 No | No | Si | No | No | No | No | No | No | 18 No | No | Si | No | No | No | No | No | No | 19 No | No | Si | No | No | No | No | No | No | 20 No | No | Si | No | No | No | No | No | No | 21 No | No | Si | No | No | No | No | No | No | 22 No | No | Si | No | No | No | No | No | No | 23 No | No | Si | No | No | No | No | No | No | 24 No | No | Si | No | No | No | No | No | No | 25 No | No | Si | No | No | No | No | No | No | 26 No | No | Si | No | No | No | No | No | No | 27 No | No | Si | No | No | No | No | No | No | 28 No | No | Si | No | No | No | No | No | No | 29 No | No | Si | No | No | No | No | No | No | 30 No | No | Si | No | No | No | No | No | No | 31 No | No | Si | No | No | No | No | No | No | 32 No | No | No | No | No | No | Si | No | Si | 33 ! No | No | No | No | Si | No | Si | Si | No | 34 " No | No | No | No | Si | No | Si | Si | No | 35 # No | No | No | No | Si | No | Si | Si | No | 36 $ No | No | No | No | Si | No | Si | Si | No | 37 % No | No | No | No | Si | No | Si | Si | No | 38 & No | No | No | No | Si | No | Si | Si | No | 39 ' No | No | No | No | Si | No | Si | Si | No | 40 ( No | No | No | No | Si | No | Si | Si | No | 41 ) No | No | No | No | Si | No | Si | Si | No | 42 * No | No | No | No | Si | No | Si | Si | No | 43 + No | No | No | No | Si | No | Si | Si | No | 44 , No | No | No | No | Si | No | Si | Si | No | 45 - No | No | No | No | Si | No | Si | Si | No | 46 . No | No | No | No | Si | No | Si | Si | No | 47 / No | No | No | No | Si | No | Si | Si | No | 48 0 Si | No | No | Si | Si | No | Si | No | No | 49 1 Si | No | No | Si | Si | No | Si | No | No | 50 2 Si | No | No | Si | Si | No | Si | No | No | 51 3 Si | No | No | Si | Si | No | Si | No | No | 52 4 Si | No | No | Si | Si | No | Si | No | No | 53 5 Si | No | No | Si | Si | No | Si | No | No | 54 6 Si | No | No | Si | Si | No | Si | No | No | 55 7 Si | No | No | Si | Si | No | Si | No | No | 56 8 Si | No | No | Si | Si | No | Si | No | No | 57 9 Si | No | No | Si | Si | No | Si | No | No | 58 : No | No | No | No | Si | No | Si | Si | No | 59 ; No | No | No | No | Si | No | Si | Si | No | 60 < No | No | No | No | Si | No | Si | Si | No | 61 = No | No | No | No | Si | No | Si | Si | No | 62 > No | No | No | No | Si | No | Si | Si | No | 63 ? No | No | No | No | Si | No | Si | Si | No | 64 @ No | No | No | No | Si | No | Si | Si | No | 65 A Si | Si | No | No | Si | No | Si | No | No | 66 B Si | Si | No | No | Si | No | Si | No | No | 67 C Si | Si | No | No | Si | No | Si | No | No | 68 D Si | Si | No | No | Si | No | Si | No | No | 69 E Si | Si | No | No | Si | No | Si | No | No | 70 F Si | Si | No | No | Si | No | Si | No | No | 71 G Si | Si | No | No | Si | No | Si | No | No | 72 H Si | Si | No | No | Si | No | Si | No | No | 73 I Si | Si | No | No | Si | No | Si | No | No | 74 J Si | Si | No | No | Si | No | Si | No | No | 75 K Si | Si | No | No | Si | No | Si | No | No | 76 L Si | Si | No | No | Si | No | Si | No | No | 77 M Si | Si | No | No | Si | No | Si | No | No | 78 N Si | Si | No | No | Si | No | Si | No | No | 79 O Si | Si | No | No | Si | No | Si | No | No | 80 P Si | Si | No | No | Si | No | Si | No | No | 81 Q Si | Si | No | No | Si | No | Si | No | No | 82 R Si | Si | No | No | Si | No | Si | No | No | 83 S Si | Si | No | No | Si | No | Si | No | No | 84 T Si | Si | No | No | Si | No | Si | No | No | 85 U Si | Si | No | No | Si | No | Si | No | No | 86 V Si | Si | No | No | Si | No | Si | No | No | 87 W Si | Si | No | No | Si | No | Si | No | No | 88 X Si | Si | No | No | Si | No | Si | No | No | 89 Y Si | Si | No | No | Si | No | Si | No | No | 90 Z Si | Si | No | No | Si | No | Si | No | No | 91 [ No | No | No | No | Si | No | Si | Si | No | 92 \ No | No | No | No | Si | No | Si | Si | No | 93 ] No | No | No | No | Si | No | Si | Si | No | 94 ^ No | No | No | No | Si | No | Si | Si | No | 95 _ No | No | No | No | Si | No | Si | Si | No | 96 ` No | No | No | No | Si | No | Si | Si | No | 97 a Si | Si | No | No | Si | Si | Si | No | No | 98 b Si | Si | No | No | Si | Si | Si | No | No | 99 c Si | Si | No | No | Si | Si | Si | No | No | 100 d Si | Si | No | No | Si | Si | Si | No | No | 101 e Si | Si | No | No | Si | Si | Si | No | No | 102 f Si | Si | No | No | Si | Si | Si | No | No | 103 g Si | Si | No | No | Si | Si | Si | No | No | 104 h Si | Si | No | No | Si | Si | Si | No | No | 105 i Si | Si | No | No | Si | Si | Si | No | No | 106 j Si | Si | No | No | Si | Si | Si | No | No | 107 k Si | Si | No | No | Si | Si | Si | No | No | 108 l Si | Si | No | No | Si | Si | Si | No | No | 109 m Si | Si | No | No | Si | Si | Si | No | No | 110 n Si | Si | No | No | Si | Si | Si | No | No | 111 o Si | Si | No | No | Si | Si | Si | No | No | 112 p Si | Si | No | No | Si | Si | Si | No | No | 113 q Si | Si | No | No | Si | Si | Si | No | No | 114 r Si | Si | No | No | Si | Si | Si | No | No | 115 s Si | Si | No | No | Si | Si | Si | No | No | 116 t Si | Si | No | No | Si | Si | Si | No | No | 117 u Si | Si | No | No | Si | Si | Si | No | No | 118 v Si | Si | No | No | Si | Si | Si | No | No | 119 w Si | Si | No | No | Si | Si | Si | No | No | 120 x Si | Si | No | No | Si | Si | Si | No | No | 121 y Si | Si | No | No | Si | Si | Si | No | No | 122 z Si | Si | No | No | Si | Si | Si | No | No | 123 { No | No | No | No | Si | No | Si | Si | No | 124 | No | No | No | No | Si | No | Si | Si | No | 125 } No | No | No | No | Si | No | Si | Si | No | 126 ~ No | No | No | No | Si | No | Si | Si | No | 127 No | No | Si | No | No | No | No | No | No | isalnum|isalpha|iscntrl|isdigit|isgraph|islower|isprint|ispunct|isspace|
Comentarios .- Como puede comprobarse, es posible seleccionar grupos concretos de caracteres empleando expresiones formadas por varias funciones del tipo isxxx . Por otra parte, si se desea buscar la función más adecuada para detectar la presencia de un carácter en una cierta cadena, nada más fácil que buscar el carácter en cuestión en la tabla anterior. De este modo se puede hallar la función o funciones convenientes, que después emplearemos para determinar la presencia o ausencia del carácter deseado.

Ejercicios propuestos



  1. Ejercicio 0502r01.- Un programa contiene una función destinada a pedir al usuario valores numéricos enteros. La función emplea una llamada a strtol() , que proporciona el valor cero si el usuario inserta caracteres alfanuméricos. Esto plantea un problema cuando el valor cero es admisible: no se puede distinguir entre el valor cero y el valor que proporciona la función en caso de error. Utilizar las funciones isxxx para asegurar la corrección de la respuesta.

  2. Ejercicio 0502r02.- Un programa contiene una función destinada a pedir al usuario valores numéricos de coma flotante. La función emplea una llamada a strtof() , que proporciona el valor 0.0 si el usuario inserta caracteres alfanuméricos. Esto plantea un problema cuando el valor 0.0 es admisible: no se puede distinguir entre el valor 0.0 y el valor que proporciona la función en caso de error, por haberse insertado caracteres inadecuados en la respuesta. Utilizar las funciones isxxx para asegurar la corrección de la respuesta.

  3. Ejercicio 0502r03.- Por diversas razones, no se dispone de strtol() . Construir una versión operativa de strtol() .

  4. Ejercicio 0502r04.- Por razones diversas, no se dispone de strtof(). Construir una versión limitada de strtof() , que no admita notación exponencial.

  5. Ejercicio 0502r05.- Construir una función isdiac() que proporcione un resultado verdadero cuando el int proporcionado corresponda a una vocal con un signo diacrítico. Se admitirán como válidas las letras mayúsculas y minúsculas con acentos de todo tipo. Téngase en cuenta que pertenecen a la parte extendida del código ASCII.

  6. Ejercicio 0502r06.- Construir una versión extendida de isalpha() , isalpha_extendida() , que admita letras con signos diacríticos.

  7. Ejercicio 0502r07.- Construir una versión extendida de isupper() , isupper_extendido() , que admita letras mayúsculas con signos diacríticos. ¿Se atreve a extender toupper()?

  8. Ejercicio 0502r08.- Construir una versión extendida de islower() , islower_extendida() , que admita letras mayúsculas con signos diacríticos. ¿Se atreve a extender tolower() ?