Puntatori a Funzione[C]

Linguaggi di programmazione: php, perl, python, C, bash e tutti gli altri.
Avatar utente
vbextreme
Entusiasta Emergente
Entusiasta Emergente
Messaggi: 1214
Iscrizione: domenica 12 gennaio 2014, 14:06
Desktop: lxde
Distribuzione: xubuntu 14.10

Re: Puntatori a Funzione[C]

Messaggio da vbextreme »

Vabbè, qui' stiamo andando OT di brutto... e ci sarebbe molto da dire. Limitiamoci a dire che il prototype puo' essere definito meglio.
Il piccolo OT mi è servito per spiegare il discorso sucessivo,ovvero che ci sono tecniche di programmazione che riescono a far capire che parametri servono leggendo solo il prototipo,senza dover aprire ogni volta il reference.
Ecco, dato che si parla di typedef, mi sembra giusto far notare che questa è una cosa che in generale va evitata: nascondere un puntatore dentro una typedef.
Concordo pienamente.Anche qui però è tutto a proprio gusto,in Windows è tutto sotto forma di nome modificato e i puntatori diventano LP e non *.
Il mio punto di vista e' differente perche' a mio avviso e' di gran lunga piu' esplicativo il primo. E qui leggo chiaramente:
dichiarazione della funzione newTimer che ritorna un intero, passa un intero ed un puntatore a funzione con 4 parametri interi
E che significa quell'intero che ritorna? a cosa serve quel puntatore a funzione che devo passare?
Mi tocca prire il manuale,cerca,cerca,cerca e leggere.
Nel secondo caso mi basta avere l'header vedermi come è specificata la callback e implementarla,HANDLE è naturalmente un identificativo e poco importa la metologia di implementazione.La completa comprensione del codice si andrà poi a concludere con l'analisi del prototipo "void freehandle(HANDLE t);".
Il piu grande problema che ho proprio riscontrato sul mondo linux è la assoluta necessità di leggere il manuale per capire cosa faccia una determinata funzione, cosa che sotto windows non è resa necessaria,una volta che leggi il prototipo hai già meta del lavoro fatto.
Detto questo, non vedo in un contesto aperto particolari linee guida,; c'e' chi preferisce in un modo, chi nell'altro.
Esatto.
Easy framework per il linguaggio C.
vbextreme hack your life
Avatar utente
M_A_W_ 1968
Scoppiettante Seguace
Scoppiettante Seguace
Messaggi: 856
Iscrizione: venerdì 15 febbraio 2013, 3:57
Desktop: KDE
Distribuzione: SuSE
Sesso: Maschile
Località: Un luogo geometrico
Contatti:

Re: Puntatori a Funzione[C]

Messaggio da M_A_W_ 1968 »

Intervengo brevemente per ricordare un paio di punti di riferimento.

1) Anche standard relativamente vecchi, sebbene scarsamente diffusi per motivi che non è possibile richiamare qui, risolvono definitivamente ogni dubbio sull'uso delle utilissime typedef "parlanti" per i tipi numerici, con denominazioni intuitivamente costruite sui campi <SIGNEDNESS><TYPE><SIZE>_t. La normativa MISRA/C 2004 (minimo mutuabile per l'uso robusto del C in mercati anche blandamente normati, automotive in primis) contiene ad esempio la seguente regola:
6.3 (advisory): typedefs that indicate size and signedness should be used in place of the basic types.
Tale regola è riscontrabile universalmente nelle norme di stile almeno a partire dal ventennio precedente, a fortiori relative a cross-compiler embedded, ed è stata recepita già all'epoca dello standard C'99 che - ai paragrafi 7.8 e soprattutto 7.18 - definisce due header, portabili ed ampiamente preferenziali per simili definizioni. Naturalmente nulla cambia passando a C'11 e relativa Misra/C 2012.
Vengono dunque, in ultima analisi, da più parti (standard del linguaggio incluso) fortemente raccomandate definizioni come int16_t o uint64_t, contenute nello header minimale stdint.h e nella sua estensione (che lo include) inttypes.h. A margine sottolineo che Visual Studio è stato, in ordine di tempo, uno degli ultimi ambienti di sviluppo a larga diffusione per PC ad adeguarsi (solo dopo il 2008) alle raccomandazioni dello standard, per motivi storici: la presenza in parallelo di una denominazione arbitraria solo leggermente differente, fin dal progetto delle prime API di Windows. Tuttavia, nulla osta all'uso della sottosezione C della Boost Library, sempre più in odore di santità, che offre (oltre ad una cornucopia di altri benefits) anche tali header.

2) Non si dimentichi un ulteriore uso fondamentale, seppure non quotidiano nel codice mainstream, dei puntatori a funzione: il dispatching, tipicamente operato su un array (eventualmente dinamico) di puntatori a funzione. Esso in genere assume la forma di una LUT implementata tramite un array di puntatori a funzione, e nella sostanza altro non è che l'epifenomeno, la versione edulcorata del linguaggio C, delle jump tables supportate in modo più o meno esplicito da un'infinità numerabile di CPU e piattaforme di elaborazione del real world, fin dagli albori.

3) Quanto all'uso di typedef per la definzione del prototipo di una callback e più in generale di un puntatore a funzione, esso è larghissimamente diffuso in numerose librerie ed implementazioni commerciali, e suffragato da numerose norme di stile. Restando al caso banale, ecco alcuni esempi di pessimo codice del real world, ridotti ad usum delphini per il buon Gila, che riassumono le più diffuse possibili alternative all'uso di typedef: tutte caratterizzate da inutile complessificazione sintattica o nella definizione della UDF di confronto, o nella chiamata della medesima da qsort(), lsearch(), bsearch() e quant'altro. Che succede in simili modelli errati, a livello sintattico, quando si ha a che fare con un array multidimensionale di puntatori a strutture, e magari gli elementi da confrontare sono a loro volta contenuti in una union subordinata?

