Newsgroup: it.comp.lang.c
Titolo: incremento di troppo

Ciao a tutti. Sto scrivendo una funzione che dovrebbe controllare due cose: 
se quello che inserisco in una stringa č un numero e se č un intero. In caso 
positivo voglio convertirlo da carattere in numero con atoi. Per farlo 
pensavo di controllare con isdigit() se il carattere č una cifra tra 0 e 9 
(in modo da escludere anche la virgola o il punto,e cosě verificare se si 
tratta di un intero). Ho usato un ciclo while, e come condizioni il 
risultato di isdigit() e il valore dell'indice con cui identifico le 
posizioni della stringa. Solo che l'indice viene incrementato una volta di 
troppo. Esempio se inserisco 1000 l'indice 'i' dovrebbe essere incrementato 
fino a 3, e invece sale fino a 4. Chi riesce a trovarmi l'errore? Sto 
diventando matto! Grazie in anticipo.
#include<stdio.h>
#include<ctype.h>
#include<stdlib.h>
int controllo_valore(char stringa[]);
main()
{
 int pezzi_giornalieri,numero_scatole,pezzi_avanzati;
 char numero_pezzi[20];
  do
  {
   printf("\n\n");
   printf("numero pezzi prodotti?: ");
   gets(numero_pezzi);
   pezzi_giornalieri=controllo_valore(numero_pezzi);
    printf("\n\npezzi giornalieri: %d",pezzi_giornalieri);
  }
  while(pezzi_giornalieri<=0);
  numero_scatole=pezzi_giornalieri/32;
  pezzi_avanzati=pezzi_giornalieri%32;
  printf("\n\npezzi prodotti: %d",pezzi_giornalieri);
  printf("\n\nscatole necessarie: %d",numero_scatole);
  printf("\n\npezzi non spediti: %d",pezzi_avanzati);
  getchar();
} //chiusura main
int controllo_valore(char stringa[])
{
 int i=0;
 int lunghezza=(strlen(stringa));
 printf("\n\nlunghezza stringa: %d",lunghezza);
 while(isdigit(stringa[i])&& i<lunghezza) //perche' "i" viene incrementata 
fino a "lunghezza"??
 {
  i++;
 };
 printf("\n\nindice i=%d",i);
 if(i<lunghezza-1) return 0;
 else if(i==lunghezza)
 {
  int pezzi=atoi(stringa);
  return pezzi;
 }
};
 

 };
o più semplicemente:
int controllo_valore(const char *stringa) {
   const char *p;
   for (p=stringa;isdigit(*p);p++);
   if (*p)
      return 0;
   return atoi(stringa);
}
E.

 sei "fuori" dalla stringa.
Ho capito, grazie. Spesso gli errori li abbiamo davanti agli occhi ma non li 
vediamo! Ecco la versione corretta:
int controllo_valore(char stringa[])
{
 int i=0;
 int lunghezza=(strlen(stringa));
 while(isdigit(stringa[i])&& i<lunghezza)
 {
  i++;
 };
 if(i<lunghezza) return 0;


 }
Non capisco bene: dichiaro un puntantore a char e passo la stringa per 
riferimento, giusto? Questa assegnazione però mi sembra strana:
for (p=stringa;isdigit(*p);p++);
eventualmente non dovrebbe essere p&stringa? Assegno al puntatore 
l'indirizzo della stringa? E' un operazione valida p++? Incrementare un 
puntatore? Scusa, ma sono alle prime armi con i puntatori.

 puntatore? Scusa, ma sono alle prime armi con i puntatori.
Mi pare che come l'ho scritto sia giusto (ma non l'ho testato). Le 
variabili p e stringa sono dello stesso tipo, dunque l'assegnamento 
p=&stringa non sarebbe accettato.
Per capire il funzionamento dei puntatori ti posso dire che il codice 
precedente è equivalente a questo:
int controllo_valore(const char *stringa) {
    int i;
    for (i=0;isdigit(p[i]);i++);
    if (p[i])
       return 0;
    return atoi(stringa);
  }
