Sobre los Cast

Iniciado por BlackZeroX, 26 Octubre 2012, 04:50 AM

0 Miembros y 1 Visitante están viendo este tema.

BlackZeroX

.
¿Es bueno hacer varios cast de tipos?, Se que es recomendable pero no lo se hasta que punto.

por ejemplo:

list.h


typedef
enum {
   LIST_CMP_EQUAL          = 0x0,
   LIST_CMP_LESS_THAT      = 1,
   LIST_CMP_GREATER_THAT   = -1
} list_cmp_t;

typedef enum {
   LIST_SORT_ASC,
   LIST_SORT_DESC
} list_sort_t;

typedef void*               list_t;
typedef list_t*             list_ptr_t;

typedef intptr_t            list_value_t;
typedef list_value_t*       list_value_ptr_t;

typedef list_value_ptr_t    list_item_t;
typedef list_item_t*        list_item_ptr_t;

typedef size_t              list_size_t;
typedef list_size_t*        list_size_ptr_t;

/** Definición de Callback que se encarga de crear un duplicado del list_value_t indicado en parámetro value.
* @param value: list_value_t que se duplicara.
* @return Retorna el elemento list_value_t duplicado del parámetro value.
*/
typedef list_value_t(*list_callback_clone_item)(const list_value_t value);

/** Definicion de Callback que se encarga de destruir la memoria asignada al list_value_t.
* @param value: list_value_t que se liberara.
*/
typedef void(*list_callback_release_item)(const list_value_t value);

/** Definicion de CALLBACK que se encarga comparar dos list_value_t.
* @param value1: list_value_t que se comparara con value2.
* @param value2: list_value_t que se comparara con value1.
* @return Retorna el resultado de la comparación.
*    AVL_CMP_LESS_THAT: Si value1 < value2.
*    AVL_CMP_EQUAL: Si value1 == value2.
*    AVL_CMP_GREATER_THAT: Si value1 > value2.
*/
typedef list_cmp_t(*list_callback_compare_item)(const list_value_t value1, const list_value_t value2);


/** Crea una nueva lista de elementos list_value_t.
* @param clone_item: Apuntador a la función CALLBACK que retornara la copia del valor a asignar.
* @param release_item: Apuntador a la función CALLBACK que liberara la copia del valor duplicado en el list_callback_clone_item.
* @param clone_item: Apuntador a un proceso CALLBACK que se encarga de comparar los list_item_t.
* @return Retorna la nueva cola de datos queue_t que debera ser destruida con list_release().
*/
list_t list_allocator(list_callback_clone_item clone_item, list_callback_release_item release_item, list_callback_compare_item compare_item);



Código (c,36) [Seleccionar]


/** Tipo de dato list_data_t **/
typedef struct list_data        list_data_t;

/** Tipo de dato list_data_ptr_t **/
typedef list_data_t*            list_data_ptr_t;

/** Tipo de dato list_data_t **/
typedef struct list_item_data   list_item_data_t;

/** Tipo de dato list_data_ptr_t **/
typedef list_item_data_t*       list_item_data_ptr_t;

struct list_item_data {
   list_value_t            value;  /** Valor del item actual. **/
   list_item_data_ptr_t    prev;   /** Apuntador al item derecho. **/
   list_item_data_ptr_t    next;   /** Apuntador al item izquierdo. **/
   list_data_ptr_t         list;   /** Apuntador a los datos generales de la lista. **/
};

/** Estructura que guarda la información generalizada de una lista de datos **/
struct list_data {
   list_item_data_ptr_t        first;          /** Primer elemento agregado a la lista **/
   list_item_data_ptr_t        last;           /** Ultimo elemento agregado a la lista **/
   list_size_t                 size;           /** Cantidad de elementos **/
   list_callback_clone_item    clone_item;     /** Apuntador a la función CALLBACK que retornara la copia del valor a asignar **/
   list_callback_release_item  release_item;   /** Apuntador a la función CALLBACK que liberara la copia del valor duplicado en el list_callback_allocator **/
   list_callback_compare_item  compare_item;   /** Apuntador a la función CALLBACK que compara dos elementos de la lista **/
};

...

list_item_t
list_begin(list_t list) {
   if (list_empty(list)) return NULL;
   return (list_item_t)(((list_data_ptr_t)list)->first);
}



Tambien en casos donde provienen de un puntero X y se quiere convertir a un int32_t

Código (c,8,18,42,43,57) [Seleccionar]


/** Tipo de dato para la estructura AVL **/
typedef void*               avl_t;
typedef avl_t*              avl_ptr_t;

/** Tipo del dato que guarda el valor de un nodo **/
typedef uintptr_t           avl_value_t;
typedef avl_value_t*        avl_value_ptr_t;