Codice: Seleziona tutto

#define F1 ((struct file_entry **) (f1))
#define F2 ((struct file_entry **) (f2))
int cmp_files(const void *f1, const void *f2)
...
#undef F2
#undef F1
/************************************************/
int cmp( const void *a, const void *b)
{
    const char *_a = *(const char **)a;
    const char *_b = *(const char **)b;
    ...
}
/************************************************/
int comp(const void *a, const void *b)
{
    unsigned int aux1, aux2;

    aux1 = ((struct MyStruct_t**)a)->segment ;
    aux2 = ((struct MyStruct_t**)b)->segment ;

    return (aux1 < aux2 ? -1 : (aux1 > aux2));
}
/************************************************/
int Cmp(MyPtr_t *a, MyPtr_t *b)
{
    return strcmp(a->name, b->name);
}

void MyFun(char *foo)
{
    ...
    p = (MyPtr_t *) bsearch(...
        (int(*)(const void*, const void*))Cmp);
    ...
}
/************************************************/
Nel caso specifico, l'uso di una typedef per il cast (peraltro univoca, nel caso banale delle funzioni della libreria standard e di tutte quelle - inumerevoli... - modellate sulla loro falsariga) risulta altamente preferibile a qualsiasi altra soluzione analoga, secondo un banale ma basilare criterio di economia e leggibilità - accolto in modo pressoché universale da guide di stile e manuali di engineering, a prescindere dal livello di normazione del settore specifico nel quale è applicato il software.

Codice: Seleziona tutto

typedef int (*fptr)(const void*, const void*);
...
    qsort(Array, Asize, sizeof(MyStruct_t), (fptr)Cmp);
A proposito di normazione, un altro simpatico paradosso: i primi e massimi beneficiari delle norme di codifica applicate nei settori critici, a partire dalla banale MISRA/C, sarebbero per contro... i programmatori operanti nel mainstream. E' lì che si vedono i peggiori orrori, che spesso generano anche errori. Nei settori ad elevata normazione, infatti, la qualità media del codice (intesa nel senso più strettamente quantitativo, secondo i canoni e le metriche asseverate nel mondo dell'engineering e del refactoring) era già storicamente molto elevata anche prima della standardizzazione di talune norme, o di loro revisioni profonde. Tali norme, ricordo, assieme all'uso sistematico (imposto per legge o almeno per contratto) di metodi formali rigorosi per specifica e verifica come Z, CASL, VDM, CSP, CTL* e derivate, fino a OCL e all'interpretazione astratta... non provengono dai bui recessi della preistoria del software, ma hanno piuttosto sistematizzato e raccolto abitudini invalse e bodies of knowledge già in vigore da decenni nelle organizzazioni di maggiori dimensioni, e comunque di maggior qualità.
Incidenti come Therac-25 e Ariane V, eventi fortunatamente più unici che rari i quali hanno evidenziato in modo macroscopico alcune gravi carenze normative e procedurali nei settori applicativi più critici, si collocano grossolanamente nell'ultimo quarto di secolo... pauca intelligentibus. ;)
Sì, un blog ce l'ho perfino io: gli è che mi manca il tempo...

"...in una società che sembra sempre più spaventata dai problemi troppo articolati e che rigetta come un corpo estraneo ogni elemento di complessità, sapremo ancora come utilizzare il parere degli esperti?"
Avatar utente
vbextreme
Entusiasta Emergente
Entusiasta Emergente
Messaggi: 1214
Iscrizione: domenica 12 gennaio 2014, 14:06
Desktop: lxde
Distribuzione: xubuntu 14.10

Re: Puntatori a Funzione[C]

Messaggio da vbextreme »

"prode principiante" per fortuna che sei stato breve...per un attimo ho temuto nel risveglio dell'omino verde..... :lol:
Easy framework per il linguaggio C.
vbextreme hack your life
gila75
Imperturbabile Insigne
Imperturbabile Insigne
Messaggi: 2739
Iscrizione: mercoledì 16 gennaio 2013, 17:28
Desktop: ubuntu-2d
Distribuzione: Ubuntu 12.04.2 LTS i686
Località: Airuno(Lecco)

Re: Puntatori a Funzione[C]

Messaggio da gila75 »

Grazie a tutti ragazzi, ha letto ciò che avete scritto, ma devo rileggere.
A volte dimenticate che io non sono al vostro livello :D e devo rileggere più e più volte, soprattutto M_A_W che è "abbastanza" tecnico.
Ma è una fortuna avere esperti che ti ascoltano.
Detto questo, veniamo al punto.
Sull'uso di typedef per dichiarare nuovi tipi di int, float ecc, ok. Non l'ho mai fatto ma posso capire.
Ma per puntatori a funzione, almeno per gli esempi base che ho fatto io, mi sembra che sia più complicato, meno "lineare" e "spartano" se mi passate i termini.
Guardate qui, esempio super semplice
Puntatore a funzione void, fa solo una stampa

Codice: Seleziona tutto

#include <stdio.h>
#include <stdlib.h>
void Stampa()
{
    printf ("stampo funzione ed esco\n");     
}

int main(void)
{
    void (*ptr)()=Stampa;
    (*ptr)();     
    return 0;
}
tutto semplicissimo e intuitivo direi.
Versione con typedef:

Codice: Seleziona tutto

