Menú

Mostrar Mensajes

Esta sección te permite ver todos los mensajes escritos por este usuario. Ten en cuenta que sólo puedes ver los mensajes escritos en zonas a las que tienes acceso en este momento.

Mostrar Mensajes Menú

Mensajes - MAFUS

#31
Bueno, seguramente está empezando y coincidirás conmigo que esa es la forma de hacer de la mayoría de los tutoriales de internet.
#32
Te he modificado el programa para que funcione y te he escrito en los comentarios el por qué de cada cambio.

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

//
// Acuérdate de realizar uan función que libere todos los nodos que hayas creado.
//

// rNodo no existe, al menos en este código que nos has pasado
typedef struct rLista {
int dato;
struct rLista * sig;
} * tLista;

// tLista ya es un puntero. Esta función sólo genera un dato en en heap y lo devuelve,
// por tanto no hay que regresar un puntero a puntero.
tLista crear_nodo (int nuevo_dato) {
tLista nuevo_elemento = malloc (sizeof(struct rLista));
nuevo_elemento->dato = nuevo_dato;
nuevo_elemento->sig = NULL;
return nuevo_elemento;
}

// nuevo_elemento debe ser un puntero a la dirección del heap con el contenido
// relleno de la estructura y por tanto no un puntero a puntero,
tLista agregar_final (tLista * ppio, tLista nuevo_elemento) {
    // ppio es un puntero a puntero y eso es para que puedas modificar el puntero que
    // está en otra función. ppio debe ser dereferenciado
if (!*ppio) *ppio = nuevo_elemento;
// El parámetro que se debe pasar a agregar_final es el puntero al campo sig del dato apuntado por ppio
// por eso debe pasarse la referencia & a sig del puntero -> al que hace referencia * ppio
else (*ppio)->sig = agregar_final(&((*ppio)->sig), nuevo_elemento);

return *ppio;
}

//tLista ya es un puntero y solo hay que recorrerlo, no hay que pasarle un puntero a puntero
int contar_elementos (tLista ppio) {
if (!ppio) return 0;
// Usar una variable contandor haría que se reiniciara cada vez, por tanto lo que hay que hacer
// es añadir 1 a la siguiente llamada a la faunción. Así es como funciona la recursión:
// Cuando ppio sea NULL devolverá 0 y no hará más llamadas, la función anterior devolverá 1 + 0,
// la anterior devolverá 1 + 1 + 0... y así se recorrerá la lista de llamadas hasta la función inicial
// que devolverá la suma de todas las llamadas al llamante
else return 1+contar_elementos(ppio->sig);
}

int main (void) {
tLista ppio = NULL;

agregar_final(&ppio,crear_nodo(5));
agregar_final(&ppio,crear_nodo(6));
       
        // Muestra el uso de contar_elementos
printf("Elementos en la lista: %d", contar_elementos(ppio));
}
#33
n es el número al que se buscan los primos.
[latex]\frac{n}{\frac{n}{2}} = 2[/latex]
y 2 es el primer primo. Así
[latex]\frac{n}{\frac{n}{2}+1} < 2[/latex]
Por lo que cualquier número por encima de
[latex]\frac{n}{2}[/latex]
salvo n, no dará un número entero y por tanto no es divisible.
#34
No tiene sentido usar puntero triple. Lo que te importa es la dirección de memoria que contendrá pArreglo y cambiarlo en otra función. Como sabes un argumento de una función es una copia de un dato y para modificar el valor de una variable se pasa el puntero a esa variable (así el puntero es la copia y lo que hay dentro se modifica), por eso se pasa un puntero a puntero.

Como crearArreglo va a crear un array dinámico que debe ser devuelto (a a introducir en pArreglo un valor), esta función debe recibir la dirección de pArreglo (que es copia, pero su dato sí se puede modificar).

Ídem para asignarMemoria. No debe ser un puntero triple porque es realmente la encargada de crear el array e introducir esa dirección de memoria en pArreglo. La dirección de pArreglo ya la has pasado en el argumento punteroApuntero de crearArreglo y es lo que te interesa, así que se lo das sin más.

Totalmente diferente es el trabajo en copiarValores. Allí sólo te interesa rellenar el array, no debes modificar su dirección de memoria y por tanto debes pasarle la dirección del propio array. Vas a modificar su contenido.

