www.mikrocontroller.net

Forum: Compiler & IDEs #define vs. const


Autor: marjus (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo!

Ich mache zur Zeit einen Kurs in der Programmiersprache C und habe einen 
ziemlich kleinkarierten Lehrer.
Neulich haben wir die Datentypen durchgenommen. Anschließend kamen die 
Konstanten dran.

Nun meine Frage:
Wo liegt der Unterschied zwischen einer konstanten Variablen (const int 
xyz;) und der Präprozessordirektive (#define ) um Konstanten zu 
definieren?

Der Lehrer meinte, dass die #define-Variante, um eine Konstante zu 
definieren, wohl heutzutage nicht mehr genutzt wird.
-Sieht man allerdings einige Listings hier im Forum, so würde ich 
wetten, dass die #define-Variante doch häufiger anzutreffen ist.

Warum wird sie "heutzutage nicht mehr genutzt"/wo liegen die 
Unterschiede?



Vielen Dank!

Autor: MeinerEiner (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Naja #define ist nur eine Textersetzung.
Bei ner const ist halt ne Typenprüfung mit dabei.
Also "const ui8_t test = 583" wird ne Warning werfen.

Wobei ich z.B. noch nie consts verwendet hab, sondern immer nur defines.

Autor: A. K. (prx)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
marjus schrieb:

> Wo liegt der Unterschied zwischen einer konstanten Variablen (const int
> xyz;) und der Präprozessordirektive (#define ) um Konstanten zu
> definieren?

"const" sind Variablen, die nicht verändert werden dürfen. Nicht 
geeignet als lexikalische Konstanten, also nicht äquivalent zu Zahlen. 
Folglich ist
    const int N = 10;
    int array[N];
in C nicht möglich.

> Der Lehrer meinte, dass die #define-Variante, um eine Konstante zu
> definieren, wohl heutzutage nicht mehr genutzt wird.

Da liegt er völlig falsch. Es gibt in C weiterhin keine Alternative. In 
C++ sieht das völlig anders aus, da ist "const" anders definiert.

Autor: Peter (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>Der Lehrer meinte, dass die #define-Variante, um eine Konstante zu
>definieren, wohl heutzutage nicht mehr genutzt wird.

Da erzält er Mist...!

Autor: jozi59 (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Mit const int wird einer Konstante ein Speicherplatz zugewiesen. Bei 
#define wird der Wert direkt per Opcode, also direkt in dem 
auszuführenden Programm abgelegt.

Die Vorteile von const sind.
- Es gibt eine feste Speicherstelle, auf die auch mit einem Pointer 
zugegriffen werden kann.
- Die Konstante wird nur einmal im Speicher gehalten, wogegen Konstanten 
per #define bei jedem Aufruf wieder als Wert im Opcode auftauchen.
- Wenn man die Konstante mit const im RAM-Bereich ablegt, kann man sie 
beim Debuggen verändern, wenn man möchte.

Man kann sich das so erklären.
#define sind praktisch Makros, die vor dem eigentlichen Compilerlauf, 
also vom Präprozessor, im Source-Code ausgetauscht werden.

Autor: Peter Dannegger (peda)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
const legt eine Variable an.
Man braucht also Speicherplatz dafür und man braucht Code, um die 
Variable aus dem Speicher zu laden.

#define ist eine Textersetzung im Präprozessor.
Es wird dann jedes Auftreten des define-Namens durch die Zahl ersetzt.
D.h. es wird kein Speicherplatz benötigt und es wird auch weniger Code 
erzeugt.
Ein Register kann direkt mit der Zahl geladen werden, ein Vergleich 
direkt mit der Zahl erfolgen.

In der MC-Programmierung ist deswegen const recht ungebräuchlich.


Peter

Autor: A. K. (prx)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Peter Dannegger schrieb:

> In der MC-Programmierung ist deswegen const recht ungebräuchlich.

Es ist durchaus gebräuchlich, aber eben nicht für lexikalische 
Konstanten, sondern für Daten/Tabellen/Strings die ins ROM sollen.

Autor: Jörg Wunsch (dl8dtl) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
A. K. schrieb:

> Folglich ist
>     const int N = 10;
>     int array[N];
> in C nicht möglich.

Jein.  In C99 ist es möglich, wenn es sich innerhalb einer Funktion
befindet -- allerdings nur deshalb, weil es dann dort als "variably
modified array" gilt.

Autor: Jörg Wunsch (dl8dtl) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Peter Dannegger schrieb:

> const legt eine Variable an.
> Man braucht also Speicherplatz dafür und man braucht Code, um die
> Variable aus dem Speicher zu laden.

Bevor das hier gebetsmühlenartig wiederholt wird: das ist in der Form
auch nicht korrekt.  Es kann Speicherplatz und Code dafür benötigt
werden, aber es muss nicht.  Gerade das genannte
const int N = 10;

ist ein guter Kandidat, bei dem die Variable N vom Optimierer
weggeworfen wird, und die Zahl 10 stattdessen im Assemblercode als
Direktoperand landet.

Autor: A. K. (prx)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Jörg Wunsch schrieb:

> vom Optimierer
> weggeworfen wird, und die Zahl 10 stattdessen im Assemblercode als
> Direktoperand landet.

Yep, inlinen kann er, aber solange das Dings global ist wird der 
Compiler nicht drum herum kommen, dafür Platz vorzusehen, weil eben auch 
exportiert.

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

Bewertung
0 lesenswert
nicht lesenswert
A. K. schrieb:
> Jörg Wunsch schrieb:
>
>> vom Optimierer
>> weggeworfen wird, und die Zahl 10 stattdessen im Assemblercode als
>> Direktoperand landet.
>
> Yep, inlinen kann er, aber solange das Dings global ist wird der
> Compiler nicht drum herum kommen, dafür Platz vorzusehen, weil eben auch
> exportiert.

Jein.
Wenn so was als globale Konstante benötigt wird, egal ob in einem Header 
File oder File-global, dann macht man das als
static const int N = 10;

und dann darf der Optimizer wieder zuschlagen :-)

Autor: Marcus Harnisch (mharnisch) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Peter Dannegger schrieb:
> const legt eine Variable an.
> Man braucht also Speicherplatz dafür und man braucht Code, um die
> Variable aus dem Speicher zu laden.

Der Compiler muss aber keine Variable anlegen, sofern nicht auf die 
Adresse des const-Objekts zugegriffen wird.

Auf der anderen Seite brauche ich u.U. natürlich auch Speicherzugriffe 
um einen durch #define erzeugten Wert aus dem Speicher zu holen, wenn er 
nicht als immediate darstellbar ist.

Leider lässt es sich nicht pauschal sagen, dass das eine dem anderen 
überlegen ist.

Gruß
Marcus
http://www.doulos.com/arm/

Autor: A. K. (prx)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Karl heinz Buchegger schrieb:

> und dann darf der Optimizer wieder zuschlagen :-)

Yep, nur stand das "static" grad eben noch nicht drin als ich das 
kommentierte ;-). Ausserdem kommt mir der Begriff "File global" ein 
bischen ungewohnt vor, ich kenne das als "file scope" und damit eben 
nicht global.

Autor: Peter Dannegger (peda)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Jörg Wunsch schrieb:
> Es kann Speicherplatz und Code dafür benötigt
> werden, aber es muss nicht.

Das ist aber ne völlig andere Baustelle.

Dem Optimierer ist es völlig wurscht, ob die Variable const ist.
Er kann auch normale Variablen wegoptimieren.

Z.B. in Schleifen optimiert er leider oft die Zählvariable weg, was dann 
größeren Code erzeugt.


Peter

Autor: A. K. (prx)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Marcus Harnisch schrieb:

> Der Compiler muss aber keine Variable anlegen, sofern nicht auf die
> Adresse des const-Objekts zugegriffen wird.

Was er aber nur weiss, wenn das Ding eben nicht global ist, sondern wie 
grad aufgeführt lokal oder file scope (aka static) ist.

Bei globalen Daten kann er das üblicherweise nicht wissen.

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

Bewertung
0 lesenswert
nicht lesenswert
A. K. schrieb:
> Karl heinz Buchegger schrieb:
>
>> und dann darf der Optimizer wieder zuschlagen :-)
>
> Yep, nur stand das "static" grad eben noch nicht drin als ich das
> kommentierte ;-).

Ich weiß :-)
Drum habe ich es ja auch noch ergänzt.

