Frage

Ich bin an dem Stringliterale zugewiesen bekommen / gespeichert.

habe ich eine interessante Antwort hier und sagt:

  

Inline einen String definieren tatsächlich die Daten in das Programm bettet sich und kann nicht geändert werden (einige Compiler ermöglichen dies durch einen intelligenten Trick, nicht stören).

Aber, es hatte mit C ++ zu tun, nicht zu erwähnen, dass es sagt, nicht zu stören.

Ich stört. = D

Also meine Frage ist, wo und wie ist mein Stringliteral gehalten? Warum sollte ich nicht versuchen, es zu ändern? Variiert die Implementierung von Plattform? Hat jemand Sorgfalt auf dem erarbeiten „smart Trick?“

War es hilfreich?

Lösung

Eine übliche Technik ist für Stringliterale in „read-only-Daten“ gesetzt werden, die in den Prozessraum als Nur-Lese-abgebildet wird (weshalb man es nicht ändern kann).

Es ist je nach Plattform variieren. Zum Beispiel einfache Chip-Architekturen können nicht schreibgeschützt Speichersegmente unterstützen, so wird das Datensegment beschreibbar sein.

versuchen vielmehr dann, um herauszufinden, einen Trick, um Stringliterale wechselbar (es wird in hohem Maße abhängig von den verwendeten Plattform und könnte Zeit ändern) zu machen, nur Arrays verwenden:

char foo[] = "...";

Der Compiler wird für die Array anordnen aus dem buchstäblichen initialisiert werden, und Sie können das Array ändern.

Andere Tipps

Es gibt keine Antwort auf diese Frage. Die C- und C ++ Standards nur sagen, dass Stringliterale statische Speicherdauer hat, jeder Versuch, sie zu modifizieren undefinierte Verhalten gibt, und mehr Zeichenkette mit dem gleichen Inhalt kann oder auch nicht die gleichen Speicher teilen.

Je nach System Sie schreiben für, und die Fähigkeiten des ausführbaren Dateiformat nutzt, können sie mit dem Programmcode in dem Textsegment gespeichert entlang werden, oder sie können ein eigenes Segment für initialisierte Daten.

die Details Bestimmung variiert je nach Plattform und - höchstwahrscheinlich gehören Werkzeuge, die Sie sagen, wo es sie setzen wird. Einige werden sogar geben Sie Details Steuerung über so, wenn Sie es wollen (zum Beispiel Gnu ld ermöglicht es Ihnen, ein Skript zu liefern, es zu sagen, alles darüber, wie Sie Gruppendaten, Code, etc.)

Warum sollte ich nicht versuchen, es zu ändern?

Da ist es nicht definiertes Verhalten. Zitat von C99 N1256 Entwurf 6.7 0,8 / 32 "Initialisierung" :

  

Beispiel 8: Die Deklaration

char s[] = "abc", t[3] = "abc";
     

Definiert "plain" char Array-Objekte s t und deren Elemente mit Zeichenfolge Literale initialisiert.

     

Diese Erklärung ist identisch mit

char s[] = { 'a', 'b', 'c', '\0' },
t[] = { 'a', 'b', 'c' };
     

Der Inhalt der Arrays ist modifizierbar. Auf der anderen Seite, die Deklaration

char *p = "abc";
     

Definiert p mit Typ „Zeiger auf char“ und initialisiert sie, um Punkt zu einem Objekt mit Typ „array of char“ mit der Länge 4, deren Elemente mit einem Zeichenkettenliteral initialisiert. Wenn ein Versuch, die Verwendung p gemacht wird, den Inhalt des Arrays zu ändern, wird das Verhalten nicht definiert.

Wohin gehen sie?

GCC 4.8 x86-64 ELF Ubuntu 14.04:

  • char s[]: stack
  • char *s:
    • .rodata Abschnitt der Objektdatei
    • das gleiche Segment, in dem der .text Abschnitt der Objektdatei gespeichert wird, die lesen und Exec-Berechtigungen verfügen, aber nicht schreiben

Programm:

#include <stdio.h>

int main() {
    char *s = "abc";
    printf("%s\n", s);
    return 0;
}

Übersetzen und decompile:

gcc -ggdb -std=c99 -c main.c
objdump -Sr main.o

Ausgabe enthält:

 char *s = "abc";
