GCC: Warnung vor "unused global variable" aktivieren?

#4438316
Lesenswert?

Carl D. schrieb:
> BTW, ein guter Linker link keine Objekte dazu, die keiner braucht.

Wenn ein globales Objekt Teil einer aus anderen Gründen verwendeten 
Section ist, dann kann der Linker es nicht weglassen. Auf explizit 
initialisierte globale Variablen und auf Funktionen trifft das i.A. zu. 
Bei nicht initialisierten globalen Variablen kann es je nach Umgebung 
anders sein.

Dies gilt jedenfalls im klassischen Modell von Compiler/Assemblern, die 
Objectfiles bestehend aus je einer Code/Data/... Section erzeugen.

In GCC gibt es Optionen in Compiler und Linker, um alle Objekte in 
individuellen Sections unterzubringen und deshalb weglassen zu können. 
Alterativ geht das auch mit Übersetzung des gesamten Programms in einem 
Schritt.
Gast #4438360
Lesenswert?

Hallo,

ich gehe davon aus, du benutzt GCC.
Also wenn die Variable im C Modul global ist und nicht
1
static
 davor steht, dann geht der Compiler davon aus, die wird auch von einem 
anderen Modul benutzt.
Wenn du
1
static
 davor schreibst, ist das die Voraussetzung, dass der Compiler zumindest 
was sagen kann.

Der Linker kann mit folgender Anweisung unreferenzierte Funktionen und 
Daten beim Linken weglassen:
- beim Compileren folgende optionen dazunehmen: -ffunction-sections 
-fdata-sections
- beim Linken: -Wl,--gc-sections

Bei mir warnt der GCC 4.8 und 4.9 zuverlässig vor unbenutzten:
1
static int wert = 2;

Schau auch mal -funused-data bei 
https://gcc.gnu.org/onlinedocs/gcc/Warning-Options.html

Grüße, Adib.
Gast #4438416
Lesenswert?

Ich bin dabei, eine größere Anwendung "aufzuräumen". Die globalen 
Variablen stehen in den .h-Dateien. Da ist nix mit static, weil es 
globale Variablen sind. Viele werden aber nicht weiter verwendet. Ich 
muss wissen, welche.

Im Prinzip das gleiche brauche ich dann noch für Funktionen.
#4438506
Lesenswert?

Mark B. schrieb:
> Ich dachte die gibt es laut C-Standard nicht? Wenn mich nicht alles
> täuscht, sind globale Variablen per Default zu Null initialisiert.

Korrekt. Ich hatte mich oben direkt davor aber ausdrücklich auf explizit 
initialisierte globale Variablen bezogen. Der Folgesatz bezieht sich 
daher auf nicht explizit initialisierte Variablen.

Je nach Compiler werde die u.U. einzeln als sogenannte "commons" an den 
Linker durchgereicht (Fortran lässt grüssen). Dann sind sie im 
Objectfile in keiner Section alloziert, sondern tauchen nur in der 
Symboltable auf. Erst der Linker sammelt die ein und platziert sie. Er 
könnte sie daher auch problemlos weglassen, wenn er unabhängig davon die 
Referenzierung erfasst.

Charakteristisch für solche Umgebungen ist, dass man sowas wie "int 
global_variable;" in mehreren Übersetzungseinheiten verwenden kann, ohne 
im Linker auf einen Fehler zu laufen. Obwohl eigentlich die Regel gilt, 
dass dies nur in einer einzigen Übersetzungseinheit zulässig ist.
Gast #4438507
Lesenswert?

In der Zeit hättest dir schon längst ein perl Skript geschrieben, dass 
das für dich erledigt.
Wenn du ein größeres Projekt hast, dann hast du auch dort die Finger 
drin. Dann solltest du auch in der Lage sein, dir ein einfaches Skript 
mit regex zu schreiben, dass dir genau diese Aufgabe löst.

