Newsgroup: it.comp.lang.c
Titolo: Funzione strchr() dà problemi...

Anche una semplice funzione come strchr() crea problemi a me
incomprensibili! Un semplice programma che legge la prima riga da un
file.txt e cerca la prima occorrenza di un dato carattere:
loc = strchr(buf, ch)
dove: char *loc, buf[512] , int ch = 'p'   (buf contiene la riga letta
dal file)
insomma da manuale!
Eppure il prog non funzia correttamente perchè "loc"  ritorna come
risultato sempre 1 !! qualsiasi sia il carattere da cercare.
Qualcuno sà dirmi che cavolo possa essere??!
Nel caso servisse a chiarire meglio le idee vi copio il piccolo source.
Grazie mille!
int main(int argc, char **argv)
{
 char *loc, buf[512], buf2[512];
 FILE *f;
 int  ch = 'p';
if (argc != 2) {
    printf("\n Usage: %s <filename>\n", argv[0]);
    exit(-1);
    }
if ((f = fopen(argv[1], "r")) == NULL) {
    puts("error opening file");
    exit(1);
    }
if (fgets(buf, 512, f) == NULL)   {
        puts("error read file");
        exit(1);
        }
if (loc = strchr(buf, ch) != NULL) {
        printf("\nvalore di loc = %d e ch = %d\n", loc, ch);
        strncpy(buf2, buf, loc - buf);
        printf("\nrisultato: %s\n", buf2);
        }
 fclose(f);
 return 0;
}

 if (loc = strchr(buf, ch) != NULL) {
Trabocchetto C numero 237: priorita` diverse da quelle che ti
aspetti.  Questa espressione confronta il risultato di strchr con
NULL, ottenendo un intero 0 o 1, e _poi_ questo intero lo
assegna a loc.  Scrivi invece:
    if ( (loc = strchr(buf, ch)) != NULL) {
Incidentalmente, il compilatore dovrebbe strillare come un matto
quando assegni un intero a un puntatore; metti al massimo il
livello di warning, e tratta i warning come errori, e dovrebbe
se non altro segnalarti una maggiore percentuale dei problemi.
Ad esempio, se usi gcc o un suo derivato,
    -Wall -Werror -pedantic
sono opzioni che dovresti avere *SEMPRE*, a mio giudizio.
Alex

 if (loc = strchr(buf, ch) != NULL) {
Devi mettere le parentesi in questo modo:
if ((loc = strchr(buf, ch)) != NULL) {
EMG

 livello di warning, e tratta i warning come errori,
Con il Borland C++ 5.5 lo switch equivalente al -Werror del gcc è -gb.
EMG

 aspetti.  <snip>
Il motivo di quello che succede, giustamente detto da Alex, e' perche
gli operatori hanno un ordine di precedenza ed una associativita'; se
andiamo a vedere quelli usati nella if, troviamo i seguenti:
() = != che sono in questo ordine (dal piu' alto al piu' basso):
() l-r
!= l-r
=  r-l
Quindi lo statement in oggetto viene eseguito/valutato in questo modo
(uso la notazione a regole di inferenza):
(loc = strchr(buf, ch) != NULL)
------------------------------- ()
loc = strchr(buf, ch) != NULL
------------------------------- precedenza di !=
      strchr(buf, ch) != NULL
      -----------------------   semantica di != 
loc =            A, dove A in {vero (!=0), falso (0)}
------------------ =
A
Da qui vedi immediatamente cosa assegni a loc, e perche'.
Nota: strchr(buf, ch) non e' una operazione atomica, anch'essa andrebbe
'spezzata', essendo poi il != associativo l-r, viene valutato prima
strchr(buf, ch), poi NULL, e quindi si ottiene il risultato A.
Se ricordo bene, nel K&R c'e' una tabellina di precedenza e
associativita', molto utile per evitare errori come quello sopra.
Anche se, in un contesto di produzione software aziendale, e' molto
meglio abbondare (o ridondare ?!) con le parentesi, almeno si capisce
'al volo' cosa succede nel codice.
Altra piccola nota: nel caso detto, !=0 e' il valore 1, ma altre
implementazioni possono fornire valori differenti, lo standard dice
solo che e' un numero diverso da zero.
Andrea
 ('< ----------------+-------------------+-----------------
 /V\   Andrea        | arimicci @@       | Inet Abuse Dept.
<(_)        Rimicci  |      aritaly. com |     AR Italy
--~~-+---------------+-------------------+-----------------
     |     Phone & Fax 
+39-019-489197
            /__ /\
_____#__ Male, 33, Italian, C programmer________](__=_`)apx

 solo che e' un numero diverso da zero.
