Dudas con cola e INT_MAX lenguaje C.

Iniciado por NOB2014, 21 Agosto 2016, 20:36 PM

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

NOB2014

Hola, gente, que tengan un muy buen día. -
Tengo claro que deberíamos pedir ayuda cuando el programa falla y este no es el caso (por lo menos es lo que parece) pero estoy tratando de hacer esto solo y como la mayoría sabe no tengo a quien consultar que no sean Uds. los molesto. -    
Mi duda está desde la línea 78 a la 96, ¿es todo correcto lo que se encuentra dentro de esas líneas? y otra cosita, como puedo hacer para liberar la memoria de Elem y elem (lineas 78 y 88).
Me falta la función quitar que intentare luego de vuestra(s) opinión(es) .-
Estoy aprendiendo, por lo tanto, cualquier sugerencia de cambios en el código será bienvenida por dura que sea. -

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

typedef struct ElementoLista{
int dato;
int elementos;
struct ElementoLista *siguiente;
}Elemento;

Elemento *inicio = NULL;
Elemento *fin = NULL;

void menu( void );
void limpiar( void );
Elemento *agregar( Elemento *E );
void mostrar( Elemento *E );

int main( void ){

menu();

return 0;
}


void menu( void ){
int opc, ok, ch;
Elemento *elemento = NULL;

do{
do{
limpiar( );
printf( "\n =============== Menu principal ===============\n"
"\n 1 - Agregar a la cola"
"\n 2 - Quitar de la cola"
"\n 3 - Listar cola"
"\n 4 - Salir\n"
"\n ingrese opcion.....:" );

ok = scanf( "%d", &opc ) == 1 && opc > 0 && opc <= 4;
while ((ch = getchar()) != EOF && ch != '\n');
}while( !ok );


switch ( opc ){
case 1: elemento = agregar( elemento );
break;
case 2: //cola = quitar( elemento );
break;
case 3: mostrar( elemento );
break;
case 4:
free( elemento );
break;
}
}while( opc != 4 );


}

void limpiar( void ){
system("cls||clear");
}


Elemento *agregar( Elemento *E ){
int ok, ch, dto;

do{
limpiar();
printf( "\n Ingrese dato (mayor a 0 y menor a %d)....: ", INT_MAX );
ok = scanf( "%d", &dto ) == 1 && dto >0 && dto <= INT_MAX;
while ((ch = getchar()) != EOF && ch != '\n');
}while( !ok );

if( E != NULL ){
Elemento *Elem = calloc( sizeof( Elemento ), 1 );

Elem->dato = dto;
Elem->siguiente = NULL;
fin = Elem;
Elem->elementos += 1;
E->siguiente = Elem;

return Elem;
}else{
Elemento *elem = calloc( sizeof( Elemento ), 1 );

elem->dato = dto;
elem->siguiente = NULL;
inicio = elem;
fin = elem;
elem->elementos = 1;

return elem;
}

return E;
}

void mostrar( Elemento *E ){
Elemento *auxiliar;  

if( E != NULL ){
auxiliar = inicio;
while( auxiliar != NULL ){
printf( "\n %d", auxiliar->dato );
auxiliar = auxiliar->siguiente;
}
free( auxiliar );
}else{
printf( "\n Cola vacia" );
}
printf( "\n\n Pulse una tecla para continuar..." );
getchar();

}


Saludos.
abraza las cosas y personas malas como si fueran tu mas preciada joya,Son tus mas grandes maestros de paciencia sabiduría y amor y cuando lo abrazas dejan de causar dolor.-

class_OpenGL

A ver, si, técnicamente yo lo veo bien, pero en mi opinión, está mal estructurado. Lo que yo haría es la siguiente estructura:

typedef struct ElementoLista{
    int dato;
    struct ElementoLista *siguiente;
} Elemento;

typedef struct {
    Elemento *ultimo;
    unsigned int num_elementos;
} Pila;

typedef struct {
    Elemento *primero;
    unsigned int num_elementos;
} Cola;

// Si se retorna 0 por alguna de las dos funciones siguientes, ha habido error. De lo contrario, todo ha ido correctamente
int agregar_a_pila(pila *pila);
int agregar_a_cola(cola *cola);

int quitar_a_pila(pila *pila);
int quitar_a_cola(cola *cola);

void mostrar_pila(const pila *pila);
void mostrar_cola(const cola *cola);


Pequeño detalle a tener en cuenta:
ok = scanf( "%d", &dto ) == 1 && dto >0 && dto <= INT_MAX;

