PDA

Visualizza la versione completa : [C] Calcolatrice polacca da riga di comando


tigerjack89
08-02-2012, 17:55
Un saluto a tutti i programmatori (o agli aspiranti tali :) )
Sto provando ad orientarmi tra i complicati meandri dell'uso di puntatori. In questo momento sto provando a realizzare una calcolatrice da riga di comando in notazione polacca.

Facendo delle prove generiche per provare il codice volta per volta, ottengo un errore di segmentation fault se provo ad eseguire questo codice



int main(int argc, char *argv[])
{
while (--argc > 0)
{
if ( isdigit(*++argv))
printf("numero %f\n", (double) *argv);
}
}


Inoltre, poichè la calcolatrice polacca dovrebbe essere gestita come una pila, ho provato a scrivere queste due funzioni push e pop in questo modo.


#define MAX_OP 100
double stack[MAX_OP];
double *p = stack;

void push(double n)
{
*p++ = n;
}

double pop(void)
{
return *--p;
}


Sembra funzionare bene dopo svariate prove, ma volevo capire se è possibile migliorarlo in qualche modo.

Grazie mille a tutti per l'attenzione :)

ramy89
08-02-2012, 18:55
Prima cosa: non ti consiglio di cambiare argc perchè poi perdi traccia del numero di elementi.Nel tuo caso va bene, ma se poi la calcolatrice la vuoi estendere per fargli fare cose più complesse, perdi traccia di argc (usa una variabile di appoggio).
Poi (double)*argv ha come effetto quello di convertire il valore ascii del primo carattere della stringa.
Usa la atof al posto del cast esplicito (la trovi sempre su c++ reference).
Ma il perchè del segmentation fault mi sfugge, forse faresti meglio a gestire il tutto con un for:


for(int i=0;i<argc;i++)
// gestisci argv[i]

tigerjack89
08-02-2012, 19:02
Originariamente inviato da ramy89
....
Prima di tutto grazie per i consigli, sempre ottimi spunti :)
Per quanto riguarda il double, in effetti mi ero accorto che avevo scritto qualcosa che non era proprio ben chiaro. Il problema è che anche provando a dare qualcosa come


int main(int argc, char *argv[])
{
n = argc;
while (--n > 0)
{
if ( isdigit(*++argv))
printf("E' un numero\n");
}
}

Mentre in fase di compilazione non ho errori, appena eseguo il programma mi esce un errore di segmentazione :S


Originariamente inviato da ramy89
forse faresti meglio a gestire il tutto con un for:


for(int i=0;i<argc;i++)
// gestisci argv[i]

Il fatto è che volevo sbattere la testa il più possibile sui puntatori per capirli per bene, non mi piace conoscere le cose solo per sommi capi.

PS: dici che si può postare il codice intero della calcolatrice? Non è molto lungo e forse potrei cavarne buoni consigli.

ramy89
08-02-2012, 19:14
Originariamente inviato da tigerjack89

PS: dici che si può postare il codice intero della calcolatrice? Non è molto lungo e forse potrei cavarne buoni consigli.

Certo, anzi meglio se posti sempre il codice intero così si capisce meglio.

tigerjack89
08-02-2012, 19:20
Ci sto ancora lavorando, quindi potrebbero esserci degli errori grossolani, a parte ovviamente il segmentation fault del main a cui ora provo a porre rimedio.
Grazie mille per l'aiuto :)


#include <stdio.h>

void push(double);
double pop(void);
int isoperator(char *);
void operation(void);

int main(int argc, char *argv[])
{
double value;
int n = argc;
int i = 0;
double op2 = 0.0;
while (--n > 0)
{
if ( isdigit(*argv))
push(atof(*argv));

else if( isoperator(*argv) )
{
switch ( (*argv)[0])
{
case '+':
push (pop() + pop() );
break;

case '*':
push (pop() * pop() );
break;

case '-':
op2 = pop();
push(pop() - op2);
break;

case '/':
op2 = pop();
push(pop() - op2);
break;

default:
printf("Undefined operations\n");
break;
};
}
else
printf("Stringa non valida\n");
}
argv++;
}

#define MAX_OP 100
double stack[MAX_OP];
double *p = stack;
int stackp = 0;

void push(double n)
{
*p++ = n;
}

double pop(void)
{
return *--p;
}