1. Schritt: In allen *.h alle globalen Variablen suchen und in einen 
hash speichern
2. in allen *.c Dateien genau diese Daten suchen. Sollte der Name 
verwendet werden, dann kommt er aus der Liste raus, sonst bleibt er 
drin. Evtl. noch auf Namespaces achten, allerdings sollten Verletzungen 
dessen in "größeren" Projekten sowieso nicht auftauchen.
Gast #4438522
Lesenswert?

Fritzchen schrieb:
> Ich bin dabei, eine größere Anwendung "aufzuräumen". Die globalen
> Variablen stehen in den .h-Dateien.

Variblen solltest du nur in einem Modul definieren.
1
int variable;
Im headerfile sollte nur die Deklaration stehen, damit C Module den Typ 
kennen.
1
extern int variable;

Wenn du jetzt ohne Compilerfehler die Variable im .h File weglassen 
kannst, dann wird die Variable nur in dem Modul geraucht, wo sie auch 
definiert ist.

Grüsse.
#4438839
Lesenswert?

Zuverlässig ist das doch alles nicht. Es kann ja auch sein, dass die 
Variable nur geschrieben, aber nie gelesen wird, z.B. wenn sie in 
irgendeiner Init-Funktion einmalig einen Wert bekommt, aber danach nie 
mehr verwendet wird. Dann kann sie auch weg.

A. K. schrieb:
> Ich hatte mich oben direkt davor aber ausdrücklich auf explizit
> initialisierte globale Variablen bezogen. Der Folgesatz bezieht sich
> daher auf nicht explizit initialisierte Variablen.

Soweit ich weiß unterscheiden gcc und ld aber nicht zwischen implizit 
und explizit mit 0 initialisierten Variablen. Nur Initialisierungswerte, 
die von 0 verschieden sind, werden anders behandelt.

> Je nach Compiler werde die u.U. einzeln als sogenannte "commons" an den
> Linker durchgereicht (Fortran lässt grüssen). Dann sind sie im
> Objectfile in keiner Section alloziert, sondern tauchen nur in der
> Symboltable auf.

Ich kenne das so, dass sie in die Section .bss kommen und dann vom 
Startup-Code im memset-Stil alle am Stück mit 0 initialisiert werden.

Fritzchen schrieb:
> Hmmmmm... das heißt, gcc kann das gar nicht???

Der gcc kann es gar nicht können.
Persönliche Seite #4438858
Lesenswert?

A. K. schrieb:
> Charakteristisch für solche Umgebungen ist, dass man sowas wie "int
> global_variable;" in mehreren Übersetzungseinheiten verwenden kann,
> ohne im Linker auf einen Fehler zu laufen.

Es gibt sogar keinen Fehler, wenn in einem Modul "int global_variable;" 
steht und in einem anderen "float global_variable;".

Einen Fehler gibt es in dem Fall erst dann, wenn modulübergreifend 
optimiert wird (z.B. LTO per -flto) oder per -fno-common auf COMMON 
verzichtet wird.
Gast #4438932
Lesenswert?

Fritzchen schrieb:
> Ich bin dabei, eine größere Anwendung "aufzuräumen". Die globalen
> Variablen stehen in den .h-Dateien. Da ist nix mit static, weil es
> globale Variablen sind. Viele werden aber nicht weiter verw

Dann kommentiere sämtliche extern-Deklarationen in den Header-Dateien 
aus. Der Compiler sagt Dir dann, welche er braucht. Die aktivierst Du 
dann wieder,  die anderen löschst Du.
#4438933
Lesenswert?

Der einfachste Tip wurde ja schon genant: Die Definitionen der 
fraglichen Variablen auskommentieren, und schauen, welche dann vom 
Linker vermisst werden. Bei Funktionen geht es prinzipiell genauso.

Hilfreich Hinweise gibt dir da auch Doxygen. Das kann dir Caller und 
Calles auflisten.