#include <stdio.h>

typedef void (*stampafunz) (void);

void Stampa(void) 
{
    printf("stampo funzione ed esco\n");
}

int main (void)
{
    stampafunz a = &Stampa; /* a = pointer funz Stampa*/
    a(); /* a() = stampo messaggio, con chiamata funz Stampa*/
    return (0);
}
Perchè mi devo complicare la vita ad usare un puntatore per la funzione ?

Codice: Seleziona tutto

 stampafunz a = &Stampa; /* a = pointer funz Stampa*/
in questo caso chiamato a
Scomodando poi l'operatore & (anche se ho visto che compila e funziona ugualmente togliendolo).
Non capisco per questo esempio banale dove sia la leggibilità...creo un "secondo nome" per la funzione per poi dovermi appoggiare ad un puntatore per chiamarla????
Vorrei pregarvi di stare un momento su questo esempio. Non dico che su cose più complesse typedf non aiuti, ma per ora fermiamoci qui.
Poi rileggo ancora tutto quello che avete scritto.
Non mi ostino, ma io non trovo molto leggibile il listato che usa typedf no?
Ultima modifica di gila75 il venerdì 28 marzo 2014, 20:02, modificato 1 volta in totale.
Avatar utente
vbextreme
Entusiasta Emergente
Entusiasta Emergente
Messaggi: 1214
Iscrizione: domenica 12 gennaio 2014, 14:06
Desktop: lxde
Distribuzione: xubuntu 14.10

Re: Puntatori a Funzione[C]

Messaggio da vbextreme »

Gila forse non hai letto bene il codice di Maw.
Il caso che hai messo è inutile e quindi impossibile da mostrare il perchè quindi prendiamo questa modifica:

Codice: Seleziona tutto

void fnc(char* s) {puts(s);}

void stampa( void(*fnc)(char*), int n)
{
    int i;
    for ( i = 0; i < 10; i++)
        fnc("Helloworld");
}

int main(void)
{
    stampa(fnc,10);
    return 0;
}
Questo è il tuo classico metodo,la funzione stampa usa una funzione di callback per stampare a video una stringa.
ora cosa succede se io voglio che la mia funzione accetti un parametro const per migliorare la programmazione difensiva?
Ecco cosa ottengo:

Codice: Seleziona tutto

void fnc(char* s) {puts(s);}
void fnc_secure(const char* s) {puts(s);}

void stampa( void(*fnc)(char*), int n)
{
    int i;
    for ( i = 0; i < 10; i++)
        fnc("Helloworld");
}

int main(void)
{
    stampa( (void(*)(char*)) fnc_secure,10);
    return 0;
}
come vedi abbiamo complicato notevolmente il codice,diventando quasi illeggibile.

Codice: Seleziona tutto

typedef void(*CALLPRINT)(char*);// edit,cut paste errato

void fnc(char* s) {puts(s);}
void fnc_secure(const char* s) {puts(s);}

void stampa( CALLPRINT fnc, int n)
{
    int i;
    for ( i = 0; i < 10; i++)
        fnc("Helloworld");
}

int main(void)
{
    stampa( (CALLPRINT) fnc_secure,10);
    return 0;
}
Ora con questo typedef diventa tutto semplice,so che la funzione stampa accetta un puntatore a funzione "fnc" che serve per stampare un messaggio, e posso eseguire un cast in maniera decisamente piu elegante rispetto al metodo sopra citato.
Questo poi è proprio un esempio terra terra.
Se poi nel kernel o in talune librerie non vengono usati è per lasciar libero sfogo al programmatore,lasciargli libero arbitrio su come implementarli,ma fidati che prima o poi ti ritroverai sicuramente con una tua "type.h" che ti aiuterà a districarti nel mondo del c.
Ripeto poi ognuno è libero di commentare o no,di identare oppure no,tantopiu di usare typedef oppure no,come dopotutto potresti scrivere codice come questo:

Codice: Seleziona tutto

#include <stdio.h>
#define per for
#define piu *
#define abbraccio(u,f) u ## f
#define sex_number 69
#define Ox
#define Ox1296388692 1296388692
#define Ox707406378 707406378
#define Ox42 42
#define mio (
#define next )
#define tuo (char*)
int main()
{
    int amore[5],odio;
    int piu caldo = amore;
    int (piu emozione)(const char piu,...) = printf;
    piu caldo++ = abbraccio(Ox,707406378); piu caldo++ = abbraccio(Ox,42);
    piu caldo++ = abbraccio(0x,58454256);  piu caldo++ = abbraccio(Ox,1296388692);
    piu caldo = sex_number;
    per mio odio = 0; odio < abbraccio(0x,97) ; odio++ next emozione("%s",tuo amore);
    emozione("%s",tuo &amore[2]);
    per mio odio = 0; odio < abbraccio(0x,97) ; odio++ next emozione("%s",tuo amore);
    printf("*");
    return 0;
}
Ultima modifica di vbextreme il sabato 29 marzo 2014, 4:43, modificato 1 volta in totale.
Easy framework per il linguaggio C.
vbextreme hack your life
gila75
Imperturbabile Insigne
Imperturbabile Insigne
Messaggi: 2739
Iscrizione: mercoledì 16 gennaio 2013, 17:28
Desktop: ubuntu-2d
Distribuzione: Ubuntu 12.04.2 LTS i686
Località: Airuno(Lecco)

Re: Puntatori a Funzione[C]

Messaggio da gila75 »