int isoperator(char *s)
{
if (*s == '+' || *s == '-' || *s == '*' || *s == '/')
if (*++s == '\0')
return 1;
return 0;
}

void operation(void)
{
//Vorrei delegare a questa funzione la gestione delle operazioni, ma per il
//momento non so ancora come fare
}

tigerjack89
08-02-2012, 19:32
Il problema principale è riuscire a gestire le stringhe di caratteri memorizzate in char *argv[].
Infatti, usando *argv[], dovrebbe restituirmi la stringa, che poi posso usare o passare a una funzione, ma se mi da Errore di segmentazione non so in che altro modo passare direttamente la stringa puntata :S

ramy89
08-02-2012, 19:49
Nella pop:



double pop(void)
{
return *--p;
}


Se p punta all' ultimo elemento inserito, con un pre-decremento restituisci il penultimo inserito, non l' ultimo.
Meglio fare un post-decremento:


double pop(void)
{
return *p--;
}


Sai la riga esatta dove avviene il segmentation fault?
E altra cosa: gli argomenti possono anche essere tantissimi? Cioè si può digitare 2+4*6+7 ? Perchè in questo caso devi dare la precedenza agli operatori.

tigerjack89
08-02-2012, 20:06
Originariamente inviato da ramy89
Nella pop:
Se p punta all' ultimo elemento inserito, con un pre-decremento restituisci il penultimo inserito, non l' ultimo.
Meglio fare un post-decremento:

La pop, collaudata, dovrebbe essere giusta, in quanto dovrebbe essere chiamata solo in caso sia già stato invocato push(). Quando invochi push, prima copi il valore nella posizione corrente e poi incrementi la posizione; per questo la posizione dell'ultimo valore è quella precedente a quella puntata attualmente.
Che poi ci dovrebbe essere qualcosa per controllare gli errori e le eccezioni, beh su quello hai ragione, ma ci lavorerò in seguito, appena mi riesce di finire il programma per sommi capi :)


Sai la riga esatta dove avviene il segmentation fault?
E altra cosa: gli argomenti possono anche essere tantissimi? Cioè si può digitare 2+4*6+7 ? Perchè in questo caso devi dare la precedenza agli operatori.
Dovrebbe essere in questa parte di codice

if ( isdigit(*argv))
push(atof(*argv));
Riguardando meglio, penso che la funzione isdigit si aspetta un carattere e non una stringa.

Per quanto riguarda gli argomenti, si, possono essere massimo 100, come nella definizione di MAX_OP; non so come gestire argomenti infiniti poi per usarli nelle funzioni push e pop.
Ad ogni modo, la calcolatrice polacca è leggermente diversa da quella tradizionale.
Se scrivi qualcosa del tipo 2 3 4 + * dovrebbe darti (4 + 3) * 2.
In pratica mette i valori in un pila e li estrai in ordine inverso; estrae il primo, prende l'operatore e fa (primo estratto (operatore) 2)) e lo memorizza di nuovo nella pila, per poi continuare. Un po' grossolana come spiegazione, ma spero si capisca :D

ramy89
08-02-2012, 20:13
Prova a cambiare l' argomento della isdigit con *argv[0] , però tieni presente che così stai controllando un solo carattere: il primo.
Faresti meglio a usare la atoi.

tigerjack89
08-02-2012, 21:01
Allora, ho modificato dopo vari sbattimenti il main in questo modo

EDIT: FUNZIONA !! :unz:

int main(int argc, char *argv[])
{
int value;
int n = argc;
int i;
int op2 = 0;

while (--n > 0)
{
argv++;
i = 0;
while ( isdigit( (*argv)[i++]))
;
/*
for (i = 0; (*argv)[i]; i++)
printf("i = %d \tc=%c\n", i, (*argv)[i]);
printf ("i = %d e argv[i] = %d\n", i, (*argv)[i]);
*/
if (i > 1 && (*argv)[i-1] == '\0')
{
value = atoi(*argv);
push(value);
printf("value is %d\n", value);
}
else if( isoperator(*argv) )
{
switch ( (*argv)[0])
{
case '+':
push (pop() + pop() );
break;

case '*':
push (pop() * pop() );
break;

case '-':
op2 = pop();
push(pop() - op2);
break;

case '/':
op2 = pop();
push(pop() - op2);
break;

default:
printf("Undefined operations\n");
break;
};
}
else
printf("Stringa non valida\n");
//

//
}

printf("%d\n", pop());
}

Loading