typedef size_t              avl_size_t;
typedef avl_size_t*         avl_size_ptr_t;

/** Funcion que retorna el elemento menor de un elemento avl.
* @param value: Nodo del arbol AVL anteriormente creado con avl_allocator(), sirve como pivote.
* @return
*      NULl: No existe o hubo un error.
*/
avl_value_ptr_t avl_getlower(const avl_value_ptr_t value);

/**
* Funcion que busca un elemento (avl_value_t) en un elemento avl (avl_t)
* @param avl: Arbol AVL anteriormente creado con avl_allocator().
* @param value: Elemento a buscar.
* @return Retorna el apuntador al valor guardado.
*      NULL: No existe el valor buscado.
**/
avl_value_ptr_t avl_find(avl_t avl, const avl_value_t value);

/** Funcion que extrae un nodo del elemento avl indicado.
* @param avl: Apuntador a una variable tipo avl_t anteriormente creada con avl_allocator().
* @param value: Elemento que se buscara.
* @return
*      true: Si se extrajo del elemento avl
*      false: No se extrajo ya que no se encontro o el elemento avl esta vacio o se paso un parametro NULL.
*/
bool avl_remove(avl_t avl, const avl_value_ptr_t value);

avl_value_ptr_t avl_getroot(const avl_t avl);
...
       while (!avl_empty(avl)) {
           //Comprobamos la integridad de eliminación con estas dos lineas.
           avl_value_t elim = (avl_value_t)randomnumber(*avl_getlower(avl_getroot(avl)),
                                                        *avl_getupper(avl_getroot(avl)));
           avl_remove(avl, avl_find(avl, elim));
       }

long min(uint32_t a, uint32_t b) {
   return (a<b) ? a:b;
}

void swap(int32_t *a, int32_t *b) {
   *a ^= *b;
   *b ^= *a;
   *a ^= *b;
}

inline int randomnumber(int32_t lower, int32_t upper) {
   if (min(lower, upper) != lower) {
       swap(&lower, &upper);
   }
   return lower + rand() % ((upper + 1) - lower);
}



Uso los list_t como sinónimo de un tipo void* que apunta a una estructura list_data_t, es decir list_t = void* = list_data_ptr_t esto lo hago debido a que estoy creando varias bibliotecas donde las estructuras están dentro de los archivos .c (algunas) y creando varias funciones para el trato de las mismas... aun estoy espesando a creer que esto es una idiotes pero este no es el punto.

El problema radica al momento de compilar con -std=c99 -ansi -wall -pedantic -pedantic-errors

me salta:



/home/blackzerox/Documentos/Programacion/C/CScript/main.c|123|aviso: formato '%d' espera un argumento de tipo 'int', pero el argumento 2 es de tipo 'avl_size_t' [-Wformat]|
/home/blackzerox/Documentos/Programacion/C/CScript/main.c|130|error: el paso del argumento 1 de 'randomnumber' crea un entero desde un puntero sin una conversión|
/home/blackzerox/Documentos/Programacion/C/CScript/main.c|17|nota: se esperaba 'int32_t' pero el argumento es de tipo 'avl_t'|
/home/blackzerox/Documentos/Programacion/C/CScript/main.c|130|error: el paso del argumento 2 de 'randomnumber' crea un entero desde un puntero sin una conversión|
/home/blackzerox/Documentos/Programacion/C/CScript/main.c|17|nota: se esperaba 'int32_t' pero el argumento es de tipo 'avl_t'|
/home/blackzerox/Documentos/Programacion/C/CScript/main.c|144|aviso: formato '%d' espera un argumento de tipo 'int', pero el argumento 2 es de tipo 'avl_size_t' [-Wformat]|
/home/blackzerox/Documentos/Programacion/C/CScript/main.c|147|aviso: formato '%d' espera un argumento de tipo 'int', pero el argumento 2 es de tipo 'avl_size_t' [-Wformat]|
/home/blackzerox/Documentos/Programacion/C/CScript/main.c|160|aviso: formato '%d' espera un argumento de tipo 'int', pero el argumento 2 es de tipo 'avl_size_t' [-Wformat]|
/home/blackzerox/Documentos/Programacion/C/CScript/main.c|173|aviso: formato '%d' espera un argumento de tipo 'int', pero el argumento 2 es de tipo 'avl_size_t' [-Wformat]|
/home/blackzerox/Documentos/Programacion/C/CScript/main.c|189|aviso: formato '%d' espera un argumento de tipo 'int', pero el argumento 2 es de tipo 'avl_size_t' [-Wformat]|
/home/blackzerox/Documentos/Programacion/C/CScript/main.c|192|aviso: formato '%d' espera un argumento de tipo 'int', pero el argumento 2 es de tipo 'avl_size_t' [-Wformat]|
/home/blackzerox/Documentos/Programacion/C/CScript/main.c|228|aviso: formato '%d' espera un argumento de tipo 'int', pero el argumento 2 es de tipo 'map_size_t' [-Wformat]|
/home/blackzerox/Documentos/Programacion/C/CScript/main.c|248|aviso: formato '%d' espera un argumento de tipo 'int', pero el argumento 2 es de tipo 'map_size_t' [-Wformat]|
/home/blackzerox/Documentos/Programacion/C/CScript/main.c|251|aviso: formato '%d' espera un argumento de tipo 'int', pero el argumento 2 es de tipo 'map_size_t' [-Wformat]|
/home/blackzerox/Documentos/Programacion/C/CScript/main.c|264|aviso: formato '%d' espera un argumento de tipo 'int', pero el argumento 2 es de tipo 'map_size_t' [-Wformat]|
/home/blackzerox/Documentos/Programacion/C/CScript/main.c|276|aviso: formato '%d' espera un argumento de tipo 'int', pero el argumento 2 es de tipo 'map_size_t' [-Wformat]|
/home/blackzerox/Documentos/Programacion/C/CScript/main.c|291|aviso: formato '%d' espera un argumento de tipo 'int', pero el argumento 2 es de tipo 'map_size_t' [-Wformat]|
/home/blackzerox/Documentos/Programacion/C/CScript/main.c|293|aviso: formato '%d' espera un argumento de tipo 'int', pero el argumento 2 es de tipo 'map_size_t' [-Wformat]|
/home/blackzerox/Documentos/Programacion/C/CScript/main.c|112|aviso: variable 'value' sin usar [-Wunused-variable]|
||=== Build finished: 19 errors, 0 warnings ===|



