Problema funzioni incapsulate in C

robbstark1
Ciao a tutti,
ho un problema che è da un po' che non riesco a risolvere, con un codice abbastanza complesso in C per il calcolo della funzione ipergeometrica F1 di Appell.
Il problema è nella gestione delle varie funzioni che lo compongono, che si attivano anche quando non dovrebbero.
Posto alcuni pezzi del codice per illustrare meglio il problema:

test.c è il file principale, contenente il main:
#include<math.h>
#include<complex.h>
#include<stdio.h>
#include"appellf1.h"

int main()
{
    double complex a, b, c, d, x, y, F1;
    
    a = 1.0 + 0.0*I;
    b = 2.0 + 0.0*I;
    c = 3.0 + 0.0*I;
    d = 4.0 + 0.0*I;
    x = 0.5 + 0.0*I;
    y = 0.1 + 0.0*I;
    
    F1 = appellf1(a,b,c,d,x,y);
    
    printf("AppelF1 = %18.15lf + %18.15lf\n", creal(F1), cimag(F1));
    
    return 0;
}


Questo richiama il file appellf1.c
#include<math.h>
#include<complex.h>
#include<stdio.h>
#include"appellf1.h"
#include"appf1_hypser.h"
#include"horng2.h"

double complex appellf1 (double complex a, double complex b, double complex c, double complex d, double complex x, double complex y)
{
    double complex F1, aus;
    double u, w, dist, distmax;
    int flag;

    distmax = 1.;
    if(cabs(x) >= cabs(y))  dist = cabs(x);
    else  dist = cabs(y);
    
    if(dist < distmax)
    {
        flag = 0;
    }
    else
    {
          ...........una serie di condizioni che a seconda dei valori di x e y scelgono una flag
    }

    if(flag==0)
    {
        F1 = appf1_hypser(a,b,c,d,x,y);
        //printf("FLAG=%d\n", flag);
        //printf("a=%lf b=%lf c=%lf d=%lf x=%lf y=%lf\n", creal(a), creal(b), creal(c), creal(d), creal(x), creal(y));
    }
    ...........a seconda del valore di flag vengono eseguite delle operazioni su x e y e vengono chiamate diverse funzioni

    return F1;



appellf1.c può chiamare dunque diverse altre funzioni, a seconda dei valori di x e y. Nel caso in questione, i valori li ho fissati in test.c, quindi deve essere flag=0 e appellf1 chiamerà appf1_hypser.
Il risultato dovrebbe essere comunicato ad appellf1 e da questa a test.

Quello che invece succede è che appf1_hypser calcola correttamente il valore, ma anziché ritornarlo indietro come mi aspetto, agisce altre 2 volte con valori diversi dei parametri. Quindi si attivano anche altre funzioni quali horng2 e appellf2, che invece non dovrebbero in questo caso. Infine il programma principale mi restituisce un risultato sbagliato.

Non pretendo correggiate l'errore, ma magari suggerirmi dove guardare o se posso avere creato possibili conflitti, perché pur avendo riempito di controlli il programma per vedere esattamente cosa fa, non riesco a capirne il motivo.

Risposte
hamming_burst
Ciao,
...........a seconda del valore di flag vengono eseguite delle operazioni su x e y e vengono chiamate diverse funzioni

come hai dichiarato le condizioni su questi flag?
degli elenchi di if?

appf1_hypser(a,b,c,d,x,y);

richiama la funzione appf1() a sua volta?

PS: consiglio di inizializzare il flag ad un valore di default per sicurezza es. $-1$

robbstark1
Ciao, grazie per la risposta.

"hamming_burst":

...........a seconda del valore di flag vengono eseguite delle operazioni su x e y e vengono chiamate diverse funzioni

come hai dichiarato le condizioni su questi flag?
degli elenchi di if?


Esattamente, un elenco di if, tipo if(flag==0) ecc..

"hamming_burst":

appf1_hypser(a,b,c,d,x,y);

richiama la funzione appf1() a sua volta?


No.

"hamming_burst":

PS: consiglio di inizializzare il flag ad un valore di default per sicurezza es. $-1$


Ok. Comunque ho controllato che il valore di flag fosse corretto facendolo stampare all'interno di ogni if:

in appellf1.c

if(flag==0)
    {
        F1 = appf1_hypser(a,b,c,d,x,y);
        printf("FLAG=%d\n", flag);
    }
if(flag==1)
{
         ........
         printf("FLAG = %d\n", flag);
}
....


Il valore di flag è corretto, però curiosamente lo stampa sia subito dopo l'esecuzione di appf1_hypser.c, come è giusto che faccia,
(solo che a quel punto, anziché uscire dall'if, restituire il risultato al main e uscire
si esegue altre 2 volte appf1_hypser più le altre funzioni che non interessano)
sia quando alla fine ritorna in appellf1 e ristampa il valore di flag, che continua a essere quello giusto, cioè 0 in questo caso.

hamming_burst
scusa un po' il ritardo delle risposte ma ho un po' di cose da fare in sti giorni :)

