Discussione Ufficiale Programmiamo con Inforge | Esercitazione 02 in C | Livello base

Una Discussione Ufficiale punta a raccogliere tutte le informazioni su un argomento o un fatto di attualità, con costanti aggiornamenti da parte del creatore e dei partecipanti.
Ultima modifica:
Eccomi qui con la mia versione. Ho utilizzato una lista ordinata bidirezionale.
C:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define MODE_INSERT 0
#define MODE_FIND 1
#define MODE_REMOVE 2

struct Person
{
  char* name;
  char* last_name;
  int age;
};

struct LLNode
{
  struct Person data;
  struct LLNode *next;
  struct LLNode *prev;
};

struct LLNode *head = NULL;
int LLSize = 0;

void clean_input_buffer()
{
  char c;
  while ((c = getchar()) != '\n' && c != EOF);
}

void pause()
{
  printf("\n\nPremere un tasto per continuare.. ");
  getchar();
}

int get_int(char* message)
{
  int input = 0;

  printf("\n%s:\t", message);
  scanf("%d", &input);

  clean_input_buffer();

  return input;
}

char* get_string(char* message)
{
  char* input = (char*)malloc(1024);

  printf("\n%s:\t", message);
  scanf("%[^\n]%*c", input);

  int len = strlen(input);
  input[len] = 0;

  return (char*)realloc(input, len + 1);
}

void print_list()
{
  struct LLNode *node = head;

  printf("\n\nLISTA PERSONE (%d):", LLSize);

  while(node != NULL)
  {
    printf("\n%s\t%s\t%d", node->data.name, node->data.last_name, node->data.age);

    node = node->next;
  }
}

struct Person insert_person(char* name, char* last_name, int age)
{
  struct Person person;
  person.name = name;
  person.last_name = last_name;
  person.age = age;

  struct LLNode *new_node = (struct LLNode*)malloc(sizeof(struct LLNode));
  new_node->data = person;

  if(head == NULL)
  {
    head = new_node;
  }
  else
  {
    struct LLNode *node = head;
    int index = 0;

    while(node != NULL)
    {
      if(node->data.age < new_node->data.age)
      {
        new_node->next = node;
        node->prev = new_node;

        if(index == 0)
        {
          head = new_node;
        }

        break;
      }

      if(node->next == NULL)
      {
        node->next = new_node;
        break;
      }

      node = node->next;
      index++;
    }
  }

  LLSize++;

  return person;
}

void find_people(char* name, char* last_name)
{
  struct LLNode *node = head;

  printf("\n\nRISULTATI:");

  while(node != NULL)
  {
    if(strstr(node->data.name, name) ||
      strstr(node->data.last_name, last_name))
    {
      printf("\n%s\t%s\t%d", node->data.name, node->data.last_name, node->data.age);
    }

    node = node->next;
  }
}

struct Person* remove_person(char* name, char* last_name)
{
  struct Person *person = NULL;
  int index = 0;
  struct LLNode *node = head;

  while(node != NULL)
  {
    if(strstr(node->data.name, name) &&
      strstr(node->data.last_name, last_name))
    {
      person = &node->data;

      if(index == 0)
      {
        if(LLSize > 1)
        {
          head = node->next;
        }
        else {
          head = NULL;
        }
      }
      else
      {
        if(index < LLSize - 1)
        {
          node->prev->next = node->next;
          node->next->prev = node->prev;
        }
        else
        {
          node->prev->next = NULL;
        }
      }

      LLSize--;

      break;
    }

    node = node->next;
    index++;
  }

  return person;
}

