[C++] Aiuto con funzioni e puntatori

paolotesla91
Salve a tutti ragazzi. E' da ieri che cerco di fare un esercizio al compilatore. L'esercizio in questione è costituito dai seguenti passaggi: 1) acquisire dallo stdin un array di interi. 2) visualizzare a video l'array inserito. 3)ordinare gli elementi dell'array in ordine crescente. 4) visualizzare a video l'array ordinato.
Il tutto dovrei farlo preferibilmente mediante l'uso di funzioni. Riscontro dei problemi col secodno punto e con i succesivi in quanto non so come far ritornare un array da una funzione e in che modo manipolarla nella funzione successiva. So che posso farlo tramite un puntatore ma non so dove sia più corretto dichiarare l puntatore e come utilizzarlo.

Posto di seguito una bozza del codice sorgente da me scritto nella speranza che qualcuno di voi mi aiuti a capire se questa prima parte è corretta ed aiutarmi a capire come affrontare i punti successivi.

#include <iostream>
#include <stdlib.h>

//dichiarazioni
const int M=100;
int a[M];
int n;



using namespace std;


//inserimento array di interi
void input(int a[], int n){
    bool ok=true;
	int temp[M];
	do{ 
	    ok=true;
		cout << "inserire numero di elementi: "<<endl;
	    cin >> n; 
		if(n<=0){
				cout << "il numero di elementi deve essere positivo"<<endl;
				ok=false;
		} else if(n>0){ 
			cout << "inserire elementi dell'array: "<<endl;
	        for(int i=0; i<n; i++)
	             cin >>temp[i];
	             ok=true;
              }
		}while(!ok);
		for(int j=0; j<n; j++){
		a[j]=temp[j];
    } 
  	 
	return;
}

//visualizzo la lista inserita
void visualizza(int a[] ){
     cout << "la lista di elementi inserita e': "<<endl;
     for(int i=0; i<n; i++)
     cout << a[i];
     cout <<endl;
     return;
     }

//MAIN
	int main(){
        
		int lista[M];
		int numero;
		
		input(lista, numero);
	    visualizza(lista);
	    
		
		system("pause");
		return 0;
	}



P.S. se provate a compilare il codice noterete che non mi visualizza a video l'array.
Sareste cosi gentili da aiutarmi?

Ringrazio in anticipo per l'attenzione.

Risposte
paolotesla91
Sono riuscito a fare i primi due punti del mio esercizio. Il compilatore compila il programma e non riscontro alcun errore n fase di esecuzione tuttavia ho un problema con l'algoritmo di ordinamento. Ho scelto il BubbleSort per semplicità e perchè è quello che ho compreso meglio, quando eseguo il programma ed inserisco i numeri a caso non avviene alcun ordinamento anzi, più precisamente, a video mi compaiono tanti zeri consecutivi come se non avessi inserito alcuna lista. Credo che il problema sia sempre lo stesso: come faccio a memorizzare una variabile (nello specifico un array) in uscita da una funzione per poterla manipolare in un altra funzione?

Posto di seguito il frammento di codice che mi da il problema:

#include <iostream>
#include <cstdlib>
#define M 100

using namespace std;

int a[M];
int riemp; 
const int *Pt=a;

void Bubble(int *Pt, int riemp){
	
	int i, j;
	int temp;
	for(i=0; i<riemp; i++)
	    for(j=0; j<riemp-1-i; j++)
	      if(a[j]>a[j+1]){
	      	temp=a[j+1];
	      	a[j+1]=a[j];
	      	a[j]=temp;
		  }
		  
		return;
}

void visualizza(int *Pt, int riemp){
	for(int i=0; i<riemp; i++)
	cout<<*Pt;
	return;
}

int main(){
	
	int array[M];
	int n;
	
	bool ok=true;
	do{
		ok=true;
		cout<<"inserire il numero di elementi della lista: ";
		cin>>n;
		if(n<=0){
			cout<<"il numero di elementi deve essere maggiore di zero"<<endl;
			ok=false;
		}else if(n>0){
			cout<<"inserire elementi della lista: "<<endl;
			for(int i=0; i<n; i++)
			cin>>array[i];
			ok=true;
		}
	}while(!ok);
	cout<<"la lista inserita e': "<<endl;
	for(int i=0; i<n; i++){
		cout<<array[i];
	}
	cout<<"ordino la lista"<<endl;
	Bubble(array, n);
	cout<<"la lista ordinata e': "<<endl;
	visualizza(array,n);
	
	
	system("pause");
	return 0;
}

vict85
Qualche correzione sulla funzione che visualizza a schermo.
void
visualizza(int * Pt, int riemp)
{
  // Se passi un puntatore è normale testare che sia non nullo
  if (Pt == nullptr)
    {
      return;
    }

  for (int i = 0; i < riemp; i++)
    {
      // cout << *Pt; // continui ad stampare il primo elemento, e manca lo
                      // spazio tra i numeri
      cout << Pt[i] << " ";  // oppure *(Pt+i)
    }
  std::cout << std::endl;
  // return; // NON necessario
}


