Newsgroup: it.comp.lang.c
Titolo: Programma che non funzione

nulla ...
Dalle FAQ di it.comp.lang.c : 
16.1c
-----
D:	Perche' la chiamata a una procedura non funziona ?
  	Mi sembra che il compilatore la salti.
R:	Probabilmente hai dimenticato le parentesi.
  	Se la chiamata e' del tipo :
  	mia_procedura ;
  	essa  non  viene  considerata  in quanto il  C  ha  soltanto
  	funzioni  e le chiamate a funzioni richiedono  sempre  l'uso
  	delle parentesi, anche se non ci sono parametri da passare.
  	Usa invece :
  	mia_procedura() ;
-----------------------------------------------------------------
Nel tuo programma hai scritto :
  Stampa;
  EliminaTesta;
quando doveva essere :
  Stampa ( listadoppia.Testa ) ;
  EliminaTesta ( listadoppia.Testa ) ;

 c'č qualcuno in grado di controllare cosa non va in questo programma
Si, ci sono diverse cose:
1. Le funzioni Stampa() e EliminaTesta() le hai chiamate senza le
parentesi.
2. La funzione EliminaTesta() si aspetta inoltre un parametro.
3. La funzione EliminaTesta() appare incompleta (forse ci devi mettere
un loop).
4. La funzione main() deve restituire un intero.
La funzione Stampa(), una volta chiamata correttamente, pare funzionare.
EMG

Ecco qua,avevi dimenticato un livell odi indirezione e un parametro.Non 
dimenticarti di dichicrare anche le allusioni alle funzioni tramite la 
parola chiave extern.;-)
Ciao.
------------------------------------------------------------------------
#include<stdio.h>
#include<stdlib.h>
struct ELD { int Info;
  struct ELD *Prec, *Succ;};
typedef struct ELD ElListaDoppia;
typedef struct {ElListaDoppia *Testa, *Coda;} ListaDoppia;
ListaDoppia listadoppia;
void Inizializza(ListaDoppia *listavuota)
{
  listavuota->Testa = NULL;
  listavuota->Coda = NULL;
}
void InserisciInOrdine(ElListaDoppia **testalista, ElListaDoppia 
**codalista, 
                       int elem)
{
  ElListaDoppia *punt;
  if ((*testalista == NULL) && (*codalista == NULL)) /* e` da inserire 
nella lista vuota */
    {
      punt = malloc(sizeof(ElListaDoppia));
      punt->Info = elem;
      punt->Prec = NULL;
      punt->Succ = NULL;
      *testalista = punt;
      *codalista = punt;
    }
  else if (*testalista == NULL) /* e` da inserire in fondo alla lista non 
vuota */
    {
      punt = malloc(sizeof(ElListaDoppia));
      punt->Info = elem;
      punt->Prec = *codalista;
      punt->Succ = NULL;
      *testalista = punt;
      *codalista = punt;
    }
  else if ((*testalista)->Info > elem)  /* bisogna inserire l'elemento in 
testa */
    {
      punt = malloc(sizeof(ElListaDoppia));
      punt->Info = elem;
      punt->Prec = (*testalista)->Prec;
      punt->Succ = (*testalista);
      (*testalista)->Prec = punt;
		*testalista = punt;
    }
  else InserisciInOrdine(&((*testalista)->Succ),codalista,elem);
  }
void Stampa()
{
ElListaDoppia *p;
p=listadoppia.Testa;
while (p!=NULL)
   {
   printf("%d ",p->Info);
   p=p->Succ;
   }
printf("\n");
}
void EliminaTesta(ElListaDoppia *puntatore)
{
ElListaDoppia *punttemp;
punttemp=puntatore->Succ;
free(puntatore);
}
main()
{
  Inizializza(&listadoppia);
  InserisciInOrdine(&(listadoppia.Testa),&(listadoppia.Coda),4);
  InserisciInOrdine(&(listadoppia.Testa),&(listadoppia.Coda),1);
  InserisciInOrdine(&(listadoppia.Testa),&(listadoppia.Coda),3);
  InserisciInOrdine(&(listadoppia.Testa),&(listadoppia.Coda),7);
  InserisciInOrdine(&(listadoppia.Testa),&(listadoppia.Coda),10);
  Stampa();
  EliminaTesta(&(*(listadoppia.Testa)));
  }

  EliminaTesta(&(*(listadoppia.Testa)));
Non vedo proprio a cosa debba servire "extern" in questo programma.
La funzione Stampa() e' giusta, ma in quanto ai livelli di
indirezione, ti faccio presente che :
&*p = p 
quindi va scritto esattamente :
EliminaTesta ( listadoppia.Testa ) ;