sto cercando di capire i tuoi codici vbextreme :)
Il caso che hai messo è inutile e quindi impossibile da mostrare il perchè quindi prendiamo questa modifica:
Bhè, forse era troppo terra terra.
Ho letto quanto scritto da M_A_W, ma credimi non è facile starvi dietro... :(

EDIT: il secondo esempio non compila però vbextreme
ixamit
Scoppiettante Seguace
Scoppiettante Seguace
Messaggi: 499
Iscrizione: giovedì 14 novembre 2013, 10:16

Re: Puntatori a Funzione[C]

Messaggio da ixamit »

imho la typedef andrebbe usata con moderazione nei casi di casting partcicolarmente articolati, evitando forme effettivamente illeggibili. Altri casi in librerie dove l'utilizzo degli eventi risulta articolato su strutture dati o unioni differenti. Per il resto, per una semplice compare non crea codice illeggibile o mal scritto...
Per contro l'uso intensivo della typedef puo' pericoloso in quanto si rischia sia di confondere il tipo ma anche sottovalutarne la dimensione.

Chiedo scusa se ho commesso errori, ma sto scrivendo da un disositivo mobile.
Avatar utente
M_A_W_ 1968
Scoppiettante Seguace
Scoppiettante Seguace
Messaggi: 856
Iscrizione: venerdì 15 febbraio 2013, 3:57
Desktop: KDE
Distribuzione: SuSE
Sesso: Maschile
Località: Un luogo geometrico
Contatti:

Re: Puntatori a Funzione[C]

Messaggio da M_A_W_ 1968 »

ixamit [url=http://forum.ubuntu-it.org/viewtopic.php?p=4554284#p4554284][img]http://forum.ubuntu-it.org/images/icons/icona-cita.gif[/img][/url] ha scritto:imho la typedef andrebbe usata con moderazione nei casi di casting partcicolarmente articolati, evitando forme effettivamente illeggibili. Altri casi in librerie dove l'utilizzo degli eventi risulta articolato su strutture dati o unioni differenti. Per il resto, per una semplice compare non crea codice illeggibile o mal scritto...
Per contro l'uso intensivo della typedef puo' pericoloso in quanto si rischia sia di confondere il tipo ma anche sottovalutarne la dimensione.
Senza rischio di confondere i neofiti, vorrei brevemente fare un passo a monte e richiamare i due fondamentali criteri, accettati largamente nel quadro osservativo-sperimentale intersoggettivo (non possiamo parlare di oggettività in questi campi, ma solo della sua migliore approssimazione...) della comunità di sviluppo, che guidano gli estensori delle regole di stile:

a) Si pensi sempre, iperbolicamente, che il codice andrà a finire in mano ad un revisore o certificatore che fino a ieri programmava in Pascal o in COBOL.

b) La nostra società non intende partecipare all'IOCCC.

La categoria a) genera obblighi e suggerimenti del tipo "Usate sempre tutte le parentesi, per quanto superflue, e fingete sistematicamente di dimenticare le regole di precedenza degli operatori", oppure "usate sempre typedef, non solo per clarificare la relazione tra architetture e tipi standard, ma anche per callback et similia tranne dove risulti controproducente (segue esplicito elenco eccezioni)".

La categoria b) interviene per mitigare ogni tipo di abuso del preprocessore, delle typedef, gli idiomi troppo "geek", eccetera (es. "Non usate #undef").

Di fatto, ambedue convergono sulla massima leggibilità: un parametro empirico chiaramente soggettivo, ma comparativamente valutabile su base statistica. Restando entro questi confini, sono innumerevoli le varianti, purché resti salva la leggibilità effettiva. In genere, si tende a calcare la mano sulla a), in vario modo. In ultima analisi, si deve comunque fare riferimento ad un accordo tra le parti: ove possibile tra tutti quelli che metteranno le mani sul codice, in ambito aziendale. In altri frangenti, come nel rilascio dei sorgenti al pubblico indistinto, fa sempre comodo adottare una delle guide di stile pubblicamente disponibili (ad esempio le guidelines GNU/Linux o BSD, o gli omologhi resi disponibili in qualche dipartimento universitario, o su qualche testo...): se non altro, il lettore del codice saprà cosa attendersi.

RIferendosi ai due semplici binari sopra richiamati, oltre ovviamente ai noti principi di engineering, sono stati scritti interi manuali aziendali di stile per il linguaggio C'89 che superano abbondantemente le duecento pagine. Non stupitevi se ad un certo punto della vostra carriera, relazionandovi con talune aziende, vi sarà chiesto di calcare più o meno la mano con le typedef, o vi saranno direttamente forniti librerie, template (nel senso banale di "modelli"), include files e file di configurazione per (SP)Lint modellati secondo tali regole interne. Taluni parser/checker sono chiaramente impostati per emettere almeno un warning se si usano cast "troppo complicati", secondo una definizione quantitativa variamente configurabile, per le callback usate dalle funzioni di libreria standard. Ma naturalmente nulla di tutto ciò può sostituire il buonsenso dello sviluppatore e la considerazione della platea di destinazione del codice, nonché le effettive probabilità che il codice sia soggetto a refactoring (non importa da chi) in futuro.

Flagello dei nostri tempi... la densità media di errori ortografici nei miei messaggi è aumentata esponenzialmente da quando sono costretto ad usare spesso queste diavolerie. :lol:
Sì, un blog ce l'ho perfino io: gli è che mi manca il tempo...

"...in una società che sembra sempre più spaventata dai problemi troppo articolati e che rigetta come un corpo estraneo ogni elemento di complessità, sapremo ancora come utilizzare il parere degli esperti?"
Avatar utente
vbextreme
Entusiasta Emergente
Entusiasta Emergente
Messaggi: 1214
Iscrizione: domenica 12 gennaio 2014, 14:06
Desktop: lxde
Distribuzione: xubuntu 14.10

