Die Adressen von zwei char-Zeigern auf verschiedene String-Literale sind gleich

Lesezeit: 7 Minuten

Benutzeravatar von seereddi sekhar
seereddi sekhar

#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. Wieso den?

  • Warum sollte es deiner Meinung nach nicht sein? Diese beiden Zeiger zeigen auf genau dasselbe. Was Sie sehen, ist wahrscheinlich der Effekt einer Optimierungstechnik namens String-Pooling.

    – Daniel Kamil Kozar

    30. September 2013 um 6:58 Uhr

  • Obwohl die Daten gleich sind, sind die Variablen unterschiedlich.

    – seereddi sekhar

    30. September 2013 um 7:00 Uhr

  • Die Variablen sind natürlich unterschiedlich. Hätten Sie die Adresse von genommen p und p1, dann wäre Ihnen aufgefallen, dass diese beiden Zeiger unter zwei unterschiedlichen Adressen gespeichert sind. Die Tatsache, dass ihr Wert gleich ist, ist in diesem Fall irrelevant.

    – Daniel Kamil Kozar

    30. September 2013 um 7:02 Uhr

  • Ja, wenn ich die Werte ändere, dann sind die Adressen anders.

    – seereddi sekhar

    30. September 2013 um 7:05 Uhr

  • @JanHudec: Lies die Frage noch einmal. In diesem Fall (aufgrund der Compiler-Optimierung) p == p1 (sie unterscheiden sich nicht), aber &p != &p1 (Sie unterscheiden sich).

    – MSalter

    30. September 2013 um 7:20 Uhr

Benutzeravatar von PP
PP

Ob zwei unterschiedliche Zeichenfolgenliterale mit gleichem Inhalt an der gleichen Speicherstelle oder an verschiedenen Speicherstellen abgelegt werden, ist implementierungsabhängig.

Behandeln sollte man immer p und p1 als zwei verschiedene Zeiger (obwohl sie denselben Inhalt haben), da sie auf dieselbe Adresse zeigen können oder nicht. Sie sollten sich nicht auf Compiler-Optimierungen verlassen.

C11 Standard, 6.4.5, String-Literale, Semantik

Es ist nicht angegeben, 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 Format für den Druck muss sein %p:

  printf("%p %p", (void*)p, (void*)p1);

Siehe diese Antwort für den Grund.

  • Ich habe volatile verwendet, damit es keine Speicheroptimierungen geben muss, auch wenn sie dieselbe Adresse verwenden. Eine Frage ist, dass, wenn ich einen der Zeiger ändere, die Daten in dem anderen Zeiger auch geändert werden.

    – Megharaj

    30. September 2013 um 7:51 Uhr

  • @Megharaj i modify one of the pointer, will the data in the other pointed also be modified Sie können die ändern Zeiger aber nicht das String-Literal. Z.B char *p="abc"; p="xyz"; ist vollkommen in Ordnung, während char *p="abc"; p[0]='x'; ruft undefiniertes Verhalten. Das hat nichts damit zu tun volatile. Ob Sie verwenden volatile oder nicht sollte kein Verhalten ändern, an dem wir hier interessiert sind. volatile zwingt grundsätzlich dazu, die Daten jedes Mal aus dem Speicher zu lesen.

    – PP

    30. September 2013 um 9:03 Uhr


  • @MSharathHegde Ja. Da p zeigt auf das String-Literal "abc" und p[0]='x' versucht, das erste Zeichen eines Zeichenfolgenliterals zu ändern. Der Versuch, ein Zeichenfolgenliteral zu ändern, ist ein undefiniertes Verhalten in C.

    – PP

    30. September 2013 um 12:22 Uhr

  • @MSharathHegde Weil C-Standard das besagt. Der Grund ist hauptsächlich historischer Natur, da die vorstandardisierte C-Sprache das Ändern von Zeichenfolgenliteralen erlaubte. Später schaffte es der C-Standard (C89). nicht definiert so dass neuer Code dies nicht tut und alter Code (Vorstandard) so funktioniert, wie er war. Grundsätzlich ist es meiner Meinung nach ein Kompromiss, bestehenden (Pre-Standard-) Code nicht zu brechen. Ein weiterer Grund ist der Typ des Zeichenfolgenliterals char [] in C. Also schreibgeschützt machen (const char* wie es in C++ der Fall ist) würde eine Änderung der Typ auch. [contd.]

    – PP

    30. September 2013 um 12:46 Uhr


  • Es gibt eine Zeile in K&R 2. Ausgabe in Anhang C: "Strings are no longer modifiable, and so may be placed in read-only memory"ein historischer Beweis dafür, dass Zeichenfolgenliterale Gebraucht veränderbar sein 😉

    – PP

    30. September 2013 um 12:46 Uhr


Benutzeravatar von alk
alk

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.

Erwähnenswert erscheint, dass dies nicht unbedingt der Fall sein muss. Bitte sehen Blauer MondAntwort darauf.


Übrigens: Die printf() Aussage sollte so aussehen

printf("%p %p", (void *) p, (void *) p1);

wie "%p" soll zum Drucken von Zeigerwerten verwendet werden und ist für Zeiger vom Typ definiert void * nur.*1


Ich würde auch sagen, dass der Code a verfehlt return Aussage, aber der C-Standard scheint gerade geändert zu werden. Andere könnten dies freundlicherweise klären.