un loop).
Esatto.
O la riscrivi cosi' :
void EliminaTesta ( ElListaDoppia *puntatore )
{
     ElListaDoppia *punttemp;
     while ( puntatore != NULL ) 
     {
           punttemp = puntatore->Succ;
           free ( puntatore ) ;
           puntatore = punttemp ;
     }
}
oppure piu' semplicemente (per me) :
void EliminaTesta ( ElListaDoppia *puntatore )
{
     if ( puntatore != NULL ) 
     {
           EliminaTesta ( puntatore->Succc ) ;
           free ( puntatore ) ;
     }
}

 }
Vedo che usi, anche quando e' eliminabile, la ricorsione; la domanda e'
: sono d'accordo sul fatto che sia piu' elegante di un'iterazione, ma
non ti sembra che sia poco utilizzabile con strutture dati grandi, visto
che usa massicciamente lo stack?
Ciao, Luca.
-- 
*******************************************************
       "Dio non e' morto.
        E' vivo e sta lavorando
        a un progetto
        meno ambizioso"
				    graffito anonimo
  Reply to 
bia...@maganet.net
*******************************************************

che usa massicciamente lo stack?
[v. risposta a A. Foco]
La scelta della ricorsione non e' dovuta alla maggiore eleganza che ne
deriva (almeno da parte mia), ma alla semplicita' con cui e' facile
pensare a un processo ricorsivo e, conseguentemente, alla sua
implementazione con un linguaggio capace di supportarlo.
E' anche vero, comunque, che ne deriva una maggiore leggibilita' del
codice.
Se per "poco utilizzabile" intendi meno efficiente di un processo
iterativo, possiamo essere d'accordo, ma non sempre il fatto che un
processo utilizzi massicciamente lo stack lo rende inutilizzabile.
Prova ad applicare dei processi iterativi ad un albero binario.

 }
In effetti questa soluzione ricorsiva è molto chiara. Mi preoccupo solo
un po' dello stack nel caso la lista abbia un gran numero di elementi.
EMG

, 
andrea_...@bigfoot.com
 says...
Sinceramente non ho letto l'intero programma,ho solo notato che mancava 
qlcosa  ed aggiungebdo le parti mancanti ha funzionato cosi l'ho postato.
Riguardo a extern io la uso sempre cosi da iutarmi a trovare 
immediatamente le funzioni che vengono richiamate oltre al controllo dei 
tipi forniti dai protoitpi.
Ciao

 tipi forniti dai protoitpi.
Staitc static non extern.:-)



In generale si', ma anche supponendo uno stack di 8 Kb e un puntatore
a 64 bit (cioe', considerando il byte a 8 bit, 8 byte) si avrebbe una
lista gestibile di massimo 8 Kb / 8 b = 1K elementi.
Bisogna ammettere che si e' considerato uno stack davvero piccolo e il
numero di elementi, per causare problemi, deve essere davvero molto
grande.

qlcosa  ed aggiungebdo le parti mancanti ha funzionato cosi l'ho postato.
il problema e' che se hai : 
int *p ;
scrivere 
&*p  equivale a scrivere p
In quanto a static non riesco a capire che cosa vuoi dire.

 Prova ad applicare dei processi iterativi ad un albero binario.
Per quel che ne so, le risorse bisogna usarle con parsimonia, quindi
usare una funzione ricorsiva quando basta un'iterazione mi sembra uno
spreco, oltre ad influire non poco sulle prestazioni.


spreco, oltre ad influire non poco sulle prestazioni.
Sono d'accordissimo.
Anzi, secondo me non solo e' uno spreco, ma e' un modo di programmare da non
seguire.
In un progetto piu' ampio delle cose simili possono portare a problemi molto
grandi.
Poi vatti a ritrovare dov'e' che e' scattato lo stack overflow (quando va
bene..)
Marco

 grande.
Spesso però le liste sono in effetti molto grandi, almeno in tutti i
casi nei quali mi è capitato di utilizzarle, per lo più in programmi
matematici. Inoltre la soluzione iterativa ha in meno anche l'overhead
della chiamata e del ritorno della ricorsione. Sarebbe interessante
sapere come sono implementati gli algoritmi della libreria standard del
C++...
EMG

 In quanto a static non riesco a capire che cosa vuoi dire.
