Problema con C (switch+for)

Buenas a todos. Estoy liado con un programa que tengo que hacer. Resulta que tengo la siguiente funcion (pego un cacho):

void processarArguments (int fargc, char **fargv,char *ptasqGlobals,char *pperEtapa,char *petapes,char *preparticio,char *pminComput,char *pmaxComput,char *pcomput,char *puserComput,char *pminComunicacio,char *pmaxComunicacio,char *pcomunicacio,char *puserComunicacio)
{
int i=0;
ptasqGlobals=petapes=0;
preparticio=0;
pperEtapa="n";

for (i=0;i<=fargc; i++)
{
switch (fargv[i])
{
case "-et": //controlem el nombre d'etapes
if (EsNumero(fargv[i+1])==0)
{
petapes=fargv[i+1];
i++;
break;
}
else
{
ErrorP("etapes");
exit (EXIT_FAILURE);
}

case "-tgl": // controlem si entre el nombre de tasques globals
if (EsNumero(fargv[i+1])==0)
{
ptasqGlobals=fargv[i+1];
i++;
break; // saltem a l'inici del bucle
}
else
{
ErrorP("globals");
exit (EXIT_FAILURE);
}

case "-tet": //controlem si vol entrar l'usuari les tasques per etapa
pperEtapa="s";

case "-re": // controlem que entre la repartició, per defecte aleatoria
if (EsNumero(fargv[i+1])==0 && atoi(fargv[i+1])>=0 && fatoi(fargv[i+1])<=4)
{
preparticio=fargv[i+1];
i++;
break;
}
else
{
ErrorP("reparticio");
exit (EXIT_FAILURE);
}

case "-comp": //controlem que entre el comput
if (EsInterval(fargv[i+1])==0) //Aleatori, usuari entre mínim i màxim
{
sscanf(fargv[i+1],&pminComput,&pmaxComput);
i++;
break;
}
else
{
if (EsNumero(fargv[i+1])==0 && atoi(fargv[i+1])>0)//Totes iguals, usuari entre quan val el comput
{
pcomput=fargv[i+1];
i++;
break;
}
else
{
if (strcmp(fargv[i+1],"user")==0)//Es l'usuari qui entre els computs
{
puserComput='s';
i++;
break;
}
else
{
ErrorP("comput");
exit (EXIT_FAILURE);
}
}
}


..................(y sigue)


Y cuando compilo me da los siguientes errores:
parametres.c: In function 'main':
parametres.c:38: error: switch quantity not an integer
parametres.c:40: error: case label does not reduce to an integer constant
parametres.c: In function 'processarArguments':
parametres.c:62: error: switch quantity not an integer
parametres.c:64: error: case label does not reduce to an integer constant
parametres.c:77: error: case label does not reduce to an integer constant
parametres.c:90: error: case label does not reduce to an integer constant
parametres.c:93: error: case label does not reduce to an integer constant
parametres.c:106: error: case label does not reduce to an integer constant
parametres.c:109: warning: passing argument 2 of 'sscanf' from incompatible pointer type
parametres.c:125: warning: assignment makes pointer from integer without a cast
parametres.c:137: error: case label does not reduce to an integer constant
parametres.c:140: warning: passing argument 2 of 'sscanf' from incompatible pointer type
parametres.c:156: warning: assignment makes pointer from integer without a cast


He estado leyendo en google, y parece que és porque el switch no puede ir dentro de un for, o tener un argumento variable como parametro.

Sabeis si esto es del todo cierto, o hay algun otro error?, y de ser así, como puedo solventarlo?

Saludos y mil gracias!!!
Parece que las constantes del case tienen que ser enteras, o algo que se pueda reducir a entero (char). No admite cadenas.
nada mas lejos de la realidad:

parametres.c:40: error: case label does not reduce to an integer constant


significa que no puedes hacer un 'switch' de una cadena de caracteres, tan solo de un valor numerico.

si quieres hacer un switch de una cadena de caracteres, no podras usar un switch, sino una serie de 'if else' con la instruccion de 'srtcmp'
f5inet escribió:nada mas lejos de la realidad:


¿?¿?

Y yo que he dicho?
He estado leyendo en google, y parece que és porque el switch no puede ir dentro de un for, o tener un argumento variable como parametro.


Se refería a eso
Gracias a todos por la ayuda!

O sea, que la solucion más acorde con el problema y mas "limpia" es sustituir los case por if's no?

Saludos y gracias!!
O sea, que la solucion más acorde con el problema y mas "limpia" es sustituir los case por if's no?


Claro, una cadena de if...else if...else if...[blah]...else

Básicamente en C es imposible que compruebe la igualdad de cadenas. Incluso comprobando la igualdad entre dos punteros puede ser peligroso (sizeof(void *) no tiene por qué ser igual que sizeof(int)).

---- Disclaimer: Coñazo que incluso puede ser mentira :P ----

La explicación técnica (para quién le interese) es que el compilador para convertir una expresión switch como la siguiente:

switch (a)
{
    case 0: /* algo */ break;
    case 1: /* otra cosa */ break;
    default: /* y esto */ break;
}


en ensamblador hace algo como lo siguiente:

# a está en un registro 'inventado' $a
    beq $a,$L0           # case 0
    cmpeq $a,1,$1        # case 1 (primera parte)
    bne $1,$L1           # case 1 (segunda parte)
    br $L2               # default
$L0:
    # algo
    b $L3
$L1:
    # otra cosa
    b $L3
$L2:
    # y esto
$L3:
    # Fin del switch


En este caso el ensamblador es el del Alpha (porque lo conozco, es fácil y no tengo a mano la referencia del asm del IEEE), pero la idea vale para cualquier ensamblador.

Aquí las comprobaciones del switch se hacen en las instrucciones marcadas. Como ves, no hay forma de comprobar datos que no quepan en un registro de la CPU. Una cadena de caracteres tiene que estar en memoria y comprobar si dos cadenas son iguales requiere ir leyendo caracter a caracter. Obviamente esto no puede hacerse en una construcción como la anterior.

Sin embargo en una cadena if...else if...else las cosas no son iguales en ensamblador dado que siempre hará cosas como la siguiente:

if (algo)
    /* una cosa */
else if (otro)
    /* otra cosa */
...
else
    /* una tercera */


Lo convertirá a un código de la siguiente forma:

# $1 contiene el resultado de ejecutar 'algo'
    beq $1,$L1
# Recalculamos $1 con el resultado de otro
    beq $1,$L2
[....]
# Este es el else, salto incondicional
    br $LN
$L1:
    # una cosa
    b $LF
$L2:
    # otra cosa
    b $LF
[....]
$LN:
    # una tercera
$LF:
    # fin de la construcción


Como podrás comprobar el switch es infinitamente más simple dado que no recalcula en ningún momento nada para hacer comprobaciones. Es por esto que es mucho menos versátil.

En lenguajes que no son C las cosas cambian dado que no suele existir una traducción directa a ensamblador.

Espero que esto te aclare las cosas. Y no solo a ti, si no también a alguno que yo me sé que está aprendiendo C :P

Un saludo.Ferdy
Ferdy escribió:Espero que esto te aclare las cosas. Y no solo a ti, si no también a alguno que yo me sé que está aprendiendo C :P

Ah, ¿pero C se termina de aprender alguna vez? :P

PD: Va a ser que sí te estás haciendo viejo, ya empieza a pegar la hebra "sin venir a cuento". XD

Un saludo.
Ah, ¿pero C se termina de aprender alguna vez?


Buena pregunta... imagino que los aspectos técnicos propios del lenguaje si se aprenden rapidillo jeje... otra cosa es que nunca dejemos de aprender a programar (ya sea en C o en blah).

PD: Va a ser que sí te estás haciendo viejo, ya empieza a pegar la hebra "sin venir a cuento".


Joer.... que algo sí venia a cuento, ¿no? :D

Saludos.Ferdy
Personalmente... adoro esas explicaciones. Otra cosa es que luego no las recuerde, pero durante el tiempo que las mantengo en mi cabeza, es la leche XD.

Por cierto... ¿los punteros void para qué se utilizan? [ayay].

¡Un saludo!
Se me ocurre que en casos donde necesites velocidad y se prefiera un switch a la hora de comparar cadenas se pueden convertir a enteros (un máximo de 4 caracteres para un int y 8 para long int, por el número de bytes de cada tipo)

Aunque tal vez sea una rayada mia :/
Los "void pointer" se suelen utilizar cuando no sabemos el tipo de datos que nos será devuelto o pasado a una función.

Aunque suelen ser una mala práctica (es mejor pensar un poco como poder indicar correctamente el tipo)... pero hay veces que es necesario.

Saludos!
Los punteros genéricos (void *) en C tienen un par de aplicaciones muy comunes:

1) La memoria que reservas 'en bruto' con malloc te es devuelta como un puntero genérico (parece normal, no?). Luego simplemente haces un casting para que el compilador sepa qué tipo de datos tendrás en esa zona de memoria.

2) Hay muchas aplicaciones en las que necesitas funciones 'polimórficas'; esto se soluciona con punteros genéricos. Un ejemplo simple es la función qsort que viene en stdlib.h según POSIX (ver: http://www.opengroup.org/onlinepubs/009695399/functions/qsort.html para más información)

El último argumento de esta función: (int (*compar)(const void *, const void *)) es un puntero a una función de tipo int (const void*,const void*).

Si queremos que esta función trabaje con una estructura simple:

typedef struct ejemplo_s {
    void *data;
    int clave;
} ejemplo;


Haremos una función comparar así:

int comparar_ej(const void *a,const void *b)
{
    int pri = ((ejemplo *)a)->clave;
    int seg = ((ejemplo *)b)->clave;

    if (pri<seg)
        return -1;
    else if (pri>seg)
        return 1;
    else
        return 0;
}


La idea es que un puntero genérico no lo podemos dereferenciar directamente. Así que tenemos que hacer un casting para decirle al compilador el tipo de datos que hay en esa zona de memoria; en este caso le decimos que 'a' y 'b' son de tipo ejemplo *; es decir punteros a estructuras 'ejemplo'.

Un ejemplo tonto de todo esto:

[ $ ~ ] cat blah.c
#include <stdio.h>
#include <stdlib.h>

typedef struct ejemplo_s {
   char *data;
   int clave;
} ejemplo;

int comparar_ej(const void *a,const void *b)
{
   int pri = ((ejemplo *)a)->clave;
   int seg = ((ejemplo *)b)->clave;

   if (pri<seg)
      return -1;
   else if (pri>seg)
      return 1;
   else
      return 0;
}

int main(int argc,char **argv)
{
   int i;
   ejemplo x[2];

   x[0].clave = 4;
   x[0].data = (char *)strdup("Hola");

   x[1].clave = 2;
   x[1].data = (char *)strdup("Adios");

   qsort((void *)x,2,sizeof(ejemplo),comparar_ej);

   for ( i = 0 ; i < 2 ; i++ )
      printf("(%d) %s\n",x[i].clave,x[i].data);

   return 0;
}
[ $ ~ ] make blah
cc     blah.c   -o blah
[ $ ~ ] ./blah
(2) Adios
(4) Hola
[ $ ~ ]


Se me ocurre que en casos donde necesites velocidad y se prefiera un switch a la hora de comparar cadenas se pueden convertir a enteros (un máximo de 4 caracteres para un int y 8 para long int, por el número de bytes de cada tipo)


No lo debí entender bien, porque no veo la mejora de velocidad :/

Saludos.Ferdy
Ferdy escribió:No lo debí entender bien, porque no veo la mejora de velocidad :/


Tengo entendido que un switch es más rapido que varios if. Si no es así, no tiene ningun sentido, claro.
Tengo entendido que un switch es más rapido que varios if. Si no es así, no tiene ningun sentido, claro.


Si, claro que lo es (ver mi explicación más arriba), lo que no he entendido es cómo representar la cadena...

Saludos.Ferdy
Pues, siempre que una cadena sea menor de 4 caracteres (u 8 si usamos un long int, aunque perderiamos velocidad), crear un int que cada byte del int sea un char de la cadena. Lo escribiria en C pero no me acuerdo como eran los desplazamientos de bytes. La idea es algo así (la traduccion ascii me la he inventado)

'h' 'o' 'l' 'a'
0x60 0x70 0x67 0x50
=>
0x60706750

Ahora faltaria saber si esos desplazamientos mas switch seria mas rapido que los ifs.
Uhm.... ahora te entiendo. Creo que no merece la pena, al fin y al cabo lo que tu comentas es O(n) al igual que iterar sobre todos los elementos de una cadena :P

No es tan costoso un strcmp creo yo jeje

Saludos.Ferdy
No, si la chicha del asunto esta en usar switch en vez de ifs; es verdad, tambien se podrian usar strcmps.
Si, si te entiendo... pero usar un switch te deja las comprobaciones en el mismo orden de magnitud que los if...else if...else. No te va a bajar de 'lineal'.

Saludos.Ferdy
No claro, sigue siendo O(n) pero segun que casos puede ser interesante reducir el coste aunque solo sea por un factor. Por ejemplo, en un emulador se podria usar un switch para proceder segun el codigo de operacion, con ifs seria más lento aunque del mismo orden. No es lo mismo que vaya a 12FPS que a 24FPS.

Saludos
PD: Además, que demonios, un switch queda más mono que if ... else if ... else xD
Jajaja! Gracias por la explicación de mi dudilla a Ruro y Ferdy. Por cierto Ferdy, qué explicativo andas hoy... ¿tienes exámen mañana ó algo así?XD. Gracias por el ejemplo tan currado.

Raharu... sabemos que estás orgulloso de tu idea... sí... y está muy guay... sí... pero, ¿no crees que la gente te mataría si al ver tu código de repente se tiene que enfrentar a eso?XD. No sé, como dices, puede que haya casos que esté justificado, pero tampoco creo que la diferencia de rendimiento sea tan brutal, ¿no?

¡Salu2!
No claro, sigue siendo O(n) pero segun que casos puede ser interesante reducir el coste aunque solo sea por un factor. Por ejemplo, en un emulador se podria usar un switch para proceder segun el codigo de operacion, con ifs seria más lento aunque del mismo orden. No es lo mismo que vaya a 12FPS que a 24FPS.


Veeeeeeeeeeenga aceptamos pulpo como animal de compañía :P Si yo no digo que sea mala idea, solo digo que no se si merece la pena jeje

¿tienes exámen mañana ó algo así?


Realmente tengo dos, pero bueno. Creo que merecía la pena :)

Saludos.Ferdy
Raharu escribió:Por ejemplo, en un emulador se podria usar un switch para proceder segun el codigo de operacion, con ifs seria más lento aunque del mismo orden. No es lo mismo que vaya a 12FPS que a 24FPS.


En un emulador sería preferible definir un vector de funciones e indexar en ese vector con el código de operación y pasarle como parámetro siempre el resto de la instrucción.
22 respuestas