> Ausserdem kommt mir der Begriff "File global" ein
> bischen ungewohnt vor,

Es war noch früh und ich hatte noch keinen Kaffee

Autor: Jörg Wunsch (dl8dtl) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Peter Dannegger schrieb:

> Dem Optimierer ist es völlig wurscht, ob die Variable const ist.

Nein.  Wenn sie const ist, kann er auch davon ausgehen, dass sie
keinen anderen Wert als den des initializers hat, damit kann er
diesen als immediate eintragen.  Das geht bei einer nicht-const-
Variablen nicht, auf die muss er zugreifen.
const int i = 42;
int j = 23;

int return_i(void)
{
        return i;
}

int return_j(void)
{
        return j;
}

ergibt:
.global return_i
        .type   return_i, @function
return_i:
/* prologue: function */
/* frame size = 0 */
        ldi r24,lo8(42)
        ldi r25,hi8(42)
/* epilogue start */
        ret
        .size   return_i, .-return_i
.global return_j
        .type   return_j, @function
return_j:
/* prologue: function */
/* frame size = 0 */
        lds r24,j
        lds r25,j+1
/* epilogue start */
        ret
        .size   return_j, .-return_j

Natürlich tritt in diesem Fall der genannte Effekt ein, dass er für
die Variable i trotzdem Speicherplatz belegt, da sie global ist und
der Compiler daher nicht entscheiden kann, ob andere translation
units sie ggf. noch benötigen.  Ich wollte sie aber nicht `static'
machen, denn bei static kann er auch für `j' entscheiden, dass diese
Variable keinen anderen Wert als 23 annehmen kann, und ersetzt die
Konstante dann ebenfalls.

Autor: Marcus Harnisch (mharnisch) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
A. K. schrieb:
> Was er aber nur weiss, wenn das Ding eben nicht global ist, sondern wie
> grad aufgeführt lokal oder file scope (aka static) ist.
>
> Bei globalen Daten kann er das üblicherweise nicht wissen.

Auch nur halb richtig. Der Compiler kann innerhalb der /compilation 
unit/ den Wert direkt verwenden, falls die const Variable einen 
Initialwert hat. Er muss aber trotzdem eine Variable anlegen.

Man sollte sich deshalb davor hüten, initialisierte const Variablen zu 
verwenden, die man später durch patchen der image Datei ändern möchte.

Gruß
Marcus
http://www.doulos.com/arm/

Autor: Peter Dannegger (peda)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Jörg Wunsch schrieb:
>  Das geht bei einer nicht-const-
> Variablen nicht, auf die muss er zugreifen.

Das sind aber Feinheiten der Optimierung und geht etwas am 
ursprünglichen Thema vorbei.

Wenn die Variable nur in der Compile-Unit existiert, ist es ihm wurscht, 
ob const oder nicht.
Z.B. hier ne lokale Variable i, die komplett wegoptimiert wurde:

uint8_t d[8];

void test( uint8_t *s )
{
  56:   dc 01           movw    r26, r24
  58:   e0 e0           ldi     r30, 0x00       ; 0
  5a:   f1 e0           ldi     r31, 0x01       ; 1
  uint8_t i;

  for( i = 0; i < sizeof( d ); i++ )
    d[i] = *s++;
  5c:   8d 91           ld      r24, X+
  5e:   81 93           st      Z+, r24
  60:   81 e0           ldi     r24, 0x01       ; 1
  62:   e8 30           cpi     r30, 0x08       ; 8
  64:   f8 07           cpc     r31, r24
  66:   d1 f7           brne    .-12            ; 0x5c <test+0x6>
    d[i] = *s++;
}
  68:   08 95           ret

Und mit
--combine -fwhole-program
kann er auch ohne "const" globale Variablen mit nur einer Zuweisung 
wegoptimieren.


Peter

Autor: Jörg Wunsch (dl8dtl) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Peter Dannegger schrieb:

> Wenn die Variable nur in der Compile-Unit existiert, ist es ihm wurscht,
> ob const oder nicht.

Ja, schrieb ich ja.  Mit "const" kann er das aber immer wegoptimieren,
was lediglich deine Behauptung widerlegen sollte, dass das "const"
für die Optimierung egal sei.  Dass eine äquivalente Optimierung auch
ohne const möglich ist, ist eine andere Frage.

Autor: klaus (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> Es wird dann jedes Auftreten des define-Namens durch die Zahl ersetzt.
> D.h. es wird kein Speicherplatz benötigt und es wird auch weniger Code
> erzeugt.

Man sollte nicht vergessen dass #define nicht auf Zahlen beschränkt ist. 
Man überlege sich einmal folgendes (Horror-)Szenario:
#define LONG_MESSAGE "Hallo Welt Hallo Welt Hallo Welt"

// ...

void f(char* x)
{
  if (0 == strcmp(LONG_MESSAGE, x))
  {
     // ...
  }
}

// ...
void g(char* y)
{
  if (0 == strcmp(LONG_MESSAGE, y))
  {
     // ...
  }
}

Hier hätte man genau das Gegenteil erreicht nämlich Speicherplatz 
verschwendet.

Leider muss man zu const sagen, dass man die Konstanten (mir etwas 
unverständlich) nicht als case Label nutzen kann...

Autor: A. K. (prx)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
klaus schrieb:

> Leider muss man zu const sagen, dass man die Konstanten (mir etwas
> unverständlich) nicht als case Label nutzen kann...

Weil es konstante Variablen sind, so komisch sich das anhört. Wo 
Variablen zulässig sind, darf der Compiler auch den Wert einer solchen 
konstanten Variablen einsetzen.

In Case-Labels und normalen Array-Indizes (Arrays variabler Länge aus 
C99 mal unberücksichtigt) sind Variablen jedweder Art jedoch rein von 
der syntaktischen Definition der Sprache her nicht zugelassen, und 
deshalb ist es egal ob die "const" sind oder nicht.

Überall dort, wo traditionell nur eine "constant expression" zulässig 
ist, müssen konstante Variablen draussen bleiben. In C. Stroustrup war 
darüber auch nicht sehr glücklich, weshalb dies in C++ möglich ist.

Autor: yalu (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
klaus schrieb:
> Leider muss man zu const sagen, dass man die Konstanten (mir etwas
> unverständlich) nicht als case Label nutzen kann...

Solange es sich um Integerwerte handelt, gibt's dafür ja noch die Enums
als dritte Form der Konstantendefinition. Das sind dann wieder "echte"
Konstanten, die man bspw. auch zur Arraydimensionierung verwenden kann.

Autor: gast (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
const wird sehr häufig als "schreibschutz" für variablen verwendet und 
macht hier sinn ansonsten würde ich keine Konsatne als const definieren, 
siehe z.b. math.h

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.