Un entero JAMÁS va a ser mayor que INT_MAX (si eso sucediera, sería un bug del compilador), así que es una tontería poner la condición 'dto <= INT_MAX'

Programador aficionado. Me quiero centrar en programar videojuegos. La API que uso para crearlos es OpenGL

AlbertoBSD

#2
Se ven bien las líneas que comentas solo un detalle con la variable elementos. y sobre liberarlos no es necesario en esa funcion ya que si se vuelven parte de la cola, solo sera necesario liberarlos cuando los quieras eliminar.

El detalle con los elementos es que cada elemento tiene su propio contador de elementos y eso no deberia de ser asi, solo necesitas un contador de elementos y no deberia de ser parte de la esteucutura individual de cada elemento.

Saludos
Donaciones
1Coffee1jV4gB5gaXfHgSHDz9xx9QSECVW

NOB2014

Hola, gracias a ambos por responder. -
class_OpenGL voy a estudiar tu código. -

CitarUn entero JAMÁS va a ser mayor que INT_MAX (si eso sucediera, sería un bug del compilador), así que es una tontería poner la condición 'dto <= INT_MAX'
No estoy de acuerdo, si se ingresa un dato mayor a INT_MAX no se sale del bucle, en cambio, si no pongo esta condición el compilador lo transforma a un número negativo o algo por el estilo, no recuerdo. 

Saludos.
abraza las cosas y personas malas como si fueran tu mas preciada joya,Son tus mas grandes maestros de paciencia sabiduría y amor y cuando lo abrazas dejan de causar dolor.-

NOB2014

CitarEl detalle con los elementos es que cada elemento tiene su propio contador de elementos y eso no deberia de ser asi, solo necesitas un contador de elementos y no deberia de ser parte de la esteucutura individual de cada elemento.
Si amigo, un graso error él mío, ya lo solucione. -
abraza las cosas y personas malas como si fueran tu mas preciada joya,Son tus mas grandes maestros de paciencia sabiduría y amor y cuando lo abrazas dejan de causar dolor.-

class_OpenGL

Cita de: NOB2014No estoy de acuerdo, si se ingresa un dato mayor a INT_MAX no se sale del bucle, en cambio, si no pongo esta condición el compilador lo transforma a un número negativo o algo por el estilo, no recuerdo.  

Lo que digo es 100% verdad, y te lo demuestro. Un entero, en informática, se almacena en binario con una cierta cantidad de bits. En C, con ordenadores actuales, se suelen almacenar con 32 bits. Entonces, dado que hay un espacio limitado en la que poder almacenar el entero, es imposible pasar ese límite, porque el tamaño de un entero en C es fijo.

Entonces, ¿por qué no sale del bucle? Imaginemos que introduces el valor INT_MAX + 1. ¿Qué es lo que va a pasar? Que scanf va a transformar lo introducido a binario, por lo que obtendremos lo siguiente:

1000000000000000000000000000000

Aquí hay un 1 y 31 0s. Eso, interpretado para un entero con signo, da como resultado un número negativo, por lo que 'ok' valdrá 0 (ya que no se cumple que sea mayor que 0). Entonces, el while se ejecuta de nuevo, y por lo tanto, te pide otro número, entonces por eso parece que si introduces un número superior a INT_MAX, entonces el bucle se repite hasta que introduzcas otro número. A modo de experimento, mira qué pasa si introduces el 4294967298, un número claramente superior a INT_MAX

Programador aficionado. Me quiero centrar en programar videojuegos. La API que uso para crearlos es OpenGL

NOB2014

estoy desconcertado con esto, yo había intentado unas cuantas veces y siempre seguía en el bucle si el ingreso era mayor que el permitido por INT_MAX, ahora resulta que en ciertos casos también me falla y pone un número distinto al ingresado y sale del bucle, debo suponer que esto no tiene solución, soy muy exigente con las validaciones de ingreso de datos. -
abraza las cosas y personas malas como si fueran tu mas preciada joya,Son tus mas grandes maestros de paciencia sabiduría y amor y cuando lo abrazas dejan de causar dolor.-

AlbertoBSD

Si se quiere validar bien ciertos inputs se puede hacer con:

fgets
strtol


asi limitas el input a ciertos limite de entrada. Y con el strtol validas si el input tiene caracteres no validos.

Saludos!
Donaciones
1Coffee1jV4gB5gaXfHgSHDz9xx9QSECVW

class_OpenGL

ATENCIÓN, RESPUESTA LARGA

Primero, para que entiendas mejor, voy a explicar algo necesario. Para entender lo que voy a explicar, tendrás que dominar un poco la base binaria y cómo pasar entre decimal y binario.

