Tiefe Analyse von Const Qualifier in C

8

Wo wird eine const Variable genau gespeichert und wie ändert sich ihr Verhalten? Sagen Sie zum Beispiel:

%Vor%

Wenn die Antwort Code-Segment ist, wie funktioniert der folgende Code?

%Vor%

Dieser Code funktioniert gut ... Wie ist es möglich einen schreibgeschützten Speicher zu ändern? Wie wird es wirklich gespeichert? Bitte erläutern Sie es mir im Detail.

    
jack 25.11.2010, 09:31
quelle

9 Antworten

9

Das Schlüsselwort const gibt eine Variable an, die schreibgeschützt ist (d. h. zur Laufzeit nicht geändert werden kann). Es gibt keine Kompilierzeitkonstante an. Daher gelten alle üblichen Attribute von Variablen; speziell wird ihm adressierbarer Speicherplatz zugewiesen.

Im Gegensatz zu #define wird Ihre Konstante nicht notwendigerweise vom Compiler eingezeichnet. Stattdessen erstellt der Compiler ein Symbol, das Ihrer Deklaration const in der Objektdatei entspricht, sodass auf andere Codedateien zugegriffen werden kann. Denken Sie daran, dass const -Objekte standardmäßig eine externe Verknüpfung in C haben (obwohl einige Compiler weiterhin inline sind) der konstante Wert innerhalb der Datei, in der er definiert ist).

Der Grund dafür, dass das Code-Snippet, das Sie gepostet haben, "funktioniert", ist, dass der unäre Operator & auf einen beliebigen L-Wert angewendet werden kann, der ein const -Objekt enthält. Obwohl das Verhalten hier undefiniert ist, vermute ich, dass Ihr Compiler diese Verwendung erkennt und sicherstellen, dass Ihre const -Deklaration Adressraum gegeben und daher nicht eingebunden wird, auch nicht innerhalb der Datei deklariert.

BEARBEITEN: Siehe auch: Ссылка

  

Schauen wir uns an, was gemeint ist, wenn const   wird eingesetzt. Es ist wirklich ganz einfach:   const bedeutet, dass etwas nicht ist   modifizierbar, also ein Datenobjekt das ist   erklärt mit const als Teil seiner   Typangabe darf nicht sein   während des Laufes in irgendeiner Weise zugewiesen werden   eines Programms. Das ist sehr wahrscheinlich   die Definition des Objekts wird   einen Initialisierer enthalten   da du es nicht zuordnen kannst, wie   Würde es jemals einen Wert bekommen?), aber das   ist nicht immer der Fall. Beispielsweise,   wenn Sie auf einen Hardware-Port zugreifen   an einer festen Speicheradresse und versprochen   nur um daraus zu lesen, dann wäre es so   erklärt, um const aber nicht zu sein   initialisiert.

Die Adresse von a   Datenobjekt eines Typs, der nicht   const und setze es in einen Zeiger auf   die const-qualifizierte Version des   Derselbe Typ ist sowohl sicher als auch explizit   zulässig; Sie können das verwenden   Zeiger auf das Objekt zu prüfen, aber nicht   modifiziere es. Die Adresse von a   const type in einen Zeiger auf die   unqualifizierter Typ ist viel mehr   gefährlich und folglich verboten   (obwohl du das durchkommen kannst   einen Cast verwenden). Zum Beispiel ...

    
Cody Gray 25.11.2010, 09:44
quelle
4

Ändern Sie Ihren Code, um den Wert zu drucken:

%Vor%

Der obige Code ergibt, wenn er mit gcc 4.3.2 bei -O1 optimization oder höher kompiliert wird, die Ausgabe 20 anstatt 21 . Dies zeigt, dass es nicht wirklich "funktioniert" hat - es ist einfach erschienen um zu funktionieren.