Per il main lo farei così
int
main(void)
{
  // usare #define per definire costanti e' meno comune nel C++
  // suggerisco di usare constexpr
  constexpr size_t M = 100;
  int array[M] = {};
  size_t N = 0;

  for (;;)
    {
      std::cout << "Inserire il numero degli elementi della lista:\t"
                << std::endl;
      std::cin >> N;
      if (N > 0 && N <= M)
        {
          break;
        }
      std::cout << "Numero inserito non valido" << std::endl;
    }

  for (size_t i = 0; i != N; ++i)
    {
      std::cout << "Inserire A[" << i << "] = ";
      std::cin >> array[i];
    }

  visualizza(array, N);

  Bubble(array, N);

  visualizza(array, N);
}



Per quanto riguarda Bubble, non ho controllato molto, ma sono bastate poche correzioni perché il risultato sembrasse corretto
void
Bubble(int * Pt, int riemp)
{
  if (Pt == nullptr)
    {
      return;
    }
  for (int i = 0; i < riemp; i++)
    {
      for (int j = 0; j < riemp - 1 - i; j++)
        {
          if (Pt[j] > Pt[j + 1])
            {
              temp = Pt[j + 1];
              Pt[j + 1] = Pt[j];
              Pt[j] = temp;
            }
        }
    }
}
Ho però l'impressione che non sia un BubbleSort.

Ho eliminato anche tutte le variabili globali e non serve includere cstdlib.

paolotesla91
Ciao vict grazie mille per aver risposto. Alcune scelte di programmazione come l'uso dei define ecc.. sono obbligate in quanto il mio prof preferisce che si faccia cosi. Avrei alcune domande che restano senza risposta come quelle che ho elencato nei post precedenti e in più: il controllo che il puntatore sia non nullo va fatto sempre?

Questo esercizio mi è servito solo per allenamento con array, puntatori, funzioni, passaggi di variabile. Spesso all'esame il prof ci fa manipolare stringhe per cui vorrei capire bene come giocare con esse e familiarizzare un po. quindi come faccio a memorizzare il contenuto di un dato (anche strutturato) per poterlo riutilizzare e manipolare nelle funzioni?

Chiedo scusa se sono rompiscatole ma devo cercare di capire bene queste cose.

Grazie ancora per l'aiuto.

Super Squirrel
Per quanto riguarda l'esercizio io farei qualcosa del genere:

#include <iostream>

using namespace std;

void riempi_array(int *v, const int &dim)
{
    for(unsigned int i = 0; i < dim; i++)
    {
        cout << "v[" << i << "] = ";
        cin >> v[i];
    }
}

void mostra_array(int *v, const int &dim)
{
    for(unsigned int i = 0; i < dim; i++)
    {
        cout << v[i] << " ";
    }
}

void scambia_valori(int &a, int &b)
{
    int temp = a;
    a = b;
    b = temp;
}

void ordina_crescente(int *v, const int &dim)
{
    bool modifica;
    for(unsigned int k = 0; k < dim - 1; k++)
    {
        modifica = false;
        for(unsigned int i = 0; i < dim - 1 - k; i++)
        {
            if(v[i] > v[i + 1])
            {
                scambia_valori(v[i], v[i + 1]);
                modifica = true;
            }
        }
        if(!modifica)
        {
            return;
        }
    }
}

int main()
{
    const unsigned int dim_max = 100;
    int v[dim_max];
    int dim;
    bool errore;
    do
    {
        errore = false;
        cout << "dim(MAX 100) = ";
        cin >> dim;
        if(dim < 0 || dim > dim_max)
        {
            errore = true;
            cout << "INSERIMENTO NON VALIDO" << endl;
        }
    }
    while(errore);
    riempi_array(v, dim);
    mostra_array(v, dim);
    ordina_crescente(v, dim);
    cout << endl;
    mostra_array(v, dim);
}


Avrei alcune domande che restano senza risposta come quelle che ho elencato nei post precedenti e in più: il controllo che il puntatore sia non nullo va fatto sempre?


E' un controllo che ci può stare come potrebbero starcene anche altri, ma al momento mi soffermerei maggiormente su altre questioni.
Per quanto riguarda le tue domande precedenti non ho capito bene quali sono i tuoi dubbi. Gli argomenti in gioco credo siano passaggio per valore e per riferimento alle funzioni, puntatori e array. Potresti essere più chiaro?
Per quanto riguarda le stringhe, utilizzate stringhe stile C (array di char) o stringhe stile C++ (oggetti della classe string)?

paolotesla91
Ciao Squirrel. L'uso che facciamo noi delle stringhe consiste nella manipolazione di array di char e il prof vuole che implementiamo funzioni che eseguano l'operazione richiesta. I miei dubbi sono sia sui passaggi di variabile che sulla dichiarazione dei puntatori. Sto rivedendo una soluzione di una prova d'esame postata dal mio prof ma non riesco a capire alcuni passaggi.
Posto di seguito il codice del programma della prova d'esame:

