Risolto Parametri d'ingresso

Cingetorge

Utente Iron
22 Gennaio 2023
31
5
2
12
salve a tutti , girando per il web ho trovato questo esempio:
C:
#include <stdio.h>
#include <stdlib.h>

int values[] = { 88, 56, 100, 2, 25 };

int cmpfunc (const void * a, const void * b) {
   return ( *(int*)a - *(int*)b );
}

int main () {
   int n;

   printf("Before sorting the list is: \n");
   for( n = 0 ; n < 5; n++ ) {
      printf("%d ", values[n]);
   }

   qsort(values, 5, sizeof(int), cmpfunc);

   printf("\nAfter sorting the list is: \n");
   for( n = 0 ; n < 5; n++ ) {   
      printf("%d ", values[n]);
   }
 
   return(0);
}
Domanda: la funzione cmpfunc() come fa a "sapere" quali sono *a e *b dal momento che quando viene chiamata dalla qsort() non gli vengono passete parametri d'ingresso?
grazie
 
qsort prende come quarto argomento una funzione che usa per comparare, con signature int (*comp)(const void *, const void *).

Quindi, il body di qsort invocherà cmpfunc passandogli due argomenti.
 
  • Mi piace
Reazioni: pvssygino
ciao , grazie per la risposta.
Praticamente gli argomenti per cmpfunc() sono impliciti nella funzione qsort e non c'è bisogno di esplicitarli? è cosi? grazie
 
Non so cosa intendi per "impliciti", ma ti faccio un esempio pratico:

C:
#include <stdio.h>
int values[] = { 88, 56, 100, 2, 25 };

void cmpfunc (int a) {
    printf("The number is %d", a);
}

void invoca_func(int b, void (*f)(int)) {
    f(b);
}

int main () {
    invoca_func(5, cmpfunc);
 
   return(0);
}

Praticamente, una funzione può prendere un'altra funzione come argomento, e poi invocarla.

Quindi la funzione invoca_func prende come argomento un intero e una funzione, e poi chiama la funzione che ha ricevuto come argomento.

qsort fa esattamente la stessa cosa, ma chiaramente con anche tutto il codice per poi fare il sort
 
Quindi la funzione invoca_func prende come argomento un intero e una funzione, e poi chiama la funzione che ha ricevuto come argomento.
Sono pienamente d'accordo.
Una volta che cmpfunc() viene invocata , dalla sua dichiarazione, questa ha come unico parametro d' ingresso int a, giusto?
Ma int a qual'e ?
grazie
 
Sono pienamente d'accordo.
Una volta che cmpfunc() viene invocata , dalla sua dichiarazione, questa ha come unico parametro d' ingresso int a, giusto?
Ma int a qual'e ?
grazie
Nel caso che ti ho scritto io, si, ha come unico argomento a.

Viene invocata con il valore che viene passato a invoca_func come b: infatti f(b); significa "prendi la funzione che ti viene passata come argomento, e invocala con l'argomento b, che anche lui ti viene passato come argomento".

Visto che nel main facciamo invoca_func(5, cmpfunc);, nella funzione invoca_func f(b); significa cmpfunc(5); (b -> 5, f -> cmpfunc)
 
Ultima modifica:
ok per la funzione invoca_func!
Cerchero dispiegare meglio il mio dubbio.
Generalmete in un listato appare un prototipo della funzione dove vengono indicati i tipi di variabili che una funzione ha come parametri d'ingresso ed uscita; una dichiarazione e una invocazione della funzione dove vengono passate i nomi delle variabili come parametri d'ingresso.
Del tipo:
Codice:
#include <stdio.h>
int somma(int,int);

int main(){
    int a=2;
    int b=2;
    int d;
    d=somma(a,b);
    printf("il risultato è %d",d);
return 0;
}

int somma(int a, int b){
    int c;
    c=a+b;
return c;
}
Quel che mi apesttavo era qualcosa di simile alla riga 8.
Nel tuo listato cosi come nel mio, quando vengono chiamate le funzioni cmpfunc e cmpfunc non viene passato alcun argomento.
 
Certo, perché alla funzione qsort, tu non vuoi passare il risultato di un'invocazione di una funzione, ma il riferimento alla funzione, cosi qsort può usarla come preferisce.

quando vengono chiamate le funzioni cmpfunc e cmpfunc
Le funzioni non vengono chiamate! Questo è il punto fondamentale!
Viene passato un riferimento alla funzione. Se venissero chiamate, verrebbe passato il risultato della funzione, non la funzione stessa, a qsort.

Puoi dare un'occhiata a https://stackoverflow.com/a/9413/2586392
 
tu non vuoi passare il risultato di un'invocazione di una funzione, ma il riferimento alla funzione, cosi qsort può usarla come preferisce.
Allora se non il risultato che cosa? ok ,ma il riferimento alla funzione , cosi la funzione chiamante puo farci quello che vuole. Ma una funzione chiamante di che cosa avrebbe bisogno di una funzione chiamata se non il suo risultato d'uscita?non riesco a capire!
grazie
 
Prendiamo come esempio proprio qsort: essa prende un'array come argomento, e lo riordina dall'elemento più piccolo al più grande.
Ma, cosa vuol dire che un elemento è più piccolo di un altro?
Certo, se parliamo di numeri è facile, ma come si fa la comparazione di una struct tipo