A const Qualifier ist nicht eine Anfrage, die Variable in eine bestimmte Art von Speicher zu legen - es ist eine Zusage von Ihnen an den Compiler, dass Sie diese Variable nicht ändern werden irgendwelche Mittel. Der Compiler kann sich auf Ihr Versprechen verlassen, den produzierten Code zu optimieren - und wenn Sie Ihr Versprechen brechen, wird es nicht notwendigerweise auf eine offensichtliche Weise brechen, es kann nur seltsame Ergebnisse erzeugen.

    
caf 25.11.2010 12:12
quelle
4

Gemäß dem C-Standard ( n1256 Entwurf):

6.7.3 Typqualifizierer
...
3 Die mit qualifizierten Typen verknüpften Eigenschaften sind nur für Ausdrücke von Bedeutung sind lvalues. 114
...
5 Wenn versucht wird, ein mit einem Const-qualifizierten Typ definiertes Objekt über use zu ändern von einem Lvalue mit nicht-const-qualified-Typ ist das Verhalten nicht definiert. Wenn ein Versuch ist wurde gemacht, um auf ein Objekt Bezug zu nehmen, das mit einem flüchtigen qualifizierten Typ durch Verwendung eines L-Wertes definiert ist mit nicht-flüchtiger qualifizierter Art ist das Verhalten undefiniert. 115
...
114) Die Implementierung kann ein Objekt const , das nicht volatile ist, in eine schreibgeschützte Region von Lager. Darüber hinaus muss die Implementierung keinen Speicher für ein solches Objekt zuweisen, wenn seine Adresse ist nie benutzt.

115) Dies gilt für Objekte, die sich so verhalten, als wären sie mit qualifizierten Typen definiert worden, selbst wenn dies der Fall ist nie wirklich als Objekte im Programm definiert (z. B. ein Objekt an einer speicherprogrammierten Ein- / Ausgabe) Adresse).

Kurz gesagt, ein const -qualifiziertes Objekt darf in einem anderen Bereich gespeichert werden als nicht const -qualifizierte Objekte, aber nicht unbedingt.

Der Qualifier const ist eine Anweisung an den Compiler, Code zurückzuweisen, der versucht, das Objekt direkt zu ändern. Versuche, das Objekt indirekt zu ändern (wie im zweiten Code-Snippet), führen zu undefiniertem Verhalten, dh jedes Ergebnis ist möglich.

    
John Bode 26.11.2010 00:05
quelle
2

Ich weiß nicht, wo es gespeichert ist, weil das implementation-defined ist, aber Ihr Code führt zu undefiniertem Verhalten.

    
Armen Tsirunyan 25.11.2010 09:35
quelle
1

Es sollte wirklich nicht funktionieren.

Konstanten werden im Allgemeinen nicht gespeichert. Sie werden inline erweitert.

Es ist möglich, dass Ihr Compiler nett zu Ihnen ist und Ihnen einen Speicherort zum Ändern gibt, aber normalerweise ist das unmöglich.

Welche Warnungen erhalten Sie? Ich kann mir vorstellen, dass du etwas bekommen musst ...

    
Mike Caron 25.11.2010 09:35
quelle
1

Es hängt ganz vom Compiler Writer ab, was mit der const passiert, und es wird je nach der von Ihnen gewünschten Optimierung variieren.

In Ihrem ersten Beispiel werden die Konstanten nie benutzt, so dass der Compiler sie wahrscheinlich einfach ignoriert.

In Ihrem zweiten Beispiel, in dem Sie "address off" verwenden, muss es tatsächlich irgendwo gespeichert werden - wahrscheinlich am Anfang des Stapels.

Da C Assemblersprache-Anweisungen ersetzt und OS-Kernel- und Gerätetreiber-Code schreibt, können Sie alle möglichen Dinge tun und davon ausgehen, dass Sie wissen, was Sie tun, wenn Sie mit Zeigern herumhantieren.

    
James Anderson 25.11.2010 09:40
quelle
1

Der Compiler bestimmt, ob die Adresse der Konstante jemals benötigt wird. Wenn dies nicht der Fall ist, wird es (normalerweise) inline in das Codesegment eingegeben, da dies normalerweise schneller ist als das Referenzieren des Speichers.