perciò vediamo...
"robbstark":
Esattamente, un elenco di if, tipo if(flag==0) ecc..

se la scelta ed il flag non viene toccato e rimane sempre lo stesso, cioè la sua funzione è di indirizzare ad un UNICO if, ti consiglio di fare degli if-then-else, cioè:

if(){

}
else if(){

}
...


oppure segli switch, ma prova con questo modello.

sia quando alla fine ritorna in appellf1 e ristampa il valore di flag, che continua a essere quello giusto, cioè 0 in questo caso.

una cosa alla fine dell'elenco degli if il flag rimane inalterat e se lo stampi rimane $0$?

robbstark1
Grazie, comunque proprio poco fa un collega mi ha fatto notare l'errore:
era un ; dopo un if, che faceva sì che le istruzioni associate venissero eseguite sempre.
Essendo il codice un po' lungo non me ne ero accorto.

hamming_burst
"robbstark":
Grazie, comunque proprio poco fa un collega mi ha fatto notare l'errore:
era un ; dopo un if, che faceva sì che le istruzioni associate venissero eseguite sempre.
Essendo il codice un po' lungo non me ne ero accorto.

ah ok. Ma posso chiederti cosa utilizzi come compilatore (gcc,...) od IDE (NetBeans...). Mi pare strano che un compilatore non te lo abbia segnalato.

robbstark1
Uso il gcc, ma non mi dava errori o warning.

hamming_burst
"robbstark":
Uso il gcc, ma non mi dava errori o warning.

ah no!
domanda forse idiots, ma hai utilizzato i flag per l'estrapolazione dei warning, es. -Wall -w ...

Mi mostreresti come era scritto questo errore.

robbstark1
"hamming_burst":

domanda forse idiots, ma hai utilizzato i flag per l'estrapolazione dei warning, es. -Wall -w ...

Mi mostreresti come era scritto questo errore.


Onestamente ho riciclato un vecchio makefile che mi hanno passato, cambiando solo i nomi dei file:


Non so cosa faccia esattamente, però in fase di compilazione non compaiono -Wall o -w, quindi non credo siano stati usati.
Inoltre non sono esperto, ma non so se può essere individuato come errore:

if(flag == 0)
{
           istruzioni;
}
if(flag == 1);
{
           istruzioni;
}
if(flag == 2)
{
          istruzioni;
}

hamming_burst
Grazie.
Per il make: ti consiglio di inserire i flag lo si può inserire in vari modi, ma fallo direttamente sovrascrivendo questa linea:

#CFLAGS = -ggdb


con

#CFLAGS = -ggdb -Wall -Wextra


Per il codice, ho provato a compilare con opzione -Wall ed in effetti non produce nessun warning.
Aggiungendo un altro tipo di flag più pedante restituisce questo:
warning: suggest braces around empty body in an ‘if’ statement


La cosa che mi lascia perplesso è che questo codice:
if(flag == 1);

è corretto e se si segue il consiglio del compilatore (si inserisce uno scope vuoto):
if(flag == 1){;}

il warning è sparito.

