[DS] Problemas con el cast

Buenas.

Abro este hilo porque al igual que yo me estaba volviendo loco con una cosa muy rara, supongo que a alguno de vosotros le habrá sucedido igual o quizá, tenga un programa que no funciona o lo hace mal y no sabe la causa.

Pues bien, resulta que el cast (la conversion de tipos en C) no funciona correctamente al convertir de char a int. Si el valor almacenado en el char es negativo, el int toma el valor como si fuera un unsigned char


Me he dado cuenta que con los unsigned short pasa eso. Si tenemos un codigo tal que así:


unsigned short pepe=-1;

int manolo=(int) pepe;

El valor que almacena manolo, no seria -1, si no 65535.

Para que funcionase correctamente, deberias utilizarlo asi:

int manolo=(int)(short) pepe;

de esta forma, si que funciona (aunque sea de perogrullo), por eso me ha llamado poderosamente la atención de que con los char la conversion fallase: la unicas dos razones posibles son o bien un bug, o bien que por alguna razón, el compilador pone el tipo char por defecto, sin signo (de toda la vida, por defecto. char es CON signo)

Para solucionarlo, tendreis que añadir la opcion -fsigned-char :

CFLAGS := -g -Wall -O0 \
-mcpu=arm9tdmi -mtune=arm9tdmi -fsigned-char \
$(ARCH)

De esta forma, en el caso de los Makefiles que estoy usando.

En fin, espero que os sirva de ayuda esta información.
Declaralo como signed char y punto (el tamaño y esas cosas dependen de la arquitectura/compilador, si algo no funciona "tipalo" más)
DemonR escribió:Declaralo como signed char y punto (el tamaño y esas cosas dependen de la arquitectura/compilador, si algo no funciona "tipalo" más)


No, si el problema no está en declararlo mas o menos... si no en SABER que hay que hacerlo XD

Lo mejor es poner la opcion en el compilador que digo, pero de todas formas, detalles como que un tipo unsigned short, lo pases a int y no tome el bit mas significativo como signo y lo extrapole, se las trae... (es lo mismo convertir un u16 a s32 que un u16 a u32 para el compilador y no debería ser asi)
Eso como mínimo debería dar un warning en el compilador, no?
O bien tendrás un valor raro en la variable (depende del compilador utilizado)

C es demasiado permisivo con los typecasts.

Como decís tú y el anterior compañero, teóricamente se debe utilizar tipo short (sin unsigned) para poderle dar un valor de -1.

Un saludo,
ninor escribió:Eso como mínimo debería dar un warning en el compilador, no?
O bien tendrás un valor raro en la variable (depende del compilador utilizado)

C es demasiado permisivo con los typecasts.

Como decís tú y el anterior compañero, teóricamente se debe utilizar tipo short (sin unsigned) para poderle dar un valor de -1.

Un saludo,


-1 equivale 65535 para un unsigned short y si, daría un warning (¿y?).

El problema viene cuando usas el cast y falla al hacer una conversion.


Por ejemplo:

unsigned short paco= (unsigned short) -1; // esto lo hace bien y no //hay warnings

int manolo= (int) paco; // esto lo hace mal, porque almacena 65535


La ultima linea equivaldria a hacer:

int manolo= (int) ((unsigned) paco);

Con lo cual ya me diras tú que clase de conversion hace (el cast se supone que trata de convertir el tipo, no solo de crear un dato mas ancho)

En el caso de los char se agrava porque por defecto, un char es igual a un unsigned char (cuando de toda la vida, char es con signo)
Creo que no he comprendido bien lo que estás explicando, porque no veo fallo alguno. Vamos por partes:

unsigned short pepe=(unsigned short)-1;

int manolo=(int) pepe;


dices que el valor que coge "manolo" es 65535, y dices que eso es incorrecto si no te he entendido mal (que no estoy seguro), pero yo diría que es correcto.

Primero le dices que convierta -1 a unsigned short. Como eso no se puede hacer, pues lo deja tal cual, al valor 65535.

Luego intentas convertir pepe que es una variable unsigned con valor 65535 (y repito, valor 65535 y no -1) a un entero de 32 bits. Como en el entero de 32 bits el valor 65535 cabe perfectamente, pues ese es el valor que la variable adquiere. Intenta verlo de otra forma, si pones este código:

unsigned short pepe = 65535;
int manolo = (int) pepe;


¿Qué valor debería coger la variable manolo, -1 ó 65535?. Debería coger 65535.

Para poder utilizar una variable como con signo o sin signo indistintamente, lo mejor es utilizar una unión:

typedef union
{
  short s;
  unsigned short u;
} Conversion;

unsigned short pepe = -1;
Conversion manolo;

manolo.u = pepe;
// manolo.u vale 65535
// manolo.s vale -1


Además los castings no es necesario ponerlos cuando la variable no va a ser truncada. Pasar un unsigned short a un int no trunca variables, así que no es necesario ponerlo. En cambio si quieres pasar de un int a un short, ahí sí que hay que poner el casting, para avisar al compilador de que realmente lo estás haciendo aposta y eres consciente de que se está truncando el tipo.