C:
struct point {
   int    x;
   int    y;
};

?

Come fa qsort a sapere quale elemento viene prima e quale dopo? Non può! E qua entra in gioco la nostra funzione: noi forniamo a qsort la funzione cmpfunc. Ogni volta che qsort dovrà comparare due elementi, invocherà la funzione cmpfunc, che dirà quale dei due valori è più grande (o se sono uguali).

Se tu invocassi cmpfunc con degli argomenti quando la passi, passeresti il valore di ritorno, per esempio qsort(values, 5, sizeof(int), cmpfunc(1, 1)); equivale a qsort(values, 5, sizeof(int), 0);.
Cosa se ne fa qsort di un argomento che vale 0?
 
Quale dei due è il più grande non è mica il risultato della funzione cmpfunc?
Esattamente. Quindi torniamo al punto iniziale: tu non invochi cmpfunc, tu passi una referenza ad essa a qsort.
qsort la invoca tutte le volte che deve fare una comparazione per capire quale elemento è più grande.

Per vedere quante volte la funzione viene invocata, puoi modificarla:

C:
int cmpfunc (const void * a, const void * b) {
   printf("Comparo %d e %d\n", *(int*)a, *(int*)b);
   return ( *(int*)a - *(int*)b );
}

e l'output dovrebbe essere:

Codice:
Before sorting the list is:
88 56 100 2 25 Comparo 88 e 56
Comparo 2 e 25
Comparo 100 e 2
Comparo 100 e 25
Comparo 56 e 2
Comparo 56 e 25
Comparo 56 e 100
Comparo 88 e 100

After sorting the list is:
2 25 56 88 100 %
 
ok , una volta che la mia funzione cmpfunc viene referenziata da qsort la mia domanda principale è:come fa a sapere il programma a sapere a che cosa si riferiscono i parametri "const void * a",( puntatore di tipo void ad ail cui valore e non modificabile dal puntatore), e "const void * b" ( puntatore di tipo void ad b il cui valore e non modificabile dal puntatore) in ingresso a cmpconf (riga 6) ?
grazie
 
In che senso?

qsort segue l'algoritmo QuickSort, e quindi deve comparare certi elementi in un certo ordine.
Se deve comparare l'elemento 0 e l'elemento 1 dell'array, per esempio, invoca cmpfunc passandogli l'elemento 0 e l'elemento 1 dell'array.

Puoi trovare il codice sorgente di qsort qua: https://codebrowser.dev/glibc/glibc/stdlib/qsort.c.html

Se guardi linea 121, per esempio, vedi come la funzione viene invocata:

C:
      if ((*cmp) ((void *) mid, (void *) lo, arg) < 0)
        SWAP (mid, lo, size);

Invoca la funzione di comparazione (cmp), con il valore di mezzo dell'array (mid) e con il primo elemento dell'array (lo).
Se mid < lo, allora fa lo swap dei due elementi.
 
Forse ci sono :)Praticamente è qsort che "coordina il funzionamento " di cmpfunc.
Ammetto che dal sorgente di qsort a primo impatto no ho capito una ma_za!
grazie
 
Hey ciao! Credo che @fennek abbia spiegato benissimo il funzionamento che sta dietro quel codice.
E' la stessa funzione qsort che associa ad 'a' e 'b' gli indirizzi di memoria degli elementi che sta confrontando, infatti la funzione cmpfunc viene richiamata più volte finchè l'array non risulta ordinato.
Hai visto la dichiarazione di qsort?

C:
typedef int (__cdecl* _CoreCrtNonSecureSearchSortCompareFunction)(void const*, void const*);

_ACRTIMP void __cdecl qsort(
    _Inout_updates_bytes_(_NumOfElements * _SizeOfElements) void*  _Base,
    _In_                                                    size_t _NumOfElements,
    _In_                                                    size_t _SizeOfElements,
    _In_                _CoreCrtNonSecureSearchSortCompareFunction _CompareFunction
    );
Qui vediamo che la funzione qsort viene dichiarata utilizzando la convenzione __cdecl, che è praticamente una 'modalità' per inserire dei dati nello stack. Quando richiami la funzione qsort, questa suddivide l'array in più porzioni per poi riunirle di nuovo alla fine dell'ordinamento, ottendendo l'array ordinato appunto. I valori da confrontare vengono quindi trasportati nello stack e la funzione qsort utilizza cmpfunc per confrontare i valori che sono stati inseriti nello stack. E' proprio la funzione qsort che accetta come quarto argomento una funzione che abbia proprio due parametri di tipo puntatore, ma ciò viene fatto perchè quei parametri puntatore rappresentano un indirizzo in memoria che nel momento in cui viene richiamata qsort coincidono con l'indirzzo di memoria dei valori da confrontare!
 
Ciao pvssygino,
ammetto senza problemi che la colpa della difficile comprensione è a carico mio e che fennek abbia spiegato benissimo il funzionamento che sta dietro quel codice non ho alcun dubbio.
Dovete perdonarmi ma, come avrete di certo ben capito, non sono un programmatore, ma un'autodidatta che cerca di capire le cose come funzionano. O almeno ci provo.
Ametto anche che il codice che hai postato tu va ben oltre le mie conoscenze e la mia immaginazione.
Ho comunque ( FINALMENTE) capito il funzionamento .
GRAZIE
 
  • Mi piace
Reazioni: pvssygino