Forum: Compiler & IDEs #define vs. const


von marjus (Gast)


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!

von MeinerEiner (Gast)


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.

von (prx) A. K. (prx)


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.

von Peter (Gast)


Lesenswert?

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

Da erzält er Mist...!

von jozi59 (Gast)


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.

von Peter D. (peda)


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

von (prx) A. K. (prx)


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.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


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.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


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
1
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.

von (prx) A. K. (prx)


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.

von Karl H. (kbuchegg)


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
1
static const int N = 10;

und dann darf der Optimizer wieder zuschlagen :-)

von Marcus H. (mharnisch) Benutzerseite


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/

von (prx) A. K. (prx)


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.

von Peter D. (peda)


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

von (prx) A. K. (prx)


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.

von Karl H. (kbuchegg)


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

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


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.
1
const int i = 42;
2
int j = 23;
3
4
int return_i(void)
5
{
6
        return i;
7
}
8
9
int return_j(void)
10
{
11
        return j;
12
}

ergibt:
1
.global return_i
2
        .type   return_i, @function
3
return_i:
4
/* prologue: function */
5
/* frame size = 0 */
6
        ldi r24,lo8(42)
7
        ldi r25,hi8(42)
8
/* epilogue start */
9
        ret
10
        .size   return_i, .-return_i
11
.global return_j
12
        .type   return_j, @function
13
return_j:
14
/* prologue: function */
15
/* frame size = 0 */
16
        lds r24,j
17
        lds r25,j+1
18
/* epilogue start */
19
        ret
20
        .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.

von Marcus H. (mharnisch) Benutzerseite


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/

von Peter D. (peda)


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:
1
uint8_t d[8];
2
3
void test( uint8_t *s )
4
{
5
  56:   dc 01           movw    r26, r24
6
  58:   e0 e0           ldi     r30, 0x00       ; 0
7
  5a:   f1 e0           ldi     r31, 0x01       ; 1
8
  uint8_t i;
9
10
  for( i = 0; i < sizeof( d ); i++ )
11
    d[i] = *s++;
12
  5c:   8d 91           ld      r24, X+
13
  5e:   81 93           st      Z+, r24
14
  60:   81 e0           ldi     r24, 0x01       ; 1
15
  62:   e8 30           cpi     r30, 0x08       ; 8
16
  64:   f8 07           cpc     r31, r24
17
  66:   d1 f7           brne    .-12            ; 0x5c <test+0x6>
18
    d[i] = *s++;
19
}
20
  68:   08 95           ret

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


Peter

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


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.

von klaus (Gast)


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:
1
#define LONG_MESSAGE "Hallo Welt Hallo Welt Hallo Welt"
2
3
// ...
4
5
void f(char* x)
6
{
7
  if (0 == strcmp(LONG_MESSAGE, x))
8
  {
9
     // ...
10
  }
11
}
12
13
// ...
14
void g(char* y)
15
{
16
  if (0 == strcmp(LONG_MESSAGE, y))
17
  {
18
     // ...
19
  }
20
}

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...

von (prx) A. K. (prx)


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.

von yalu (Gast)


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.

von gast (Gast)


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

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.