Pero claro compilando con -w se arregla todo y compila perfectamente, esto es un problema que apenas vengo captando y solucionando con varios cast pero lo veo muy, no encuentro palabras para esto solo no me agrada.



-------------- Build: Debug in CCScript_Linux ---------------

Compiling: main.c
Compiling: src/CSAVL.c
Compiling: src/CSArray.c
Compiling: src/CSBlockmem.c
Compiling: src/CSByte.c
Compiling: src/CSInfija2PreFija.c
Compiling: src/CSList.c
Compiling: src/CSMap.c
Compiling: src/CSQueue.c
Compiling: src/CSStack.c
Compiling: src/CSString.c
Compiling: src/CSUtiles.c
Compiling: src/CSVector.c
Compiling: src/CScript.c
Linking console executable: bin/Debug/CCScript_Linux
Output size is 30,17 KB
Process terminated with status 0 (0 minutes, 2 seconds)
0 errors, 0 warnings




  • ¿En que momentos en necesario realmente usar los cast?.
  • ¿Que tan bueno es usar typedef y en que casos (ademas de simplificar)?.
  • ¿Es indispensable forzar C99 con -std=c99 -ansi -wall -pedantic -pedantic-errors y por ende obligar los cast?.

Dulces Lunas!¡.
The Dark Shadow is my passion.

soyloqbuskas

¡Buenas BlackZeroX (Astaroth)!

Bueno, por lo que veo los errores de compilacion que tienes son por pasar una variable a una funcion del tipo incorrecto. Evidentemente eso se soluciona con un cast. Los cast son necesarios en casi cualquier programa ya que a menudo se necesita cambiar el tipo de una variable para operar con ella. Asi que no hay problema por usar un cast. Lo unico ha tener en cuenta es que si necesitas que tu programa sea muy rapido, los cast llevan tiempo y no deberias hacer cast muy amenudo, esto restaria eficiencia a tu programa.

Un ejemplo tipico del uso del cast es cuando lees un numero del teclado y lo guardas en un char* para evitar que el programa explote si el usuario mete un caracter. Este mecanismo de proteccion te obliga a hacer un atoi(var); (que es un cast de char* a int) para poder operar aritmeticamente con la variable leida. Asi que yo no veo ningun problema con los cast salvo el uso abusivo que reduce la eficiencia del programa.

En cuanto a los typedef...efectivamente solo sirven para abreviar y que el codigo sea mas legible, no le veo mas utilidad.

Sobre compilar con -std=c99....esto hace que no compiles con /usr/bin/gcc lo que hace es que compilas con /usr/bin/c99 que es otro compilador de C, pero no se que ventajas o inconvenientes tiene sobre gcc.

Y con gcc -w, creo que esta opcion desabilita los warnings....porque he probado a compilar un programa con un warning y con gcc -Wall si veo el warning y con gcc -w no. Asi que gcc -w no te losuciona los problemas solo te los aculta.