Re: Puntatori a Funzione[C]

Messaggio da vbextreme »

scusa gila ma durante il cut paste sembra essersi perso il punto e virgola nella prima typedef.
Ho un po di problemi a postare il codice su questo sito ma devo ancora capire la causa e la differenza con gli altri.(a volte me lo indenta anche male)
É normale trovare difficolta,chiedi pure, esponi pure i tuoi dubbi.
Anche io fatico a comprendere la dialettica di maw, figurati che una sera,per comprendere un suo messaggio, ho passano 5 ore a cercare in rete quelle parole "strane" che "sfarfugliava".Non puoi immaginare quante cose mi ha fatto imparare.
Easy framework per il linguaggio C.
vbextreme hack your life
gila75
Imperturbabile Insigne
Imperturbabile Insigne
Messaggi: 2739
Iscrizione: mercoledì 16 gennaio 2013, 17:28
Desktop: ubuntu-2d
Distribuzione: Ubuntu 12.04.2 LTS i686
Località: Airuno(Lecco)

Re: Puntatori a Funzione[C]

Messaggio da gila75 »

Vbextreme, mi sono permesso di correggere quando ho copiato per compilare

Codice: Seleziona tutto

void stampa( CALLPRINT fnc, int n)
{
    int i;
    for ( i = 0; i < n; i++)
        fnc("Helloworld");
}
tu avevi scritto:

Codice: Seleziona tutto

void stampa( CALLPRINT fnc, int n)
{
    int i;
    for ( i = 0; i < 10; i++)
        fnc("Helloworld");
}
Alrimenti non ha senso passare n giusto?
Poi, non considerarmi (anzi non consideratemi) pesante, ma perchè i due esempi base che ho postato non fanno testo?
Di norma (non solo nella programmazione), cerco di semplificare il problema e capirne la logica, per avere una linea guida generale.
Non capisco perchè non possono essere presi in considerazione, troppo semplici, forvianti, cosa?
In quei due esempi non usare typedef rende il tutto più veloce.
Mi direste il perchè?
Ho un maledetto limite io, se "m'incaglio" in qualcosa che non capisco, fatico ad andare avanti, non ho più punti fermi.
gila75
Imperturbabile Insigne
Imperturbabile Insigne
Messaggi: 2739
Iscrizione: mercoledì 16 gennaio 2013, 17:28
Desktop: ubuntu-2d
Distribuzione: Ubuntu 12.04.2 LTS i686
Località: Airuno(Lecco)

Re: Puntatori a Funzione[C]

Messaggio da gila75 »

@vbextreme

Ho provato a riscrivere il tuo esempio, proteggendo con const, come avevi detto tu.
Sembra funzionare lo stesso senza apparente complicazione. Non sono sicuro che lavori nella stessa maniera del tuo codice.
Ti va di guardarlo e parlarne?
Ho provato a modificare nella funzione la stringa, ma il programma non me lo permette, quindi significa che const fa il suo dovere:

Codice: Seleziona tutto

#include <stdio.h>
#include <stdlib.h>
void prova( const char *s,int n);// prototipo funzione

void prova( const char *s,int n)
{
    while (n>=1)
    {
        puts(s);
        n--;
    }
    
    return;
     
}

int main(void)
{
    char *s="Hello word";
    int n=10;
    void (*ptr)(const char *,int)=prova;
    (*ptr)(s,n);     
    return 0;
}
Comunque (piccolo OT), mi sto rendendo sempre più conto che a 39 anni, partendo da zero, è quasi impossibile imparare bene il C.
Ci sono decenni di programmazione dietro, un sacco di cose da sapere, scrivi un programma, funziona e pensi di aver fatto bene, poi scopri che è fatto
alla cacchio....bhè con questo a me piace e continuo, certo, ma non mi aspettavo una complessità simile.
Mi sembrava incasinato l'assembler dei pic, ma in fondo sono 35 istruzioni, e bene o male...ma il C.
Se non per far programmini didattici e poco più la vedo dura... :(
Tornando al programma sopra, se avete voglia di guardare :)
Avatar utente
vbextreme
Entusiasta Emergente
Entusiasta Emergente
Messaggi: 1214
Iscrizione: domenica 12 gennaio 2014, 14:06
Desktop: lxde
Distribuzione: xubuntu 14.10

Re: Puntatori a Funzione[C]

Messaggio da vbextreme »

il problema del codice che hai proposto é che non serve usare un puntatore a funzione,per quello l'ho modificato creando un reale esempio.
Nel c agli inizi occorre lasciaere correre certe cose perché molto complesse e tornarci su piu avanti in modo da capirle meglio.
Io ad esempio alcuni passaggi del libro k&r li ho capiti dopo un anno, solo dopo averli letti almeno 30 volte.
Ma non ti devi scoraggiare ci siamo passati tutti.
Anche il c poi ha pochissimi comandi, forse anche meno di un pic ed é proprio 'questa sua semplicita' a renderlo un linguaggio potente e difficile.

Quindi continua pure a studiare e a fare domade,io ne ho fatte molte e ancora di piu 'stupide',perché é cosi che si impara.
Una canzone dice 'se non chiedi non ti sarà dato'...
Easy framework per il linguaggio C.
vbextreme hack your life
gila75
Imperturbabile Insigne
Imperturbabile Insigne
Messaggi: 2739
Iscrizione: mercoledì 16 gennaio 2013, 17:28
Desktop: ubuntu-2d
Distribuzione: Ubuntu 12.04.2 LTS i686
Località: Airuno(Lecco)

