[C] Problema programma media pesata

di il
13 risposte

[C] Problema programma media pesata

Buonasera a tutti, mi chiamo Roberto e studio ingegneria meccanica.
Cerco spesso informazioni in questo forum, ma stasera ho deciso di iscrivermi perché sto incontrando non poche difficoltà nella parte di programmazione dell'esame di informatica.
In particolare, il problema che ho stasera è il seguente:
"Scrivere una funzione che dati due vettori di uguale lunghezza (es. voti e crediti) calcoli la media pesata dei valori nel primo vettore prendendo come pesi i valori nel secondo."
Ebbene, ho scritto tutto il programma, evitando le struct e concentrandomi sull'uso di puntatori e funzioni, con scarso successo.
Faccio ancora molta fatica a capire come applicarli bene, nonostante abbia letto la teoria un'infinità di volte
Il codice, vi anticipo già, sarà abbastanza confuso.
Eccolo:
#include <stdio.h>

// funzione per inserimento dei voti

int *inserimento_voti(int n)
{
	int i;
	int *x;
	int voti[n];
	x = voti;
	
	for (i = 0; i < n; i++) {
		printf("Inserire il voto %d\t", i+1);
		scanf("%d", x);
		x++;	
	}
	
	return x;
}

// funzione per inserimento dei crediti corrispondenti

int *inserimento_crediti(int n)
{
	int i;
	int *y;
	int crediti[n];
	y = crediti;
	
	for (i = 0; i < n; i++) {
		printf("Inserire il numero di crediti del voto %d\t", i+1);
		scanf("%d", y);
		y++;
	}
	
	return y;
}

// funzione calcolo media ponderata

double media_pesata(int *x, int *y, int n)
{
	int i;
	double numeratore = 0;
	double denominatore = 0;
	double media;
	
	for (i = 0; i < n; i++) {
		numeratore = numeratore + x[i] * y[i];
		denominatore = denominatore + y[i];
		x++;
		y++;
	}
		
	media = numeratore / denominatore;
	return media;
}

int main() {
	
	int n;
	printf("Numero di esami dati: ");
	scanf("%d", &n);
	
	int *v = inserimento_voti(n);
	int *c = inserimento_crediti(n);
	
	double m = media_pesata(v, c, n);
	
	printf("La media pesata è %lf\n", m);
	
	return 0;
}
Ora, l'ho provato usando i miei voti ma la media risulta 2.820393...
Ecco, non prenderò voti eccellenti, ma questo risultato mi sembra abbastanza improbabile

Qualcuno potrebbe dare una revisione al codice?
Grazie a tutti quelli che mi aiuteranno a uscirne