void ask_person(int mode)
{
  char* name;
  char* last_name;
  int age;

  switch(mode)
  {
    case MODE_INSERT:
      printf("\n\nINSERIMENTO NUOVA PERSONA\n");
   
      name = get_string("DIGITARE IL NOME");
      last_name = get_string("DIGITARE IL COGNOME");
      age = get_int("DIGITARE L'ETA'");

      struct Person inserted = insert_person(name, last_name, age);

      printf("\n%s %s INSERITO!", inserted.name, inserted.last_name);

      print_list();
      break;
    case MODE_FIND:
      printf("\n\nRICERCA PERSONA\n");
   
      name = get_string("DIGITARE IL NOME");
      last_name = get_string("DIGITARE IL COGNOME");

      find_people(name, last_name);
      break;
    case MODE_REMOVE:
      printf("\n\nRIMOZIONE PERSONA\n");
   
      name = get_string("DIGITARE IL NOME");
      last_name = get_string("DIGITARE IL COGNOME");

      struct Person *removed = remove_person(name, last_name);

      if(removed == NULL)
      {
        printf("\nNESSUNA PERSONA TROVATA!");
        break;
      }

      printf("\n%s %s RIMOSSO!", removed->name, removed->last_name);

      print_list();
      break;
  }
}

int main() {
    int action;
 
    do
    {
      action = -1;
      system("clear");

      printf("__________ MENU __________\n\n");
      printf("1. Inserisci una persona\n");
      printf("2. Cerca delle persone\n");
      printf("3. Rimuovi una persona\n");
      printf("0. Esci\n");
      printf("__________________________\n");

      action = get_int("SELEZIONARE UN OPERAZIONE");

      switch(action)
      {
        case 1:
          ask_person(MODE_INSERT);
          break;
        case 2:
          ask_person(MODE_FIND);
          break;
        case 3:
          ask_person(MODE_REMOVE);
          break;
      }

      if(action < 0 || action > 3)
      {
        printf("\nOPERAZIONE NON VALIDA!");
        action = 1;
      }

      if(action != 0)
      {
        pause();
      }
    } while (action > 0);

    printf("\nADDIO!");
    return action;
}

Alcuni screenshot dell'esecuzione:

1615376215342.png
1615376257499.png
1615376286741.png

1615376315289.png
1615376599919.png
 
Ultima modifica:
@Sax3r28

Perchè non stai "includendo" il namespace, dovresti fare using namespace std; oppure quando definisci la stringa usare std::. Quindi ad esempio std::string nome;. ;)
In C++ per distruggere la memoria allocata esistono i distruttori. Per vedere come si utilizzano https://en.cppreference.com/w/cpp/language/destructor
Ho provato a metterlo sia dentro lo struct che fuori, sotto gli include, ma continuava questi errori:
expected nsted-name-specifier before namespace
'string' does not name a type

Questo funziona se scritto a sé
C++:
int main()
{
    using namespace std;

    string prova="ciao";
    printf("%s",prova.c_str());
}
Crea la string, la stampa e tutto bene, nell'altro codice invece non ne voleva sapere.

Per il distruttore ho controllato anche delle slide mie e ho trovato qualcosa, ma così a prima vista non ci sto capendo molto(e nell'esempio la creazione del vettore è sempre a lunghezza definita, quindi non mi aiuta molto). Praticamente basta creare questo ~nomeClasse e metterci dentro delete [] nomearray;? Questo poi scala gli elementi in automatico o va fatto in seguito?
Un'altra cosa che mi è venuta in mente adesso vedendo tutti sti *, usando i puntatori non serve passare le variabili alle funzioni giusto?
Poi fai attenzione ad un'altra cosa: stai usando un array di oggetti di tipo elenco, quindi quando elimini una persona... dovresti trovare un modo per "compattare" il tuo array, così da non avere buchi.
Per quello ho spizzicato file che scrissi quando ancora andavo a scuola :rofl: e ho trovato questo
C++:
void azzera(int v[], int k)
{
    int *p;
    p= &v[0];
    for(int i=0;i<k;i++)
    {
        p= &v[i];
        *p=0;
    }
}
Può servirmi? O serve qualcosa che proprio pulisce dov'è stato allocato il valore e scala tutto di una posizione?
Anche nei case 2 e 3, attenzione: dovresti verificare il valore inserito, altrimenti andresti fuori dai limiti dell'array.
Un while input<0 && input >x(che dovrebbe essere sì dinamico ma sto pian piano sbloccando tutti i ricordi) può bastare?
Ultima cosa: l'elenco di persone non dovrebbe avere una lunghezza definita. Prova a modificare l'algoritmo così da accettare più di 10 persone. ;)
Sempre spulciando ho trovato questo, potrebbe andare? Ovviamente ritoccandolo per adattarlo alle richieste.
Anche se ora che lo leggo bene anche questo è a lunghezza definita. Probabilmente è pure una roba semplice da scrivere per fare sto struct dinamico, ma proprio non mi viene in mente.
C++:
struct elemento *crea_lista()
{
   struct elemento *p, *punt;
   int i, n;
   printf("\n Specificare il numero di elementi: ");
   scanf("%d", & n);
   if(n==0)
   {
   p = NULL; // lista vuota
   }
   else
   {
      /* creazione primo elemento */
    p = (struct elemento *)malloc(sizeof(struct elemento));
    printf("\nInserisci il primo valore: ");
    scanf("%d", & p->inf);
    punt = p;




// CREAZIONE ELEMENTI SUCCESSIVI
      for(i=2; i<=n; i++)
      {
         punt->pun = (struct elemento *)malloc(sizeof(struct elemento));
         punt = punt->pun;
         printf("nInserisci il %d elemento: ", i);
         scanf("%d", & punt->inf);
      } // chiudo il for
      punt->pun = NULL; // marcatore fine lista
   } // chiudo l'if-else
   return(p);
} // chiudo la funzione

Ovviamente @Not an engineer se puoi indirizzarmi pure te :frog-good:
 
  • Mi piace
Reazioni: Valley
Pubblico la mia soluzione too (fatta un po' di fretta)

C:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#define SIZE 30

typedef struct Persona
{
    char name[SIZE];
    char last_name[SIZE];
    unsigned int age;

    struct Persona* next;
} Persona;


void printPersona(Persona* node)
{
    if (node == NULL)
        return;

    printf(
        "Name: %s, Last Name: %s, Age: %u\n",
        node->name,
        node->last_name,
        node->age
    );
}

void printPersonaList(Persona* head)
{
    printf("\nBEGIN LIST\n");
    while (head != NULL)
    {
        printPersona(head);
        head = head->next;
    }
    printf("END LIST\n\n");
}

void freePersona(Persona* node)
{
    free(node);
}

void freePersonaList(Persona* head)
{
    while (head != NULL)
    {
        Persona* temp = head;
        head = head->next;

        freePersona(temp);
    }
}

Persona* insert(Persona** head, char name[], char last_name[], int age)
{
    Persona* newNode = malloc(sizeof(Persona));

    strcpy(newNode->name, name);
    strcpy(newNode->last_name, last_name);
    newNode->age = age;
    newNode->next = NULL;

    // forse c'è un modo migliore per farlo ma non ho voglia di pensarci
    if (*head == NULL || (*head)->age < newNode->age)
    {
        newNode->next = *head;
        *head = newNode;
    }
    else
    {
        Persona* previous = *head;
        Persona* current = (*head)->next;

        while (current != NULL) {
            if (current->age < newNode->age) {
                previous->next = newNode;
                newNode->next = current;
                break;
            }

            previous = current;
            current = current->next;
        }

        // tail insert
        if (newNode->next == NULL)
            previous->next = newNode;
    }

    return newNode;
}

Persona* searchByName(Persona* head, char name[])
{
    while (head != NULL) {
        if (strcmp(name, head->name) == 0)
            break;

        head = head->next;
    }

    return head;
}

void deleteByName(Persona** head, char name[])
{
    if (*head == NULL)
        return;

    if (strcmp(name, (*head)->name) == 0) {
        Persona* temp = *head;
        *head = (*head)->next;

        freePersona(temp);
    } else {
        Persona* previous = *head;
        Persona* current = (*head)->next;

        while (current != NULL && strcmp(name, current->name) != 0) {
            previous = current;
            current = current->next;
        }

        if (current) {
            previous->next = current->next;
            freePersona(current);
        }
    }
}

int main()
{
    Persona* head = NULL;
    Persona* temp = NULL;

    temp = insert(&head, "nome1", "cognome1", 15);
    printPersona(temp);

    temp = insert(&head, "nome2", "cognome2", 24);
    printPersona(temp);

    temp = insert(&head, "nome3", "cognome3", 17);
    printPersona(temp);

    char nome4[] = "nome4";
    temp = insert(&head, nome4, "cognome4", 13);
    printPersona(temp);

    printPersonaList(head);

    temp = searchByName(head, "nome3");
    printPersona(temp);

    temp = searchByName(head, "nome0"); // invalid name
    printPersona(temp);

    deleteByName(&head, "nome4");
    deleteByName(&head, "nome2"); // delete HEAD
    deleteByName(&head, "nome0"); // invalid name;

    printPersonaList(head);

    freePersonaList(head);

    return 0;
}

Output:

1615384856394.png
 
@Dazorn
Ben fatto. Ma giusto per cercare il pelo nell'uovo, ti segnalo un paio di cose.
  1. Guardando il codice non mi è sembrato di aver visto l'inserimento con ordinamento
  2. Non liberi mai la memoria allocata, nè sulle stringhe nè rispetto agli elementi della lista
  3. usi system("clear"); che non è portabile (ma in effetti non ci sono molte alternative valide)


@Sax3r28
Prendendo sempre il codice che hai pubblicato ieri, questo funziona:

C++:
class elenco
{
        std::string nome;
        std::string cognome;
        short eta;
        public:
        void inserisci();
        void visualizza();
        void cancella();
};

Comunque se usi C++, puoi usarlo "fino in fondo" ;)
Quindi ad esempio scrivendo:
C++:
using namespace std;