Sería algo así:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

void crearArreglo(int **punteroApuntero, int dim);
void asignarMemoria(int **punteroApuntero, int dim );
void copiarValores(int *puntero, int dim);

int main()
{
    int *pArreglo = NULL;
    int dim = 0; //dimension

    printf("\nIngrese la dimension del arreglo: ");
    scanf("%d",&dim);

    srand(time(NULL)); //para obtener valores random en copiarValores()
    crearArreglo(&pArreglo,dim); //se le pasa el puntero a puntero y la dimension

    printf("\nPrimer elemento desde main: %d\n",*pArreglo);

    free(pArreglo); // recuerda que siempre que adquieres memorias a mano debes liberarla a mano.

    return 0;

}// fin main

void crearArreglo(int **punteroApuntero, int dim)
{
    asignarMemoria(punteroApuntero, dim);

    copiarValores(*punteroApuntero, dim);

}// fin funcion crearArreglo

void asignarMemoria(int **punteroApuntero, int dim)
{
    (*punteroApuntero) = (int *) malloc ( dim * sizeof(int) );

    if( (*punteroApuntero) == NULL )
    {
        printf("\nError en asignacion de memoria.\n");
        exit(EXIT_FAILURE);
    }//fin if
    else
    {
        printf("\nAsignacion de memoria exitosa.\n");

    } // fin else

} // fin funcion asignarMemoria

void copiarValores(int *puntero, int dim)
{
    for(int i=0; i<dim; ++i)
    {
        puntero[i] = rand() % 10;
        printf("Elemento %d : %d\n",i,puntero[i]);
        system("pause");
    }// te lo cambio por un for. Es más conciso en este caso

} // fin funcion copiarValores
#35
No entiendo la salida. ¿Qué hace que 2-3, 5-6, 6-7 sean validos pero no 1-2, 3-4, 4-5, 8-9?
#36
Programación C/C++ / Re: Puntero doble
16 Agosto 2021, 10:45 AM
Mejor sería reestructurar los tipos de datos: uno para tener guardar el dato mismo y otro para guardar la cola.

Verás que no se han hecho uso de punteros dobles. Muchas veces se pueden evitar

Dato:typedef struct tDATO {
int DNI;
char * nombre;
struct tDATO *siguiente;
} *DATO;


El dato guarda la información útil más un puntero que hace la lista. En verdad se podría realizar una estructura anidada, la estructura interna con el dato y la externa que contendrá el puntero (pero eso ya más avanzado).

Cola:typedef struct tCola {
    DATO primero;
    DATO ultimo;
    size_t n_elementos;
} *COLA;


La cola nos sirve para trabajar con la estructura cola, nada más. Guarda el primer dato, el último y el número de elementos.

Teniendo ya los dos tipos de datos estructurados vamos a crear la funcionalidad para manejarlos de forma sencilla. A por las funciones:

Para los datos .......

Primero vamos crear el dato

DATO dato_crear(int dni, char *nombre) {
    DATO dato = malloc(sizeof(struct tDATO));
    dato->DNI = dni;
    dato->nombre = malloc((strlen(nombre)+1) * sizeof(char));
    strcpy(dato->nombre, nombre);
    dato->siguiente = NULL;

    return dato;
}


Esto genera un tipo dato en memoria dinámica, nada del otro mundo. Lo único que hay que tener en cuenta es que los campos que se deben alojar en memoria dinámica, como nombre, debe hacerse mediante el uso de *alloc. Esto es especialmente así sobre todo en cadenas que te pueden venir de diferentes formas y no se puede asignar directamente el valor de la variable nombre a dato->nombre por la razón de que te pueden pasar un puntero de un array local y al salir de la función esa memoria sería liberada, es decir, si te hacen esto:

DATO dato_crear(int dni, char *nombre) {
    ...
    dato->nombre = nombre;
    ...
}

void generar_dato() {
    ...
    char nombre[] = "Tirant Lo Blanc";
    dato_crear(dni, nombre);
    ...
}


Repito, lo anterior está mal. Por otra parte se ve que 'siguiente' va a NULL, esto es así para que se puedan encontrar fallos de lógica.

Liberar dato de la memoria

void dato_liberar(DATO dato) {
    free(dato->nombre);
    free(dato);
}


