Passaggio di parametri in C

Nei giorni in cui vi prende l'ispirazione hi-tech...prendete in mano la tastiera e sfogatevi qui!

Moderatori: cb_123, tonertemplum

Passaggio di parametri in C

Messaggiodi diegofio il 13 nov 2009, 19:12

Ecco un breve excursus sulle possibilità di passare i parametri ad una funzione in C. Lo scrivo perché al solito almeno se lo dimentico lo ritrovo :D

:arrow: per valore: è quello usato di default dal linguaggio. In questo caso dei parametri originari la funzione riceve solo una copia e di conseguenza qualsiasi modifica sui parametri all'interno della funzione non si ripercuoterà sugli stessi. Esempio:

Codice: Seleziona tutto
int main()
{
    int a = 7;
    int b = 6;
    int risultato;

result = times(a,b);
cout << the result is: < result << '\n';

return 0;
}

int times (int x, int y)
{
    return x * y;
}


:arrow: per indirizzo: è utile se si vuole permettere la modifica dei parametri forniti ad una funzione alla quale viene passato non una copia della variabile (come nel caso precedente) ma il suo indirizzo di memoria, consentendo così dentro alla funzione un suo accesso diretto. Esempio:

Codice: Seleziona tutto
int main()
{
    int a = 7;
    int b = 6;
    int risultato;

result = times(&a, &b);
cout << the result is: < result << '\n';

return 0;
}

int times (int *x, int *y)
{
    return (*x) * (*y);
}

in questo caso passiamo l'indirizzo (operatore &) delle variabili "a" e "b", in modo che eventualmente all'interno della funzione siano modificabili. Ciò avviene attraverso l'operatore *: "*x" identifica dunque il contenuto della cella di memoria di cui "x" contiene l'indirizzo. "x" è quindi dentro la funzione a tutti gli effetti un puntatore che contiene l'indirizzo di memoria della variabile "a" dichiarata all'interno del main.
Da ciò, se la variabile "a" fosse stata dichiarata dall'inizio come un puntatore, ad esempio:
Codice: Seleziona tutto
int* a = &p;

("a" contiene l'indirizzo di memoria di "p") e vogliamo passare il contenuto di "p" (modificabile, non la copia) ad una funzione, possiamo farlo in due modi equivalenti. Passando l'indirizzo di "p":
Codice: Seleziona tutto
int main()
{
    int p = 10;
    int* a = &p;
    funzione (&p)
}

void funzione (int* p)
{
    *p = 17;
}

oppure passando direttamente il puntatore contenente l'indirizzo di "p":
Codice: Seleziona tutto
int main()
{
    int p = 10;
    int* a = &p;
    funzione (a)
}

void funzione (int* p)
{
    *p = 17;
}

il tutto in modo trasparente alla funzione: da notare infatti che nella dichiarazione della funziona va indicato che si accetta un puntatore, contenente l'indirizzo della variabile. Questo puntatore può essere esplicito (secondo caso) oppure ricavato mediante l'operatore & sulla variabile stessa (primo caso).

:arrow: per riferimento: questa è la cosa per cui scrivo sta guida. In effetti a ben vedere nell'esempio precedente se vogliamo all'interno di "funzione" è possibile ("*p = 17;") modificare la cella di memoria indirizzata dal puntatore "p" ma se volessimo invece modificare direttamente "p", magari per farla puntare ad un'altra locazione, sarebbe impossibile con il metodo prima esposto. Infatti il puntatore "p" viene passato sempre per valore: non ho nessuna conoscenza, dentro "funzione", della sua posizione in memoria per poterlo modificare; un eventuale cambiamento (ad esempio "p = 32") andrebbe immediatamente perso all'uscita della funzione perché si sta lavorando su una copia del puntatore "a" dichiarato ed inizializzato nel main.
È tuttavia possibile effettuare un indirizzamento per riferimento, che ci consente di modificare sia "p" (quindi l'indirizzo cella di memoria a cui esso punta) sia il contenuto della cella di memoria:
Codice: Seleziona tutto
int main()
{
    int p = 10;
    int* a = &p;
    funzione (a)
}

void funzione (int*& p)
{
    *p = 17;
    p = 0x873dc28;
}

Nulla cambia nell'istruzione di richiamo della funzione, ma qualcosa cambia nella dichiarazione della stessa: "*&" in luogo della sola "*". Ciò ci indica che oltre al contenuto della cella di memoria riferita dal puntatore (che modifichiamo con successo mediante l'istruzione "*p = 17;"), abbiamo a disposizione anche il puntatore stesso (che, dopo aver modificato la variabile da lui indirizzata, faremo indirizzare un altra cella di memoria alla posizione "0x873dc28"). A differenza del caso precedente (passaggio di parametri per indirizzo), anche questa ultima istruzione ora, con il passaggio per riferimento, ha successo.

sto facendo un esempio piuttosto istruttivo quando avrò finito posto ;)
spero di non aver scritto cavolate!
enjoy
diegofio
AmdPlanet Guru
AmdPlanet Guru
 
Messaggi: 9270
Iscritto il: 29 lug 2005, 09:55

 

Re: Passaggio di parametri in C

Messaggiodi thrantir il 14 nov 2009, 16:45

mi permetto un appunto: il passaggio per refernce (odio l'itlianizzazione dei termini tecnici :-D) e' prerogativa del solo c++, in c non e' possibile farlo

uno spunto: diego ha scritto che il passaggio di parametri per valore e' il default del linguaggio. In effetti non e' che il compilatore tratti in modo diverso i puntatori, si comporta allo stesso modo. Per lui se "Pippo" e' un tipo di dato, lo e' anche "Pippo*", e coerentemente copia il valore del parametro passato. Il puntatore non e' altro che un numero intero , da 32 o 64 bit.

Passare le variabili per indirizzo piuttosto che per copia e' indispensabile se vogliamo poter che le modifiche fatte all'interno della funzione siano sulla variabile esterna, siano in pratica permanenti. Si parla in questo caso di parametri di output, per differenziarli da quelli di input che invece sono gli operandi. Un parametro puo' essere contemporaneamente di input e output.

Passare per indirizzo puo' essere pero' anche utile per migliorare le prestazioni del nostro applicativo. Copiare un oggetto complesso puo' essere computazionalmente molto costoso, quindi e' utile evitare di effettuare queste copie quando possibile, per esempio passando per indirizzo piuttosto che per valore! Se stiamo passando un parametro di input e vogliamo evitare di modificarlo accidentalmente (modifica che, come sappiamo, sarebbe permanente), possiamo utilizzare la keyword const, in questo modo

void fun([b]const[\b] Pippo *)

che significa che passiamo alla funzione un puntatore ad un oggetto di tipo Pippo costante

attenzione! scrivere

void fun(Pippo [b]const[\b] *)

ha un'altra semntica! infatti la questo significa che passiamo un puntatore costante ad un oggetto di tipo Pippo, che significa hce non possiamo variare il contenuto della variaible puntatore, cioe' se questa vale 56 (ricordate che e' un intero?) non possiamo assegnarle 58, pero' l'oggetto di tipo Pippo puntato lo possiamo modificare eccome!

per concludere, il fatto che passare per puntatore significa copiare un intero, vi renderete conto che per i tipi primitivi (int, char, long, double ecc) passare per puntatore per evitare la copia ha un guadagno molto scarso se non e' addirittura peggiorativo, quindi i parametri di tipo int o simili di input si passano per copia, normalmente.

il consto si puo' usare per lo stesso motivo anche nel passaggio per reference

void fun(const Pippo&)
Fletto i muscoli e sono nel vuoto
Principi di architettura degli eleboratori
X postare immagini
-----BEGIN GEEK CODE BLOCK-----
GCS/IT/L/MU d- s: a C++$>+++ UL+>++ P L+++>++++ E--- W++ N++>+++ o+>++ K? w O-- M- VMS? V- PS++ Y+ PGP+ t 5? X+ R++>+++ tv+ b+++>++++ DI+++ D++ G e++ h- r++ y++
------END GEEK CODE BLOCK------
Addio Dani, sono più ricco perchè ti ho conosciuto
Avatar utente
thrantir
Moderatore
Moderatore
 
Messaggi: 8897
Iscritto il: 27 mag 2003, 13:32
Località: Pisa

Re: Passaggio di parametri in C

Messaggiodi diegofio il 15 nov 2009, 15:48

grande thrantir utilissimo eccellente sta in guardia che è possibile debba chiederti un po' di roba
diegofio
AmdPlanet Guru
AmdPlanet Guru
 
Messaggi: 9270
Iscritto il: 29 lug 2005, 09:55

Re: Passaggio di parametri in C

Messaggiodi thrantir il 17 nov 2009, 16:00

certo diego, per te questo e altro, poi paperina ti dira' le tariffe :-P

scherzo, chiedi tutto quello che ti viene in mente, se posso rispondo :-)
Fletto i muscoli e sono nel vuoto
Principi di architettura degli eleboratori
X postare immagini
-----BEGIN GEEK CODE BLOCK-----
GCS/IT/L/MU d- s: a C++$>+++ UL+>++ P L+++>++++ E--- W++ N++>+++ o+>++ K? w O-- M- VMS? V- PS++ Y+ PGP+ t 5? X+ R++>+++ tv+ b+++>++++ DI+++ D++ G e++ h- r++ y++
------END GEEK CODE BLOCK------
Addio Dani, sono più ricco perchè ti ho conosciuto
Avatar utente
thrantir
Moderatore
Moderatore
 
Messaggi: 8897
Iscritto il: 27 mag 2003, 13:32
Località: Pisa

Re: Passaggio di parametri in C

Messaggiodi diegofio il 18 nov 2009, 19:53

ecco un programma che fa vedere un po' di passaggi di parametri
Codice: Seleziona tutto
#import <iostream>
#import <limits>
#import <stdio.h>
#import <malloc.h>
using namespace std;

    struct materia
   {
      char nome[50];
      char docente[50];
      unsigned char voto;
   };

   struct studente
   {
      char nome[50];
      unsigned int matricola;
      materia esami[];
      unsigned char media;
   };

   struct esame
   {
      unsigned int data;
      int voto;
      struct studente students;
      struct materia subject;
   };

unsigned int askChoices();
void insertAStudent (studente *&arrayOfStudents, studente *&lastStudent,
      long unsigned int *numberOfStudents, long unsigned int *arrayOfStudentsLength);
void insertASubject (materia *&arrayOfSubjects, materia *&lastSubject,
      long unsigned int *numberOfSubjects, long unsigned int *arrayOfSubjectsLength);
void viewStudents(studente *arrayOfStudents, long unsigned int *numberOfStudents);
void viewSubjects (materia *arrayOfSubjects, long unsigned int *numberOfSubjects);



int main()
{

   unsigned int choice;

   //primo elemento dell'array di studenti
   studente *arrayOfStudents = (studente *) malloc(sizeof(studente)*10);
   long unsigned int arrayOfStudentsLength =10;
   //ultimo elemento +1 dell'array di studenti, per ora coincide con il primo
   studente *lastStudent = arrayOfStudents;
   //numero totale di studenti
   long unsigned int numberOfStudents = 0;

   //primo elemento dell'array di materie
   materia *arrayOfSubjects = (materia *) malloc(sizeof(materia)*10);   //array di 10 elementi
   long unsigned int arrayOfSubjectsLength =10;
   //ultimo elemento +1 dell'array di materie, per ora coincide con il primo
   materia *lastSubject = arrayOfSubjects;
   //numero totale di materie
   long unsigned int numberOfSubjects = 0;

   do
   {
      choice = askChoices();
      switch(choice)
      {
         case 0:
         {
            return 0;
            break;
         }
         case 1:
         {
            insertAStudent(arrayOfStudents, lastStudent, &numberOfStudents, &arrayOfStudentsLength);
            break;
         }
         case 2:
         {
            insertASubject(arrayOfSubjects, lastSubject, &numberOfSubjects, &arrayOfSubjectsLength);
            break;
         }
         case 3:
         {/*
            esame exam;
            cout << "Nome della materia:";
            //cin >> subject.nome;
            cout << "Docente:";
            cin >> subject.docente;
            break;*/
         }
         case 5:
         {
            viewStudents(arrayOfStudents, &numberOfStudents);
            break;
         }
         case 6:
         {
            viewSubjects(arrayOfSubjects, &numberOfSubjects);
            break;
         }
      }
   }
   while (choice != 0);



   return 0;
}


/**
* visualizza le possibili scelte per l'utente
*/
unsigned int askChoices()
{
   unsigned int choice;
   bool error = false;

   do
   {
      cout << "Effettua una scelta:" <<endl;
      cout << "1 - Inserisci i dati di uno studente;" <<endl;
      cout << "2 - Inserisci una materia;" <<endl;
      cout << "3 - Inserisci i risultati di un esame per uno studente;" <<endl;
      cout << "4 - Visualizza i voti di uno studente e la sua media;"<<endl;
      cout << "5 - Visualizza tutti gli studenti inseriti;" <<endl;
      cout << "6 - Visualizza tutte le materie inserite;" <<endl;
      cout << "0 - Esci." <<endl;
      cin >> choice;
      if (!cin.good())
      {
         error = true;
         cout << "Inserisci un valore corretto!" <<endl;
         cin.clear();
         cin.ignore(numeric_limits<streamsize>::max(), '\n');
      }
   }
   while ((choice <0) | (choice > 6) | error);
   return choice;
}

/**
* inserisce uno studente
*/
void insertAStudent (studente *&arrayOfStudents, studente *&lastStudent,
      long unsigned int *numberOfStudents, long unsigned int *arrayOfStudentsLength)
{
   studente student;
   student.matricola = 0;

   //leggo la matricola
   cout << "\nInserisci la matricola dello studente:\n";
   bool error;
   do
   {
      error = false;
      cin >> student.matricola;
      if (!cin.good())   //un errore, ad esempio è stato digitato un carattere anzichè un numero
      {
         error = true;
         cout << "\nInserisci un valore corretto per la metricola, \"0\" se vuoi uscire!";
         cin.clear();
         cin.ignore(numeric_limits<streamsize>::max(), '\n');
         continue;
      }
      if (student.matricola == 0)      //l'utente vuole uscire senza inserire uno studente
         return;
      for (long unsigned int i = 0; i < (*numberOfStudents); i ++)   //verifico che la matricola non sia già presente
      {
         if ((*(arrayOfStudents + i*sizeof(student))).matricola == student.matricola)    //la matricola è presente
         {
            error = true;      //richiedi il reinserimento della matricola
            cout << "\nQuesta matricola è già presente! Riprova, oppure inserisci \"0\" se vuoi uscire";
            break;
         }
      }
   }
   while (error);

   //matricola OK, leggo il nome e il cognome
   printf("\nNome e cognome:\n");
   cin.clear();    //ripulisco il buffer di input
   cin.ignore(numeric_limits<streamsize>::max(), '\n');
   int i = 0;
   char c;
   while (((c = getc(stdin)) != 10) & (i < 49))   //al massimo 49 caratteri e fino a quando non si digita INVIO
   {
      student.nome[i] = c;   //leggo il carattere
      i++;
   }
   student.nome[i]='\0';   //aggiungo il carattere di fine stringa
   printf("\nHai inserito lo studente di nome %s e con matricola %u\n", student.nome, student.matricola);

   //aumento la dimensione dell'array di una cella per accogliere un possibile futuro studente
   if (*arrayOfStudentsLength == (*numberOfStudents +1))
   {
      cout << "Sposto l'array, vecchio indirizzo: "<< arrayOfStudents;
      (*arrayOfStudentsLength)+= 10;
      arrayOfStudents = (studente *) realloc(arrayOfStudents, sizeof(studente) * (*arrayOfStudentsLength) );
      cout << " nuovo indirizzo: "<< arrayOfStudents << "\n";
      cout << " nuovo indirizzo: "<< arrayOfStudents << "\n";
   }
   /* aggiorno il puntatore alla prossima cella libera in accordo con (l'eventuale)
     * riposizionamento in memoria di arrayofstudents in occasione
     * dell'utilizzo di realloc*/
   lastStudent = arrayOfStudents + (*numberOfStudents) * sizeof(studente);
   (*numberOfStudents)++;      //aumento il numero degli studenti presenti
   *lastStudent = student;   //inserisco lo studente nel posto libero alla fine dell'array
   //punta alla prossima cella libera
   lastStudent+= sizeof(studente);
   viewStudents(arrayOfStudents, numberOfStudents);
   //pulisce il buffer
   cin.clear();
}

/**
* inserisce una materia
*/
void insertASubject (materia *&arrayOfSubjects, materia *&lastSubject,
      long unsigned int *numberOfSubjects, long unsigned int *arrayOfSubjectsLength)
{
   materia subject;

   cout << "Inserisci il nome della materia:" << endl;

   int i = 0;
   char c;

   //tolgo dal buffer di input un eventuale \n già presente (altrimenti non entra nel ciclo)
   cin.ignore(numeric_limits<streamsize>::max(), '\n');

   while (((c = getc(stdin)) != 10) & (i < 49))   //al massimo 49 caratteri e fino a quando non si digita INVIO
   {
      subject.nome[i] = c;   //leggo il carattere
      i++;
   }
   subject.nome[i]='\0';   //aggiungo il carattere di fine stringa

   //leggo il nome del docente
   cout << "\nInserisci il nome del docente:\n"<< flush;
   i = 0;
   while (((c = getc(stdin)) != 10) & (i < 49))   //al massimo 49 caratteri e fino a quando non si digita INVIO
   {
      subject.docente[i] = c;   //leggo il carattere
      i++;
   }
   subject.docente[i]='\0';   //aggiungo il carattere di fine stringa

   cout << "\nHai inserito la materia di nome " << subject.nome << " e con docente " << subject.docente << endl;

   //aumento la dimensione dell'array di una cella per accogliere una possibile futura materia
   if (*arrayOfSubjectsLength == (*numberOfSubjects +1))
   {
      *arrayOfSubjectsLength = *arrayOfSubjectsLength + 10;
      arrayOfSubjects = (materia *) realloc(arrayOfSubjects, sizeof(materia) * (*arrayOfSubjectsLength) );
   }
   /* aggiorno il puntatore alla prossima cella libera in accordo con (l'eventuale)
    * riposizionamento in memoria di arrayOfSubjects in occasione
    * dell'utilizzo di realloc*/
   lastSubject = arrayOfSubjects + (*numberOfSubjects) * sizeof(materia);
   (*numberOfSubjects)++;      //aumento il numero delle materie presenti
   *lastSubject = subject;   //inserisco la materia nel posto libero alla fine dell'array
   //punta alla prossima cella libera
   lastSubject+= sizeof(materia);
   //pulisce il buffer
   cin.clear();
}


/**
* visualizza tutti gli studenti sinora inseriti
*/
void viewStudents(studente *arrayOfStudents, long unsigned int *numberOfStudents)
{
   for (long unsigned int i= 0; i < *(numberOfStudents); i++)
   {
      cout << "Matricola: "<< (*(arrayOfStudents + i*sizeof(studente))).matricola << '\n';
      cout << "Nome e cognome: "<< (*(arrayOfStudents + i*sizeof(studente))).nome << '\n';
      cout << "---------------------------------------------------------------------\n";
   }
}


/**
* visualizza tutte le materie sinora inserite
*/
void viewSubjects (materia *arrayOfSubjects, long unsigned int *numberOfSubjects)
{
   for (long unsigned int i= 0; i < *(numberOfSubjects); i++)
   {
      cout << "Materia: "<< (*(arrayOfSubjects + i*sizeof(materia))).nome << '\n';
      cout << "Docente: "<< (*(arrayOfSubjects + i*sizeof(materia))).docente << '\n';
      cout << "---------------------------------------------------------------------\n";
   }
}


ora sto programma ha un problemino (a parte che non è completo in tutte le sue funzionalità) che l'oracolo thrantir mi risolverà :asd: apro ora apposito topic in programmazione --->>>>

viewtopic.php?f=15&t=37853
diegofio
AmdPlanet Guru
AmdPlanet Guru
 
Messaggi: 9270
Iscritto il: 29 lug 2005, 09:55


Torna a Guide

Chi c’è in linea

Visitano il forum: Nessuno e 1 ospite