Qua ci sono due cose che vorrei capire:
- perchè al $;$ è concesso esser corretto scritto in quel modo, come suffisso e quale significato semantico abbia (l'unico penso sia quello di istruzione vuota). L'unico operatore sintattico che ha ; come valore di terminazione è il do-while, di if non ne ho mai sentito.
- anche in C è concesso inserire uno blocco logico per delle istruzione (tipo inizializzare) come succede nel C++, cioè mettere in mezzo a delle funzioni parentesi graffe:
void bla(){
   {
       istruzione
   }
}


facendo delle prove compila

Se passa qualcuno a fare lumi su sta faccenda, che mi lascia spiazzato :roll: lo ringrazio.

robbstark1
"hamming_burst":

La cosa che mi lascia perplesso è che questo codice:
if(flag == 1);

è corretto e se si segue il consiglio del compilatore (si inserisce uno scope vuoto):
if(flag == 1){;}

il warning è sparito.

Qua ci sono due cose che vorrei capire:
- perchè al $;$ è concesso esser corretto scritto in quel modo, come suffisso e quale significato semantico abbia (l'unico penso sia quello di istruzione vuota). L'unico operatore sintattico che ha ; come valore di terminazione è il do-while, di if non ne ho mai sentito.


Su questo punto penso che sia perché se l'if è seguito da una sola istruzione, puoi anche scriverla sulla stessa riga, senza parentesi graffe, per esempio:
if(flag == 1) printf("flag = %d\n", flag);

quindi se scrivi solo
if(flag == 1) ;

E' come se avessi scritto un'istruzione vuota e poi termini con il ; come ogni normale istruzione.

Grazie per il consiglio sui flags.

hamming_burst
"robbstark":
E' come se avessi scritto un'istruzione vuota e poi termini con il ; come ogni normale istruzione.

in effetti sarebbe plausibile. E non dipende dall'operatore condizionale $if$.

Non è il $;$ in sè, ma è proprio l'operatore vuoto (nei linguaggi segnato come $\epsilon$) che ha significato semantico valido, scritto in quel modo.

Infatti è corretto scrivere:
do{

	}while(T==1);;

Forse in standard più recenti c'è un controllo più rigoroso (almeno anche con C99 compila) e tali istruzioni non sono ammesse.

Interessante, ho scoperto una cosa nuova, grazie robbstark :-)

apatriarca
Immagino che il principale motivo per supportare istruzioni vuote sia per il for. E' infatti possibile (e ci sono diversi motivi per farlo) lasciare vuota ognuna delle tre espressioni da inserire tra le parentesi. Ci possono poi essere casi in cui una istruzione vuota potrebbe essere l'effetto dell'uso di una qualche macro. Per esempio se si scrive una macro come (in realtà sarebbe forse meglio non inserire il punto e virgola alla fine..):
#define MACRO(x) do { /* fai qualcosa.. */ } while(0);

Immagino si possano poi avere delle istruzioni vuote in situazioni in cui il codice è generato automaticamente. Ovviamente sono tutte situazioni evitabili, ma troppo codice smetterebbe di funzionare se si decidesse di abolire questa "funzionalità".

P.S. Può essere a volte utile far ricorso ad un qualche programma di analisi del codice come lint. Un elenco si trova per esempio su Wikipedia.

hamming_burst
"apatriarca":
Immagino che il principale motivo per supportare istruzioni vuote sia per il for. E' infatti possibile (e ci sono diversi motivi per farlo) lasciare vuota ognuna delle tre espressioni da inserire tra le parentesi.

aaah è vero, non ci avevo pensato :D

Ci possono poi essere casi in cui una istruzione vuota potrebbe essere l'effetto dell'uso di una qualche macro. Per esempio se si scrive una macro come (in realtà sarebbe forse meglio non inserire il punto e virgola alla fine..):
#define MACRO(x) do { /* fai qualcosa.. */ } while(0);

Immagino si possano poi avere delle istruzioni vuote in situazioni in cui il codice è generato automaticamente. Ovviamente sono tutte situazioni evitabili, ma troppo codice smetterebbe di funzionare se si decidesse di abolire questa "funzionalità".

ah ok, grazie degli esempi :)
A me però nelle define il ; non ha mai compilato, mi ha sempre dato errore, forse nelle MACRO è diverso, devo provarci.

per quanto riguarda invece il blocco:
- anche in C è concesso inserire uno blocco logico per delle istruzione (tipo inizializzare) come succede nel C++, cioè mettere in mezzo a delle funzioni parentesi graffe:
void bla(){
   {
       istruzione
   }
}


facendo delle prove compila

sai se in C tale funzionalità è implementata?

PS: per gli analizzatori statici, curiosità, te gli usi spesso?

apatriarca
Per fare un esempio pratico delle funzionalità di cui abbiamo parlato:
#include <stdio.h>

#define PROVA(a) do { \
                fputs("Inserisci un numero: ", stdout); \
                scanf("%d", &(a)); \
                printf("\na = %d\n", (a)); \
                } while(0);

int main(void)
{
    int a = 0;

    PROVA(a)
    
    {
        int a = 1;
        printf("1 = %d\n", a);        
    }
    
    printf("a(fuori dal blocco) = %d\n", a);
    
    return 0;
}

L'output (ho utilizzato gcc 4.6.3 su Fedora e inserito il numero 123) è
Inserisci un numero: 123

a = 123
1 = 1
a(fuori dal blocco) = 123


Per quanto riguarda gli analizzatori statici, dipende dal progetto. Nei progetti piccoli non li uso, in quelli più grandi ogni tanto vedo che cosa mi dicono.

hamming_burst
In effetti è tutto abbastanza logico, conoscendo abb il linguaggio. Non pensavo che il costrutto blocco fosse applicabile anche in questo modo, si scopron sempre cose nuove. cmq grazie!

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