int main()
{
string prova="ciao";
cout << prova;
}

Per il distruttore ho controllato anche delle slide mie e ho trovato qualcosa, ma così a prima vista non ci sto capendo molto(e nell'esempio la creazione del vettore è sempre a lunghezza definita, quindi non mi aiuta molto). Praticamente basta creare questo ~nomeClasse e metterci dentro delete [] nomearray;? Questo poi scala gli elementi in automatico o va fatto in seguito?

Ti faccio un esempio con una classe simile alla tua (premetto di non conoscere bene C++ e di non usarlo da qualche anno):
C++:
#include <iostream>

class Person {
    private:
        std::string name;
        std::string last_name;
        int age;
        
    public:
        Person(std::string name, std::string last_name, int age) { 
            this->name = name;
            this->last_name = last_name;
            this->age = age;
        }
    
        void printPerson() {
            std::cout << this->name << " " << this->last_name << " " << age << std::endl;
        }
};

int main() {
    Person **person = new Person*[10]; // Ho scelto 10 elementi
    
    for(int i=0; i<10; i++) {
        person[i] = new Person("Name" , "Last Name", i+1);
    }
    
    for(int i=0; i<10; i++) {
        person[i]->printPerson();
    }
    return 0;
}

Un altro esempio:
C++:
#include <iostream>

class Person {
    private:
        std::string name;
        std::string last_name;
        int age;
        
    public:
        Person() {}
        
        Person(std::string name, std::string last_name, int age) { 
            this->name = name;
            this->last_name = last_name;
            this->age = age;
        }
    
        void printPerson() {
            std::cout << this->name << " " << this->last_name << " " << age << std::endl;
        }
};

int main() {
    Person *person = new Person[10]; // Ho scelto 10 elementi
    
    for(int i=0; i<10; i++) {
        person[i] = Person("Name" , "Last Name", i+1);
    }
    
    for(int i=0; i<10; i++) {
        person[i].printPerson();
    }
    return 0;
}

Una dimensione iniziale dovrai comunque darla in quanto l'array è statico, come in C (e non solo); dovrai ridimensionarlo tu "manualmente".

Normalmente in C++ per cose come queste usi i vector, non gli array (il vector viene ridimensionato in automatico "internamente"). Quindi volendo potresti anche usare un vector; ovviamente sarebbe più semplice rispetto a una soluzione in C dove è necessario gestire anche le riallocazioni. ;)
Questo è un esempio con un vector:
C++:
#include <iostream>
#include <vector>