Espero haberte ayudado, un saludo.
"Si tienes 1 manzana y yo tengo otra manzana...
y las intercambiamos, ambos seguiremos teniendo 1 manzana.
Pero...si tu tienes 1 idea y yo tengo otra idea...
y las intercambiamos, ambos tendremos 2 ideas."


George Bernard Shaw

BlackZeroX

#2
Gracias.

De hecho me estoy leyendo TODOS los parámetros para el compilador gcc , además del las "normas" del C99 y ando viendo un poco del C11 (Se ve bueno).

http://gcc.gnu.org/onlinedocs/gcc-4.0.4/gcc/C-Dialect-Options.html
http://gcc.gnu.org/onlinedocs/gcc-4.0.4/gcc/Warning-Options.html

* A mi lo único que me molesta son esos avisos, los errores me parecen MUY LÓGICOS e innegables para el cast.
* Con respecto al cast de tipos y al peformance no lo se si los cast se hacen en tiempo de compilación o se traducen a opcodes (creo que así se llama, corrijan-me) si es lo primero entonces no veo mayor problema, aun que como bien se ve declaro varios typedef como:

list_t que es equivalente a un void* pero que a su vez es equivalente (para las funciones) a list_data_ptr_t que es equivalente a list_data_t* y a su vez struct list_data*...

list_t que = void* = list_data_ptr_t = list_data_t* = struct list_data*

o uno mas corto:

list_value_t = intptr_t y como en un intptr_t se puede agregar sin problema un void* o alguna dirección... los cast a mi parecer serian solo un plus... y es por ello mi problemática, de hecho ya es mas una problemática de decisión de usar o no los typedef.

P.D.: No creo que sea muy buen ejemplo atoi() atoi no es un cast es una funcion y en lugar de usar atoi seria mejor usar strtol() o strtoll() hay mas control de errores.

Dulces Lunas!¡
The Dark Shadow is my passion.

rir3760

Cita de: BlackZeroX (Astaroth) en 26 Octubre 2012, 04:50 AM¿Es bueno hacer varios cast de tipos?, Se que es recomendable pero no lo se hasta que punto.
No es recomendable. En C la convención es evitar en lo posible las conversiones explicitas, mientras menos mejor. Dos de las excepciones son 1) Cuando el modo estricto del compilador es paranoico y 2) Cuando se utilizan (llaman) funciones con un numero variable de argumentos.

La mayoría de los mensajes de error que mencionas se deben al segundo escenario:
main.c|123|aviso: formato '%d' espera un argumento de tipo 'int', pero el argumento 2 es de tipo 'avl_size_t' [-Wformat]|
Ya que el argumento pasado a la función es de tipo "size_t". Para imprimirlo y evitar la generación del mensaje puedes utilizar el especificador "%zu" (agregado a partir de C99).

El segundo tipo de error:
main.c|17|nota: se esperaba 'int32_t' pero el argumento es de tipo 'avl_t'|
Supongo se refiere a la llamada a "randomnumber", aqui el problema son los tipos de los argumentos y los parámetros:

* Los argumentos son de tipo "uintptr_t" (sin signo).
* Los parámetros son de tipo "int32_t" (con signo).
* El valor de retorno es de tipo "int" (con signo).

Si el valor pasado como argumento no puede almacenarse (esta fuera del rango valido) en el parámetro se pueden presentar problemas (comportamiento no definido). Peor todavía: los tipos "uintptr_t" e "int32_t" son opcionales en C99.

El tercer tipo de error:
main.c|130|error: el paso del argumento 2 de 'randomnumber' crea un entero desde un puntero sin una conversión|
Indica la obtención de un entero a base de un puntero sin conversión explicita de por medio (no es valido, este es otro de los (pocos) casos donde la conversión es requerida) pero no lo encuentro. Habría que revisar el código fuente completo.

Yo en tu lugar quitaría todas las conversiones. El siguiente paso seria solucionar cada uno de los conflictos de tipos o bien utilizar una conversión explicita (y documentarla).

Un saludo
C retains the basic philosophy that programmers know what they are doing; it only requires that they state their intentions explicitly.
--
Kernighan & Ritchie, The C programming language

BlackZeroX

#4
Bien gracias, los typedef los dejare para tipos simples de datos como punteros a funciones y evitando usarlos en struct y union.

Creo que compilare de vez en cuando en modo paranoico ( Para revisar algunas cosillas, errores y ver avisos ) pero terminare compilando con -w.

Dejo esto por si a alguien le interesa.

C99
http://www.iso.org/iso/home/store/catalogue_ics/catalogue_detail_ics.htm?csnumber=29237
http://atrey.karlin.mff.cuni.cz/projekty/vrr/doc/c99.pdf

C11
http://www.iso.org/iso/home/store/catalogue_ics/catalogue_detail_ics.htm?csnumber=57853

Dulces Lunas!¡.
The Dark Shadow is my passion.