En esta línea, la variable toma el valor -1:
int manolo=(int)(short) pepe;

pero la razón es la misma de antes: pepe tiene el valor 65535, y le dices que lo convierta a short. Como ese número no cabe en un short, el compilador lo deja tal cual, con lo que tenemos un short de valor -1. Luego le dices que convierta el short de valor -1 a int. El compilador no tiene problemas para hacer eso y así lo hace.

Otra cosa distinta es que los char el compilador por defecto los pille como unsigned. Eso ciertamente es raro (porque el char de toda la vida es con signo), y sí que es útil que indiques cómo solucionarlo.
Gracias a los 2

Por cierto, utilizais el devKitPro? U otras librerias/entornos?

Un saludo,
Si lo curioso del tema del cast, es que si tienes una variable de tipo unsigned short y le pones un (short) delatante, realmente, no se hace NINGUNA conversion de numero, debido a que binariamente, -1 y 65535 es lo mismo (65535) y el signo no es mas que una interpretacion que hacen ciertas funciones aritmeticas, principalmente XD

Por eso me parece curioso que se tenga que hacer (int)(short) manolo y no se pueda hacer de un simple paso (int) manolo teniendo en cuenta, que como dices, si quisiera copiar el valor sin signo en el int, no sería necesario el cast ya que el valor de 16 bits seria copiado en un registro del procesador de 32 bits (y los 16 bits superiores a 0) y luego copiado los 32 bits en la variable int XD.

De todas formas, no me tomes muy en serio en esto: es la eterna discusión del vaso medio lleno o medio vacio y en mi interpretacion al poner el (int) deberia extender el signo XD (entiendo perfectamente por que NO sucede así, analizandolo desde el pundo de vista del lenguaje ensamblador, pero desde C/C++, preferiria que fuera de otra forma ;)). Esto ya es un debate de preferencias, jeje

Lo del tipo char, pues si, por eso me he dado cuenta de que iba mal: porque se comportaba de la misma forma que si fuera unsigned.
Yo lo que te digo es que respecto a lo de los castings, el compilador está funcionando perfectamente. Como tú dices, 65535 y -1 en 16 bits son lo mismo, pero según cómo lo interpretes y ojo, la interpretación es muy importante para no llegar a equívocos como este. Un valor 0xFFFF en un unsigned short es 65535 y no -1. Un valor 0xFFFF en un short es -1. Y cuando pones un casting, no siempre se hace copia binaria tal cuál, a veces hay que hacer extensión de signo y a veces incluso más cosas, como al pasar por ejemplo de un int a un float y viceversa.

Y en C, cuando conviertes de un tipo sin signo a un tipo con signo, NUNCA se hace extensión de signo, porque los tipos sin signo para el compilador siempre son positivos (por algo son sin signo ;)).

ninor, yo no programo para la NDS. Me gustaría pero no tengo tiempo, siempre ando metido en varios proyectos a la vez, y encima tengo examenes en septiembre [+risas].

EDITO: Con los tipos de datos nunca puedes fiarte del compilador. Yo llevo un tiempo trabajando con DSPs y con deciros que en uno que utilicé (concretamente un TMS320VC5501) el char y el int tenían ambos el mismo tamaño y eran de 16 bits XD. Yo hasta entonces pensaba que los char tenían que ser de 8 bits por narices, pero no. De hecho entonces descubrí que en C un byte es la capacidad de almacenamiento que permite como mínimo contener cualquier caracter del entorno de ejecución, pero no tiene por qué ser 8 bits. Y el tipo char lo define como el tipo entero que puede almacenar como mínimo 8 bits.

Vamos, que con este rollo lo que quiero decir es que cuando trabajéis en una arquitectura desconocida, os podéis esperar cualquier cosa con respecto a los tipos de datos.
Por eso saco este debate aquí y me alegro de tener una parte que se contraponga a mi XD.

Lo del tamaño de los tipos, es cierto: yo por ejemplo, en el primer compilador de C que toqué, el tipo short y el tipo int tenian la misma longitud (2 bytes)y de ahí que tenga la 'manía' de querer que el cast haga una conversion en toda regla XD

Bueno, hablando en serio, mucha gente que tiene problemas con sus programas, suele ser por estas cuestiones.

Por ejemplo, antiguamente, el tipo long en los PC equivalia a 32 bits. De hecho, mucha gente tiene la 'mania' de usar long para referirse a 32 bits, cuando ya hace mucho que int es de 32 bits en nuestras maquinas y convendría reservar long para acceder a 64 bits (como en el caso de PS2), por ejemplo.

Por ese motivo, se suelen definir tipos como u16, s16 y cosas así, que luego incluyen un fichero de cabecera que utiliza el tipo 'generico' correcto para crear estas nuevas definiciones y hacer así un programa mas portable entre maquinas.

Los compiladores GNU que usamos, nos permiten asignar opciones que permiten controlar este tipo de cosas, inclusive la longitud de algunos tipos genericos. Si alguno necesita una manual sobre esto, aqui tiene este link:

http://gcc.gnu.org/onlinedocs/
10 respuestas