13 Risposte

  • Re: [C] Problema programma media pesata

    Il problema più evidente (e grave) è che le funzioni restituiscono puntatori a vettori locali che vengono distrutti al termine.
  • Re: [C] Problema programma media pesata

    Ho inserito questi valori di prova:

    Edit: ho eliminato i valori in quanto erano i miei stessi voti
  • Re: [C] Problema programma media pesata

    Ti avevo dato anche una spiegazione del problema ...
  • Re: [C] Problema programma media pesata

    Chiedo scusa, stavo controllando i valori che vi stavo postando e non avevo visto la risposta. Provvedo a modificare il programma
  • Re: [C] Problema programma media pesata

    Nessun problema... Comunque usa l'allocazione dinamica
  • Re: [C] Problema programma media pesata

    Allora: ho risolto il problema.
    Ho usato innanzitutto la tua prima risposta

    oregon ha scritto:


    Il problema più evidente (e grave) è che le funzioni restituiscono puntatori a vettori locali che vengono distrutti al termine.
    per dare in pasto alle funzioni i vettori. Ho cambiato il loro tipo in void, in quanto non mi serviva un valore di ritorno (esse dovevano solo organizzare gli array coi dati).
    Un errore grossolano era quello di aumentare nella funzione media_pesata, oltre agli indici dei puntatori agli array, anche il loro valore (non so per quale oscuro motivo io l'abbia fatto). Questo mi restituiva una media di oltre 6 milioni.

    Riposto il codice nel caso serva a qualcuno
    
    #include <stdio.h>
    
    // funzione per inserimento dei voti
    
    void inserimento_voti(int *x, int n)
    {
    	int i;
    		
    	for (i = 0; i < n; i++) {
    		printf("Inserire il voto %d\t", i+1);
    		scanf("%d", x);
    		x++;	
    	}
    }
    
    // funzione per inserimento dei crediti corrispondenti
    
    void inserimento_crediti(int *y, int n)
    {
    	int i;
    		
    	for (i = 0; i < n; i++) {
    		printf("Inserire il numero di crediti del voto %d\t", i+1);
    		scanf("%d", y);
    		y++;
    	}
    }
    
    // funzione calcolo media ponderata
    
    double media_pesata(int *x, int *y, int n)
    {
    	int i;	 
    	double numeratore = 0;
    	double denominatore = 0;
    	double media;
    	
    	for (i = 0; i < n; i++) {
    		numeratore = numeratore + x[i] * y[i];
    		denominatore = denominatore + y[i];
    	}
    		
    	media = (double) (numeratore / denominatore);
    	return media;
    }
    
    int main() {
    	
    	int n;
    	printf("Numero di esami dati: ");
    	scanf("%d", &n);
    	
    	int voti[n];
    	inserimento_voti(voti, n);
    	
    	int crediti[n];
    	inserimento_crediti(crediti, n);
    	
    	double m = media_pesata(voti, crediti, n);
    	
    	printf("La media pesata è %lf\n", m);
    	
    	return 0;
    }
    Ultima cosa: all'allocazione dinamica non sono ancora arrivato, nel senso che l'ho studiata ma non mi sono ancora applicato negli esercizi. Da quanto ho capito serve per ottimizzare l'utilizzo della memoria, per riassumere mooolto... Se sbaglio correggetemi.
    Come potrei adattare il codice per usarla?
  • Re: [C] Problema programma media pesata

    Dovresti usare malloc() o calloc() da stdlib.h (calloc() è come malloc(), solo che inizializza la memoria interamente su zeri).

    Il "trucco" è controllare sempre se l'allocazione è o non è riuscita e assicurarsi una strategia per la quale non si lascia indietro memoria allocata e non dismessa (ad ogni chiamata a malloc()/calloc() con esito positivo deve corrispondere una chiamata a free()).

    Tra le tante possibilità, il tuo main() potrebbe diventare (ad esempio e con fin troppi commenti) una cosa del genere:
    int main()int main() {
        int *voti = NULL, *crediti = NULL; /* in questo caso particolare "annullare"
                                              e' superfluo, pero' e' buona pratica
                                              abituarsi perche' non sono rare le
                                              situazioni in cui un NULL in piu' o
                                              in meno puo' fare la differenza */
        int n, errore = 1;                 /* errore=1 perche' presupponiamo che
                                              qualcosa possa andare storto */
    
        printf( "Numero di esami dati: " );
        scanf( "%d", &n );
    
        /* alloca memoria per i voti, azzerandola in quanto usa
           calloc() e controllando che voti sia un puntatore valido */
        if( (voti=calloc(n,sizeof(*voti))) != NULL ) {
    
            /* alloca memoria per i crediti, azzerandola in quanto usa
               calloc() e controllando che crediti sia un puntatore valido */
            if( (crediti=calloc(n,sizeof(*crediti))) != NULL ) {
                double m;
    
                /* le tue funzioni */
                inserimento_voti( voti n );
                inserimento_crediti( crediti, n );
    
                m = media_pesata( voti, crediti, n );
                printf( "Media pesata: %.2lf\n", m );
                
                errore = 0;      /* se siamo arrivati fin qui significa che non
                                    ci sono stati errori */
    
                free( crediti ); /* libera la memoria allocata per i crediti */
                crediti = NULL;  /* in questo caso annullare il puntatore e'
                                    superfluo, ma pare sia una buona pratica per
                                    evitare errori in situazioni piu' complesse */
            }
    
            free( voti ); /* libera la memoria allocata per i voti */
            voti = NULL;  /* anche in questo caso annullare il puntatore e'
                             superfluo, ma pare sia una buona pratica per
                             evitare errori in situazioni piu' complesse */
        }
    
        if( errore )
            printf( "%s", "Errore d'allocazione.\n\n" );
    
        return errore;
    }
  • Re: [C] Problema programma media pesata

    anche in questo caso annullare il puntatore e' superfluo, ma pare sia una buona pratica per evitare errori in situazioni piu' complesse
    Impostare il puntatore a NULL serve perchè un valore diverso da NULL ci indica che la memoria è allocata; il main() che hai scritto potrebbe procedere a fare una "pulizia" all'uscita in questo modo:
    if(voti!=NULL)
    {
       free(voti);
       voti=NULL;
    }
    if(crediti!=NULL)
    {
       free(crediti);
       crediti=NULL;
    }
    Quindi se la memoria è già stata deallocata, impostando il puntatore a NULL evitiamo che venga deallocata una seconda volta; inoltre, impostando a NULL all'inizio del main() ci assicuriamo che se la malloc() non venisse mai chiamata non facciamo la deallocazione.
  • Re: [C] Problema programma media pesata

    Osservazioni opportune, Candaluar. Alle quali mi sento d'aggiungere che free() è, in un certo senso, "schermata" da una sorta di protezione per la quale se le si passa NULL ritorna senza fare danni. Se invece le si passa un falso puntatore possono accadere disastri. Dunque... evviva l'annullamento precauzionale dei puntatori!
  • Re: [C] Problema programma media pesata

    Il comportamento standard della free deve essere questo ma alcuni compilatori (rari) non lo seguono.

    Come comportamento "difensivo" andrebbe scritto

    if(ptr) free(ptr);
  • Re: [C] Problema programma media pesata

    Dunque non ci si può fidare neppure degli standard! Prendo atto e ti ringrazio per il caveat -- mai avrei immaginato.

    E definire una macro tipo... ?
    #define DISMETTI(p) if(p){free(p);(p)=NULL;}
  • Re: [C] Problema programma media pesata

    Ovviamente non devi pensare ai compilatori più "famosi" per i PC.

    Esiste tutto un mondo di compilatori per vari device (da arduino a micro microchip, ecc...) e anche se la maggior parte sono basati su varianti di gcc, qualcuno potrebbe "soffrire" di comportamenti un po' fuori standard.
  • Re: [C] Problema programma media pesata

    Chiedo scusa se rispondo così tardi, ma sono stato preso dagli esami e mi ero dimenticato di controllare il forum
    Ho risolto grazie ai vostri consigli e al mio professore, che dopo un paio di settimane mi ha risposto (tra l'altro dandomi le vostre stesse dritte)

    Grazie a tutti
Devi accedere o registrarti per scrivere nel forum
13 risposte