Hallo
Ich benutzte avr-g++ und avr-gcc zum Kompilieren und avrdude um
Programme auf einen atmega328p zu übertragen (auf einem Arduino Uno,
aber ohne Arduino-IDE).
1. Ich laß, dass beim Kompilieren alle Funktionen die in einer
Headerdatei deklariert werden (und damit ihre Definitionen in der
entsprechenden *.c bzw. *.cpp Datei) mit in die Zieldatei eingebunden
werden, auch wenn im Programcode diese Funktionen garnicht benutzt
werden. Stimmt das? Währe es also besser, um den Progarmcode zu
minimieren, diese Funktionen raus zu schmeissen?
2. Weiter las ich, dass wenn man Klassen benutzt, der Compiler wirklich
nur die Dinge einbindet, die wirklich gebraucht werden und unbenutztes
außen vor lässt. Stimmt das? Wäre es also besser, alles mit Klassen zu
machen um Programmcode zu minimieren?
3. Wie sieht es dann mit struct aus, die ich wie eine Klasse benutzen
würde, um in C bleiben zu können?
4. Ich las, dass man Makros nicht für Konstanten benutzen soll, weil der
Compiler dafür keine Fehlererkennung anwenden kann: Also lieber
const uint8_t meine_Konstante = 1;
statt
#define meine_Konstante 1
Stimmt das?
5. Wie sieht es dann mit der Speicherbelegung aus? Das Makro verbraucht
ja keinen Speicherplatz. Verbraucht eine Konstante Speicherplatz?
Ich weiß, dass es hier nicht um kByte geht, aber mich interessiert das
jetzt mal, weil ich an einem Punkt in meinem Hobby AVR-Programmierung
stehe, wo ich die Arduino-IDE-Welt verlasse und ich denke, dass auch die
Antworten auf solche Fragen mich weiter bringen.
BTW: Ich habe einige Zeit mit dem QT-Creator Desktop-GUIs gebastelt und
habe mich immer gewundert, warum aus 10k Quellcode ein 10MB großes
Programm wird. Hat das mit den Antworten auf Frage 1 und 2 zu tun?
Danke für die Antworten im Voraus.
gfl
gfl schrieb:> Hallo> Ich benutzte avr-g++ und avr-gcc zum Kompilieren und avrdude um> Programme auf einen atmega328p zu übertragen (auf einem Arduino Uno,> aber ohne Arduino-IDE).> 1. Ich laß, dass beim Kompilieren alle Funktionen die in einer> Headerdatei deklariert werden (und damit ihre Definitionen in der> entsprechenden *.c bzw. *.cpp Datei) mit in die Zieldatei eingebunden> werden, auch wenn im Programcode diese Funktionen garnicht benutzt> werden. Stimmt das?
Jein. Zunächst werden alle Funktionen, die definiert wurden,
eingebunden. Es spielt keine Rolle, ob sie in einer Header-oder
Source-Datei eingebunden wurden (der Compiler kennt den Unterschied
sowieso nicht), und auch nicht ob sie zuvor deklariert wurden oder
nicht.
> Währe es also besser, um den Progarmcode zu> minimieren, diese Funktionen raus zu schmeissen?
Nein. Der GCC+LD kann nicht benutzte Funktionen (und globale Variablen)
automatisch entfernen. Dazu übergibst du beim Kompilieren
"-ffunction-sections -fdata-sections" und beim Linken
"-Wl,--gc-sections".
> 2. Weiter las ich, dass wenn man Klassen benutzt, der Compiler wirklich> nur die Dinge einbindet, die wirklich gebraucht werden und unbenutztes> außen vor lässt. Stimmt das?
Memberfunktionen werden hier genau so behandelt wie freie Funktionen,
also siehe 1. Membervariablen werden immer mit eingebunden, d.h.
verbrauchen Speicherplatz.
>Wäre es also besser, alles mit Klassen zu> machen um Programmcode zu minimieren?
Es ist sinnvoll, viel mit Klassen zu arbeiten, um die Programmstruktur
und Wiederverwendbarkeit zu verbessern. Mit Performance hat das nichts
zu tun.
> 3. Wie sieht es dann mit struct aus, die ich wie eine Klasse benutzen> würde, um in C bleiben zu können?
Gilt genau wie Klassen in C++, Elemente eines C-Struct werden nicht
entfernt falls nie benutzt.
> 4. Ich las, dass man Makros nicht für Konstanten benutzen soll, weil der> Compiler dafür keine Fehlererkennung anwenden kann: Also lieber> const uint8_t meine_Konstante = 1;> statt> #define meine_Konstante 1> Stimmt das?
Ja. Besser allerdings mit "static const", und in C++ mit "static
constexpr".
> 5. Wie sieht es dann mit der Speicherbelegung aus? Das Makro verbraucht> ja keinen Speicherplatz. Verbraucht eine Konstante Speicherplatz?
Prinzipiell schon, der Compiler kann sie aber in Instruktionen hinein
optimieren. Ein Makro an sich verbraucht keinen Platz, aber bei der
Verwendung muss der Compiler die Daten natürlich irgendwie ablegen - das
Ergebnis ist meist gleich zum "static const".
> BTW: Ich habe einige Zeit mit dem QT-Creator Desktop-GUIs gebastelt und> habe mich immer gewundert, warum aus 10k Quellcode ein 10MB großes> Programm wird. Hat das mit den Antworten auf Frage 1 und 2 zu tun?
Hast du statisch gelinkt? Qt ist ein riesiges Framework, das verbraucht
nunmal viel Speicher.
Programmierer schrieb:> Dazu übergibst du beim Kompilieren> "-ffunction-sections -fdata-sections" und beim Linken> "-Wl,--gc-sections".
PS: Die Arduino-IDE und auch andere IDEs machen das schon automatisch,
d.h. dort werden nicht benötigte Funktionen, funktionslokale statische
Variablen sowie globale Variablen bereits entfernt.
Programmierer schrieb:> PS: Die Arduino-IDE und auch andere IDEs machen das schon automatisch,> d.h. dort werden nicht benötigte Funktionen, funktionslokale statische> Variablen sowie globale Variablen bereits entfernt.
Das geht manchmal auch zu weit!
Drum kennt GCC das __attribute__((used))
Fragender schrieb:> Warum static?
Weil der Compiler dann weiß, dass die Variable nicht in anderen
Translation Units benutzt wird, und kann sie somit wegoptimieren falls
möglich, und z.B. als Immediate-Wert in eine Instruktion kodieren. Das
geht natürlich nicht wenn man einen Zeiger auf die Variable bildet (und
später erst dereferenziert). Zeiger auf Macros kann man natürlich nicht
bilden, auf Variablen/Konstanten aber schon - somit ermöglichen diese
eine größere Flexibilität, während der Compiler automatisch dafür sorgt
dass nur dann Speicher verbraucht wird, wenn es nicht anders geht.
EAF schrieb:> Das geht manchmal auch zu weit!
Aber nicht in "normalem" Code, nur ISR-Vektoren/Startup-Code o.ä.
static macht nicht unsichtbar wenn in einer .h verwendet? Hab das in
main.cpp probiert, man kann die Variable dennoch verwenden. Sollte man
nicht lieber namespace nehmen?
Fragender schrieb:> static macht nicht unsichtbar wenn in einer .h verwendet?
Doch. Wie gesagt, der Compiler kennt den Unterschied zwischen .c / .cpp
und .h nicht. Bei der Verwendung von "#include" wird einfach nur der
Inhalt der inkludierten Datei dorthin "kopiert". Der Compiler sieht nur
einen einzelnen langen Text.
Fragender schrieb:> Hab das in> main.cpp probiert, man kann die Variable dennoch verwenden.
Wenn mehrere .c / .cpp Dateien die gleiche .h-Datei inkludieren, welche
eine "static const"-Variable enthält, bekommst du letztendlich mehrere
Variablen gleichen Namens. Der Compiler weiß gar nicht dass diese
mehreren Variablen auf die selbe Codestelle zurückgehen, weil er beim
Compilieren der einzelnen .c-Dateien gar nicht weiß dass eine
Header-Datei im Spiel ist. Würdest du in diesem Fall das "static"
weglassen, würdest du "Multiple-Definition"-Errors vom Linker bekommen.
Eine "static const"-Variable in einem Header zu definieren ist aber gar
nicht so verkehrt, so kannst du sie in jeder Source-Datei nutzen und der
Compiler optimiert sie jedes Mal weg. Nur bei größeren Datentypen sollte
man das nicht so machen.
Fragender schrieb:> static macht nicht unsichtbar wenn in einer .h verwendet? Hab das in> main.cpp probiert, man kann die Variable dennoch verwenden.
Ein als static definierte Variable ist nur für die Übersetzungseinheiten
sichtbar, in welcher sie definiert ist.
Der Hauptzweck:
Weniger Namenskollisionen.
z.B. darf jede Einheit eine eigene mit "count" benannte Variable
besitzen.
Und ja, mit anonymen Namensräumen kann man ähnliches erreichen.
Kann nicht sein. Eine Variable in einer .cpp ist in main.cpp nicht
sichtbar und auch nicht benutzbar. Die ist immer lokal und der
Übersetzungseinheit vorenthalten.
staticTest.h
1
#pragma once
2
#include<avr/io.h>
3
#include<stddef.h>
staticTest.cpp
1
#include"staticTest.h"
2
staticconstexpruint8_tvar{100};// woanders nicht benutzbar
Nächster Test ohne .cpp
staticTest.h
1
#pragma once
2
#include<avr/io.h>
3
#include<stddef.h>
4
constexpruint8_tvar{100};// überall benutzbar
staticTest2.h
1
#pragma once
2
#include<avr/io.h>
3
#include<stddef.h>
4
constexpruint8_tvar{100};// überall benutzbar
Ich kann in main.cpp die Variable 'var' verwenden egal ob static oder
nicht static. Warnungen oder Fehler gibts auch keine. Diese Variable
'var' wird in einem delay zum Led blinken verwendet. Kann also nicht
unbenutzt wegoptimiert werden. Irgendwie stimmt nicht. Hier müßte eine
Fehlermeldung kommen.
Fragender schrieb:> Ich kann in main.cpp die Variable 'var' verwenden egal ob static oder> nicht static. Warnungen oder Fehler gibts auch keine.
Zeig mal alle dabei involvierten Header und Source-Dateien. Aus deinem
Beitrag ist die genaue Konstellation nicht ersichtlich.
EAF schrieb:> Ähnlich:
Überhaupt nicht ähnlich, "#define" ist eine stupide Textersetzung,
"constexpr" definiert eine C++-Variable mit Scope, Typ, ggf. Speicher
und eben Compiletime-Evaluation.
Programmierer schrieb:> Keinswegs:
Hier dreht es sich um Optimierung und Namensräume.
Und nicht darum, dass int der default Type für numerische Literale ist.
Gibt keine Fehler. Hier hatte ich gedacht das static lokal macht. Klarer
Denkfehler wegen Headerfile Inkludierung. Hattest du schon erklärt. Ich
halte das static für verwirrend je nachdem wo es steht, dann lieber
gleich namespace zum kapseln.
Nochmal zum mitmeißeln. Wenn 'static const' in einem Headerfile vewendet
wird und diese Variable nirgends woanders verwendet wird, dann kann der
Compiler das optimieren? Aber wehe man verwendet sie irgendwo?
Nur benötigt man dazu eigentlich nicht noch export?
Vielen Dank für die erhellenden Antworten. Ich muss allerdings noch mal
darüber nachdenken, bis ich alles verstanden habe. Jedoch:
Programmierer schrieb:> Eine "static const"-Variable in einem Header zu definieren ist aber gar> nicht so verkehrt, so kannst du sie in jeder Source-Datei nutzen und der> Compiler optimiert sie jedes Mal weg.
????
Der Compiler optimiert sie weg, wenn sie nicht benutzt wird? Oder? Das
habe ich jetzt nicht verstanden.
gfl schrieb:> Der Compiler optimiert sie weg, wenn sie nicht benutzt wird? Oder? Das> habe ich jetzt nicht verstanden.
Er setzt sie so ein, wie er ein Literal einsetzen würde.
Der Wert taucht dann schon noch im Programmcode auf. Aber nicht im RAM
als (readonly) Variable.
Es sei denn, man richtet einen Zeiger darauf, oder fordert sonstwie die
Adresse der Variablen an.