Re: Puntatori a Funzione[C]

Messaggio da gila75 »

ok, Grazie vbextreme.
Che ne dici della variante che ho proposto?
il problema del codice che hai proposto é che non serve usare un puntatore a funzione,per quello l'ho modificato creando un reale esempio.
Ti riferisci al primo giusto? La semplice stampa void, no?
Io adesso parlo di quello che stampa Hello Word, vorrei capire se il "mio" può andare, pregi e difetti rispetto al tuo, che per altro devo ancora capire bene.
Ora però devo scappare...
ixamit
Scoppiettante Seguace
Scoppiettante Seguace
Messaggi: 499
Iscrizione: giovedì 14 novembre 2013, 10:16

Re: Puntatori a Funzione[C]

Messaggio da ixamit »

@MAW_1968

Grazie per aver risposto,
Il mio e' un punto di vista soggettivo, non intendo qui proporre l'unica verita' sull'utilizzo sistematico della typedef,
voglio solamente sottolineare alcuni problemi che potrebbero essere evitati a cui non si sta effettivamente rispondendo, se non
in merito a solo uno dei due punti chiave principali: _leggibilita'_

Nessuna presa di posizione, solo uno scambio di opinioni. :)

Per quello che riguarda le linee guida, io tendo a seguire il kernel linux coding style; in merito all'utilizzo delle typedef cita:
Chapter 5: Typedefs

Please don't use things like "vps_t".

It's a _mistake_ to use typedef for structures and pointers. When you see a

vps_t a;

in the source, what does it mean?

In contrast, if it says

struct virtual_container *a;

you can actually tell what "a" is.

Lots of people think that typedefs "help readability". Not so. They are
useful only for:

(a) totally opaque objects (where the typedef is actively used to _hide_
what the object is).

Example: "pte_t" etc. opaque objects that you can only access using
the proper accessor functions.

NOTE! Opaqueness and "accessor functions" are not good in themselves.
The reason we have them for things like pte_t etc. is that there
really is absolutely _zero_ portably accessible information there.

(b) Clear integer types, where the abstraction _helps_ avoid confusion
whether it is "int" or "long".

u8/u16/u32 are perfectly fine typedefs, although they fit into
category (d) better than here.

NOTE! Again - there needs to be a _reason_ for this. If something is
"unsigned long", then there's no reason to do

typedef unsigned long myflags_t;

but if there is a clear reason for why it under certain circumstances
might be an "unsigned int" and under other configurations might be
"unsigned long", then by all means go ahead and use a typedef.

(c) when you use sparse to literally create a _new_ type for
type-checking.

(d) New types which are identical to standard C99 types, in certain
exceptional circumstances.

Although it would only take a short amount of time for the eyes and
brain to become accustomed to the standard types like 'uint32_t',
some people object to their use anyway.

Therefore, the Linux-specific 'u8/u16/u32/u64' types and their
signed equivalents which are identical to standard types are
permitted -- although they are not mandatory in new code of your
own.

When editing existing code which already uses one or the other set
of types, you should conform to the existing choices in that code.

(e) Types safe for use in userspace.

In certain structures which are visible to userspace, we cannot
require C99 types and cannot use the 'u32' form above. Thus, we
use __u32 and similar types in all structures which are shared
with userspace.

Maybe there are other cases too, but the rule should basically be to NEVER
EVER use a typedef unless you can clearly match one of those rules.

In general, a pointer, or a struct that has elements that can reasonably
be directly accessed should _never_ be a typedef.
Anche lo standard C89, sempre in merito alle typedef riporta:
...
"Limiti di attuazione"

    L'applicazione deve consentire la specificazione dei tipi che hanno
almeno 12 dichiaratori puntatore, matrice, e la funzione (in qualsiasi valida
combinazioni) che modificano l'aritmetica, una struttura, un sindacato, o di un
tipo incompleto, direttamente o tramite uno o più typedef s.
...
Ora devo scappare, ti ringrazio anticipatamente per un'eventuale risposta.

Ciao Max
Avatar utente
vbextreme
Entusiasta Emergente
Entusiasta Emergente
Messaggi: 1214
Iscrizione: domenica 12 gennaio 2014, 14:06
Desktop: lxde
Distribuzione: xubuntu 14.10

Re: Puntatori a Funzione[C]

Messaggio da vbextreme »

No mi riferivo all'ultimo che hai postato.
Ora ti spiego il perchè delle mie parole.
Il tuo codice equivale a questa forma:

Codice: Seleziona tutto

#include <stdio.h>
#include <stdlib.h>
//void prova( const char *s,int n);// prototipo funzione
//prototipo inutile prova è presente prima di main

void prova( const char *s,int n)
{
    while (n>=1)
    {
        puts(s);
        n--;
    }
    // return;//superfluo
}

int main(void)
{
    char *s="Hello word";
    int n=10;
    //void (*ptr)(const char *,int)=prova;
    // (*ptr)(s,n);     
    //in questo caso la classica chiamata alla funzione fa il giuto lavoro:
    prova(s,n);
    return 0;
}
Come vedi il caso specifico non permette un'analisi corretta.
Se si vuole capire allora bisogna usarla per quello che è stata concepita.

Codice: Seleziona tutto

void fnc(char* s) {puts(s);} //funzione che stampa una stringa

//funzione che usa un'altra funzione per stampare nvolte la stringa
void stampa( void(*fnc)(char*) , int n) 
{
    int i;
    for ( i = 0; i < n; i++)
        fnc("Helloworld");
}