Questo è il segmento di codice del file head:

#include <iostream>

using namespace std;

struct Elemento{
    char prod[20];
    int quant;
    double prezzo;
    struct Elemento *prossimo;
};
    
    typedef struct Elemento *Lista;

void inserisciLista(Lista &,char [], int,double);
void visualizzaLista(Lista);
double calcolaCosto(Lista l);


Posto il segmento di codice che non riesco a capire:
Questo il segmento di codice del file .cpp :

#include <iostream>
#include <cstring>
#include ".\lista.h"

using namespace std;

void inserisciLista(Lista &l,char n[20],int q,double pr)
{
    Lista p=l;
    bool trovato=false;
	while (p!=NULL && !trovato){
		trovato=false;
		if (strcmp(p->prod,n)==0){
			p->quant+=q;
			p->prezzo=pr;
			trovato=true;
		}
		else
			p=p->prossimo;
		}
		
	if (!trovato){
		struct Elemento *e=new Elemento;
    	strcpy(e->prod,n);
    	e->quant=q;
		e->prezzo=pr;    	
    	e->prossimo=l;
    	l=e;
	}			
    return;
}

void visualizzaLista(Lista l){
     Lista q=l;
     while (q!=NULL){
          cout<<q->prod<<" : "<<q->quant<<" unita'. Prezzo: "<<q->prezzo<<endl;
          q=q->prossimo;
     }
    return;
}

double calcolaCosto(Lista l){
     Lista q=l;
     double costo=0.0;
     while (q!=NULL){
          costo+=(q->quant*q->prezzo);
          q=q->prossimo;
     }
     cout<<endl;
    return costo;
}


Più precisamente non riesco a capire perchè passa la lista per riferimento e poi scrive "Lista p=l;" , poi nell'if annidato scrive "e->prossimo=l;
l=e;
".

Gentilmente mi aiutereste acapire?

P.S. non ho postato la traccia per non essere troppo rompiscatole.

Super Squirrel
Dando un'occhiata al codice, noto (tralasciando la questione stringhe) un gran miscuglio tra C e C++.
Da una lettura veloce non so dirti se ci sono errori veri e propri, ma sicuramente il tutto non è impostato in modo molto chiaro (in particolare mi riferisco al fatto che non vengono separati i concetti di lista e nodo).
Comunque ho provato a svolgere l'esercizio impostandolo in un modo che ritengo più chiaro. In questa versione le funzioni sono metodi della classe lista:

#include <iostream>
#include <cstring>

using namespace std;

struct prodotto
{
    char nome[20];
    int quantita;
    double prezzo;
    prodotto *prossimo;
};

struct lista
{
    prodotto *testa = nullptr;
    void inserisci_prodotto (const char*, const int&, const double&);
    void visualizza_lista();
    double calcola_costo_totale();
};

void lista::inserisci_prodotto(const char *n, const int &q, const double &p)
{
    prodotto *a = testa;
    prodotto *precedente;
    while(a != nullptr)
    {
        if(!strcmp(a->nome, n))
        {
            a->quantita += q;
            a->prezzo = p;
            return;
        }
        else
        {
            precedente = a;
            a = a->prossimo;
        }
    }
    prodotto *nuovo = new prodotto;
    strcpy(nuovo->nome, n);
    nuovo->quantita = q;
    nuovo->prezzo = p;
    nuovo->prossimo = nullptr;
    if(testa == nullptr)
    {
        testa = nuovo;
    }
    else
    {
        precedente->prossimo = nuovo;
    }
}

void lista::visualizza_lista()
{
    prodotto *a = testa;
    while(a != nullptr)
    {
        cout << "- " << a->nome << ": " << a->quantita << " unita'. Prezzo: " << a->prezzo << endl;
        a = a->prossimo;
    }
}

double lista::calcola_costo_totale()
{
    prodotto *a = testa;
    double costo_totale = 0;
    while(a != nullptr)
    {
        costo_totale += a->quantita * a->prezzo;
        a = a->prossimo;
    }
    return costo_totale;
}

int main()
{
    lista l;
    int scelta;
    char n[20];
    int q;
    double p;
    cout << "1: INSERIRE PRODOTTO" << endl;
    cout << "2: VISUALIZZARE LISTA" << endl;
    cout << "3: CALCOLARE COSTO TOTALE" << endl;
    cout << "4: USCIRE" << endl;
    while(true)
    {
        cout << endl << "INSERIRE AZIONE: ";
        cin >> scelta;
        if(scelta == 1)
        {
            cout << "NOME PRODOTTO: ";
            cin >> n;
            cout << "QUANTITA' PRODOTTO: ";
            cin >> q;
            cout << "PREZZO PRODOTTO: ";
            cin >> p;
            l.inserisci_prodotto(n, q, p);
        }
        else if(scelta == 2)
        {
            l.visualizza_lista();
        }
        else if(scelta == 3)
        {
            cout << l.calcola_costo_totale() << endl;
        }
        else if(scelta == 4)
        {
            break;
        }
        else
        {
            cout << "INSERIMENTO NON VALIDO" << endl;
        }
    }
}