Procedo a explicar cómo se puede almacenar un entero con signo

Imaginemos que queremos guardar el número -123 como un entero sin signo. Para hacerlo lo que tenemos que hacer es restar a 2^31 - 1 (2 elevado a 31 menos 1) con 123 (he puesto 31 porque un entero suele ocupar 32 bits, y hay que restar 1 porque el último bit se usa para "saber si el número es positivo o negativo"). El resultado de esa operación es 2147483524. Ahora convertimos ese número a binario:

1111111111111111111111110000100

Aquí hay 31 bits. Por último, tenemos que poner el último bit como 1, para indicar que el número es negativo (lo ponemos a la derecha):

11111111111111111111111110000100

CONCLUSIONES IMPORTANTES DE ESTE PROCESO:

  • Si el último bit vale 1, entonces seguro que el número es negativo, de lo contrario, el número es positivo

(En realidad, la CPU hace otro proceso más simple, pero que es un poco más difícil de ver a simple vista, por eso lo expliqué así).
_____________________________________________________________________

Una vez sabido esto, procedamos a explicar tu duda:
Cita de: NOB2014 en 21 Agosto 2016, 23:29 PM
estoy desconcertado con esto, yo había intentado unas cuantas veces y siempre seguía en el bucle si el ingreso era mayor que el permitido por INT_MAX, ahora resulta que en ciertos casos también me falla y pone un número distinto al ingresado y sale del bucle, debo suponer que esto no tiene solución, soy muy exigente con las validaciones de ingreso de datos. -

Vamos a enumerar lo que hace la función scanf:

scanf lee el entero, y lo convierte a binario hasta que se acaben los dígitos introducidos. Una vez obtenido el número, guarda los primeros 32 bits en la variable.

Ahora imagina que has introducido 2147483650 (que es INT_MAX + 1). Vamos a convertir ese número a binario:

10000000000000000000000000000001

Aquí, scanf ya tiene directamente los 32 bits, por lo que los copia sin fijarse en la variable y dice que todo ha ido bien. Entonces, una vez que la variable tiene esos bits, vamos a convertir ese número binario a entero con signo.



  • Primero, el bit número 32 es 1, por lo que el número va a ser negativo.
  • Segundo, teniendo en cuenta lo que expliqué al principio de esta respuesta, convertimos los 31 primeros bits a decimal. Resultado: 1.
  • Tercero, realizamos la operación inversa a lo del principio de la respuesta: 2^31 - 1 - valor = 1. Despejamos la ecuación... valor = 2^31 - 2 = 2147483646

Por lo tanto, si por la consola introducimos INT_MAX + 1, lo que en realidad almacenaremos en la variable entera con signo es -2147483646.
____________
Ahora bien, ¿por qué a veces si que da como resultado un número positivo?

Eso es sencillo, lo que pasa es que el número resultante tenía como último bit un 0, por lo que como ya hemos dicho, eso significa que la variable será positiva.

EJEMPLO RÁPIDO:
Introducimos por consola 4294967336 (mayor a INT_MAX)
Este número en binario es 100000000000000000000000000101000
Copiamos los 32 primeros bits a la variable: 00000000000000000000000000101000
El bit número 32 de la variable vale 0 (puedes contarlo tu mismo).

Como el bit nº 32 vale 0, entonces la variable será positiva, pero valdrá 40 (esto lo sacas de convertir 00000000000000000000000000101000 a binario).

Programador aficionado. Me quiero centrar en programar videojuegos. La API que uso para crearlos es OpenGL

NOB2014

class_OpenGL que decir del tiempo que te tomaste, muchas gracias pero muchas gracias, si bien no puedo decir que lo interpreto todo al pie de la letra, con unos pocos repasos ya me ha aclarado bastante el panorama. -
A continuación, pongo el código de la función quitar que era lo que me faltaba, con un solo cambio parece que funciona correctamente, lo que me queda duda es como hago para liberar la memoria del nodo que ya no utilizo, lo quería hacer creando un nodo temporal y luego liberarlo desde hay, pero no estoy seguro si es lo correcto. -

Elemento *quitar( Elemento *E, int *cantElementos ){

if( *cantElementos > 0 ){
if( *cantElementos == 1){
E = NULL;
}else{
inicio = inicio->siguiente;
}
*cantElementos -= 1;
}else{
printf( "\n Cola vacia\n\n Pulse una tecla para continuar..." );
getchar();
}

return E;
}


Saludos.
abraza las cosas y personas malas como si fueran tu mas preciada joya,Son tus mas grandes maestros de paciencia sabiduría y amor y cuando lo abrazas dejan de causar dolor.-