class Person {
    private:
        std::string name;
        std::string last_name;
        int age;
        
    public:
        Person(std::string name, std::string last_name, int age) { 
            this->name = name;
            this->last_name = last_name;
            this->age = age;
        }
    
        void printPerson() {
            std::cout << this->name << " " << this->last_name << " " << age << std::endl;
        }
};

int main() {
    std::vector<Person> person;
    
    for(int i=0; i<10; i++) {
        person.push_back(Person("Name" , "Last Name", i+1));
    }
    
    for(int i=0; i<10; i++) {
        person[i].printPerson();
    }
    return 0;
}

Ti ho fatto fare un pò di confusione io, mea culpa. Se usi un array, non puoi rimpicciolirlo eliminando un elemento.
L'operatore delete a cui fai riferimento libera (dealloca) la memoria; prima di questo però viene invocato il distruttore, se è definito (e se si tratta di una classe).

Un'altra cosa che mi è venuta in mente adesso vedendo tutti sti *, usando i puntatori non serve passare le variabili alle funzioni giusto?

Non ho capito bene che intendi...

Un while input<0 && input >x(che dovrebbe essere sì dinamico ma sto pian piano sbloccando tutti i ricordi) può bastare?

Certo ;)

In merito all'altra questione: se vuoi usare C++, allora usa direttamente vector e amen, a mio avviso è meglio se non vai a "mischiare" le cose.

@demtor
Ho guardato solo velocemente la tua soluzione (non ho più tempo), ma direi sia ben fatta. ;)



Vedo che tutti quanti avete usato bene o male una lista... nessuno ha voglia di provare altre soluzioni?
 
Normalmente in C++ per cose come queste usi i vector, non gli array (il vector viene ridimensionato in automatico "internamente"). Quindi volendo potresti anche usare un vector; ovviamente sarebbe più semplice rispetto a una soluzione in C dove è necessario gestire anche le riallocazioni. ;)
Da quel che ricordo, e riguardando i file vecchi, i vector non ce li fecero mai usare :eek: abbiamo sempre usato array.

Riguardo il puntatore intendevo che solitamente alle funzioni si passano gli argomenti giusto? Come dei valori ad una funzione(int x, int y, int z) fuori dal main che ne calcola la media.
Con il puntatore invece vai a lavorare direttamente sulla memoria se ricordo bene, e la funzione dell'ordinare dopo ogni inserimento potrei scriverla come quella dell'inserimento normale aggiungendo il sort, o sto mischiando le cose?
Vedo che tutti quanti avete usato bene o male una lista... nessuno ha voglia di provare altre soluzioni?
Non ho la minima idea di cos'altro si può fare e ne butto una così, una matrice? :asd:
 
Guardando il codice non mi è sembrato di aver visto l'inserimento con ordinamento
E' di default quando vai ad inserire una nuova persona con la funzione insert_person.
Non liberi mai la memoria allocata, nè sulle stringhe nè rispetto agli elementi della lista
Questo è vero hahaha, me ne sono proprio dimenticato :)
Messaggio unito automaticamente:

Non ho la minima idea di cos'altro si può fare e ne butto una così, una matrice? :asd:
Una cosa che mi viene in mente è un'albero binario.
 
  • Mi piace
Reazioni: Valley
Da quel che ricordo, e riguardando i file vecchi, i vector non ce li fecero mai usare :eek: abbiamo sempre usato array.