In questa versione invece le funzioni sono esterne:

#include <iostream>
#include <cstring>

using namespace std;

struct prodotto
{
    char nome[20];
    int quantita;
    double prezzo;
    prodotto *prossimo;
};

struct lista
{
    prodotto *testa = nullptr;
};

void inserisci_prodotto (lista&, const char*, const int&, const double&);
void visualizza_lista(lista&);
double calcola_costo_totale(lista&);

void inserisci_prodotto(lista &l, const char *n, const int &q, const double &p)
{
    prodotto *a = l.testa;
    prodotto *precedente;
    while(a != nullptr)
    {
        if(!strcmp(a->nome, n))
        {
            a->quantita += q;
            a->prezzo = p;
            return;
        }
        else
        {
            precedente = a;
            a = a->prossimo;
        }
    }
    prodotto *nuovo = new prodotto;
    strcpy(nuovo->nome, n);
    nuovo->quantita = q;
    nuovo->prezzo = p;
    nuovo->prossimo = nullptr;
    if(l.testa == nullptr)
    {
        l.testa = nuovo;
    }
    else
    {
        precedente->prossimo = nuovo;
    }
}

void visualizza_lista(lista &l)
{
    prodotto *a = l.testa;
    while(a != nullptr)
    {
        cout << "- " << a->nome << ": " << a->quantita << " unita'. Prezzo: " << a->prezzo << endl;
        a = a->prossimo;
    }
}

double calcola_costo_totale(lista &l)
{
    prodotto *a = l.testa;
    double costo_totale = 0;
    while(a != nullptr)
    {
        costo_totale += a->quantita * a->prezzo;
        a = a->prossimo;
    }
    return costo_totale;
}

int main()
{
    lista l;
    int scelta;
    char n[20];
    int q;
    double p;
    cout << "1: INSERIRE PRODOTTO" << endl;
    cout << "2: VISUALIZZARE LISTA" << endl;
    cout << "3: CALCOLARE COSTO TOTALE" << endl;
    cout << "4: USCIRE" << endl;
    while(true)
    {
        cout << endl << "INSERIRE AZIONE: ";
        cin >> scelta;
        if(scelta == 1)
        {
            cout << "NOME PRODOTTO: ";
            cin >> n;
            cout << "QUANTITA' PRODOTTO: ";
            cin >> q;
            cout << "PREZZO PRODOTTO: ";
            cin >> p;
            inserisci_prodotto(l, n, q, p);
        }
        else if(scelta == 2)
        {
            visualizza_lista(l);
        }
        else if(scelta == 3)
        {
            cout << calcola_costo_totale(l) << endl;
        }
        else if(scelta == 4)
        {
            break;
        }
        else
        {
            cout << "INSERIMENTO NON VALIDO" << endl;
        }
    }
}


Ho impostato il main in modo da poter provare le 3 funzioni. Se hai qualche dubbio chiedi pure.

I miei dubbi sono sia sui passaggi di variabile che sulla dichiarazione dei puntatori.


Sono argomenti abbastanza vasti, se hai qualche domanda più specifica chiedi pure, altrimenti puoi dare un'occhiata al seguente sito che è davvero ben fatto:
http://www.bo.cnr.it/corsi-di-informatica/corsoCstandard/Lezioni/01Indice.html#virtual

apatriarca
@Super Squirrel: La soluzione adottata di identificare nodo e lista è tipica del C e lo stesso vale per molti altri aspetti dell'implementazione. Sembra un codice scritto nella seconda metà degli anni '90. Immagino sia la data in cui il professore ha smesso di informarsi sullo sviluppo del linguaggio.

Non vedo sinceramente alcun vantaggio nella creazione di una classe lista, mentre avrebbe avuto più senso separare il concetto di lista dal suo contenuto. Un prodotto non è un nodo in una lista insomma, ma qualcosa che può essere contenuta in essa. Ha senso usare una classe lista solo se nasconde in qualche modo i dettagli implementativi. Se per fare ogni cosa è comunque necessario ricorrere all'uso dei nodo interni tanto vale usare la vecchia strategia adottata nel C.

Super Squirrel
Non vedo sinceramente alcun vantaggio nella creazione di una classe lista, mentre avrebbe avuto più senso separare il concetto di lista dal suo contenuto. Un prodotto non è un nodo in una lista insomma, ma qualcosa che può essere contenuta in essa. Ha senso usare una classe lista solo se nasconde in qualche modo i dettagli implementativi. Se per fare ogni cosa è comunque necessario ricorrere all'uso dei nodo interni tanto vale usare la vecchia strategia adottata nel C.