Io anche in programmini molto -ini- utilizzo sempre i prototipi per 
dichiarare le funzioni esterne anche se residenti nello stesso file 
sorgenet.In questo modo sfrutto i vantaggi dei prototipi:inanzitutto mi 
garantisce la coerenza tra il numero e il tipo dei parametri attuali e 
formali e le conversioni automatiche degli argomenti non sono necesarie; 
i tipi interi ridotti non devono essere piů convertiti in int e i float 
in double.Se poi capita di utilizzare la funzione insieme ad altri moduli 
so dove trovare le funzioni correlate semplicemente leggendo la sezione 
di dichiarazione.
Quindi nella sezione di dichiarazione di main() avrei scritto:
int main(..){
static void Inizializza(ListaDoppia *);
static void InserisciInOrdine(ElListaDoppia **, ElListaDoppia **, int );
....
...
..
.
return 0;
}
Sebbene i compialtori moderni emettano un warning nel caso di una perdita 
di precisione sia utilizzando e non utilizzando i prototipi.
P.e. il seguente programma sul mio compialtore mi da un solo warning di 
possibile perdita di precisione,ma il programma viene eseguito comunque
eventualmente con risultati errati.Il risultato per un programam simile 
secondo l'ANSI č indefinito.Mentre se eseguo lo stesso programam dul 
vecchio Borland 2.0 non notifica nulla ed esegue il programma ovviamente 
con risultati errati;in questo caso mi restituisce nulla :-)
---------------------------------------------------------------------
#include<stdio.h>
int gcd(u,v)
float u;
short v;
{
int t;
while(u > 0){
 if(u < v)
   {t = u;u = v;v = t;}
 u = u-v;
 }
 return v;
}
int main(){
 long int x;
 double y;
 printf("Immetti due numeri interi separati da uno spazio: ");
 scanf("%d %d",&x,&y);
 if(x > 0 && y > 0)
  printf("\n%d %d %d\n",x,y,gcd(x,y));
 return 0;
}
Intendevo dire questo.
Ciao 

 wrote:
[ snip ]
B>Per quel che ne so, le risorse bisogna usarle con parsimonia, quindi
B>usare una funzione ricorsiva quando basta un'iterazione mi sembra uno
B>spreco, oltre ad influire non poco sulle prestazioni.
Pienamente d'accordo 8)
Ciao ciao,
Andrea
-- 
"...Unix, MS-DOS, and Windows NT (also known as the Good, the Bad, and 
 the Ugly)."
(by Matt Welsh)

Per quel che ne so, le risorse bisogna usarle con parsimonia [...]
E ci sono situazioni nelle quali e' del tutto impossibile usare
le ricorsioni: ci sono in giro microcontrollori con uno stack
profondo anche poche word, e li' non si scappa:)
-- 
sr - null...@PAMflashnet.it <strip NOS & PAM to email>

della chiamata e del ritorno della ricorsione. 
Stiamo continuando a girare in tondo senza raggiungere mai un punto
d'accordo e la discussione sta diventando piuttosto noiosa.
Ho gia' detto negli altri post che l'utilizzo di una funzione
iterativa e' sicuramente piu' efficiente di una ricorsiva, ma il
problema dell'indispensabilita' dell'utilizzo di quest'ultima si ha
quando bisogna implementare determinate strutture dati.

spreco, oltre ad influire non poco sulle prestazioni.
Ti ripeto ancora una volta che l'utilizzo delle funzioni ricorsive e'
indispensabile in certi casi di gestione di determinate strutture
dati.

bene..)
Le soluzioni dei problemi si adattano proprio alla dimensione degli
stessi problemi.
Se il programma necessita di scrivere una piccola lista di dati,
l'utilizzo di una funzione ricorsiva o di una iterativa e'
indifferente se si punta esclusivamente al risultato.

 short v;
Mamma mia! Questa è una dichiarazione pre-ANSI veramente antidiluviana.
EMG