8:  48 c7 45 f8 00 00 00    movq   $0x0,-0x8(%rbp)
f:  00 
        c: R_X86_64_32S .rodata

So die Zeichenfolge im .rodata Abschnitt gespeichert werden.

Dann:

readelf -l a.out

Enthält (vereinfacht):

Program Headers:
  Type           Offset             VirtAddr           PhysAddr
                 FileSiz            MemSiz              Flags  Align
      [Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]
  LOAD           0x0000000000000000 0x0000000000400000 0x0000000000400000
                 0x0000000000000704 0x0000000000000704  R E    200000

 Section to Segment mapping:
  Segment Sections...
   02     .text .rodata

Das bedeutet, dass die Standard-Linker-Skript-Dumps sowohl .text und .rodata in ein Segment, das ausgeführt werden kann, aber nicht verändert werden (Flags = R E). Der Versuch, so zu modifizieren, um ein Segment führt zu einem segfault in Linux.

Wenn wir das gleiche für char[] tun:

 char s[] = "abc";

erhalten wir:

17:   c7 45 f0 61 62 63 00    movl   $0x636261,-0x10(%rbp)

, so dass es in dem Stapel (bezogen auf %rbp) gespeichert wird, und wir können es natürlich ändern.

FYI, nur die anderen Antworten zu sichern:

Der Standard: ISO / IEC 14882: 2003 sagt:

  

2,13. Stringliterale

     
      
  1. [...] Ein gewöhnlicher Stringliteral hat Typ „Array von n const char“ und   statische Speicherdauer (3.7)

  2.   
  3. Ob alle Stringliterale verschieden sind (die sind ist gespeichert in   nicht überlappenden Objekten) wird   implementierungs definiert. Der Effekt von   Versuch, eine Stringliteral zu modifizieren   nicht definiert ist.

  4.   

gcc macht einen .rodata Abschnitt, der „irgendwo“ in Adressraum abgebildet wird und markierte nur gelesen wird,

Visual C ++ (cl.exe) macht einen .rdata Abschnitt für den gleichen Zweck.

Sie können die Ausgabe von dumpbin oder objdump sehen (unter Linux), um die Abschnitte Ihrer ausführbaren Datei zu sehen.

z.

>dumpbin vec1.exe
Microsoft (R) COFF/PE Dumper Version 8.00.50727.762
Copyright (C) Microsoft Corporation.  All rights reserved.


Dump of file vec1.exe

File Type: EXECUTABLE IMAGE

  Summary

        4000 .data
        5000 .rdata  <-- here are strings and other read-only stuff.
       14000 .text

Es hängt von dem Format Ihre ausführbare . Eine Möglichkeit, darüber nachzudenken ist, dass, wenn Sie Montage wurden Programmierung, können Sie Stringliterale im Datensegment der Assembly-Programm setzen. Ihr C-Compiler tut so etwas, aber es hängt alles davon ab, welches System Sie sind binär für kompiliert wird.

Stringliterale werden häufig mit dem Nur-Lese-Speicher zugewiesen, so dass sie unveränderlich zu machen. Allerdings ist in einigen Compilern Modifikation möglich durch einen „intelligenten Trick“ .. Und der Smart Trick ist durch „mit Zeichenzeiger zeigt auf Speicher“ .. erinnern einige Compiler kann nicht zulassen, dass this..Here ist Demo

char *tabHeader = "Sound";
*tabHeader = 'L';
printf("%s\n",tabHeader); // Displays "Lound"

Da dies von Compiler zu Compiler unterscheiden könnte, ist der beste Weg ist, um ein Objekt Dump-Filter für die Stringliteral gesucht:

objdump -s main.o | grep -B 1 str

wo -s Kräfte objdump den gesamten Inhalt aller Abschnitte angezeigt wird, ist main.o die Objektdatei, -B 1 Kräfte grep auch vor dem Spiel eine Zeile drucken (so dass Sie die Abschnittsnamen sehen) und str ist die Stringliteral ‚re der Suche nach.

Mit gcc auf einem Windows-Rechner, und eine Variable deklariert in main wie

char *c = "whatever";

läuft

objdump -s main.o | grep -B 1 whatever

Rückkehr

Contents of section .rdata:
 0000 77686174 65766572 00000000           whatever....
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top