Risolto [c] sottrazione di puntatori

Stato
Discussione chiusa ad ulteriori risposte.

Cingetorge

Utente Iron
22 Gennaio 2023
31
5
2
12
salve a tutti,
stavo considerando il seguente listato:
C:
#include <stdio.h>
char str[] = "BATUFFO";
int strlen(char *); //Prototipo della funzione.
main()
{
printf(" srt=%s \n", str);
printf("la stringa %s è lunga %d\n", str, strlen( str ));
}
int strlen( char *p )
{
printf( " p e: %c \n", *p );

char *q = p;
printf( "dopo = q e: %c \n", *q );
printf( "dopo = p e: %c \n", *p );

while ( *q++);
printf( "il valore di q e: %c \n", *q );
printf( "il valore di p e: %c\n", *p );

return (q-p-1);
}
il quale mi genera a video il seguente outpus:
p e: . dopo = q e: . dopo = p e: . il valore di q e: il valore di p e: . p e: C dopo = q e: C dopo = p e: C il valore di q e: il valore di p e: C srt=BATUFFO p e: B dopo = q e: B dopo = p e: B il valore di q e: il valore di p e: B la stringa BATUFFO Þ lunga 7

Domande:
- seguendo l'ordine delle istruzioni nella funzione main mi sarei aspettato che la prima cosa che il programma avrebbe fatto, sarebbe quello di stamparmi il valore di str e solo sucessivamente eseguire la funzione strlen ;
- chi mi spiega la logica dell'espressione (q-p-1) che usa la sottrazione di puntatori?
grazie
 
Ultima modifica:
seguendo l'ordine delle istruzioni nella funzione main mi sarei aspettato che la prima cosa che il programma avrebbe fatto, sarebbe quello di stamparmi il valore di str e solo sucessivamente eseguire la funzione strlen ;
Mi sembra molto ragionevole ed è quello che succede a me. Compilando ed eseguendo il tuo programma l'output che ottengo è questo:
Codice:
 srt=BATUFFO
 p e: B
dopo = q e: B
dopo = p e: B
il valore di q e:
il valore di p e: B
la stringa BATUFFO è lunga 7
Sono sicuro che se ricompili e riesegui il codice che hai postato (eseguendolo una volta sola) l'output che vedi sarà identico a quello che ho postato.

chi mi spiega la logica dell'espressione (q-p-1) che usa la sottrazione di puntatori?
Quando chiami strlen(str), prima di eseguire il ciclo while, ti trovi in questa situazione con p = q che puntano a 'B' (i.e., *p = *q = 'B')
Codice:
| q-1 | q   | q+1 | q+2 | q+3 | q+4 | q+5 | q+6 | q+7 | q+8 | q+9 |
| p-1 | p   | p+1 | p+2 | p+3 | p+4 | p+5 | p+6 | p+7 | p+8 | p+9 |
|-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----|
| ... | B   | A   | T   | U   | F   | F   | O   | \0  | ... | ... |
Il while è un po' intricato. Ti spiego cosa fa concettualmente per non farti perdere il filo del discorso, poi entrerò nel dettaglio. Il significato del while è: incrementa q finché *q è diverso da zero (ASCII '\0') e dopo aver fatto questo controllo incrementalo un altra volta e termina il while.
Codice:
| q-9 | q-8 | q-7 | q-6 | q-5 | q-4 | q-3 | q-2 | q-1 | q   | q+1 |
| p-1 | p   | p+1 | p+2 | p+3 | p+4 | p+5 | p+6 | p+7 | p+8 | p+9 |
|-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----|
| ... | B   | A   | T   | U   | F   | F   | O   | \0  | ... | ... |
Alla fine restituisci $$\(q - p - 1 = q - (q-8) - 1 = (p+8) - p - 1 = 8 - 1 = 7$$\), che è la lunghezza della stringa.

Adesso torniamo al while:
  • l'espressione q++ vuol dire "incrementa q e poi restituisci il sul valore prima dell'incremento";
  • l'espressione while(*q) vuol dire "esegui il while finché q non punta a 0" (in ASCII '\0');
  • l'espressione *q++ vuol dire "incrementa q e poi vai all'area di memoria che aveva prima dell'incremento";
