Il programma non modifica l '"indirizzo di base" dell'array. Non ci sta nemmeno provando.
Quello che si passa a fn è l'indirizzo di un blocco di 256 caratteri in memoria. È numericamente identico al puntatore che decodifica str
in altre espressioni, solo in modo diverso tipizzato. In questo caso, la matrice rimane davvero un array: l'applicazione dell'operatore di indirizzo a un array è una delle istanze in cui un array non decompone a un puntatore. L'incremento &str
, ad esempio, lo aumenterebbe numericamente di 256. Questo è importante per gli array multidimensionali che, come sappiamo, sono in realtà matrici unidimensionali di matrici in C. L'incremento del primo indice di una matrice "bidimensionale" deve anticipare l'indirizzo all'inizio del successivo "blocco" o "riga".
Ora il problema. Per quanto riguarda fn, l'indirizzo che passi punta a una posizione che contiene un altro indirizzo. Quello non è vero; indica una sequenza di caratteri. La stampa della sequenza di byte interpretata come un puntatore rivela i valori del byte 'A', 65 o 0x41.
fn, tuttavia, pensando che la memoria puntata a contenere un indirizzo, la sovrascriva con l'indirizzo in cui "kj" risiede nella memoria. Poiché c'è abbastanza memoria allocata in str per contenere un indirizzo, l'assegnazione ha esito positivo e si traduce in un indirizzo utilizzabile in quella posizione.
Va notato che questo, ovviamente, non è garantito per funzionare. La causa più comune di errore dovrebbe essere problemi di allineamento - str
non è necessario che sia allineato correttamente per un valore puntatore. Lo standard impone che gli argomenti alle funzioni debbano essere compatibili con le dichiarazioni dei parametri. I tipi di puntatori arbitrari non possono essere assegnati l'uno all'altro (è necessario passare attraverso i puntatori void per quello o il cast).
Edit: david.pfx sottolineato che (anche con una vera e propria fusione) il codice invoca comportamento non definito. Lo standard richiede l'accesso agli oggetti tramite lvalue compatibili (compresi i puntatori referenziati) nella sezione 6.5/7 dell'ultima bozza pubblica. Quando si esegue correttamente il cast e si compila con gcc -fstrict-aliasing -Wstrict-aliasing=2 ...
gcc avvisa del "tipo punning". La logica è che il compilatore dovrebbe essere libero di assumere che i puntatori incompatibili non modificano la stessa regione di memoria; qui non è necessario assumere che fn cambi il contenuto di str. Ciò consente al compilatore di ottimizzare i ricaricamenti (ad esempio dalla memoria al registro) che sarebbero altrimenti necessari. Questo giocherà un ruolo con l'ottimizzazione; un probabile esempio in cui una sessione di debug non riuscirebbe a riprodurre l'errore (vale a dire se il programma in fase di debug verrebbe compilato senza ottimizzazione per scopi di debug). Detto questo, sarei sorpreso se un compilatore non ottimizzante producesse risultati inaspettati qui, quindi lascio che il resto della risposta resti valido.-
Ho inserito un numero di debug printfs per illustrare cosa sta succedendo. Un esempio dal vivo può essere visto qui: http://ideone.com/aL407L.
#include<stdio.h>
#include<string.h>
static char* abc = "kj";
// Helper function to print the first bytes a char pointer points to
void printBytes(const char *const caption, const char *const ptr)
{
int i=0;
printf("%s: {", caption);
for(i=0; i<sizeof(char *)-1; ++i)
{
printf("0x%x,", ptr[i]);
}
printf("0x%x ...}\n", ptr[sizeof(char *)-1]);
}
// What exactly does this function do?
void fn(char**s) {
printf("Inside fn: Argument value is %p\n", s);
printBytes("Inside fn: Bytes at address above are", (char *)s);
// This throws. *s is not a valid address.
// printf("contents: ->%s<-\n", *s);
*s = abc;
printf("Inside fn: Bytes at address above after assignment\n");
printBytes(" (should be address of \"kj\")", (char *)s);
// Now *s holds a valid address (that of "kj").
printf("Inside fn: Printing *s as string (should be kj): ->%s<-\n", *s);
}
int main() {
char str[256];
printf("size of ptr: %zu\n", sizeof(void *));
strcpy(str, "AAAAAAAA"); // 9 defined bytes
printf("addr of \"kj\": %p\n", abc);
printf("str addr: %p (%p)\n", &str, str);
printBytes("str contents before fn", str);
printf("------------------------------\n");
// Paramter type does not match! Illegal code
// (6.5.16.1 of the latest public draft; incompatible
// types for assignment).
fn(&str);
printf("------------------------------\n");
printBytes("str contents after fn (i.e. abc -- note byte order!): ", str);
printf("str addr after fn -- still the same! --: %p (%p)\n", &str, str);
return 0;
}
per i posteri, [qui] (http://stackoverflow.com/questions/25660493/assegnazione-per-a-char-array-using-assignment-operator-and-double-pointers) è la domanda cancellata –