Variabili, puntatori, oggetti e indirizzi di memoria: perché ottengo questo strano risultato?
-
03-07-2019 - |
Domanda
Dopo aver pensato di aver capito come funzionano, ho provato questo:
NSString *str1 = [NSString stringWithCString:"one"];
NSString *str2 = [NSString stringWithCString:"two"];
NSLog(@"str1: %x, %@", &str1, str1); //bfffd3cc, one
NSLog(@"str2: %x, %@", &str2, str2); //bfffd3c8, two
str1 = str2;
NSLog(@"str1: %x, %@", &str1, str1); //bfffd3cc, two
Il valore di un puntatore (come str1, str2) è un indirizzo di memoria. Quando vai a quell'indirizzo, raggiungi l'area " " nella memoria in cui è archiviato l'oggetto.
Ma: quando assegno str2 a str1, str1 dovrebbe avere come valore l'indirizzo di memoria dello stesso oggetto a cui fa riferimento str2, giusto? La cosa strana qui è che il valore del puntatore rimane lo stesso (indirizzo di memoria bfffd3cc), dove cambia la cosa dietro quell'indirizzo. questo è in realtà completamente logico per me;) perché penso che l'indirizzo di memoria sia l'oggetto (o la casa dell'oggetto nel mattone della memoria, qualunque cosa). Quindi mi aspettavo questo:
NSString *str1 = [NSString stringWithCString:"one"];
NSString *str2 = [NSString stringWithCString:"two"];
NSLog(@"str1: %x, %@", &str1, str1); //bfffd3cc, one
NSLog(@"str2: %x, %@", &str2, str2); //bfffd3c8, two
str1 = str2;
NSLog(@"str1: %x, %@", &str1, str1); //bfffd3c8, two
Altrimenti, non ho ancora capito il punto. La relazione tra il valore della variabile "pointer pointer" e il "valore reale", diciamo l'oggetto che si trova dietro quell'indirizzo di memoria.
Soluzione
Ricorda che una variabile puntatore è una variabile stessa, quindi ha un indirizzo. Quindi & amp; str1
risulta nell'indirizzo della variabile puntatore, e str1
è un'espressione che si traduce in qualunque cosa la variabile puntatore trattiene - l'indirizzo dell'oggetto a cui punta .
Supponi che:
- l'oggetto che contiene l'NSString "uno" è all'indirizzo 0x00000100
- l'oggetto che contiene l'NSString "due" è all'indirizzo 0x00000200
Quindi i tuoi puntatori potrebbero assomigliare a questo:
All'inizializzazione:
str1
0xbfffd3c8 +---------------+
| |
| 0x00000100 |
| ("one") |
+---------------+
str2
0xbfffd3cc +---------------+
| |
| 0x00000200 |
| ("two") |
+---------------+
=======================================
Dopo l'assegnazione str1 = str2;
:
str1
0xbfffd3c8 +---------------+
| |
| 0x00000200 |
| ("two") |
+---------------+
str2
0xbfffd3cc +---------------+
| |
| 0x00000200 |
| ("two") |
+---------------+
Altri suggerimenti
stai stampando il riferimento del tuo puntatore, che è l'indirizzo del puntatore, non il valore del puntatore.
& amp; str1
prende l'indirizzo del puntatore.
Ok, ricorda come abbiamo detto che tutte le cose "vivono" a qualche indirizzo in memoria? E che un puntatore è solo il numero, un numero che è l'indirizzo?
Anche i puntatori sono cose, e come tali hanno hanno indirizzi.
Quando stampi & amp; str1
, stai stampando la memoria occupata dal puntatore. Quando stampi str1
, stai stampando il valore del puntatore, che è l'indirizzo di memoria a cui punta .
Quando stampi * str1
stai dereferenziando il puntatore e stampando che viene stampato il valore indicato.
Per inciso, buon lavoro: ho letto le tue ultime due domande, e tu le stai ricevendo, e al tuo codice di scrittura del credito per dimostrare o confutare che la tua comprensione è corretta. Questo è esattamente ciò che dovresti fare e sei sulla strada giusta.
Parte del problema che stai riscontrando è che le stringhe sono cose divertenti, in quanto una stringa è in realtà un array di caratteri (const) e funzioni come NSLog sono scritte per fare la solita cosa con un puntatore a char, che è per stampare la stringa. Dato un puntatore alla stringa, è davvero un ciclo: dereferenziare il puntatore, stampare il carattere puntato, aggiungerne uno al puntatore, stampare il carattere successivo, fino a quando il carattere indicato è un carattere speciale con il valore 0, a quel punto esso ha trovato la fine della stringa e smette di stampare caratteri.
Potresti trovare tutto questo più facile da capire se usi qualcosa come ints e puntatori a ints.
I puntatori sono, infatti, numeri interi con un'aritmetica dedicata (IIRC, è garantito che sizeof (int) == sizeof (void *))
Se fai riferimento a puntatori, otterrai l'indirizzo in cui è memorizzato; se la variabile è automatica, allora otterrai l'indirizzo nello stack in cui è memorizzato il puntatore: ecco perché ottieni quei risultati.
L'assegnazione si comporta come un'assegnazione tra numeri interi; in effetti:
#include <stdio.h>
int main() {
char *str1 = "Ciao";
char *str2 = "Mondo";
printf("%x\n", str1);
printf("%x\n", str2);
str1 = str2;
printf("%x\n", str1);
}
stampe:
:~$ ./a.out
80484f8
80484fd
80484fd
str1
è un puntatore a un valore NSString
.
& amp; str1
significa: l'indirizzo della variabile str1
. Questa è una doppia indiretta.
Suppongo che tu voglia l'indirizzo del valore, cioè str1
stesso, non & amp; str1
.
Supponiamo che il valore dell'oggetto NSString
contenente @ " one "
si trovi all'indirizzo 0x12345678
e @ " due " ;
è in 0x1234ABC0
.
Inizialmente il valore di str1
è 0x12345678
e il valore di str2
è 0x1234ABC0
.
Questi due valori sono memorizzati rispettivamente negli indirizzi 0xbfffd3cc
e 0xbfffd3c8
. Questi sono gli indirizzi delle variabili str1
e str2
, ovvero i valori di & amp; str1
e & amp; str2
.
Quando scrivi str1 = str2
, l'assegnazione cambia il valore di str1
in modo che corrisponda al valore di str2
, ovvero il valore di str1
è quindi 0x1234ABC0
. Questo è l'indirizzo dell'oggetto @ " two "
. Ma l'indirizzo della variabile str1
( 0xbfffd3cc
) non è cambiato.
Gli oggetti in Objective-C vengono elaborati usando i puntatori. Quindi, per gestire il tuo oggetto NSString, devi prendere un puntatore ad esso. Che cos'è str1, un puntatore a un oggetto NSString.
Se vuoi inviare un messaggio al tuo NSString, puoi usare la sintassi [str1 doSomething].
Se vuoi stampare la tua stringa, devi passare il puntatore a NSLog E dire a NSLog che il puntatore punta a una NSString, che è fatta dalla regola di formattazione "% @ " ;.
Ora, se vuoi solo stampare l'indirizzo indicato da str1, devi comunque passare il puntatore str1 MA devi dire a NSLog che lo stai stampando come valore esadecimale usando la regola di formattazione "% x " ;.
Qualcosa del genere dovrebbe stampare quello che vuoi:
// First print str1 as an hexadecimal value
// Then print the content of the NSString object str1 points to
NSLog(@"str1: %x, %@", str1, str1);