Ah si`?  Ma e` un'innovazione C99?  Nel C90 gli operatori relazionali
erano definiti come vincolati a tornare 0 o 1.
Alex

 solo che e' un numero diverso da zero.
grazie a tutti!
capito chiaramente il prob  :)

 =  r-l
mi và di sfiga come al solito allora. Non funzia nemmeno così !!  :(
questa volta *loc mi ritorna un valore assurdo :
loc = -1073743923
eppure ho tolto anche l'operatore !!
rieccovi il source:
int main(int argc, char **argv)
{
 char *loc, buf[512], buf2[512];
 FILE *f;
 int  ch = 'p';
if (argc != 2) {
    printf("\n Usage: %s <filename>\n", argv[0]);
    exit(-1);
    }
if ((f = fopen(argv[1], "r")) == NULL) {
    puts("error opening file");
    exit(1);
    }
if (fgets(buf, 512, f) == NULL)   {
	puts("error read file");
	exit(1);
	}
loc = strchr(buf, ch);
printf("\nvalore di loc = %d e ch = %d\n", loc, ch);
strncpy(buf2, buf, loc - buf);	
printf("\nrisultato: %s\n", buf2);
 fclose(f);
 return 0;
}
che cavolo può essere stavolta!??
grazie ancora raga!
bye
-- 
"That vulnerability is completely theoretical."
                                        -- Microsoft

 che cavolo può essere stavolta!??
Forse intendevi stampare loc-buf?  Quello si` che e`
un numero (se e solo se loc!=0).
Alex

printf("\nvalore di loc = %d e ch = %d\n", loc, ch);
Il printf() che hai scritto non stampa *loc (il contenuto della cella
puntata) ma loc, cioè il puntatore medesimo (l'indirizzo di memoria).
Se ti stai chiedendo perché un indirizzo di memoria sia negativo, la
risposta è che il %d che hai messo dice al printf() di interpretare il
valore come un signed, ma questo non ha senso. Se proprio vuoi
visualizzare un puntatore, usa %u (unsigned) o %x (esadecimale).

ok, finalmente il tutto funziona.. ma non è finita  :(
Il mio intento era quello di leggere tutto ciò che è scritto su un file
di testo con fgets() e con la funzione strchr() individuare gli spazi
vuoti per poi (in output) mandare a capo ogni stringa.
Però c'è un problema:  come faccio a memorizzare ogni volta le varie
stringhe di una riga in un buf[] ??
Purtroppo riesco a mandare a capo solo la PRIMA stringa di caratteri di
OGNI riga! salta subito alla riga successiva senza controllare le altre
stringhe che fanno parte della riga stessa.
Dovrei creare un while() ma non sò come gestirlo..
Questa è la parte di source interessata:
while (!feof(f)) {		/* legge ogni riga del file  */
    if (fgets(buf, 500, f) == NULL)   {
        exit(1);
        if ((loc = strchr(buf, ch)) != NULL) {
         num = loc - buf;
         strncpy(buf2, buf, num );
         buf2[num] = '\0';
          if ((fprintf(f2, "%s\n", buf2)) == NULL) {
             printf("\nerror create file\n");
             exit(1);
              }
      }
 }
grazie mille per l'aiuto!


 vuoti per poi (in output) mandare a capo ogni stringa.
Potresti provare con qualcosa del genere, considerando anche
il TAB come 'spazio':
        ...
	while (fgets(buffer, sizeof buffer, in)) {
		char *c = buffer;
		while ((c = strpbrk(c, " \t"))) {
			do
				c++;
			while ((*c == ' ') || (*c == '\t'));
			*(c-1) = '\n';
		}	
		fputs(buffer, out);
	}
        ...
Ciao ciao,
Andrea
-- 
"...Unix, MS-DOS, and Windows NT (also known as the Good, the Bad, and 
 the Ugly)."
(by Matt Welsh)