#include<stdio.h>
#include<string.h>
int main()
{
char * p = "abc";
char * p1 = "abc";
printf("%d %d", p, p1);
}
Wenn ich die Werte der beiden Zeiger drucke, wird dieselbe Adresse gedruckt. Warum?
#include<stdio.h>
#include<string.h>
int main()
{
char * p = "abc";
char * p1 = "abc";
printf("%d %d", p, p1);
}
Wenn ich die Werte der beiden Zeiger drucke, wird dieselbe Adresse gedruckt. Warum?
p
und genommen hätten p1
, hätten Sie bemerkt, dass diese beiden Zeiger unter zwei unterschiedlichen Adressen gespeichert sind. Die Tatsache, dass ihr Wert gleich ist, ist in diesem Fall irrelevant.
p == p1
(sie unterscheiden sich nicht), aber &p != &p1
(sie unterscheiden sich).
Antworten:
Ob zwei verschiedene Zeichenfolgenliterale mit demselben Inhalt am selben Speicherort oder an verschiedenen Speicherorten abgelegt werden, hängt von der Implementierung ab.
Sie sollten immer p
und p1
als zwei verschiedene Zeiger behandeln (obwohl sie denselben Inhalt haben), da sie möglicherweise auf dieselbe Adresse verweisen oder nicht. Sie sollten sich nicht auf Compiler-Optimierungen verlassen.
C11 Standard, 6.4.5, String-Literale, Semantik
Es ist nicht spezifiziert, ob diese Arrays unterschiedlich sind, vorausgesetzt, ihre Elemente haben die entsprechenden Werte. Wenn das Programm versucht, ein solches Array zu ändern, ist das Verhalten undefiniert.
Das Druckformat muss sein %p
:
printf("%p %p", (void*)p, (void*)p1);
In dieser Antwort erfahren Sie, warum.
i modify one of the pointer, will the data in the other pointed also be modified
Sie können den Zeiger ändern, aber nicht das Zeichenfolgenliteral. Zum Beispiel char *p="abc"; p="xyz";
ist völlig in Ordnung , während char *p="abc"; p[0]='x';
Invokes Verhalten undefiniert . Das hat nichts damit zu tun volatile
. Ob Sie verwenden volatile
oder nicht, sollte kein Verhalten ändern, das uns hier interessiert. volatile
Erzwingt grundsätzlich, die Daten jedes Mal aus dem Speicher zu lesen.
p
auf das String-Literal verweist "abc"
und p[0]='x'
versucht, das erste Zeichen eines String-Literals zu ändern. Der Versuch, ein String-Literal zu ändern, ist ein undefiniertes Verhalten in C.
char []
in C ist. const char*
Wenn es also schreibgeschützt ist ( wie dies in C ++ der Fall ist), muss auch der Typ geändert werden . [Fortsetzung]
"Strings are no longer modifiable, and so may be placed in read-only memory"
, ein historischer Beweis dafür, dass String-Literale früher modifizierbar waren ;-)
Ihr Compiler scheint ziemlich schlau zu sein und erkennt, dass beide Literale gleich sind. Und da Literale konstant sind, hat der Compiler beschlossen, sie nicht zweimal zu speichern.
Es scheint erwähnenswert zu sein, dass dies nicht unbedingt der Fall sein muss. Bitte lesen Sie die Antwort von Blue Moon dazu .
Übrigens: Die printf()
Aussage sollte so aussehen
printf("%p %p", (void *) p, (void *) p1);
as "%p"
wird zum Drucken von Zeigerwerten verwendet und ist nur für Zeiger vom Typ definiert void *
. * 1
Ich würde auch sagen, dass dem Code eine return
Anweisung fehlt , aber der C-Standard scheint gerade geändert zu werden. Andere könnten dies freundlicherweise klarstellen.
* 1: Das Casting void *
hierher ist nicht für char *
Zeiger erforderlich , sondern für Zeiger auf alle anderen Typen.
==
Sie die strcmpy()
Funktion verwenden. Da andere Compiler möglicherweise keine Optimierung verwenden (es liegt an der Compiler-Implementierung), antwortete Alk PS: Blue Moon hat gerade etwas hinzugefügt.
Ihr Compiler hat etwas namens "String Pooling" durchgeführt. Sie haben angegeben, dass Sie zwei Zeiger möchten, die beide auf dasselbe Zeichenfolgenliteral verweisen. Daher wurde nur eine Kopie des Literal erstellt.
Technisch: Es hätte sich bei Ihnen beschweren sollen, dass Sie die Zeiger nicht "const" gemacht haben.
const char* p = "abc";
Dies liegt wahrscheinlich daran, dass Sie Visual Studio oder GCC ohne -Wall verwenden.
Wenn Sie ausdrücklich möchten, dass sie zweimal im Speicher gespeichert werden, versuchen Sie Folgendes:
char s1[] = "abc";
char s2[] = "abc";
Hier geben Sie ausdrücklich an, dass Sie zwei C-String-Zeichenarrays anstelle von zwei Zeigern auf Zeichen möchten.
Vorsichtsmaßnahme: String-Pooling ist eine Compiler- / Optimierungsfunktion und keine Facette der Sprache. Daher erzeugen unterschiedliche Compiler in unterschiedlichen Umgebungen ein unterschiedliches Verhalten, abhängig von der Optimierungsstufe, den Compiler-Flags und der Frage, ob sich die Zeichenfolgen in unterschiedlichen Kompilierungseinheiten befinden.
gcc (Debian 4.4.5-8) 4.4.5
beschwert sich nicht (warnt), obwohl mit -Wall -Wextra -pedantic
.
const
Zeichenfolgenliterale zu verwenden. Die Warnung wird optional aktiviert -Wwrite-strings
. Es ist offenbar nicht durch eine andere Option aktiviert ist (wie zum Beispiel -Wall
, -Wextra
oder -pedantic
).
Wie andere bereits gesagt haben, bemerkt der Compiler, dass sie denselben Wert haben, und entscheidet sich daher dafür, dass sie Daten in der endgültigen ausführbaren Datei gemeinsam nutzen. Aber es wird schicker: wenn ich folgendes mit kompilieregcc -O
#include<stdio.h>
#include<string.h>
int main()
{
char * p = "abcdef";
char * p1 = "def";
printf("%d %d", p, p1);
}
es druckt 4195780 4195783
für mich. Das heißt, p1
beginnt 3 Bytes später p
, sodass GCC das gemeinsame Suffix von def
(einschließlich des \0
Terminators) erkannt und eine ähnliche Optimierung wie die von Ihnen gezeigte durchgeführt hat.
(Dies ist eine Antwort, da es zu lang ist, um einen Kommentar abzugeben.)
Zeichenfolgenliterale im Code werden in einem schreibgeschützten Datensegment des Codes gespeichert. Wenn Sie ein String-Literal wie "abc" aufschreiben, wird tatsächlich ein 'const char *' zurückgegeben, und wenn Sie alle Compiler-Warnungen darauf hätten, würden Sie feststellen, dass Sie an diesem Punkt Casting durchführen. Sie dürfen diese Zeichenfolgen nicht aus dem Grund ändern, auf den Sie in dieser Frage hingewiesen haben.
Wenn Sie ein Zeichenfolgenliteral ("abc") erstellen, wird es in einem Speicher gespeichert, der Zeichenfolgenliterale enthält. Wenn Sie auf dasselbe Zeichenfolgenliteral verweisen, werden beide Zeiger auf dieselbe Stelle verweisen, an der das " abc "String-Literal wird gespeichert.
Ich habe das vor einiger Zeit gelernt, daher hätte ich es vielleicht nicht wirklich klar erklärt, sorry.
Dies hängt tatsächlich davon ab, welchen Compiler Sie verwenden .
In meinem System mit TC ++ 3.5 druckt es zwei verschiedene Werte für die beiden Zeiger also zwei verschiedene Adressen .
Ihr Compiler ist so konzipiert, dass er prüft, ob ein Wert im Speicher vorhanden ist, und abhängig von seiner Existenz dieselbe Referenz des zuvor gespeicherten Werts neu zuweist oder verwendet, wenn auf denselben Wert verwiesen wird.
Denken Sie also nicht zu viel darüber nach, da dies davon abhängt, wie der Compiler den Code analysiert .
Es ist eine Compiler-Optimierung, aber vergessen Sie die Optimierung für die Portabilität. Manchmal sind kompilierte Codes besser lesbar als tatsächliche Codes.