Quindi con while(*q++); stai dicendo "incrementa q e poi controlla se il valore a cui puntava prima dell'incremento è zero; se è zero esci dal ciclo, altrimenti continua".

Se fai fatica a capire il postincremento, ti lascio questo snippet:
C:
#include <stdio.h>
int main() {
    int x = 0;
    printf("x = %d\n", x++); // stampa x = 0 (il valore prima dell'incremento)
    printf("x = %d\n", x);   // stampa x = 1 (la variabile è stata effettivamente incrementata)
    return 0;
}
 
  • Mi piace
Reazioni: fennek e nfvblog
ciao St3ve,
grazie per la risposta. Ora tutto più chiaro tranne che per la prima domanda: pur avendo riscritto e ricompilato il listato, continuo ad avere lo stesso visualizzazione.
Con il primo il comando "printf( "il valore di q e: %c \n", *q );" di while non avrei dovuto avere visualizzato il valore puntato da q per ogni suo incremento?
grazie
 
Ora tutto più chiaro tranne che per la prima domanda: pur avendo riscritto e ricompilato il listato, continuo ad avere lo stesso visualizzazione.
Il tuo codice non è perfetto, ma se ottieni un output diverso da quello che ho postato è perché evidentemente stai sbagliando ad utilizzare il tuo IDE. Tra le discussioni in rilievo ci sono le mie FAQ dove, tra le altre cose, do consigli su quali IDE e compilatori utilizzare e spiego come compilare un codice sorgente da linea di comando. Ti consiglio di leggere tutto il thread perché sono solo due pagine e ci sono considerazioni interessanti anche nei commenti. Prova a scaricare gcc o clang e compila da linea di comando. Se usi windows, trovi vari links nella seconda pagina del thread.

Con il primo il comando "printf( "il valore di q e: %c \n", *q );" di while non avrei dovuto avere visualizzato il valore puntato da q per ogni suo incremento?
No, perché è fuori dal ciclo while. Il body del tuo ciclo while è completamente vuoto perché hai messo il punto e virgola invece delle graffe.
C:
#include <stdio.h>

char str[] = "BATUFFO";

// strlen è il nome di una funzione standard, diamogli un altro nome
int mystrlen(char *p);

int main() {
    printf("str = %s\n\n", str);
    printf("\nLa stringa \"%s\" è lunga %d\n", str, mystrlen(str));
    return 0;
}

int mystrlen(char *p) {
    char *q = p;
    printf("prima del while: p = %p, *p = '%c', q = %p, *q = '%c', q - p = %ld\n", p, *p, q, *q, q - p);

    while (*q++) {
        printf("durante il while: p = %p, *p = '%c', q = %p, *q = '%c', q - p = %ld\n", p, *p, q, *q, q - p);
    }

    printf("dopo il while: p = %p, *p = '%c', q = %p, *q = '%c', q - p = %ld\n", p, *p, q, *q, q - p);
    return q - p - 1;
}
Codice:
str = BATUFFO

prima del while: p = 0x404010, *p = 'B', q = 0x404010, *q = 'B', q - p = 0
durante il while: p = 0x404010, *p = 'B', q = 0x404011, *q = 'A', q - p = 1
durante il while: p = 0x404010, *p = 'B', q = 0x404012, *q = 'T', q - p = 2
durante il while: p = 0x404010, *p = 'B', q = 0x404013, *q = 'U', q - p = 3
durante il while: p = 0x404010, *p = 'B', q = 0x404014, *q = 'F', q - p = 4
durante il while: p = 0x404010, *p = 'B', q = 0x404015, *q = 'F', q - p = 5
durante il while: p = 0x404010, *p = 'B', q = 0x404016, *q = 'O', q - p = 6
durante il while: p = 0x404010, *p = 'B', q = 0x404017, *q = '', q - p = 7
dopo il while: p = 0x404010, *p = 'B', q = 0x404018, *q = '', q - p = 8         <--- WARNING: con *q sono fuori da str, il programma potrebbe crashare

La stringa "BATUFFO" è lunga 7
 