E' indifferente *a prescindere* dalla quantita' di dati, a parte il discorso
sulla velocita'.
Solo che in un progetto reale (cioe' non didattico) spesso la soluzione
iterativa risulta l'unica applicabile.
Ci sono casi in cui si e' quasi costretti ad usare la ricorsione, e in quei
casi occorre sempre andarsi a preoccupare dello stack, pena overflow.
Questo, ovviamente, non era il caso. Ma in generale non conoscendo le
dimensioni del problema se lo stesso e' trattabile senza troppi patemi
d'animo in maniera iterativa e' meglio, dove meglio significa piu' veloce e
piu' parsimonioso con lo stack.
Marco

iterativa risulta l'unica applicabile.
Continuiamo a dire sempre le stesse cose, non la finiremo piu'...

  printf("\n%d %d %d\n",x,y,gcd(x,y));
utilizzando lo specificatore %d per dei tipi di dati inadeguati !!!!

Ok, discorso chiuso. Grazie per l'attenzione.
Ciao, Luca.


sulla velocita'.
Diciamo che la *scelta* dell'uno o dell'altro metodo dipende in modo
preponderante dalla quantita' di dati e non puo' affatto prescinderne.

funzioni che vengono richiamate e dove si trovano.
E' il principio che e' sbagliato e rende lo stile piu' che scadente.
Puoi applicare i prototipi utilizzando "static" per funzioni che
risiedono nello stesso file.

A garantire la coerenza tra i parametri formali e quelli attauali.
Forse soltanto perche' definisci le funzioni DOPO il main() e non
PRIMA.

 
No,ho infatti notato che avevo dimenticato di apportare la correzione 
anche nel file inviato sul news.Anche con gli specificatori adeguati il 
comportamento rimane lo stesso,cambia il risultato,ma è pur sempre 
inattendibile.
Ciao.

 In quali casi?
Vedi il prog del post precedente.Se lo lancio sull'anti-diluviano viene 
eseguito,ma non se uso static all'interno di una funzione.
Se invece uso extern lo accetta anche all'interno della funzione e,sempre 
sull'antidiluviano,mi avverte delle conversioni che riducono la 
dimensione degli oggetti: float a short int.Se invece non uso i prototipi 
lo esegue e fornisce risultati inattendibili.
Probabilmente no avrei dovuto consigliare all'amico di usare i 
protipi,ho scatenato un putiferio,cmq intendevo anche dire di rivedere il 
file che ha inviato prima di considerarlo "finito".
Ciao.

 allusioni non siano talmente tante da rendere preferibile un file header.
Il vantaggio di un prototipo centralizzato in un file header č quello di
annullare il rischio di errori di digitazione dello stesso. Se tale
prototipo viene messo "a mano" nel sorgente tale rischio diventa
importante.
EMG

 semi-antidiluviano(inizio 1991)
Nel 1991 quel tipo di dichiarazioni pre-ANSI erano già abbondantemente
obsolete.
EMG


Diciamo anche che se calcoli il fattoriale ricorsivamente vai fuori stack
anche con un solo dato.
La scelta della ricorsione NON va fatta solo per semplicita' o eleganza. Ci
sono casi (da ponderare, quando si lavora) in cui la ricorsione non e'
proprio applicabile.
Marco

 obsolete.
Non nel mi ocompilatore :-(

 importante.
Bah!Non mi pare.Certo che bisogna scrivere più volte la stessa cosa,se ho 
ben capito qurl che ntendi.
E poi i file header viene propagato in ogni file in cui è inserito.Ed è 
ancor più utile nei lavori di gruppo.
Ciao.

proprio applicabile.
Cosi' come ci sono casi (da ponderare, quando si lavora) in cui essa
e' strettamente necessaria.
Non vorrei ridurre la discussione a ovvieta' di questo genere.

 risiedono nello stesso file.
Intendi dichiararle static da subito?
p.e.
static int func()..
int main(){
...
}
invece di :
int func(int a){
..
}
int main(){
static  int func(int a);
...
}
Pero' cosi perdo la comodita' di sapere "all'istante"le funzioni 
dichiarate nello stesso file semplicemente leggendo la sezione di 
dichiarazione.Altrimenti dovrei "sfogliare" tutto il file soregnte
per sapere quali sono.Sinceramente n nci avevi neanche mai pensato.Ci 
meditero' un po' su.:-)
Ancora:lo scopo delle allusioni con prototipi e di garantire che vengano 
passati il numero e il tipo di argomenti corretti.Inoltre con static 
informo il compilatore che le funzioni in questione sono residenti nello 
stesso file.Sebsono in un altro uso extern.Possibile che mi devi sempre 
incasinare la vita?? :-)))
Ciao/.
 

Non vorrei ridurre la discussione a ovvieta' di questo genere.
Si stava discutendo della scelta, obbligata o meno, opportuna o meno, della
ricorsione al posto dell'iterazione.
Se non credi sia interessante possiamo smettere.
Ma non dire che si tratta di cose ovvie.
Marco
PS: grazie per aver fatto riferimento al K&R ma non era necessario. Ce l'ho
anche io.

anche io.
Una cosa e' averlo e una cosa e' averlo letto. ;)

incasinare la vita?? :-)))
Sinceramente, ritengo che sei tu che te la incasini da solo.
Una definizione del genere di main() mi sembra molto ma molto scomoda.