int main(void)
{
    stampa(fnc,10);
    return 0;
}
ecco partiamo da qui.
Ora mettiamo che la funzione stampa sia dentro una libreria di terze parti e quindi non modificabile.
La funzione "fnc" non fa altro che incapsulare la "puts" e se allora noi vogliamo passargli direttamente la "puts" senza passare da "fnc"?
Semplice:

Codice: Seleziona tutto

int main(void)
{
    stampa(puts,10);
    return 0;
}
Ma cosi facendo ci ritroviamo con un "warning per tipo incompatibile" e come facciamo a toglierlo?
semplice,dico al compilatore che è la stessa cosa:

Codice: Seleziona tutto

int main(void)
{
    stampa((void(*)(char*))puts,10);
    return 0;
}
Nonostante la semplicità di questa funzione già inizia a essere complicato leggere la funzione stampa.
Se io ora invece gli do un nome allora risulterà piu semplice la comprensione della riga:

Codice: Seleziona tutto

#include <stdio.h>
#include <stdlib.h>

typedef void(*CALLPRINT)(char*);

void fnc(char* s) {puts(s);} //funzione che stampa una stringa

//funzione che usa un'altra funzione per stampare nvolte la stringa
void stampa( void(*fnc)(char*), int n)
{
    int i;
    for ( i = 0; i < n; i++)
        fnc("Helloworld");
}

int main(void)
{
    stampa((CALLPRINT) puts,10);
    return 0;
}
è difficile comunque spiegarlo per casi cosi semplici,prova a pensare ad una funzione che accetti 4 puntatori a funzione e devi fare in tutti e 4 dei cast! Penso faticherebbe chiunque nella sua comprensione.Ovvio che poi se circoscritto ad un unico caso sporadico allora la typedef diventa piu sconveniente che altro,ma generalmente non è mai cosi.

ixamit,tutti i casi proposti sulla typedef rientrano in quelle specifiche a parte i puntatori a funzione e le strutture.
Ma a mio avviso per entrambi non ci sono grosse regole perchè varia proprio in base al caso specifico la strada da percorrere.
Io preferisco sempre la forma compatta per tutte quelle cose che siano ripetitive e frustranti(come fanno la maggior parte dei framework in circolazione.)
Se io scrivo:

Codice: Seleziona tutto

struct BUTTON
{
...
};
quanto diventa inutile la dicitura "struct", il nome "BUTTON" è già significativo.
Tutto il "contorno" delle typedef creano un modello di programmazione che aiuta lo sviluppatore nel creare il programma nel modo corretto.
Inolte bisognerebbe prendere tutto con le pinzette,lo stesso "standard" è pieno di codice deprecato e di vero "standard" alla fine di un programma rimane ben poco.
Easy framework per il linguaggio C.
vbextreme hack your life
gila75
Imperturbabile Insigne
Imperturbabile Insigne
Messaggi: 2739
Iscrizione: mercoledì 16 gennaio 2013, 17:28
Desktop: ubuntu-2d
Distribuzione: Ubuntu 12.04.2 LTS i686
Località: Airuno(Lecco)

Re: Puntatori a Funzione[C]

Messaggio da gila75 »

In effetti avevo il dubbio che il mio codice lavorasse diversamente dal tuo.
Come dici giustamente tu, il tuo listato usa una funzione che richiama un'altra funzione (detto a spanne).
il mio no, èun semplice puntatore a funzione void, dove stampo n volte la scritta.
Non ho molto tempo nel tra oggi e domani, ma voglio studiare bene il tuo listato, e capirlo.
Purtroppo finchè non provo e pesto il naso, non comprendo fino in fondo.
Devo scrivere il codice, capire e vedere che ho scritto una bojata, e solo allora riesco ad avere il quadro chiaro.
Non è sfiducia,sono solo fatto così :)
antex
Prode Principiante
Messaggi: 85
Iscrizione: mercoledì 14 marzo 2012, 20:59

Re: Puntatori a Funzione[C]

Messaggio da antex »

M_A_W_ 1968 [url=http://forum.ubuntu-it.org/viewtopic.php?p=4554044#p4554044][img]http://forum.ubuntu-it.org/images/icons/icona-cita.gif[/img][/url] ha scritto:2) Non si dimentichi un ulteriore uso fondamentale, seppure non quotidiano nel codice mainstream, dei puntatori a funzione: il dispatching, tipicamente operato su un array (eventualmente dinamico) di puntatori a funzione. Esso in genere assume la forma di una LUT implementata tramite un array di puntatori a funzione, e nella sostanza altro non è che l'epifenomeno, la versione edulcorata del linguaggio C, delle jump tables supportate in modo più o meno esplicito da un'infinità numerabile di CPU e piattaforme di elaborazione del real world, fin dagli albori.
+1 per l'epifenomeno :maestro: :)

Però era già stata citata questa tecnica, e non richiede certo typedef.
...

Codice: Seleziona tutto

#define F1 ((struct file_entry **) (f1))
#define F2 ((struct file_entry **) (f2))
int cmp_files(const void *f1, const void *f2)
...
#undef F2
#undef F1
/************************************************/
int cmp( const void *a, const void *b)
{
    const char *_a = *(const char **)a;
    const char *_b = *(const char **)b;
    ...
}
/************************************************/
int comp(const void *a, const void *b)
{
    unsigned int aux1, aux2;

    aux1 = ((struct MyStruct_t**)a)->segment ;
    aux2 = ((struct MyStruct_t**)b)->segment ;

    return (aux1 < aux2 ? -1 : (aux1 > aux2));
}
/************************************************/
int Cmp(MyPtr_t *a, MyPtr_t *b)
{
    return strcmp(a->name, b->name);
}

void MyFun(char *foo)
{
    ...
    p = (MyPtr_t *) bsearch(...
        (int(*)(const void*, const void*))Cmp);
    ...
}
/************************************************/
Nel caso specifico, l'uso di una typedef per il cast (peraltro univoca, nel caso banale delle funzioni della libreria standard e di tutte quelle - inumerevoli... - modellate sulla loro falsariga) risulta altamente preferibile a qualsiasi altra soluzione analoga, secondo un banale ma basilare criterio di economia e leggibilità - accolto in modo pressoché universale da guide di stile e manuali di engineering, a prescindere dal livello di normazione del settore specifico nel quale è applicato il software.

Codice: Seleziona tutto

typedef int (*fptr)(const void*, const void*);
...
    qsort(Array, Asize, sizeof(MyStruct_t), (fptr)Cmp);
Beh, quello non mi sembra certo un codice da manuale.

Non è difficile, credo, esser d'accordo che sfruttare typedef aumenti la leggibilità di un cast di puntatori a funzioni (io non ne farei una regola). Però, se volete postare un codice di esempio che lo mostri, che sia decente. ;) Sinceramente, a me, al momento non mi sovviene niente di interessante (entro il C89/99).


EDIT: ho trovato un esempio più semplice, quando torno al PC lo posto... :)


