Ich habe immer geglaubt, dass GCC einen platzieren würde static const
variabel zu .rodata
Segmente (bzw .text
Segmente für Optimierungen) einer ELF- oder einer solchen Datei. Aber das scheint nicht der Fall zu sein.
Ich verwende derzeit gcc (GCC) 4.7.0 20120505 (prerelease)
auf einem Laptop mit GNU/Linux. Und es platziert eine statische konstante Variable an .bss
Segment:
/*
* this is a.c, and in its generated asm file a.s, the following line gives:
* .comm a,4,4
* which would place variable a in .bss but not .rodata(or .text)
*/
static const int a;
int main()
{
int *p = (int*)&a;
*p = 0; /* since a is in .data, write access to that region */
/* won't trigger an exception */
return 0;
}
Also, ist das ein Bug oder ein Feature? Ich habe beschlossen, dies als Fehler bei Bugzilla zu melden, aber es wäre vielleicht besser, zuerst um Hilfe zu bitten.
Gibt es Gründe, warum GCC keine konstante Variable platzieren kann? .rodata
?
AKTUALISIERT:
Wie getestet, eine konstante Variable mit einer expliziten Initialisierung (wie const int a = 0;
) platziert werden würde .rodata
von GCC, während ich die Variable nicht initialisiert habe. Daher könnte diese Frage später geschlossen werden – ich habe vielleicht keine richtige Frage gestellt.
Außerdem habe ich in meinen vorherigen Worten geschrieben, dass die Variable a im Abschnitt „.data“ platziert ist, was falsch ist. Es ist tatsächlich in platziert .bss
Abschnitt da nicht initialisiert. Text oben ist jetzt korrigiert.
Der Compiler hat es zu einem gemeinsamen gemacht, das mit anderen kompatiblen Symbolen zusammengeführt werden kann und das in bss gehen kann (kein Speicherplatz auf der Festplatte), wenn es ohne explizit initialisierte Definition endet. Es in Rodata zu setzen, wäre ein Kompromiss; Sie würden zur Laufzeit Speicher sparen (Gebühr festschreiben), aber mehr Speicherplatz auf der Festplatte verbrauchen (möglicherweise viel für ein riesiges Array).
Wenn Sie lieber in Rodata gehen möchten, verwenden Sie die -fno-common
Option zu GCC.
Warum GCC macht es? Ich kann diese Frage nicht wirklich beantworten, ohne die Entwickler selbst zu fragen. Wenn ich spekulieren darf, würde ich wetten, dass es mit Optimierung zu tun hat – Compiler nicht haben const erzwingen
Trotzdem denke ich, dass es besser ist, wenn wir uns die Sprache selbst ansehen, insbesondere undefiniertes Verhalten. Es gibt ein wenig Erwähnungen von undefiniertem Verhalten, aber keine davon geht in die Tiefe.
Ändern einer Konstante ist undefiniertes Verhalten. Const ist ein Vertragund das gilt insbesondere für C (und C++).
„Aber wenn ich const_cast
die Konstante weg und ändere y trotzdem?” Dann hast du ein undefiniertes Verhalten.
Was undefiniertes Verhalten meint ist, dass der Compiler buchstäblich alles tun darf, was er will, und was auch immer der Compiler tut, wird nicht als Verstoß gegen den ISO 9899-Standard angesehen.
3.4.3
1 undefiniertes Verhalten
Verhalten, bei Verwendung eines nicht portierbaren oder fehlerhaften Programmkonstrukts oder fehlerhafter Daten, für die diese Internationale Norm keine Anforderungen stellt
2 ANMERKUNG Mögliches undefiniertes Verhalten reicht vom völligen Ignorieren der Situation mit unvorhersehbaren Ergebnissen über ein für die Umgebung charakteristisches Verhalten während der Übersetzung oder Programmausführung (mit oder ohne Ausgabe einer Diagnosemeldung) bis zum Abbruch einer Übersetzung oder Ausführung (mit Ausgabe einer Diagnosemeldung).
ISO/IEC 9899:1999, §3.4.3
Das bedeutet, dass alles, was der Compiler tut, technisch korrekt ist, weil Sie undefiniertes Verhalten aufgerufen haben nicht Sein falsch. Ergo ist es richtig, dass GCC nimmt …
static const int a = 0;
…und verwandle es in ein .rodata
Symbol, während der Einnahme…
static const int a; // guaranteed to be zero
… und es in ein verwandeln .bss
Symbol.
Im ersteren Fall jeder Versuch einer Änderung a
–sogar per Proxy–wird typischerweise zu einer Segmentierungsverletzung führen, was dazu führt, dass der Kernel das laufende Programm zwangsweise beendet. Im letzteren Fall wird das Programm wahrscheinlich ohne Absturz ausgeführt.
Allerdings ist es nicht vernünftig zu erraten, welche der Compiler tun wird. Const ist ein Vertrag, und es liegt an Ihnen, dem Programmierer, diesen Vertrag aufrechtzuerhalten, indem Sie Daten, die als konstant gelten sollen, nicht ändern. Ein Verstoß gegen diesen Vertrag bedeutet undefiniertes Verhalten und alle damit verbundenen Portabilitätsprobleme und Programmfehler.
GCC kann also ein paar Dinge tun.
Es könnte Schreiben Sie das Symbol in .rodata, um es unter dem Betriebssystemkernel zu schützen
Es könnte Schreiben Sie das Objekt an einen Ort, an dem der Speicherschutz nicht gewährleistet ist. In diesem Fall …
Es könnte ändern Sie den Wert
Es könnte Ändern Sie den Wert und ändern Sie ihn sofort wieder zurück
Es könnte Löschen Sie den fehlerhaften Code vollständig mit der Begründung, dass sich der Wert nicht ändert (0 -> 0
), im Wesentlichen optimieren …
int main(){
int *p = &a;
*p = 0;
return 0;
}
…zu…
int main(void){
return 0;
}
Es könnte schicken Sie sogar ein Modell T-800 rechtzeitig zurück, um Ihre Eltern vor Ihrer Geburt zu kündigen.
Alle diese Verhaltensweisen sind legal (na ja, legal im Sinne der Einhaltung des Standards), so dass der Fehlerbericht nicht gerechtfertigt war.
Schreiben auf ein deklariertes Objekt const
qualifiziert ist undefiniertes Verhalten: Alles kann passieren, sogar das.
Es gibt in C keine Möglichkeit, das Objekt selbst als unveränderlich zu deklarieren, Sie verbieten ihm nur die Veränderbarkeit durch den besonderen Zugriff, den Sie darauf haben. Hier haben Sie eine int*
, daher ist eine Änderung in dem Sinne “erlaubt”, dass der Compiler nicht gezwungen ist, eine Diagnose auszugeben. Eine Umwandlung in C bedeutet, dass Sie wissen, was Sie tun.
Gibt es Gründe, warum GCC keine konstante Variable in .rodata platzieren kann?
Ihr Programm wird vom Compiler optimiert (sogar in -O0
einige Optimierungen vorgenommen werden). Es erfolgt eine konstante Ausbreitung: http://en.wikipedia.org/wiki/Constant_folding
Versuchen Sie, den Compiler wie folgt zu täuschen (beachten Sie, dass dieses Programm noch technisch undefiniertes Verhalten hat):
#include <stdio.h>
static const int a;
int main(void)
{
*(int *) &a = printf(""); // compiler cannot assume it is 0
printf("%d\n", a);
return 0;
}
In C++ können Sie a initialisieren
const
Variable von einem Wert, der keine Kompilierzeitkonstante ist. Aber ich habe es überprüft, und GCC erlaubt das nicht als Erweiterung im C-Modus.– Kartoffelklatsche
30. Mai 2012 um 14:59 Uhr
@Potatoswatter In C könnte man das auch legal schreiben:
void test(int a){ const int b = a; /* ... */ }
. Ich frage mich eigentlich, ob eine globale konstante Variable in einen schreibgeschützten Speicherbereich gestellt werden soll.– versternen
30. Mai 2012 um 15:14 Uhr
FWIW, es geht in einen schreibgeschützten Abschnitt, wenn Sie es explizit initialisieren.
– Matte
30. Mai 2012 um 15:16 Uhr
Auch Programmierfehler in C++ tun nicht “Ausnahmen auslösen”. Ausnahmen gehören dazu Korrekt Programmierung, keine Debugging-Tools.
– Kerrek SB
30. Mai 2012 um 15:42 Uhr
@Potatoswatter: Nicht konstante Initialisierer sind in C für nicht statische Objekte (dh Objekte, die innerhalb einer Funktion ohne die
static
Stichwort):const int r = rand();
. Nicht konstante Initialisierer sind für statische Objekte nicht zulässig. Das Vorhandensein oder Fehlen vonconst
spielt keine Rolle; in C,const
meint schreibgeschütztnicht “konstant”.– Keith Thompson
15. September 2014 um 15:29 Uhr