>Una cosa e' averlo e una cosa e' averlo letto. ;)
Ok, andrea, vedo che non hai modo di discutere in termini tranquilli.
Non credo ci sia bisogno di snocciolare nozioni (!) non richieste o fare
riferimento a libri che, in un ng come questo, dovrebbero essere popolari
come il corriere dello sport. Ti assicuro che non sei il solo ad aver letto
quel libro.
Non ti preoccupare: nessuno vuole mettere in dubbio le tue capacita' di
programmatore.
Non io, almeno: nessuno mi paga per questo.
Saluti,
Marco

Non io, almeno: nessuno mi paga per questo.
Peccato, perche' nell'eventuale improbabile caso che riuscissi nel tuo
intento, avresti una montagna di soldi come Zio Paperone... ;)

un'iterazione.
Se per ricorsione si intende in questi mex "funzione ricorsiva" ovvero funzione
che richiama se stessa, non e` affatto indispensabile (almeno nei limiti delle
mie conoscenze).
_mirko_
--
Knowledge is power.
"ciucciati.il.calzino" is antispam.

intento, avresti una montagna di soldi come Zio Paperone... ;)
Quale intento?
Farti capire che a volte la ricorsione e' meglio non applicarla?
:-)
Marco

mie conoscenze).
Ok, allora implementa la gestione di un albero binario con
un'iterazione e vediamo se ti e' piu' facile gestirla in questo modo.

Era una tranquilla notte di regime, ma Andrea Laforgia disse:

difficile != impossibile


Farti capire che a volte la ricorsione e' meglio non applicarla?
Non proprio, farmi capire che sai in quali casi va applicata...
:-)
^^^ uh ?! che cos'e' ???

difficile != impossibile
Rileggi i thread dall'inizio se vuoi partecipare in modo produttivo a
una discussione, senza avvilirla con la ripetizione di cose gia'
dette.
L'indispensabilita' della soluzione ricorsiva per la trattazione di un
tale problema, deriva dal fatto che esiste si' un'altra strada
possibile, ma da seguire con un tale sforzo di programmazione da
risultare alla fine impraticabile.
Nel caso delle liste esiste una seconda soluzione ed e' quella
iterativa che si applica in modo semplice e conveniente.

Se prima avevo dei dubbi ora li hai fugati del tutto, vedo che insisti col
tuo comportamento da permaloso saputello.
Impara un po' di educazione, oltre che a come funziona il bios, bimbo.
Saluti,
Marco
PS: Ma che hai mangiato la mucca pazza?

risultare alla fine impraticabile.
La soluzione non ricorsiva -- ad ogni modo -- e` solo un po' piu` complicata di
quella ricorsiva (che, nel caso peggiore, puo` essere implementata esplicitando
lo stack). Se vuoi approfondire consiglio Knuth e Ausiello.


>mie conoscenze).
AL>Ok, allora implementa la gestione di un albero binario con
AL>un'iterazione e vediamo se ti e' piu' facile gestirla in questo modo.
Non sei attento ;)
Lui sostiene che le 'funzioni ricorsive' non sono indispensabili, non
che si possono sempre sostituire con un'iterazione.
Per esempio si potrebbe gestire l'albero binario tramite funzioni non 
ricorsive che utilizzino uno stack.
Ciao ciao,
Andrea
-- 
"...Unix, MS-DOS, and Windows NT (also known as the Good, the Bad, and 
 the Ugly)."
(by Matt Welsh)

lo stack).
Se devo esplicitare lo stack, a questo punto lo uso implicitamente ed
utilizzo la ricorsione.

ricorsive che utilizzino uno stack.
Ma a questo punto la convenienza in che cosa consiste ?
Se devo utilizzare funzioni non ricorsive che utilizzano uno stack
diverso da quello implicito, utilizzo funzioni ricorsive che sfruttano
lo stack implicito.
Se si pone il problema dell'overflow, e' chiaro che esso avviene in
entrambi i casi.

PS: Ma che hai mangiato la mucca pazza?
No, pero' ho bevuto la Coca Cola... :)

 utilizzo la ricorsione.
Non voglio entrare nel merito della discussione ma solo fare un piccolo
appunto: lo stack esplicito potrebbe essere allocato in memoria dinamica
e organizzato in modo tale da autoespandersi nel momento del bisogno e
quindi potrebbe non avere i problemi dello stack implicito.
EMG

>ricorsive che utilizzino uno stack.
AL>Ma a questo punto la convenienza in che cosa consiste ?
A questo ti ha gia' risposto EMG.


Ciao Enrico Maria, come va?
Enrico Maria Giordano ha scritto:

  Senza contare che un compilatore "intelligente" potrebbe "srotolare"
le ricorsivita` in iterativita`. ;)
	Alla prossima,
		Marco

 le ricorsivita` in iterativita`. ;)
Si, in teoria forse si. Ma in pratica pensi che sia possibile?
EMG