Antes de liberar el dato propiamente dicho hay que liberar la memoria que ha adquirido para su funcionamiento, en este caso la cadena nombre.

Como puedes haber observado este es el funcionamiento básico, no hay guardas para saber si no se han pasado datos NULL o que no hayan ocurrido errores, pero esto es a lo que vamos.

Vamos a por la cola.....

Primero se crea la cola

COLA cola_crear() {
    COLA cola = malloc(sizeof(struct tCola));
    cola->primero = NULL;
    cola->ultimo = NULL;
    cola->n_elementos = 0;

    return cola;
}


Sencillo se genera en memoria dinámica y se ponen todos los elementos a 0 o NULL.

Agregar un dato a la cola

void cola_agregar(COLA cola, DATO dato) {
    if(cola_es_vacia(cola)) {
        cola->primero = dato;
    } else {
        cola->ultimo->siguiente = dato;
    }
    cola->ultimo = dato;
    cola->n_elementos++;
}


Los datos a una cola se introducen por el final, nuestro dato COLA ya tiene un puntero a su último elemento así que el trabajo es sencillo. Lo único que hay que tener en cuenta es si la cola está vacía: si está vacía el elemento introducido no sólo es el último sino que también es el primero; si la cola no está vacía hay que tener presente para el buen funcionamiento de nuestra estructura de datos que el último elemento de la cola apunte al que vamos a agregar.

Extraer un dato de la cola

DATO cola_extraer(COLA cola) {
    DATO dato = cola->primero;

    if(!cola_es_vacia(cola)) {
        cola->primero = cola->primero->siguiente;
        dato->siguiente = NULL;
        if(cola->primero == NULL) {
            cola->ultimo = NULL;
        }
        cola->n_elementos--;
    }

    return dato;
}


Como en una pila los datos se extraen desde e principio. Es importante saber dos cosas: si al principio la cola está vacía por lo que se tendrá que devolver NULL y no hacer nada más; que después de la extracción la cola se vacíe (donde cola->primero tendrá el valor NULL) en ese caso y para evitar problemas lógicos también se lleva a NULL cola->ultimo.

Desalojar la cola de memoria

void cola_liberar(COLA cola) {
    DATO x;
    while(cola->primero) {
        x = cola->primero;
        cola->primero = cola->primero->siguiente;
        dato_liberar(x);
    }
    free(cola);
}


Como con dato primero hay que liberar toda la memoria que haya alojado. Esto se hace recorriendo la lista con cola->primero hasta que sea NULL. Ver que se usa la funcion dato_liberar porque cada dato se debe desalojar usando su propia función (ya que también hace su propia limpieza). Se podrían haber extraído datos con cola_extraer, pero sería más lento.

Después, para evitar trabajar directamente con el dato cola se pueden hacer algunas funciones auxiliares que de hecho ya se han usado en el código anterior, pero esas van más para un usuario de nuestro código:

int cola_es_vacia(COLA cola) {
    return cola->n_elementos == 0;
}

size_t cola_longitud(COLA cola) {
    return cola->n_elementos;
}

#37
Claro, dibuja tu mismo ese salto de línea:
Escribes "Cuál es tu nombre?\n"
-- El usuario introduce su nombre --
Escribes "\nTe llamas [nombre]"

Nota el carácter de salto de línea '\n'.
#38
Programación C/C++ / Re: Puntero doble
15 Agosto 2021, 16:06 PM
Bueno, la verdad es que es te estás liando un poco con los punteros y los dobles punteros.

(*aux)->sgte = nuevoNodo;

Aquí nuevoNodo es COLA * nuevoNodo, lo que es lo mismo, struct tCola ** nuevoNodo (un puntero doble). Pero sgte espera un puntero simple:

typedef struct tCola {
DATO contenido;
struct tCola *sgte;
} * COLA;


En la definición se puede ver que sgte es un puntero simple.

Ídem para aux = (*aux)->sgte;
#39
El sistema guarda las pulsaciones del teclado en un buffer. kbhit, getch, getchar, scanf, etc. lo que hacen es leer desde ese búffer. Esto es porque es muy improbable que un usuario pulse una tecla en el momento preciso que tu la pides.