Il tuo codice non è perfetto,
in che cosa si potrebbe migliorare?
ma se ottieni un output diverso da quello che ho postato è perché evidentemente stai sbagliando ad utilizzare il tuo IDE
io sto usando il code::blocks.
Ho provato ha installare il MinGW-w64 come suggerito nel tuo post, anche se è lo stesso usato dall'IDE da me utilizzato, e dopo aver sistemato qualche warning inerente ai commenti e al nome della funzione tutto finalmente è filato come mi aspettavo dall'inizio.
 
in che cosa si potrebbe migliorare?
I problemi principali sono puramente stilistici:
  • la funzione main dev'essere definita come int main(), int main(void), oppure int main(int argc, char *argv[]), la sintassi senza return type era valida nello standard del 1989 ed è diventata invalida dal 1999 in poi;
  • la funzione main deve terminare con return 0;, return EXIT_SUCCESS; o comunque deve restituire un intero, il return implicito è diventato valido nello standard del 1999 ma è buona norma renderlo esplicito;
  • strlen è il nome di una funzione standard e in quanto tale è un nome riservato che tecnicamente (secondo lo standard) non ti è concesso ridefinire.
Cito questi tre perché spesso è veramente solo questione di disimparare cattive abitudini.

Ho provato ha installare il MinGW-w64 come suggerito nel tuo post, anche se è lo stesso usato dall'IDE da me utilizzato
Secondo me il problema principale è che eseguivi il programma all'interno di code blocks. Gli IDE servono per programmare, poi per eseguire il codice ti conviene sempre aprire il terminale o la powershell ed eseguire il programma da li. Un altro errore comune è non accorgersi di avere un errore nel codice (che fa fallire la compilazione) e continuare ad eseguire il programma vecchio. In ogni caso, imparare a compilare da linea di comando ti sarà sicuramente utile. Magari continuerai ad usare l'IDE per il 90% del tempo, ma nel momento in cui avrai output che non riesci a spiegarti ti sarà più facile capire cosa sta succedendo facendo tutto manualmente. Poi una volta che ti fai un po' le ossa sceglierai tu la tua strada.

PS. Il link per scaricare mingw-w64 spero tu l'abbia preso dal thread che ti ho linkato, se l'hai cercato su google sono quasi sicuro che tu abbia installato una versione obsoleta.
 
  • Mi piace
Reazioni: --- Ra ---
Secondo me il problema principale è che eseguivi il programma all'interno di code blocks.
avevo provato ad eseguire il file exe anche da shell. Il risultato non cambiava
Un altro errore comune è non accorgersi di avere un errore nel codice (che fa fallire la compilazione) e continuare ad eseguire il programma vecchio.
Se dovesse fallire la compilazione il compilatore, che sia "sotto" l'IDE o meno dovrebbe comunque dare errore?
PS. Il link per scaricare mingw-w64 spero tu l'abbia preso dal thread che ti ho linkato, se l'hai cercato su google sono quasi sicuro che tu abbia installato una versione obsoleta.
ho scaricato dalla pagina da te indicata. Tra le opzioni di scelta ve ne erano un paio. La scelta, per quanto casuale, si è rivelata corretta. (almeno per il momento ! ;-) )
 
avevo provato ad eseguire il file exe anche da shell. Il risultato non cambiava
Allora probabilmente stavi facendo partire un eseguibile vecchio perché la compilazione falliva o per qualche problema simile. Non posso dirti con certezza qual'era il problema, ma di sicuro l'output che vedevi non rispecchiava il codice sorgente che hai postato. Hai detto bene che la prima cosa che dovevi vedere è il valore di str.

Se dovesse fallire la compilazione il compilatore, che sia "sotto" l'IDE o meno dovrebbe comunque dare errore?
Presumibilmente sì, anche se ogni IDE è libero di fare come crede. Magari sei tu che non hai notato l'errore, magari è l'IDE che non te l'ha presentato in modo abbastanza chiaro, magari era il sorgente che non era stato ancora salvato, magari stava compilando un codice sorgente diverso da quello che ti aspettavi (eg., se hai creato un altro file) o forse c'era qualche altro problema. Posso solo tirare a indovinare. Quando vedi questo tipo di comportamenti strani, prova a cancellare l'eseguibile o compilare da linea di comando. Quando fai tutto a mano riesci ad avere più cose sotto controllo.
 
Stato
Discussione chiusa ad ulteriori risposte.