Le semplificazioni rispetto al tuo codice si devono a queste osservazioni:
1. le stringhe in stile C sono terminate dal carattere 0
2. il carattere 0 non è un digit (non si confonda 0 con '0').
3. il ciclo for esce quando p[i] non è un digit (in particolare se si 
raggiunge la fine della stringa)
4. il test "if (p[i])" passa quando il primo carattere "non digit" non è 
il terminatore della stringa. In caso contrario significa che tutti i 
caratteri della stringa sono digit (tranne ovviamente il terminatore).
E.

sia equivalente.
Lo è. Nei passaggi di parametri a funzione, la notazione non implica
differenze: un array decade sempre in puntatore al primo elemento.

  while(isdigit(stringa[i])&& i<lunghezza)
No. Questo è ancora rischioso. Quando arrivi a 
i == strlen(stringa)
fai un accesso fuori dall'array *prima* di fermarti con il controllo i <
lunghezza. La faccenda funziona *esclusivamente* perchè le stringhe in C
sono terminate da '\0'. Quindi *di fatto* esci già a isdigit e non hai
bisogno di usare i < lunghezza.
Guarda come ti modifico la condizione del while:
 while(isdigit(stringa[i]) && (assert(i<lunghezza), i < lunghezza))
Come vedi, ora fa un'asserzione su i < lunghezza. Se la condizione viene
incontrata, il programma termina violentemente, eppure non succede
proprio nulla di tutto questo. Il motivo è semplice: la condizione viene
già falsificata quando isdigit('\0') torna 0, come ti ha già spiegato
manu?. 
NB! Io *NON* consiglio di scrivere il codice come lo *ho* scritto. È
solo una modifica per farti notare una cosa, non è *assolutamente* una
cosa da fare.
Torniamo a noi... la tentazione sarebbe quindi quella di togliere il
check a i < lunghezza. Di fatto, lavorando su stringhe C ben formate la
semantica è la stessa. In effetti la semantica è la stessa anche se
passi una stringa C *non* ben formata. E si chiama undefined-behaviour.
Cosa che vorresti evitare.
Per cui la soluzione più semplice è *girare* le condizioni.
 i < lunghezza && isdigit(stringa[i])
La cosa è generica. Prima di fare un accesso di quel tipo fai il
controllo di essere nei limiti.
Poi, a margine, faccio notare che la tua funzione non è utilissima. Per
esempio trasforma una stringa che rappresenti *esattamente* un intero in
un intero. Questo vuole dire che non *recuperi* un intero da una parte
iniziale di una stringa che contiene un intero. La stringa deve essere
*esattamente* un intero.
Di fatto già che fai il loop, sei più efficiente (e impari qualcosa),
calcolando tu l'intero di ritorno invece di usare atoi. Hai tutti i
pezzi pronti!
Come ultima nota, se proprio non vuoi scriverti tu il codice, invece di
atoi puoi usare strtol. È un po' più flessibile: oltre a supportare basi
diverse, usa anche un parametro char** endptr che mette in *endptr
l'indirizzo del primo carattere non valido.
In questo modo hai tutto il controllo che vuoi sul problema.
-- 
-riko

  i < lunghezza && isdigit(stringa[i])
è inutile, visto che lunghezza è stata ottenuta con strlen, e quindi se 
la stringa non era terminata da 0, già strlen avrebbe fatto un accesso 
oltre i limiti dell'array.
E.

 oltre i limiti dell'array.
Vero. E infatti io non ci avrei messo strlen (di fatto mi sono scordato
di scriverlo) ma ci avrei passato un parametro aggiuntivo che mi dice il
massimo della lunghezza.
Oltretutto li stavo parlando più in generale, ovvero sulla buona pratica
di fare in modo di assicurarsi che gli accessi in memoria siano savi.
-- 
-riko

 E.
Domanda (non ho voglia di provare): se dichiari *p come const poi si puo' 
assegnare e incrementare?
Ciao Jack
-- 
Eroi non si nasce, ti incastrano
- Jim Belushi

 assegnare e incrementare?
Sì, perché
	const char *p;
non significa che p è costante ma che è un puntatore ad un carattere 
costante. Puoi modificare p, ma non quello che punta.
E.

 costante. Puoi modificare p, ma non quello che punta.
ah ok.
quindi si puo' fare p++, ma non *p = 4.