Oder nutz einen vernünftigen Editor/IDE, wie z.B. QtCreator. Der findet 
alle Stellen, an denen eine Variable oder eine Funktion verwendet wird, 
mit einem Mausklick.

Oliver
#4439069
Lesenswert?

Rolf M. schrieb:
> Soweit ich weiß unterscheiden gcc und ld aber nicht zwischen implizit
> und explizit mit 0 initialisierten Variablen. Nur Initialisierungswerte,
> die von 0 verschieden sind, werden anders behandelt.

Vielleicht wird irgendein Compiler oder eine Version eine explizite 
Initialisierung mit 0 zu einer impliziten Initialisierung machen. GCC 
4.7 und GCC 4.8 tun dies aber offenbar nicht automatisch.

> Ich kenne das so, dass sie in die Section .bss kommen

Aber erst im Linker. Der Compiler (einer solchen Umgebung) erzeugt nur 
einen "common" Eintrag in der Symboltabelle, mit der zu allozierenden 
Grösse. Wenn dieser Name anderswo als echte Definition auftaucht (unten 
"D"), dann wird der "C" Eintrag effektiv ignoriert.
1
extern int      extern_int;
2
int             implicit_int;
3
int             explicit_int = 0;
1
file.s:
2
        .comm   implicit_int,4,4
3
        .globl  explicit_int
4
        .data
5
        .align 4
6
explicit_int:
7
        .zero   4
1
file.o:
2
0000000000000000 D explicit_int
3
                 U extern_int
4
0000000000000004 C implicit_int
#4439589
Lesenswert?

A. K. schrieb:
>> Soweit ich weiß unterscheiden gcc und ld aber nicht zwischen implizit
>> und explizit mit 0 initialisierten Variablen. Nur Initialisierungswerte,
>> die von 0 verschieden sind, werden anders behandelt.
>
> Vielleicht wird irgendein Compiler oder eine Version eine explizite
> Initialisierung mit 0 zu einer impliziten Initialisierung machen. GCC
> 4.7 und GCC 4.8 tun dies aber offenbar nicht automatisch.

Korrektur: Beides ist ein wenig richtig und ein wenig falsch. :-)

Explizite Initialisierungen mit 0 werden erkannt, werden aber nicht wie 
bei impliziten Initialisierungen zu Commons, sondern landen explizit in 
.bss statt .data (obiger .s Code ist nicht korrekt). Damit entfällt der 
Platz im Image, aber es sind echte harte Definitionen, keine schwachen 
Definitionen wie implizit initialisierte Variablen.
1
        .bss
2
explicit_int:
3
        .zero   4
1
        .data
2
explicit_int:
3
        .long   1

Während man also in einer solchen Umgebung
file1.c:  int var;     //C
file2.c:  int var;     //C
schreiben kann, geht zwar auch
file1.c:  int var;     //C
file2.c:  int var = 0; //D
aber nicht
file1.c:  int var = 0; //D
file2.c:  int var = 0; //D
#4440898
Lesenswert?

In Eclipse kann man sich (wenn der Cursor auf einem Bezeichner steht) 
mit Ctrl+Alt+H die Aufrufer- und Aufrufgraphen jeweils in 
Baumdarstellung zeigen lassen.

Und obwohl der Menüpunkt "Call Hierarchy" eigentlich zunächst vermuten 
lässt es ginge nur für Funktionen funktioniert der Aufrufergraph 
ebensogut mit einer globalen Variablen als Wurzel. Dann siehst Du in 
welchen Funktionen die fragliche Variable verwendet wird.

Sehr praktische Sache das.

Und um mich meinem Vorredner anzuschließen: Doxygen (mit allen Optionen 
aktiviert, vor allem die Graphen) drüberlaufen lassen gibt auch eine 
stellenweise sehr hilfreiche Sicht auf die Struktur des Gesamtgebildes.

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren