mikrocontroller.net

Forum: Compiler & IDEs Konstanten-Deklaration


Autor: TechInfo (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,

ich habe eine .c-Datei in der nur ein Funktion steht. Diese Funktion 
benötigt drei Konstanten. Wo sollte ich diese jetzt deklarieren? 
Außerhalb oder innerhalb der Funktion?

Kann man Konstanten auch static deklarieren?

Autor: Rufus Τ. Firefly (rufus) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Wenn Du tatsächlich Konstanten meinst, ja, die lassen sich auch static 
deklarieren. Innerhalb einer Funktion dürfte das albern sein, aber 
innerhalb eines C-Modules ist das durchaus sinnvoll:
static const int MeineKonstante = 17;

Häufig werden aber gar keine C-Konstanten verwendet, sondern 
Präprozessor-Defintionen:
#define MEINEKONSTANTE 17

Die können per se nicht static sein (sind ja gar keine C-Objekte) und 
sollten -der Übersichtlichkeit halber- am Anfang des C-Quelltextes 
stehen und mit sinngebenden Kommentaren versehen sein.

Autor: TechInfo (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Es handelt sich um String-Konstanten.

Diese werden nur in dieser einen Funktion in dieser einen Datei 
benötigt.

Ich habe also die Wahl, sie außerhalb der Funktion als

static const char* string="Test";

zu deklarieren, oder innerhalb der Funktion ohne static (macht ja als 
static keinen Sinn).

Was ist denn sinvoller? Wird eine lokale Konstante wie eine lokale 
Variable bei jedem Funktionsaufruf neu generiert?

Autor: kosmonaut_pirx (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
hallo,
eine static-konstante in der routine macht durchaus sinn. sie wird 
einmal initialisiert und bleibt immer gleich. cmiiw.

schade, dass immer gleich der präprozessor-hammer geschwungen wird. 
grade im mirkrocontroller-beriech mit derart eingeschränktem debugging 
braucht man jede hilfe vom compiler, die man kriegen kann.

bye kosmo

Autor: TechInfo (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
D.h. eine static-Konstante innerhalb einer Funktion verhält sich wie 
eine static-Variable und wir nicht immer wieder zerstört bzw. neu 
definiert?

Was ist denn nun sinnvoller in meinem Fall?

Autor: Rufus Τ. Firefly (rufus) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> eine static-konstante in der routine macht durchaus sinn. sie wird
> einmal initialisiert und bleibt immer gleich. cmiiw.

Was würde denn eine nicht-static-Konstante in einer Funktion anderes 
machen?

Anstelle von
static const char* string="Test";

solltest Du
static const char* const string="Test";

verwenden.

Ersteres verbietet nur
string[0] = 'A';

letzteres verbietet auch
string = "bla";



Autor: yalu (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
... oder
static const char string[] = "Test";
Dann kannst du sicher sein, dass der Compiler unabhängig von seinen
Optimierungsfähigkeiten nur 1 Objekt erstellt, nämlich den String
"Test".
static const char *const string = "Test";
erzeugt prinzipiell 2 Objekte, nämlich den String "Test" und einen
Pointer auf diesen String. Da letzterer lokal ist, optimiert ihn der
GCC weg, so dass kein Unterschied zwischen den beiden Lösungen
besteht. Wäre string hingegen global, würde zusätzlicher Speicherplatz
für den Pointer verbraucht.

Autor: Rolf Magnus (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@kosmonaut_pirx: (Hat der was mit Captain Pirk aus Star Wreck zu tun?)

> schade, dass immer gleich der präprozessor-hammer geschwungen wird.
> grade im mirkrocontroller-beriech mit derart eingeschränktem debugging
> braucht man jede hilfe vom compiler, die man kriegen kann.

In C ist das manchmal etwas schwierig, da Konstanten dort keine echten 
Konstanten sind. Das ist in C++ anders, aber das wird eher selten 
eingesetzt, obwohl es gerade auch im Mikrocontrollerbereich einige 
Vorzüge hat.

@yalu:

> static const char string[] = "Test";
>
> Dann kannst du sicher sein, dass der Compiler unabhängig von seinen
> Optimierungsfähigkeiten nur 1 Objekt erstellt, nämlich den String
> "Test".

Genau dann eigentlich nicht, denn string wird hier (zumindest ohne 
passende Optimierung) als Kopie der Stringkonstante "Test" angelegt.

> static const char *const string = "Test";
>
> erzeugt prinzipiell 2 Objekte, nämlich den String "Test" und einen
> Pointer auf diesen String.

Ja.

> Da letzterer lokal ist, optimiert ihn der GCC weg, so dass kein
> Unterschied zwischen den beiden Lösungen besteht. Wäre string hingegen
> global, würde zusätzlicher Speicherplatz für den Pointer verbraucht.

Außer wenn er auch da als static deklariert wird. In diesem Fall kann 
der Compiler ihn ebenfalls wegoptimieren.

Autor: Rufus Τ. Firefly (rufus) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
OT:

"Kosmonaut Pirx" hat nichts, aber auch gar nichts mit Herrn Roddenberry 
zu tun.
Sondern mit Stanisław Lem, einem beeindruckenden polnischen SF-Autor, 
Universalphilosophen und mehr. Die Romanvorlage für "Solaris" stammt 
auch von ihm.

Autor: yalu (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@Rolf Magnus:

>> static const char string[] = "Test";
>>
>> Dann kannst du sicher sein, dass der Compiler unabhängig von seinen
>> Optimierungsfähigkeiten nur 1 Objekt erstellt, nämlich den String
>> "Test".
>
> Genau dann eigentlich nicht, denn string wird hier (zumindest ohne
> passende Optimierung) als Kopie der Stringkonstante "Test" angelegt.

Du meinst, da wird zweimal die Zeichenfolge "Test" angelegt, oder habe
ich dich falsch verstanden? Das ist dann der Fall, wenn string eine
automatiche Variable in einer Funktion ist. Ein schlechter Compiler,
der den const-Qualifier ignoriert, wird den Text "Test" bei jedem
Aufruf der Funktion in die Variable kopieren -> doppelter
Speicherverbrauch. Das passiert aber bei initialisierten statischen
Variablen (statisch im Sinne von funktionslokal mit static oder global
mit oder ohne static) nicht. Sie haben ihren Wert bereits beim
Programmstart, da werden keine Kopien angelegt.

Ok, beim AVR wird architekturbedingt der Wert i. Allg. erst ins RAM
kopiert, aber das passiert bei allen in diesem Thread vorgestellten
Alternativen und kann bekannterweise mittels PROGMEM verhindert
werden.

Ich bin nach wie vor der Meinung, dass bei der von mir vorgeschlagenen
Definition von string im Vergleich zu den anderen Lösung in diesem
Thread je nach Qualität der Optimierung Datenspeicherplatz bis zur
Größe eines Pointers eingespart, aber in keinem Fall zusätzlicher
Datenspeicher verbraucht wird.

Der Platz für den Pointer wird bei Compilern, die nicht zwischen
einzelnen Übersetzungseinheiten optimieren (also praktisch allen),
sogar sicher eingespart, wenn string global und nicht static ist.

>> Da letzterer lokal ist, optimiert ihn der GCC weg, so dass kein
>> Unterschied zwischen den beiden Lösungen besteht. Wäre string hingegen
>> global, würde zusätzlicher Speicherplatz für den Pointer verbraucht.
>
> Außer wenn er auch da als static deklariert wird. In diesem Fall kann
> der Compiler ihn ebenfalls wegoptimieren.

Mit 'lokal' meinte ich nicht lokal innerhalb einer Funktion, sondern
lokal innerhalb einer Übersetzungseinheit (so dass die Benutzung der
Variable für den Compiler vollständig sichtbar ist). Dazu zählen auch
globale static-Variablen.

Autor: Rolf Magnus (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> "Kosmonaut Pirx" hat nichts, aber auch gar nichts mit Herrn Roddenberry
> zu tun.

Star Wreck hat ja auch nur teilweise damit zu tun.

> Sondern mit Stanisław Lem, einem beeindruckenden polnischen SF-Autor,
> Universalphilosophen und mehr. Die Romanvorlage für "Solaris" stammt
> auch von ihm.

Da muß ich mich wohl als Kulturbanausen outen. Solaris habe ich weder 
gesehen, noch gelesen, und den Autor oder einen Kosmonaut Pirx kenne ich 
nicht.

Autor: Rolf Magnus (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> Du meinst, da wird zweimal die Zeichenfolge "Test" angelegt, oder habe
> ich dich falsch verstanden?

Du hast richtig verstanden.

> Sie haben ihren Wert bereits beim Programmstart, da werden keine Kopien
> angelegt.

Nun, "Test" und string sind zwei getrennte Objekte. Ein Compiler wird 
zwar in der Regel erkennen, daß ein Programm das nicht feststellen kann 
und daher eine entsprechende Optimierung durchführen, aber du sagtest 
ja, es sei vollkommen unabhängig von irgendwelchen Optimierungen.

> Der Platz für den Pointer wird bei Compilern, die nicht zwischen
> einzelnen Übersetzungseinheiten optimieren (also praktisch allen),
> sogar sicher eingespart, wenn string global und nicht static ist.

Ja. Man kann aber den Pointer in einem Header static deklarieren.

>> Da letzterer lokal ist, optimiert ihn der GCC weg, so dass kein
>> Unterschied zwischen den beiden Lösungen besteht. Wäre string hingegen
>> global, würde zusätzlicher Speicherplatz für den Pointer verbraucht.
>
> Außer wenn er auch da als static deklariert wird. In diesem Fall kann
> der Compiler ihn ebenfalls wegoptimieren.

> Mit 'lokal' meinte ich nicht lokal innerhalb einer Funktion, sondern
> lokal innerhalb einer Übersetzungseinheit

Ach so. Für mich ist immer alles, was nicht lokal in einer Funktion ist, 
global, auch wenn es sich auf eine einzelne Übersetzungseinheit 
beschränkt. Vielleicht könnte man ja den Begriff "regional" dafür 
einführen. ;-)

Autor: Rufus Τ. Firefly (rufus) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Tja - dann geh mal in die Buchhandlung/Bibliothek Deiner Wahl.

"Der Unbesiegbare", "Eden", "Die Piloten", "Die Astronauten" und 
natürlich "Solaris" sind eher ernstgemeinte Romane, während die 
"Sterntagebücher", "Der futurologische Kongress", "Waffensysteme des 21. 
Jahrhunderts", "Memoiren, gefunden in der Badewanne" etc. nur schwer 
einzuordnen sind.

Hervorragende Übersetzungen liefert hier der Suhrkamp-Verlag; die 
zeitgleich in der DDR erschienenen Übersetzungen sind teilweise sehr 
"sozialistisch". Ein direkter Vergleich von "Die Astronaten" (Suhrkamp) 
und "Der Planet des Todes" (DDR) ist gerade in den ersten Kapiteln sehr 
interessant.
Dieses Buch wurde übrigens Anfang der 60er als "Der schweigende Stern" 
von der DEFA verfilmt, ein nach heutigen Gesichtspunkten eher ... 
merkwürdiger Film.

Ander Bücher wie "Der Transfer" zeigen in Details eine erschreckend 
genaue Vorherahnung der heutigen Welt (im 1961 geschriebenen Buch wird 
die für uns "normale" alles überflutende Werbung recht schön 
dargestellt).

Teilweise schon recht alt, aber dennoch lesenswert.

Hier die "offizielle" Seite des 2006 verstorbenen Lem
http://www.lem.pl/cyberiadinfo/english/main.htm

Autor: TechInfo (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Karl-Heinz Buchegger hat mal in einem Posting geschrieben, dass man 
Strings die zur Laufzeit nicht verändert werden sollen, immer als 
Pointer deklariert

const char* string="Teststring";

Nur wenn innerhalb des Strings "gearbeitet" werden soll, sollte ein 
Array definiert werden.

Anscheinend gehen da die Meinungen aber auseinander.

Autor: yalu (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@Rolf:

> Nun, "Test" und string sind zwei getrennte Objekte. Ein Compiler
> wird zwar in der Regel erkennen, daß ein Programm das nicht
> feststellen kann und daher eine entsprechende Optimierung
> durchführen

Ok, das ist vielleicht etwas Auslegungssache. Für mich ist "Test" ein
Objekt, auf das über das Symbol string zugegriffen werden kann.

  static const char string[] = "Test";

definiert eine Bindung, die das Symbol string und das Objekt "Test"
unlösbar miteinander verknüpft.

Aber es kann gut sein, dass das der C-Standard etwas anders sieht.
Letztendlich ist relevant, was typische Compiler daraus machen und ob
dies der Vorstellung des Programmierers entspricht.

> Ja. Man kann aber den Pointer in einem Header static deklarieren.

Dann ist er in jeder Übersetzungseinheit regional (um deinen Ausdruck,
den ich sehr gelungen finde, aufzugreifen) und kann somit wegoptimiert
werden. Aber wo schreibst du dann die Initialisierung ("Test") hin? In
die Deklaration des Pointers? Dann besteht aber die Gefahr, dass so
viele "Test"s angelegt werden, wie der Header Male includet wird.

@TechInfo:

Zur Unterscheidung, ob veränderlich oder nicht, gibt es ja den
const-Qualifier. Mir ist nicht ganz klar, warum man ein weiteres
Unterscheidungsmerkmal einführen sollte.

Aber meine Meinung ist alles andere als const. Hast du zufälligerweise
den Link auf das Posting von Karl Heinz parat?

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
TechInfo wrote:

>
> const char* string="Teststring";
>
> Nur wenn innerhalb des Strings "gearbeitet" werden soll, sollte ein
> Array definiert werden.

Das 'soll' muss in diesem Fall ein 'muss' sein.
Allerdings kann es dann klarerweise kein

const char string[]

mehr sein :-)

Hm. Wenn ich es mir recht überlege, habe ich über dieses
Konstrukt
  const char string[]
noch nie wirklich nachgedacht. Da muss ich noch ein bischen
drüber brüten :-)


Autor: Rolf Magnus (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> Ok, das ist vielleicht etwas Auslegungssache. Für mich ist "Test" ein
> Objekt, auf das über das Symbol string zugegriffen werden kann.
>
>  static const char string[] = "Test";
>
> definiert eine Bindung, die das Symbol string und das Objekt "Test"
> unlösbar miteinander verknüpft.

Und was wenn string nicht const ist? "Test" ist eine Stringkonstante, 
die nicht verändert werden darf, string darf aber verändert werden. Ich 
wüßte jetzt nichts von einer Sonderregelung, die sagt, daß nur kopiert 
werden soll, wenn die Variable nicht const ist.

> Letztendlich ist relevant, was typische Compiler daraus machen und ob
> dies der Vorstellung des Programmierers entspricht.

Allerdings. Notfalls schaut man halt im generierten ASM-Code nach.

> Aber wo schreibst du dann die Initialisierung ("Test") hin?

Die Frage kam mir dann auch, aber erst nach dem Posten.

> In die Deklaration des Pointers? Dann besteht aber die Gefahr, dass so
> viele "Test"s angelegt werden, wie der Header Male includet wird.

gcc bzw. der Linker erkennen das. Der macht sogar ganz kranke 
Optimierungen. Beispiel:
#include <stdio.h>

int main()
{
    const char* p1 = "Hallo Welt";
    const char* p2 = "Welt";
    printf("%d\n", p2 - p1);
    return 0;
}

Obiges Programm für Linux mit Optimierungen compiliert gibt bei mir aus:

6

Das heißt, er hat bemerkt, daß die Stringkonstanten teilweise gleich 
sind und sie zu einer zusammengezogen.

Autor: Simon K. (simon) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Rolf Magnus wrote:
> ...
> Obiges Programm für Linux mit Optimierungen compiliert gibt bei mir aus:
>
> 6
>
> Das heißt, er hat bemerkt, daß die Stringkonstanten teilweise gleich
> sind und sie zu einer zusammengezogen.

Welcher Compiler denn?

Autor: Matthias (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Blöde Frage:
------------
Was für ein Compiler und was für eine Anwendung?

Wenn man das für einen PC schreiben will, dann sieht das anders aus,
wie wenn man das für einen µC schreiben will.

Beispiel (Visual C++):
----------------------

Wenn ich die Zeichenkette nur einmal brauch

FunktionX( _T("Dies ist der Text"), ... );

Brauch ich die nur in einer Funktion geht auch:

CString xyz = "Dies ist der Text"; //bzw.
CString xyz = _T("Dies ist der Text");

Beispiel (ATMEL AVR-gcc):
-------------------------

const char * xyz = "Text";

FunktionX(...)
{
...
pgm_readbyte(xyz);
...
}

etc....

Also wofür soll die Konstante benutzt werden?

Autor: yalu (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Rolf Magnus schrieb:

> Und was wenn string nicht const ist? "Test" ist eine
> Stringkonstante, die nicht verändert werden darf, string darf aber
> verändert werden. Ich wüßte jetzt nichts von einer Sonderregelung,
> die sagt, daß nur kopiert werden soll, wenn die Variable nicht const
> ist.

Ohne const ist es das Gleiche. Vielleicht noch einmal ein Versuch,
darzustellen, was ich glaube, wie der Compiler die unterschiedliche
Initialisierungen interpretiert:

Ein statisches (mit static und/oder global)
char string[] = "Test";
egal, ob mit oder ohne const, weist den Compiler an, im Speicher ein
char-Array gegeigneter Größe anzulegen mit Namen string und dafür zu
sorgen, dass in diesem Speicherbereich beim Start des Programms die
Zeichen 'T', 'e', 's', 't' und '\0' stehen. "Test" ist somit keine
Konstante im üblichen Sinne, sondern ein Initialisierungswert. Weil es
sich aber nicht um eine Stringkonstante handelt, darf dieser
Speicherbereich nach Belieben überschrieben werden, wobei dies
optional durch const verhindert werden kann.

Ein statisches (mit static und/oder global)
char *string = "Test";
(wieder wahlweise mit oder ohne const) weist den Compiler ebenfalls
an, im Speicher ein char-Array geeigneter Größe anzulegen und dafür zu
sorgen dass es mit dem entsprechenden Inhalt gefüllt wird. Im
Gegensatz zum obigen Beispiel hat das Array aber keinen Namen. Es
handelt sich um den gleichen anonymen Objekttyp wie beispielsweise
eine Stringkonstante, die an eine Funktion übergeben wird wie in
printf("Test"). Das char-Array ist (zumindest in C90) der einzige
Datentyp, der in dieser Weise verwendet werden kann, mit int-Arrays
oder Strukturen geht das nicht. Diese anonymen Strings dürfen nicht
beschrieben werden, so dass Compiler und Linker verschiedene
Optimierungen darauf anwenden können. Zusätzlich zu ersten Beispiel
wird im Speicher noch eine Pointervariable mit dem Namen string
angelegt, und dafür gesorgt, dass diese beim Programmstart die Adresse
der anonymen Konstante "Test" enthält. Der Initialisierungswert ist
als in diesem Fall nicht die Zeichenfolge "Test" (wie im ersten
Beispiel), sondern die Speicheradresse, an der dieser String anonym
abgelegt wurde. In bestimmten Fällen wird nun diese Pointervariable
wegoptimiert, so wie es mit vielen anderen unnützen Variablen
ebenfalls passiert.

> gcc bzw. der Linker erkennen das. Der macht sogar ganz kranke
> Optimierungen.
> ...
> Das heißt, er hat bemerkt, daß die Stringkonstanten teilweise gleich
> sind und sie zu einer zusammengezogen.

Das ist cool! Ich habe nicht gewusst, dass sogar Stringkonstanten, die
Suffixe eines anderen Strings sind, wegoptimiert werden. Das wird, wie
ich gerade ausprobiert habe, nur bei diesen anonymen Stringkonstanten
gemacht, übrigens auch, wenn kein const davor steht. Bei
initialisierten char-Array wie im ersten Beispiel greift die
Optimierung nicht, und das trotz const. Damit hast du ein starkes
Argument für die Deklaration mit char-Pointern gefunden.

Leider scheint diese Optimierung nur in der x86-Version des GCC bzw.
der Binutils drin zu sein. Die AVR-Version lässt beiden Strings stehen
(Ich habe GCC 4.1.2 für x86 und AVR und Binutils 2.16.1 für x86 und
2.17 mit coff-avr-patch für AVR).

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.