Per esempio, il codice scritto da vbextreme non mi convince:
vbextreme ha scritto:

Codice: Seleziona tutto

void fnc(char* s) {puts(s);} //funzione che stampa una stringa

//funzione che usa un'altra funzione per stampare nvolte la stringa
void stampa( void(*fnc)(char*) , int n) 
{
    int i;
    for ( i = 0; i < n; i++)
        fnc("Helloworld");
}

int main(void)
{
    stampa(fnc,10);
    ...
    stampa((void(*)(char*))puts,10);

    return 0;
}
La puts è dichiarata come int puts(const char * str). Quel const ha la sua ragion d'essere, ed è un errore non dichiararlo negli altri puntatori a funzione (se davvero la fnc modificasse la stringa passata, potrebbe causare un crash). Inoltre, il valore di ritorno è diverso (int vs void): questo potrebbe causare un "undefined behavior" (e quindi un bug difficile da scovare), a seconda di come il compilatore traduce il codice.
è difficile comunque spiegarlo per casi cosi semplici,prova a pensare ad una funzione che accetti 4 puntatori a funzione e devi fare in tutti e 4 dei cast! Penso faticherebbe chiunque nella sua comprensione.Ovvio che poi se circoscritto ad un unico caso sporadico allora la typedef diventa piu sconveniente che altro,ma generalmente non è mai cosi.
Capisco cosa vuoi dire, ma con quel "mai" diventa un'affermazione piuttosto forte.
Io preferisco sempre la forma compatta per tutte quelle cose che siano ripetitive e frustranti(come fanno la maggior parte dei framework in circolazione.)
Se io scrivo:

Codice: Seleziona tutto

struct BUTTON
{
...
};
quanto diventa inutile la dicitura "struct", il nome "BUTTON" è già significativo.
Su questo sono pienamente d'accordo, in disaccordo con il citato Torvalds (se una struttura viene chiamata vps_t, non è certo facile immaginarsi che è una struttura!).
Ultima modifica di antex il sabato 29 marzo 2014, 15:36, modificato 1 volta in totale.
gila75
Imperturbabile Insigne
Imperturbabile Insigne
Messaggi: 2739
Iscrizione: mercoledì 16 gennaio 2013, 17:28
Desktop: ubuntu-2d
Distribuzione: Ubuntu 12.04.2 LTS i686
Località: Airuno(Lecco)

Re: Puntatori a Funzione[C]

Messaggio da gila75 »

Rallentate ragazzi :) Mi si accumulano i post da leggere e rileggere...'mazza oh..ne sapete di cose...
ixamit
Scoppiettante Seguace
Scoppiettante Seguace
Messaggi: 499
Iscrizione: giovedì 14 novembre 2013, 10:16

Re: Puntatori a Funzione[C]

Messaggio da ixamit »

antex ha scritto: Su questo sono pienamente d'accordo, in disaccordo con il citato Torvalds (se una struttura viene chiamata vps_t, non è certo facile immaginarsi che è una struttura!).
Si, quel nome dell'esempio non convince nemmeno me, ma convengo sul fatto che molte volte l'alias serva solo ad evitare di "battere"
qualche tasto in piu', indipendentemente da un eventuale utilizzo esterno della struttura dati. Ma cosa dire quando vps_t è puntatore?! Usiamo la "_p" come suffisso?!
Avatar utente
vbextreme
Entusiasta Emergente
Entusiasta Emergente
Messaggi: 1214
Iscrizione: domenica 12 gennaio 2014, 14:06
Desktop: lxde
Distribuzione: xubuntu 14.10

Re: Puntatori a Funzione[C]

Messaggio da vbextreme »

se volete esempi piu concreti scaricatevi qual dir si voglia framework.
Gtk,opengl, ecco alcuni esempi tra i piu famosi.dove é strapieno di typedef dai puntatori di ogni sorta alle strutture fino ai propri tipi.Per non parlare di windows che li ben non rimane traccia di quel che chiami 'standard'.
A mio avviso lo standar é un punto di partenza,non una linea da seguire nemmeno troppo da rispettare.
Scappo a mangiare.
Easy framework per il linguaggio C.
vbextreme hack your life
Scrivi risposta

Ritorna a “Programmazione”

Chi c’è in linea

Visualizzano questa sezione: Google [Bot] e 2 ospiti