Wenn die Adresse benötigt wird, wird die Konstante so gespeichert, als wäre sie eine nicht-konstante Variable im aktuellen Bereich (relativ abhängig vom Compiler). Das heißt, globale Konflikte werden im Allgemeinen in Ihrem Datensegment gespeichert, Funktions- (Parameter oder deklarierte) Konflikte werden im Allgemeinen auf dem Stapel gespeichert.

Stellen Sie sich das wie eine Registervariable vor. Es ist in Ihrem CPU-Register, wenn Sie damit vertraut sind. Es ist in Ihrem CPU-Register, bis Sie seine Adresse benötigen. Dann wird es in adressierbaren Raum gebracht.

Die eigentliche Frage ist die Initialisierung - wenn Sie ihre Adresse benötigen und daher tatsächlich zugewiesen werden, wo wird sie dann initialisiert? Da ist etwas, worüber Sie nachdenken können.

    
inetknght 25.11.2010 09:40
quelle
1

Der zweite Code sollte überhaupt nicht kompiliert werden. Und die Compiler, die ich hier habe, stimmen überein: gcc gibt einen Fehler mit -pedantic-errors (dessen Zweck es ist, einige obligatorische Diagnosen zu fälschen, die gcc in der Vergangenheit nicht als Fehler angesehen hat), xlc gibt ebenfalls einen Fehler.

>

Wenn Sie eine Referenz haben möchten: 6.3.16.1 im C90-Standard beschreibt, wann eine Zuweisung möglich ist:

  

beide Operanden sind Zeiger auf qualifizierte oder unqualifizierte Versionen von   kompatible Typen, und der Typ, auf den von links gezeigt wird, hat alle   Qualifier des Typs, auf den das Recht

zeigt

und c99 als eine ähnliche Einschränkung.

    
AProgrammer 25.11.2010 10:15
quelle
1

Wenn Sie (non-extern, non-argument) deklarieren und die Variable als const initialisieren, bedeutet dies, dass die Variable überhaupt nicht beschreibbar ist. Dem Compiler steht es also frei, ihn nur in den Abschnitt zum Lesen zu stellen. Obwohl es physikalisch modifizierbar sein kann (wenn Hardware es erlaubt). Oder nicht modifizierbar, wenn es von MMU geschützt oder in einer eigenständigen Anwendung in ROM gespeichert wird.

Standard gibt nicht an, was passieren soll, wenn Sie versuchen, const zu schreiben (es heißt "undefined behaviour"), also kann alles passieren: Es kann geschrieben werden, nicht geschrieben, verursacht Exception, Hang oder etwas anderes, das Sie nicht können vorstellen. C ist nicht so paranoid wie, sagen wir, Ada, und alles unexpected Verhalten liegt beim Programmierer, nicht beim Compiler oder RTL.

Wie viele sagen, ist es in den meisten Fällen inline (wenn der Compiler das inline weiß), behält aber trotzdem Attribute von Variablen, wie Adresse (und Sie können einen Zeiger bekommen), Größe. Wenn alle const-Lesevorgänge und Zeiger darauf eliminiert werden, wird Speicher für const auch durch Compiler (wenn es statisch oder lokal ist) oder Linker (wenn es global ist) eliminiert.

Beachten Sie, dass lokale Zeiger auch eliminiert werden können, wenn ihre Positionen in der Kompilierzeit berechnet werden können. Außerdem können Schreibvorgänge in lokalen Variablen eliminiert werden, wenn sie nicht danach gelesen werden. Daher enthält Ihr Code-Snippet möglicherweise gar keinen Code.

Automatische lokale Variable kann im statischen Speicher kompiliert werden, wenn der Compiler beweist, dass nur eine Instanz davon benötigt wird. Da const nicht modifizierbar ist, wird es auch im statischen Speicher kompiliert, kann aber wie oben beschrieben eliminiert werden.

In allen Ihren Beispielen können alle Konflikte zuerst statisch gespeichert (const-Abschnitt) und dann leicht eliminiert werden.

    
Vovanium 25.11.2010 10:36
quelle

Tags und Links