Por tanto debes encontrar una forma de descartar esas pulsaciones mientras estés en el aire (por ejemplo que cuando toques el suelo primero se vacíe el búffer del teclado (una única vez) y a partir de después ver si pulsa la tecla de salto.

Otra idea sería que mientras estás en el aire puedas recibir las pulsaciones de teclado del usuario pero no hacer nada ante ellas.
#40
Me he tomado la libertad de modificar tu código y añadir unas cuantas cosas. De igual forma he comentado cambios y añadidos.

Espero que te sirva.

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

typedef struct rNodo {
    int dato;
    struct rNodo * siguiente;
} tNodo;

typedef struct rLista {
    tNodo * primero;
    tNodo * ultimo;
    int longitud;
} tLista;

typedef tNodo * Nodo;
typedef tLista * Lista;

tLista* inicializar();
tNodo* crear_nodo(int nuevo_dato);
void ins_de_orden_ascen(Lista lista, Nodo nuevo_nodo);
void mostrar(Lista lista);
void liberar(Lista lista);

int main(void) {
    Lista lista = inicializar();
    int nuevo_dato;
    int continuar; // cambio el nombre de variable para hacerlo  más coherente. No lo inicializo ya que en el bucle se debe hacer obligatoriamente.
do {
printf("ingrese un numero: ");
        scanf("%i", &nuevo_dato);
        ins_de_orden_ascen(lista, crear_nodo(nuevo_dato));
        printf("Quiere ingresar otro numero? 1(si) o 0(no): ");
        scanf("%i", &continuar);
    } while (continuar);

    mostrar(lista);
    liberar(lista);
    return 0;
}


tLista* inicializar() { // no tiene razón la existencia de un argumento, no se usaba
    Lista nueva_lista = malloc(sizeof (tLista));
    nueva_lista->primero = NULL;
    nueva_lista->ultimo = NULL;
    nueva_lista->longitud = 0;
    return nueva_lista;
}

tNodo* crear_nodo(int nuevo_dato) {
    Nodo nuevo_elemento = malloc(sizeof (tNodo));
    nuevo_elemento->dato = nuevo_dato;
    nuevo_elemento->siguiente = NULL;
    return nuevo_elemento;
}

void ins_de_orden_ascen(Lista lista, Nodo nuevo_nodo) { // no tiene razón que se devuelva la lista, no se modifica su puntero
    if (lista->longitud == 0) { // Si la lista es vacia
        nuevo_nodo->siguiente = lista->primero;
        lista->primero = nuevo_nodo;
        lista->ultimo = nuevo_nodo;
    } else {
        Nodo aux = lista->primero; // aux apunta al primer elemento
        Nodo ant = NULL;
        while(aux && aux->dato < nuevo_nodo->dato) { // busco el nodo con un dato mayor al nuevo dato, también puede llegar al fin de la lista
            ant = aux; // y me quedo con el nodo anterior
            aux = aux->siguiente;
        }
        if(ant) { // si el nodo anterior existe
            ant->siguiente = nuevo_nodo; // hago que su siguiente apunte al nuevo nodo
            if(lista->ultimo == ant) { // si el nodo anterior era el último de la lista
                lista->ultimo = nuevo_nodo; // ahora debe serlo el nuevo nodo
            }
        } else {
            lista->primero = nuevo_nodo; // en caso contrario quiere decir que el nuevo nodo debe ser el primero
        }
        nuevo_nodo->siguiente = aux; // el siguiente del nodo nuevo debe ser el nodo que debía ser el siguiente, o NULL si había llegado al final de la lista

    }
    lista->longitud++; // Aumenta la cantidad de elementos
}

void mostrar(Lista lista) { // pinta la lista en pantalla
    puts("--- Lista ---");
    if(!lista) { // No existe lista
        printf("Lista no inicializada");
        return;
    }
    if(lista->longitud == 0) { // Lista vacía
        printf("Lista vacía");
        return;
    }

    for(Nodo n = lista->primero; n; n = n->siguiente) { // Muestra todos los nodos
        printf("%d\n", n->dato);
    }
}

void liberar(Lista lista) { // Desaloja la lista de la memoria
    Nodo sig;
    if(!lista) { // Si no existe lista no hay que liberar nada
        return;
    }
    while(lista->primero) {
        sig = lista->primero->siguiente;
        free(lista->primero);
        lista->primero = sig;
        // lista->longitud--; // No hace falta esta línea ya que la lista se va a desalojar de la memoria
    }
    free(lista);
}