*1: Casting zu void * hier ist nicht notwendig für char * Zeiger, sondern für Zeiger auf alle anderen Typen.

  • Vielen Dank. Die Schlussfolgerung lautet also Compiler-Optimierung, richtig? in der C-Hauptfunktion gibt standardmäßig 0 zurück

    – seereddi sekhar

    30. September 2013 um 7:03 Uhr


  • @seereddisekhar: Ja, es ist eine Art Optimierung.

    – alk

    30. September 2013 um 7:05 Uhr

  • @seereddisekhar Aber seien Sie vorsichtig, es bedeutet nicht, dass Sie zwei Zeichenfolgen (gerade Zeiger) mit vergleichen sollten == du solltest benutzen strcmpy() Funktion. Da andere Compiler möglicherweise keine Optimierung verwenden (es liegt am Compiler – abhängig von der Implementierung), wie Alk antwortete. PS: Blue Moon hat gerade darüber hinzugefügt.

    – Grijesh Chauhan

    30. September 2013 um 7:07 Uhr


  • Liebe @Megharaj: Darf ich freundlicherweise darum bitten, eine separate Frage dazu zu stellen? Sie können einen Link zu dieser neuen Frage hier als Kommentar posten.

    – alk

    30. September 2013 um 9:07 Uhr


  • @Megharaj: Sie können den Wert eines Zeichenfolgenliterals nicht ändern. Wie ich in meiner Frage erwähnt habe, ist es konstant.

    – alk

    30. September 2013 um 9:09 Uhr


Benutzeravatar von kfsone
kfson

Ihr Compiler hat etwas namens “String-Pooling” durchgeführt. Sie haben angegeben, dass Sie zwei Zeiger haben möchten, die beide auf dasselbe Zeichenfolgenliteral zeigen, sodass nur eine Kopie des Literals erstellt wird.

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.

Achtung: Das String-Pooling ist ein Compiler-/Optimierer-Feature und keine Facette der Sprache. Daher erzeugen verschiedene Compiler in verschiedenen Umgebungen ein unterschiedliches Verhalten, abhängig von Dingen wie Optimierungsstufe, Compiler-Flags und davon, ob sich die Zeichenfolgen in verschiedenen Kompilierungseinheiten befinden.

  • gcc (Debian 4.4.5-8) 4.4.5 beschwert sich nicht (warnt), obwohl verwendet -Wall -Wextra -pedantic.

    – alk

    30. September 2013 um 7:37 Uhr


  • Ja, ab V4.8.1 warnt gcc standardmäßig nicht vor der Nichtbenutzung const für String-Literale. Die Warnung wird per Option aktiviert -Wwrite-strings. Es wird anscheinend nicht durch eine andere Option aktiviert (z -Wall, -Wextra oder -pedantic).

    – schleske

    30. September 2013 um 10:35 Uhr

  • Sowohl GCC 4.4.7 als auch 4.7.2 geben mir die Warnung mit oder ohne -Wall. pastebin.com/1DtYEzUN

    – kfsone

    30. September 2013 um 20:15 Uhr

Benutzeravatar von huon
huon

Wie andere 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 kompiliere gcc -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 ist, p1 beginnt 3 Bytes danach palso hat GCC das gemeinsame Suffix von gesehen def (einschließlich der \0 Terminator) und eine ähnliche Optimierung wie die von Ihnen gezeigte vorgenommen.

(Dies ist eine Antwort, weil es zu lang ist, um ein Kommentar zu sein.)

Zeichenfolgenliterale im Code werden in einem schreibgeschützten Datensegment des Codes gespeichert. Wenn Sie ein Zeichenfolgenliteral wie “abc” aufschreiben, gibt es tatsächlich ein “const char*” zurück, und wenn Sie alle Compiler-Warnungen darauf hätten, würden Sie sagen, dass Sie an diesem Punkt umwandeln. Sie dürfen diese Zeichenfolgen aus genau dem Grund, den Sie in dieser Frage angegeben haben, nicht ändern.

Benutzeravatar von Lord Zsolt
Herr Zsolt

Wenn Sie ein Zeichenfolgenliteral (“abc”) erstellen, wird es in einem Speicher gespeichert, der Zeichenfolgenliterale enthält, und dann wiederverwendet, wenn Sie auf dasselbe Zeichenfolgenliteral verweisen, sodass beide Zeiger auf dieselbe Stelle zeigen, an der ” abc” String-Literal gespeichert.

Ich habe das vor einiger Zeit gelernt, also habe ich es vielleicht nicht wirklich klar erklärt, sorry.

Das eigentlich hängt davon ab, welchen Compiler Sie verwenden.

In meinem System mit TC++ 3.5 es druckt zwei unterschiedliche Werte für die beiden Zeiger, dh zwei unterschiedliche Adressen.

Ihr Compiler ist so konzipiert, dass er es will prüfen, ob irgendein Wert im Speicher vorhanden ist und abhängig von seiner Existenz es wird neu zugewiesen oder Verwenden Sie dieselbe Referenz des zuvor gespeicherten Wertes, wenn auf denselben Wert verwiesen wird.

Denken Sie also nicht zu viel darüber nach hängt davon ab, wie der Compiler analysiert der Code.

DAS IST ALLES…

1418530cookie-checkDie Adressen von zwei char-Zeigern auf verschiedene String-Literale sind gleich

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy