Forum: Compiler & IDEs type von index in for loop


von H. R. (hacker_r)


Lesenswert?

Hi
was nimmt idealerweise von einem type für den index einer for loop
#define MAX_NUM 32
for (uint32_t index = 0; index< MAX_NUM; index++)
soll ich für index int, uint32_t, uint16_t oder uint8_t nehmen?
Was ist der bessere Programmier Stil? Was generioert den effizienteren 
code?
Ich hatte mal gehört dass wenn ich uint8_t oder nicht den native 
Register width nehme dass ein "and" noch dazu kommt!

: Bearbeitet durch User
von Wilhelm M. (wimalopaan)


Lesenswert?

size_t, wenn Du sonst nichts weißt

von Jim M. (turboj)


Lesenswert?

H. R. schrieb:
> soll ich für index int, uint32_t, uint16_t oder uint8_t nehmen?
> Was ist der bessere Programmier Stil? Was generioert den effizienteren
> code?

Kommt schwerstens auf den Zielprozessor an. Der size_t wäre für moderne 
32-Bitter schnell, für den AVR 8-Bitter langsam.

Es gäbe notfalls auch noch sowas wie uint_fast8_t, wenn der 
Schleifenindex garantiert <= 255 ist.

Der einfache int oder unsigned int ist nur auf den 8-Bittern langsam, 
weil int mindestens 2 Byte breit sein muss.

von A. S. (Gast)


Lesenswert?

Eigentlich ist int genau dafür vorgesehenen: eine Ganzzahl in der 
Default-Prozessor-Breite.

von c-hater (Gast)


Lesenswert?

A. S. schrieb:

> Eigentlich ist int genau dafür vorgesehenen: eine Ganzzahl in der
> Default-Prozessor-Breite.

Nö. Er muss ja mindestens 16 Bit haben. Also für alles mit weniger als 
16 Bit ziemlich für'n Arsch. Naja, nicht ganz, manche Compiler erkennen 
in manchen Situationen immerhin die völlig unnütze Aufgeblasenheit und 
reduzieren sie kommentarlos auf das tatsächlich Nötige...

von Wilhelm M. (wimalopaan)


Lesenswert?

Das Problem bei diesem Code ist, dass der max. Indexwert durch den 
Präprozessor editiert wird, und damit keine Beziehung zum DT der 
Schleifenvariable index hat. Deswegen habe ich oben size_t gesagt. Denn 
size_t ist immer ausreichend groß. Doch kleiner kann man den DT hier 
nicht machen, weil ein uint8_t schnell zu klein sein kann, falls man 
später mal das define ändert. Der Code bricht also leicht. Auch wenn an 
dieser speziellen Stelle wohl die meisten Compiler warnen werden. Jedoch 
an anderen Stellen?

Deswegen wäre es besser, mit einer Meta-Funktion denn minimalen DT aus 
der Konstanten zu berechnen:
1
constexpr auto MAX{32};
2
//...
3
using index_t = type_for_t<MAX>;
4
for(index_t i{}; i < MAX; ++i) {...}

von Teo (Gast)


Lesenswert?

H. R. schrieb:
> Ich hatte mal gehört dass wenn ich uint8_t oder nicht den native
> Register width nehme dass ein "and" noch dazu kommt!

Ja so ähnlich. Immer wenn dein Int nicht der nominalen Datenbreite 
(Bits) entspricht, mit dem dein µC arbeitet, muss "rumgewurschtelt" 
werden.

von Oliver S. (oliverso)


Lesenswert?

Ob besser signed oder unsigned, hängt dann noch davon ab, was mit dem 
Zähler in der Schleife gemacht wird.

Oliver

von Rolf M. (rmagnus)


Lesenswert?

H. R. schrieb:
> Hi
> was nimmt idealerweise von einem type für den index einer for loop

Ich nehme int, wenn's keinen konkreten Grund gibt, was anderes zu 
nehmen.

> Was ist der bessere Programmier Stil? Was generioert den effizienteren
> code?
> Ich hatte mal gehört dass wenn ich uint8_t oder nicht den native
> Register width nehme dass ein "and" noch dazu kommt!

Das ist vom Prozessor abhängig. Bei manchen ist eine bestimmte Größe 
besonders effizient, bei anderen macht es kaum Unterschied. Die native 
Größe ist natürlich in der Regel am schnellsten.

Wilhelm M. schrieb:
> Das Problem bei diesem Code ist, dass der max. Indexwert durch den
> Präprozessor editiert wird, und damit keine Beziehung zum DT der
> Schleifenvariable index hat.

Ich sehe darin kein Problem, da es sich um eine Compilezeit-Konstante 
handelt. Offiziell werden Vergleiche (und alle anderen arithmetischen 
Operationen) sowieso grundsätzlich immer mit int-Breite durchgeführt. 
Der Compiler darf das allerdings optimieren, wenn er garantieren kann, 
dass sich das Ergebnis dadurch nicht ändert.
Damit ist es so oder so für die Effizienz der Schleife egal, ob der 
Vergleichswert jetzt ein int oder was kleineres ist ist.

> Deswegen habe ich oben size_t gesagt. Denn size_t ist immer ausreichend
> groß. Doch kleiner kann man den DT hier nicht machen, weil ein uint8_t
> schnell zu klein sein kann, falls man später mal das define ändert.

Das kommt ganz darauf an, was man machen will. Letztendlich könnte man 
die gleiche Aussage über size_t treffen. Im einfachsten Fall schreibt 
man beim #define einen Kommentar hin, der angibt, dass der Wert nicht 
größer als 255 sein darf und fügt eine eine compile-time-Assertion ein.
Den Typ prophylaktisch einfach unnötig groß zu machen, nur weil man ja 
später irgendwann theoretisch vielleicht mal mehr brauchen könnte, halte 
ich jedenfalls für eine wenig geeignete Strategie.

> Deswegen wäre es besser, mit einer Meta-Funktion denn minimalen DT aus
> der Konstanten zu berechnen:
> constexpr auto MAX{32};
> //...
> using index_t = type_for_t<MAX>;
> for(index_t i{}; i < MAX; ++i) {...}

Das geht dann allerdings nur in C++.

Oliver S. schrieb:
> Ob besser signed oder unsigned, hängt dann noch davon ab, was mit dem
> Zähler in der Schleife gemacht wird.

Ja. Man sollte vor allem auch vermeiden, signed und unsigned zu mischen.

von Wilhelm M. (wimalopaan)


Lesenswert?

Rolf M. schrieb:
> Der Compiler darf das allerdings optimieren, wenn er garantieren kann,
> dass sich das Ergebnis dadurch nicht ändert.

Und das tut er mit Sicherheit, wenn der DT von index so klein wie 
möglich ist (hier: uint8_t). Kann er das nicht nachweisen, optimiert er 
es nicht. ich habe ja auch oben geschrieben, dass das für diese eine 
Schleife kein Problem für den Compiler ist. Allerdings kann das 
natürlich noch weiter gehen ...

Rolf M. schrieb:
> Letztendlich könnte man
> die gleiche Aussage über size_t treffen.

Nein. size_t ist immer (!) ausreichend groß für die jeweilige Plattform, 
um als Indizierungstyp auszureichen.

Rolf M. schrieb:
> Im einfachsten Fall schreibt
> man beim #define einen Kommentar hin, der angibt, dass der Wert nicht
> größer als 255 sein darf und fügt eine eine compile-time-Assertion ein.

Und falls es dann doch mal größer ist, und die Assertion fehl schlägt, 
darf man anschließend überall im Code ändern. Prima ;-)

Rolf M. schrieb:
> Das geht dann allerdings nur in C++.

Es war keine Sprache erwähnt. Der Code kann genauso gut C++ sein.

von Rolf M. (rmagnus)


Lesenswert?

Wilhelm M. schrieb:
> Rolf M. schrieb:
>> Letztendlich könnte man
>> die gleiche Aussage über size_t treffen.
>
> Nein. size_t ist immer (!) ausreichend groß für die jeweilige Plattform,
> um als Indizierungstyp auszureichen.

... für Array-Indizes. Wir wissen aber nicht, wofür diese Schleife 
verwendet wird.

> Rolf M. schrieb:
>> Im einfachsten Fall schreibt
>> man beim #define einen Kommentar hin, der angibt, dass der Wert nicht
>> größer als 255 sein darf und fügt eine eine compile-time-Assertion ein.
>
> Und falls es dann doch mal größer ist, und die Assertion fehl schlägt,
> darf man anschließend überall im Code ändern. Prima ;-)

Wieso "überall"? Man muss es genau an dieser Stelle ändern:

H. R. schrieb:
1
for (uint32_t index = 0; index< MAX_NUM; index++)
2
     ^^^^^^^^

> Rolf M. schrieb:
>> Das geht dann allerdings nur in C++.
>
> Es war keine Sprache erwähnt.

Ja, eben. Du hast aber C++ angenommen. Ich hab lediglich darauf 
hingewiesen, dass es nur dort funktioniert.

> Der Code kann genauso gut C++ sein.

Oder halt C.

von Wilhelm M. (wimalopaan)


Lesenswert?

Rolf M. schrieb:
> ... für Array-Indizes. Wir wissen aber nicht, wofür diese Schleife
> verwendet wird.

Genau, das ist das Problem!

Rolf M. schrieb:
> Wieso "überall"? Man muss es genau an dieser Stelle ändern:

Nein, überall wo der Textersatz MAX_NUM verwendet wird. Und da der noch 
nicht einmal gescoped ist, kann das überall sein.

von Nick M. (Gast)


Lesenswert?

H. R. schrieb:
> Was generioert den effizienteren code?

Wenn in der Schleife ein Haufen Rechnerei ist, ist das Nachdenken 
darüber Zeitverschwendung.
Ansonsten ein fast_intX_t und ein geeignetes assert oder Makro das 
sicherstellt dass der Bereich groß genug ist.

von fop (Gast)


Lesenswert?

1
#include <stdint.h>
2
3
#define MAX_NUM (32)
4
5
#if (MAX_NUM) < 256
6
typedef uint_fast8_t perfect_loop_t;
7
#elif (MAX_NUM) < 65536
8
typedef uint_fast16_t perfect_loop_t;
9
#elif (MAX_NUM) < 4294967296
10
typedef uint_fast32_t perfect_loop_t;
11
#else
12
#error "Haben Sie es nicht ein Bischen kleiner ?"
13
#endif
14
15
for (perfect_loop_t index = (perfect_loop_t)0; index< (perfect_loop_t)(MAX_NUM); index++)

So etwa ?

von Wilhelm M. (wimalopaan)


Lesenswert?

fop schrieb:
> So etwa ?

Im Ernst?

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.