Magari al posto della struct lista sarebbe bastato un typedef per rendere chiara la distinzione tra lista e nodo, ma sinceramente non capisco perchè creare una classe lista (dove racchiudere le 3 funzioni come metodi e conservare l'informazione sul nodo di testa) sia tanto sbagliato!
In che senso un prodotto non è un nodo in una lista? Ti riferisci ai nomi che ho dato alle struct o avresti proprio impostato il tutto diversamente?
Avendo studiato da autodidatta e non conoscendo il modo in cui il C viene insegnato nelle scuole non capisco di preciso cosa intendi con "vecchia strategia adottata dal C". Alla fine credo che il concetto di lista sia lo stesso per ogni linguaggio di programmazione, quindi mi chiedo in che modo si possa fare a meno di ricorrere ai nodi interni!?

apatriarca
Magari al posto della struct lista sarebbe bastato un typedef per rendere chiara la distinzione tra lista e nodo, ma sinceramente non capisco perchè creare una classe lista (dove racchiudere le 3 funzioni come metodi e conservare l'informazione sul nodo di testa) sia tanto sbagliato!

In realtà il typedef c'è.. Non c'è niente di sbagliato, ma non ne vedo alcun vantaggio. E' certamente una soluzione possibile.

In che senso un prodotto non è un nodo in una lista? Ti riferisci ai nomi che ho dato alle struct o avresti proprio impostato il tutto diversamente?

Invece di avere una struttura come la seguente:
struct prodotto
{
    char nome[20];
    int quantita;
    double prezzo;
    prodotto *prossimo;
};

avrei separato quello che rappresenta il contenuto della lista con la lista stessa. Cioè qualcosa come segue:
struct Product {
    char name[20];
    int quantity;
    double price;
};

struct Node {
    Product product;
    Node *next;
};

Questo permette di lavorare su oggetti del tipo Product senza mischiare tale implementazione con quella della lista. Dal punto di vista di funzionalità più avanzate, l'idea sarebbe di avere la lista come un template e il prodotto sarebbe quello che è contenuto nella lista.

Alla fine credo che il concetto di lista sia lo stesso per ogni linguaggio di programmazione, quindi mi chiedo in che modo si possa fare a meno di ricorrere ai nodi interni!?

Nell'implementazione della classe è certamente impossibile. Ma se supponi che un utilizzatore della tua classe volesse ordinare i prodotti nella lista o fare altre operazioni, l'unica loro alternativa è di usare la rappresentazione interna della classe come lista concatenata. Ma è possibile fare come std::list e nascondere l'implementazione all'utente che è a questo punto obbligato ad interagire con la classe in modo "generico".

paolotesla91
Buonasera ragazzi. Vi ringrazio davvero per avermi donato un po del vostro tempo per rispondere ed aiutarmi. Purtroppo di classi il mio prof non ne parla affatto, quindi non capisco proprio di cosa state parlando. Purtroppo io non avendo seguito il corso trovo ancor più difficoltà nel comprendere. Vorrei capire bene i concetti che voi definite Nodo e Lista, potreste essere più esaustivi? Inoltre vorrei capire una cosa per quanto riguarda le variabili: vorrei capire quando, durante l'implementazione, conviene passare una variabile per valore e quando per riferimento. Nell'esercizio che ho postato il prof passa una variabile di Tipo Lista (definita con la typedef) per riferimento, vorrei capire perchè.

Grazie ancora per il tempo che dedicate a rispondermi. Chiedo scusa se posso sembrare una capra ma davvero non so dove mettere mano e trovo molte difficoltà nel comprendere alcuni passaggi.

apatriarca
Una lista è in generale una struttura dati in grado di rappresentare una successione di valori di lunghezza arbitraria. Esistono diverse implementazioni, ma spesso quando si parla di lista a livello scolastico/universitario si parla di lista concatenata. Una lista concatenata è una struttura dati che si ottiene legando tra di loro (attraverso dei puntatori) strutture chiamate nodi. Ogni nodo contiene un solo elemento della lista e il puntatore al nodo successivo. Siccome il concetto di lista è logicamente separato dal concetto di nodo, Super Squirrel ha preferito separare tale definizione in una struttura separata. Non è necessario e ha alcuni vantaggi/svantaggi che in realtà non credo siano poi così importanti al tuo livello attuale di preparazione.

Si passa una variabile attraverso un puntatore o per riferimento quando si vuole modificare il suo valore all'interno della funzione. Quando si tratta insomma in un certo senso sia di un input che di un output. Un puntatore viene usato quando il valore può essere uguale a NULL, mentre è meglio usare riferimenti in ogni altro caso. Nel caso dell'esempio del tuo professore la lista è passata per riferimento perché la funzione di inserimento potrebbe modificare qual'è il primo nodo della lista e quindi è necessario modificare il valore del puntatore al primo nodo (quello che viene chiamato lista nel codice).

Sono stato un po' veloce ma sono tutti argomenti molto ampi e generici ed è difficile essere più precisi senza dilungarsi enormemente.

Super Squirrel
Inoltre vorrei capire una cosa per quanto riguarda le variabili: vorrei capire quando, durante l'implementazione, conviene passare una variabile per valore e quando per riferimento. Nell'esercizio che ho postato il prof passa una variabile di Tipo Lista (definita con la typedef) per riferimento, vorrei capire perchè.


Presumo tu già conosca la differenza tra passaggio per valore e per riferimento, in ogni caso:
- nel passaggio per valore viene creata una copia della variabile passata come argomento e tutte le modifiche effettuate all'interno della funzione avvengono su tale copia e quindi di ritorno nel main la variabile passata alla funzione risulterà inalterata;
- i riferimenti sono variabili introdotte dall'operatore di dichiarazione & e il loro significato è quello di occupare la stessa memoria delle variabili a cui si riferiscono. Nel passaggio per riferimento quindi si lavora direttamente con la variabile passata e di conseguenza ogni modifica effettuata dalla funzione su di essa resterà anche all'uscita dalla funzione.
Nel C (non essendoci a differenza del C++ i riferimenti) il passaggio avviene sempre per valore, l'unico modo per simulare un passaggio per riferimento è quello di passare alla funzione una variabile puntatore (in questo caso si parla più correttamente di passaggio per indirizzo). In questo modo le modifiche apportate dalla funzione alla variabile puntata resteranno, ma non quelle apportate al puntatore (per fare ciò bisognerebbe utilizzare un puntatore doppio).
Detto questo il passaggio per riferimento si usa quando si vogliono conservare le modifiche apportate dalla funzione alla variabile passata come argomento. Inoltre sarebbe preferibile utilizzare il passaggio per riferimento anche quando la funzione non opera nessuna modifica sull'argomento, in quanto una copia di tale variabile (nel passaggio per valore) costituirebbe solo una perdita di tempo e memoria. Consegue che il passaggio per valore va utilizzato solo nei casi in cui l'argomento viene modificato dalla funzione, ma non si vuole modificare la variabile passata come argomento.

Vorrei capire bene i concetti che voi definite Nodo e Lista, potreste essere più esaustivi?


Cito Wikipedia:

In informatica, una lista concatenata (o linked list) è una struttura dati dinamica, tra quelle fondamentali usate nella programmazione. Consiste di una sequenza di nodi, ognuno contenente campi di dati arbitrari ed uno o due riferimenti ("link") che puntano al nodo successivo e/o precedente. Una lista concatenata è un tipo di dato auto-referente, in quanto contiene un puntatore ad un altro dato dello stesso tipo. Le liste concatenate permettono l'inserzione e la rimozione di nodi in ogni punto della lista in tempo costante, ma non permettono l'accesso casuale, solo quello sequenziale. Esistono diversi tipi di liste concatenate: liste concatenate semplici, liste concatenate doppie e liste circolari.


Consideriamo una struct del tipo:

struct nodo
{
   int dato;
   nodo *prossimo;
};


Immagina che la lista sia costituita da due nodi:
- il primo nodo avrà un certo valore della variabile dato, mentre la variabile prossimo conterrà l'indirizzo di memoria del secondo nodo;
- il secondo nodo avrà un certo valore della variabile dato, mentre la variabile prossimo non punterà a niente (valore "NULL" nel C e "nullptr" nel C++).
Ovviamente per poter accedere a tutti i nodi della lista bisognerà salvare l'indirizzo del primo nodo in una variabile puntatore a nodo che chiameremo "testa". Inoltre bisogna anche considerare il caso di lista vuota, in tal caso avremo testa=NULL.
Visivamente puoi immaginare qualcosa del genere (a differenza dell'esempio qui i nodi sono 4):



Tornando all'esercizio che hai postato, ogni prodotto costituisce un nodo della lista.

Purtroppo di classi il mio prof non ne parla affatto, quindi non capisco proprio di cosa state parlando. Purtroppo io non avendo seguito il corso trovo ancor più difficoltà nel comprendere.


Da alcune tue domande mi sembra di capire che stai cercando di imparare la teoria a partire da esercizi svolti, secondo me l'approccio giusto sarebbe l'inverso, ossia studiare bene la teoria e poi partendo dalla traccia cercare di fare l'esercizio da solo anche sbagliando.
La seconda versione del codice che ho postato in precedenza non utilizza le "classi", semplicemente ho cercato di implementare il tutto in un modo a mio parere più chiaro. Inoltre ho implementato il main in modo che puoi testare le 3 funzioni:

#include <iostream>
#include <cstring>

using namespace std;

struct prodotto
{
    char nome[20];
    int quantita;
    double prezzo;
    prodotto *prossimo;
};

struct lista
{
    prodotto *testa = nullptr;
};

void inserisci_prodotto (lista&, const char*, const int&, const double&);
void visualizza_lista(lista&);
double calcola_costo_totale(lista&);

void inserisci_prodotto(lista &l, const char *n, const int &q, const double &p)
{
    prodotto *a = l.testa;
    prodotto *precedente;
    while(a != nullptr)
    {
        if(!strcmp(a->nome, n))
        {
            a->quantita += q;
            a->prezzo = p;
            return;
        }
        else
        {
            precedente = a;
            a = a->prossimo;
        }
    }
    prodotto *nuovo = new prodotto;
    strcpy(nuovo->nome, n);
    nuovo->quantita = q;
    nuovo->prezzo = p;
    nuovo->prossimo = nullptr;
    if(l.testa == nullptr)
    {
        l.testa = nuovo;
    }
    else
    {
        precedente->prossimo = nuovo;
    }
}

void visualizza_lista(lista &l)
{
    prodotto *a = l.testa;
    while(a != nullptr)
    {
        cout << "- " << a->nome << ": " << a->quantita << " unita'. Prezzo: " << a->prezzo << endl;
        a = a->prossimo;
    }
}

double calcola_costo_totale(lista &l)
{
    prodotto *a = l.testa;
    double costo_totale = 0;
    while(a != nullptr)
    {
        costo_totale += a->quantita * a->prezzo;
        a = a->prossimo;
    }
    return costo_totale;
}

int main()
{
    lista l;
    int scelta;
    char n[20];
    int q;
    double p;
    cout << "1: INSERIRE PRODOTTO" << endl;
    cout << "2: VISUALIZZARE LISTA" << endl;
    cout << "3: CALCOLARE COSTO TOTALE" << endl;
    cout << "4: USCIRE" << endl;
    while(true)
    {
        cout << endl << "INSERIRE AZIONE: ";
        cin >> scelta;
        if(scelta == 1)
        {
            cout << "NOME PRODOTTO: ";
            cin >> n;
            cout << "QUANTITA' PRODOTTO: ";
            cin >> q;
            cout << "PREZZO PRODOTTO: ";
            cin >> p;
            inserisci_prodotto(l, n, q, p);
        }
        else if(scelta == 2)
        {
            visualizza_lista(l);
        }
        else if(scelta == 3)
        {
            cout << calcola_costo_totale(l) << endl;
        }
        else if(scelta == 4)
        {
            break;
        }
        else
        {
            cout << "INSERIMENTO NON VALIDO" << endl;
        }
    }
}



EDIT:
preceduto

paolotesla91
Vi ringrazio davvero tanto ragazzi. Ora mi è più chiaro. Forse sto sbagliando approccio ma sulle mie dispense questi argomenti vengono trattati in modo vago e senza esempi purtroppo, per questo ho scritto sul forum. Per quanto riguarda i passaggi di variabile li ho studiati ma non ho avuto modo di vedere un esempio di come vengono utilizzati e quindi di poter capire. Squirrel ora mi studio meglio il tuo codice e cerco di capire meglio, nel caso avessi domande scrivo sempre qui.

Grazie ancora ad entrambi.

paolotesla91
Salve ragazzi. Ieri il prof ha pubblicato la traccia e la proposta di soluzione dell'ultimo appello d'esame. Si tratta di un esercizio sulle stringhe e consiste nell'inserimento di una stringa di un indirizzo internet con delle precise specifiche sui caratteri.

Posto di seguito il codice fino al punto che non ho compreso bene:

#include <cstring>
#include <iostream>

using namespace std;

	
bool input(char s[256]){		
		char protocollo[6];
		char dominio[255];
		char nazione[255];
				
		if (strlen(s)>255) {
			cout<<"Stringa troppo lunga"<<endl;
			return false;
		}
		int cont=0;
		int contfine=strlen(s)-1;
		if (s[0]=='h' && s[1]=='t' && s[2]=='t' && s[3]=='p'){
			if (s[4]=='s'){
				strcpy(protocollo,"https");
				cont=5;
			} else {
				strcpy(protocollo,"http");
				cont=4;
			}
		} else {
			cout<<"Non riconosco il protocollo (http o https)"<<endl;
			return false;
		}
		if (s[cont]!=':' || s[cont+1]!='/' || s[cont+2]!='/'){
				cout<<"Dopo il protocollo non ci sono i simboli :// "<<endl;
				return false;
			}
		cont=cont+3;
		int ultimopunto;
		for (ultimopunto=strlen(s)-1;s[ultimopunto]!='.' &&ultimopunto>cont+1;ultimopunto--);
		if (ultimopunto==cont){
			cout<<"Non riesco a trovare il punto che separa il dominio dalla nazione"<<endl;
			return false;
		}		
		//il dominio e' tra il carattere cont e il carattere ultimopunto
		int i;
		for (i=cont;i<ultimopunto;i++){
			if ((s[i]<'a'||s[i]>'z')&&(s[i]<'0'||s[i]>'9')&&(s[i]!='.')){
				cout<<"Il dominio contiene dei simboli non ammessi"<<endl;
				return false;
			} else
			dominio[i-cont]=s[i];
		}
		dominio[i-cont]=0;



Perchè nel codice dopo il for c'è dominio[i-cont]=0;[ ??

apatriarca
Non ho ancora guardato il codice, ma qual è la traccia?

paolotesla91
Si realizzi un programma che riceve in input una stringa corrispondente ad un indirizzo internet e deve provare a decodificarlo e controllarne la correttezza.

Nell’ambito di questo programma, si assume che un indirizzo web valido sia una stringa di al più 256 caratteri che abbia una forma compatibile con la seguente:

protocollo://dominio.nazione/

nel quale:
- Protocollo può assumere i valori “http” oppure “https”;
- I simboli “://” dopo il protocollo sono obbligatori;
- Dominio è una stringa che può contenere soltanto lettere minuscole, numeri o il simbolo punto (.);
- Nazione è una stringa che può contenere soltanto lettere minuscole;
- Il simbolo “/” dopo la nazione è obbligatorio;

Si realizzi un programma il quale:
- Prenda in input una stringa
- Controlli che la stringa sia valida, cioè controlli in sequenza tutte e cinque le condizioni scritte sopra, fornendo messaggi di errore differenziati nei casi contrari;
- Nel caso in cui la stringa corrisponda ad un indirizzo valido scriva a video il protocollo, il dominio e la nazione.



Il programma può essere liberamente realizzato con l’utilizzo o meno di funzioni e librerie.
In particolare, potranno tornare utili funzioni come strcpy e strlen.
Suggerimento: per maggiore chiarezza e semplicità, è utile risolvere le 5 condizioni in maniera separata e sequenziale, nell’ambito della stessa funzione. Verrà valutato anche il numero di condizioni implementato correttamente. Inoltre, potrà probabilmente tornare utile utilizzare alcune variabili di tipo intero per il conteggio dei caratteri della stringa.

vict85
Il codice del professore ha alcuni problemi. Per prima cosa, se una stringa può avere al massimo 256 caratteri allora l'array che lo contiene deve essere di almeno 257 elementi (manca il terminatore di stringa!). Tra l'altro hai al massimo \(\displaystyle 256-4-3-1-1 = 247 \) caratteri divisi tra dominio e nazione e devono contenere almeno un carattere ciascuno (non i capisce il senso di usare \(\displaystyle 255 \)). Seconda cosa, che senso ha copiare protocollo, dominio e nazione su delle stringhe statiche locali alla funzione!? Alla fine della funzione queste informazioni verranno perse :roll: . Alla fine stampa a video queste stringhe?
Un'altra scelta discutibile è che la funzione prende come argomento un "array" di char. Dico che è discutibile perché il compilatore ti creerà problemi sulla chiamata [inline]input( "https://prova.it/" )[/inline] perché la stringa in questione è costante.

Riguardo alla tua domanda. Serve per terminare la stringa dominio. Infatti risulta che [inline]'\0' == 0[/inline].

paolotesla91
Ciao vict. Sul numero dei caratteri sono d'accordo, nemmeno io mi trovo. Credo che da traccia il numero di 256 caratteri sia compreso del terminatore, in ogni caso questo non giustifica il fatto che utilizzi due array di char con 255 caratteri. Comunque per quanto riguarda l'impostazione dell'esercizio non trovo alcuna difficoltà, non ho provato a compilarlo ed eseguirlo ma comunque credo che non riscontrerò problemi. in merito alla domanda che ho posto potresti essere più chiaro?
In quelle riga c'è scritto che il carattere $(i-cont)$esimo dell'array $ dominio $ deve essere uguale a 0, facendo i conti con i parametri descritti prima io ho che cont=5 (nel caso il protocollo sia https) quindi significa che il termine $i-cont=i-5$ parte da dopo il protocollo https e mette il terminatore?

Grazie per l'attenzione.

vict85
Comincia a copiare in dominio dalla posizione cont.

paolotesla91
Salve ragazzi. Scusate se rompo le scatole, vi ringrazio perchè piano piano sto capendo alcune cose che prima non riuscivo a capire. Ho provato a fare un altro esercizio, voglio inserire da tastiera una stringa e ricercare in questa stringa una parola e contare quante volte essa si ripete nella stringa. Il programma compila ed esegue ma non fa il suo dovere. Gentilmente mi fareste capire dove sbaglio?

Questo è il codice:

#include <iostream>
#include <cstring>
#define M 100

using namespace std;

void inputstringa(char ar[M], char str1[M]){
	int j=0;
	bool ok=true;
	do{
		cout<<"inserire stringa di caratteri: ";
		cin>>ar;
		if(strlen(ar)>100)
		    cout<<"la stringa inserita e' troppo lunga"<<endl;
		    ok=false;
	}while(strlen(ar)>100);
	return;
}

int ricerca(char ar[M], char str1[M]){
	int cont=0;
	for(int i=0; i<strlen(ar); i++){
		if(strcmp(ar,str1)==0){
			cont++;
		}  
	}
	return cont; 
    }

void visualizza(char ar[M], char str1[M]){
	cout<<"la sottostringa cercata e': ";
	for(int i=0; i<strlen(str1); i++){
		cout<<str1[i];
	    cout<<endl;
	}
	return;
}

int main(){
	char stringa[M];
	char parola1[M];
	
	int n;
	
	inputstringa(stringa,parola1);
	
	n = ricerca(stringa,parola1);
	
	visualizza(stringa,parola1);
	
	cout<<"la parola cercata compare "<<n<<" volte"<<endl;
	
	system("pause");
	return 0;
	
}

Rispondi
Per rispondere a questa discussione devi prima effettuare il login.