A scuola è molto probabile non ve li abbiano fatti utilizzare. Alla fine è importante capire le strutture di base.
Ma se consulti la documentazione vedrai che non è complesso da utilizzare, e ti semplifica anche le operazioni. https://www.cplusplus.com/reference/vector/vector/
Chiaro che essendo C++ un linguaggio complesso, dovrai poi andare a guardare altri argomenti (iteratori, template e quant'altro).

Riguardo il puntatore intendevo che solitamente alle funzioni si passano gli argomenti giusto? Come dei valori ad una funzione(int x, int y, int z) fuori dal main che ne calcola la media.
Con il puntatore invece vai a lavorare direttamente sulla memoria se ricordo bene, e la funzione dell'ordinare dopo ogni inserimento potrei scriverla come quella dell'inserimento normale aggiungendo il sort, o sto mischiando le cose?

Si, il puntatore memorizza un indirizzo. Quando modifichi della memoria puntata, il valore cambia per tutti (ovvero se hai 2 puntatori al medesimo indirizzo, entrambi risentiranno della variazione del valore).

Comunque si, puoi anche aggiungere un sort.

E' di default quando vai ad inserire una nuova persona con la funzione insert_person.

E' vero, ieri me l'ero perso.

Non ho la minima idea di cos'altro si può fare e ne butto una così, una matrice? :asd:

Una cosa che mi viene in mente è un'albero binario.

Io ad esempio per non utilizzare una linked list ho riservato N bytes contigui che vado ad espandere quando non sono più sufficienti, v->p_vector = malloc(v->size * sizeof(person));. Quindi in sostanza è un "array dinamico".
 
Visualizza allegato 50643

Programmiamo con Inforge | Presentazione

Ad oggi, sul web, si trovano moltissime guide sui vari linguaggi di programmazione e sulle loro molteplici applicazioni. Tuttavia, chi si approccia a queste risorse, non sempre riesce a mettere in pratica ciò che ha appreso. Al fine di limitare queste mancanze, nasce Programmiamo con Inforge.

In questa rubrica potrai scrivere codice per la risoluzione di alcuni problemi legati alla programmazione, mettendo in pratica quanto stai apprendendo dalla teoria oppure mostrando le tue abilità e competenze nel campo dell’informatica.


Partiamo dalle basi del C

In questa guida puoi trovare i testi per studiare e approfondire il C: I migliori libri per imparare e approfondire il C
In questa discussione puoi trovare le risposte alle domande più frequenti su come scrivere codice in C: Frequently asked questions: da dove si parte?


Esercitazione 02 in C | Livello base | [Strutture dati]

Conoscere le strutture dati e saper lavorare su di esse è un aspetto fondamentale della programmazione; con queste entità è possibile organizzare insiemi di dati e compiere una serie di operazioni su di essi.
L'esercitazione si compone di 2 esercizi nei quali ti sarà richiesto di definire una struttura dati che supporti determinate operazioni basilari (è possibile scegliere la struttura che più ti aggrada, ad esempio una lista) come l'inserimento, la ricerca e la cancellazione.

*** Hidden text: cannot be quoted. ***



Soluzioni

Per rendere l'esercitazione più interessante, non verrà pubblicata alcuna soluzione! Spetterà a te scrivere la tua versione del codice e pubblicarla in questo thread così che possa essere valutata dai moderatori e dalla community; il modo migliore per imparare!


Conclusioni

Pubblica la soluzione ottimale per risolvere gli esercizi e ricorda che puoi confrontarti con il resto della community in questo thread, chiedere aiuto o aiutare gli altri ;)
Bene
 
Comunque vi state superando in questa esercitazione, mi avete impressionato. Ho visto soluzioni semplici ed efficace e complesse (o come dico io "risultati dello smanettare"). Continuate cosi :)
 
  • Mi piace
Reazioni: Shin Rea
Vi ricordo che potete utilizzare i commenti per qualsiasi forma di domanda sugli esercizi, utilizzate questo canale di comunicazione per ricevere maggiori informazioni! ;)