Forum: Compiler & IDEs Wie Array-Size bekannt machen


von Peter D. (peda)


Lesenswert?

Problem mit AVR-GCC:

Ich hab ein Array in einem Objekt.
Nun müssen aber auch andere Objekte wissen, wie groß das Array ist.
Gibt es dafür ne andere Lösung, außer daß ich die Einträge immer 
abzählen muß und dann ins h-File reinschreiben?

Die Tabelle selber darf ich ja nicht ins h-File schreiben, das gibt nen 
bösen Error: multiple definition of `TAB_PWM'
1
prog_uint8_t TAB_PWM[] = {              // logarithmic table
2
                1,
3
                1,
4
                2,
5
                3,
6
                4,
7
                6,
8
                7,
9
                9,
10
                12,
11
                15,
12
                19,
13
                24,
14
                31,
15
                39,
16
                50,
17
                63,
18
                78,
19
                100,
20
                127,
21
                160,
22
                202,
23
                255
24
                };
25
26
#define PWM_STEPS       (sizeof(TAB_PWM))


Peter

von Peter (Gast)


Lesenswert?

soweit ich weiss gibt es das nicht, du aber üblich ist am ende z.b. ein 
0 reinzuschrieben und alle funktionen gehen das array bis zur 0 durch 
und wissen das dann schluss ist.

von Simon K. (simon) Benutzerseite


Lesenswert?

Das geht leider nicht so einfach.

Eine "saubere" Methode wäre einen typedef in eine .h Datei zu schreiben:
typedef prog_uint8_t PWMTab_t[<benötigte größe>];

Die Größe kann dann mit sizeof(PWMTab_t) abgefragt werden. Aber ja: Du 
musst hier auch die Größe immer von Hand neu eintragen.

von Peter D. (peda)


Lesenswert?

Ich machs jetzt zur Laufzeit:
1
u8 pwm_steps( void )
2
{
3
  return PWM_STEPS;
4
}


Peter

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Wenn man die Größe wirklich zur Compilezeit braucht, etwa weil andere 
Objekte davon abhängen, gibt's nicht wirklich eine überzeugende Lösung.

Ich hab ein ähnlichen Problem bei der Bestimmung einer Puffergröße 
(union), in der Objekte mehrere unabhängiger Module enthalten sein 
können.

Lösung 1
Wenn das Objekt nur 1x referenziert wird, dann geht ein
1
// #Einträge: sizeof (array) / sizeof (array[0])
2
static const type_t array[] = { ... };

im Header.

Wenn das Objekt in einem Modul nicht referenziert wird (sizeof 
referenziert nicht), dann legt gcc auch kein Objekt dafür an. Ansonsten 
erhält man wohl mehrere lokale Kopien von dem Ding.

Falls es mehrfach referenziert wird und im LST auftaucht, versuch es mal 
als .weak zu definieren.

Lösung 2
Man schreibt die Größe in eine Variable, allerdings ist sie dann 
ausserhalb des definierenden Moduls nicht zur Compilezeit bekannt, 
sondern erst zur Laufzeit:
1
extern const size_t array_size;
2
...
3
4
static const type_t array[] = { ... };
5
6
const size_t array_size = sizeof (array) / sizeof (array[0]);

Lösung 3
Vorsicht,  Hack (tm):
1: Man schreibt die Größe ins Assembler-File, zB via
1
asm volatile ("/*\n#define array_size %0\n*/" :: "n" (sizeof (array) / sizeof (array[0])));

2: Im Makefile grept man darauf und pipet das in ein array-size.h, das 
in den verwendenden Modulen includet wird:
1
%.s: %.c
2
  $(CC) -S $< $(CFLAGS) ...
3
4
array-size.h: array-modul.s
5
  cat $< | grep -E '^\#define array_size' > $@
6
7
array-verwender-1.c: array-size.h
8
9
array-verwender-2.c: array-size.h

Eigentlich ganz nett, weil man nicht alles, was nur zur Bekanntmachung 
der Größe benötigt wird, in Header gekloppt werden muss.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Simon K. wrote:
> Die Größe kann dann mit sizeof(PWMTab_t) abgefragt werden. Aber ja: Du
> musst hier auch die Größe immer von Hand neu eintragen.

Eigentlich wäre das nicht weiter schlimm, das Ärgernis daran ist, daß, 
wenn sich mal die Arraygröße ändert, man keinen Fehler/Warnung bekommt 
bzw. erzeugen kann.

Es gibt keine Möglichkeit, zur Compilezeit die Korrektheit der Größe 
zu testen, auch wenn alles Compilezeit-Konstanten sind. Sowas wie 
__builtin_error oder __builtin_warning vermisse ich schon lange in GCC, 
welche man zB zusammen mit __builtin_constant_p oder 
__builtin_choose_expr verwenden könnte, um bei Differenzen zu meckern.

von Karl H. (kbuchegg)


Lesenswert?

IMHO liegt das eiegentliche Problem darin, wie in C der Konflikt 
zwischen Deklaration und Definition aufgelöst wird, wenn gleichzeitig 
extern und eine Initialisierung angegeben ist.
Seit je her ist es so, dass dann die Initialisierung gewinnt und das 
Ganze eine Definition ist. Compiler bzw. Linker, die eine multiple 
Definition ignorieren, kommen damit gut zurecht, aber standardkonform 
ist das natürlich nicht.

Hab ich mich auch schon oft drüber geärgert, denn im Grunde macht sowas 
eine vernünftige Verwendung von Konstanten ebenfalls unmöglich.

constants.h
1
extern const int NrSteps = 5;
2
extern const double Pi = 3.141592654;

includiert in 2 Translation Units -> multiple definition error

Nimmst du aber die Initialisierung raus, dann nimmst du dem Compiler 
eine Mange von Optimiermöglichkeiten weg, die eigentlich trivial sind.

Also bleibt wieder nichts anderes übrig als über Präprozessorkonstanten 
zu gehen. Und davon will man ja eigentlich seit Jahren wegkommen.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Karl heinz Buchegger wrote:

> constants.h
>
1
> extern const int NrSteps = 5;
2
> extern const double Pi = 3.141592654;
3
>

Gerade hier sollte doch static const gut gangbar sein?

Selbst wenn das extern nicht zu einem Fehler führt wenn der Compiler die 
Instanziierung nur 1x sieht, kann er nicht optimieren, weil sich ein 
const-Objekt ändern kann. const bedeutet nur so viel wie "read only", 
aber nicht, daß das Ding nach der Initialisierung immer diesen Wert hat!

Bei static kann der Compiler jedoch sehen, ob Zugriffe da sind, etw 
Schweiereien (die man nicht verwenden will, aber die ein Compiler 
handhaben können muss, weil sie prinzipiell auftreten können) wie
1
*((int*) & NrSteps) = 6;

von Karl H. (kbuchegg)


Lesenswert?

Johann L. wrote:

> Gerade hier sollte doch static const gut gangbar sein?

Ja, das hab ich beim schreiben übersehen. static ist ne Möglichkeit.

>
> Selbst wenn das extern nicht zu einem Fehler führt wenn der Compiler die
> Instanziierung nur 1x sieht, kann er nicht optimieren, weil sich ein
> const-Objekt ändern kann.

Huch, wie das?

> const bedeutet nur so viel wie "read only",
> aber nicht, daß das Ding nach der Initialisierung immer diesen Wert hat!

Doch, genau das bedeutet const.

Eine const Variable bedeutet immer, dass sich der Wert nach der 
Initialisierung nicht mehr verändert. Wenn der Compiler also den Wert 
kennt, kann er ihn für Optimierungen benutzen. Wenn du etwas als const 
deklarierst und das Ding ändert sich nach der Initialisierung, hast du 
den Compiler angelogen und darfst ihn nicht für Fehler verantwortlich 
machen.

> Bei static kann der Compiler jedoch sehen, ob Zugriffe da sind, etw
> Schweiereien (die man nicht verwenden will, aber die ein Compiler
> handhaben können muss, weil sie prinzipiell auftreten können) wie
>
>
1
> *((int*) & NrSteps) = 6;
2
>

Äh, nein. Das muss ein Compiler nicht handhaben. Wenn du nach dieser 
'Modifikation' den Wert von NrSteps ausgibst, und am stdout erscheint 
eine 5, dann ist das völlig in Ordnung.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Karl heinz Buchegger wrote:
> Johann L. wrote:
>
>> Gerade hier sollte doch static const gut gangbar sein?
>
> Ja, das hab ich beim schreiben übersehen. static ist ne Möglichkeit.
>
>>
>> Selbst wenn das extern nicht zu einem Fehler führt wenn der Compiler die
>> Instanziierung nur 1x sieht, kann er nicht optimieren, weil sich ein
>> const-Objekt ändern kann.
>
> Huch, wie das?

Historische Altlast.

http://en.wikipedia.org/wiki/Const_correctness#Loopholes_to_const-correctness

Aber keine Ahnung, wie der momentane GCC das handhabt...

von Peter D. (peda)


Lesenswert?

Karl heinz Buchegger wrote:
>> Selbst wenn das extern nicht zu einem Fehler führt wenn der Compiler die
>> Instanziierung nur 1x sieht, kann er nicht optimieren, weil sich ein
>> const-Objekt ändern kann.
>
> Huch, wie das?
>
>> const bedeutet nur so viel wie "read only",
>> aber nicht, daß das Ding nach der Initialisierung immer diesen Wert hat!

Ich hab sowas schonmal gemacht und es geht sehr gut.
Ein Task liest über 74HC165 digitale Eingänge ein und legt sie im SRAM 
ab.

Für alle anderen Tasks sind diese Eingänge im SRAM als const definiert, 
d.h. sie dürfen sie lesen, aber nicht ändern.
Ansonsten würden sie ja kein Abbild der physischen Eingänge am 74HC165 
mehr sein.


Peter

von Karl H. (kbuchegg)


Lesenswert?

Johann L. wrote:

>> Huch, wie das?
>
> Historische Altlast.
>
> http://en.wikipedia.org/wiki/Const_correctness#Loopholes_to_const-correctness
>
> Aber keine Ahnung, wie der momentane GCC das handhabt...

Was sagt dir der Satz
"It should be noted, however, that any attempt to modify an object that 
is itself declared const by means of const_cast results in undefined 
behavior"

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Karl heinz Buchegger wrote:

> Was sagt dir der Satz
> "It should be noted, however, that any attempt to modify an object that
> is itself declared const by means of const_cast results in undefined
> behavior"

Es gibt Fallstricke, wie ich schrieb. Insbesondere auch dann, wenn ein 
Komposit const ist, seine Komponenten hingegen nicht.

von Karl H. (kbuchegg)


Lesenswert?

Peter Dannegger wrote:
> Karl heinz Buchegger wrote:
>>> Selbst wenn das extern nicht zu einem Fehler führt wenn der Compiler die
>>> Instanziierung nur 1x sieht, kann er nicht optimieren, weil sich ein
>>> const-Objekt ändern kann.
>>
>> Huch, wie das?
>>
>>> const bedeutet nur so viel wie "read only",
>>> aber nicht, daß das Ding nach der Initialisierung immer diesen Wert hat!
>
> Ich hab sowas schonmal gemacht und es geht sehr gut.
> Ein Task liest über 74HC165 digitale Eingänge ein und legt sie im SRAM
> ab.

Klar. Da der Optimizer keinen Zahlenwert hat um den Zugriff zu ersetzen, 
bleibt ihm letztendlich nichts anderes übrig als den Zugriff tatsächlich 
zu machen. Effektiv hast du damit ein 'read only' erreicht.
Aber: Nichts und niemand aus dem Sprachstandard hindert den Optimizer 
letztendlich das ganze so zu gestalten, dass er den Zugriff nur beim 
ersten mal macht, um in weiterer Folge den bereits gelesenen Wert bei 
allen anderen Abfragen zu verwenden.

Klar. Kein Compilerbauer wird das je so machen. Aber es ist auch nicht 
explizit verboten.

Es gibt durchaus Leute in comp.lang.c die über folgendes gestolpert sind
1
int main()
2
{
3
  const int i = 5;
4
5
  *(int*)&i = 6;
6
7
  printf( "%d\n", i );
8
}

und die Ausgabe war: 5
und 5 ist ein völlig 'korrektes' Ergebnis, denn nach dem wegcasten des 
const (egal wie das passiert) entsteht automatisch undefiniertes 
Verhalten.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Karl heinz Buchegger wrote:

> Es gibt durchaus Leute in comp.lang.c die über folgendes gestolpert sind
>
>
1
> int main()
2
> {
3
>   const int i = 5;
4
> 
5
>   *(int*)&i = 6;
6
> 
7
>   printf( "%d\n", i );
8
> }
9
>
>
> und die Ausgabe war: 5

Hier ist die Gemängelage anders, weil i nicht extern ist sondern auto.

Und es ging auch nicht darum Hack zu befordern, sondern daß man sich 
nicht wundern muss, falls der Compiler was nicht wegoptmiert obwohl man 
das ganz nett fände.

von Karl H. (kbuchegg)


Lesenswert?

Johann L. wrote:

> Hier ist die Gemängelage anders, weil i nicht extern ist sondern auto.

Ist aber trotzdem nur syntaktischer Zucker


a.c
1
#include <stdio.h>
2
3
extern const int const_i;
4
5
int main()
6
{
7
  *(int*)&const_i = 6;
8
9
  printf( "%d\n", const_i );
10
}

b.c
1
extern const int const_i = 4;

liefert zb. auf VC++ eine Access Violation. Und das darf der Compiler 
auch
so umsetzen.

Im Übrigen hab ich mich hier

> Hab ich mich auch schon oft drüber geärgert, denn im Grunde macht
> sowas eine vernünftige Verwendung von Konstanten ebenfalls unmöglich.
>
>constants.h
> extern const int NrSteps = 5;
> extern const double Pi = 3.141592654;
>
> includiert in 2 Translation Units -> multiple definition error

geirrt. Lass einfach das extern weg und gut ists.
const zusammen mit einer Initialisierung ist dasselbe wie static.
Das man damit allerdings Arrays duplizieren wird, steht auf einem 
anderen Blatt und ist ja das was Peter von Anfang an bekrittelt hat.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Karl heinz Buchegger wrote:

>> Hab ich mich auch schon oft drüber geärgert, denn im Grunde macht
>> sowas eine vernünftige Verwendung von Konstanten ebenfalls unmöglich.
>>
>>constants.h
>> extern const int NrSteps = 5;
>> extern const double Pi = 3.141592654;
>>
>> includiert in 2 Translation Units -> multiple definition error
>
> geirrt. Lass einfach das extern weg und gut ists.

Hmmm. Bei mir meckert gcc 4.4 das an, egal ob mit -fcommon oder 
-fno-common. Das einzige was hilft ist ein __attribute__((weak)), oder 
mit welchem Compiler hast du das übersetzt? Bzw mit welchem Linker die 
Objekte gelinkt?

> Das man damit allerdings Arrays duplizieren wird, steht auf einem
> anderen Blatt und ist ja das was Peter von Anfang an bekrittelt hat.

von Michael A. (micha54)


Lesenswert?

Hallo,

also teilweise sträuben sich mir hier beim Lesen die Haare.
Die Frage, wie eine Sache zu lösen ist entscheidet die Sprachdefinition.
Das Handling von Const wird z.B. durch die formale Definition bestimmt.

Ob die reale Implementierung das dann so handhabt ist eine andere Frage.
Innerhalb von C ist definiert, dass CONST eine Variable nicht mehr 
änderbar macht. Wenn ein bestimmter COmpiler dies also zulässt, so nenne 
ich das eine Bug.

Etwas anderes ist das programmieren um eine Bug herum, hier sollte man 
versuchen, mit den funktionierenden Mitteln des Standards den Bug zu 
vermeiden oder zu korrigieren.

Konsequenz: eine Programmierung auf der Basis von Bugs ist für mich 
nicht tragbar, ist nicht professionell, weil mir jede neue 
Compilerversion potentiell die Basis unterm Hintern wegzieht.

Wer hier also schreibt, daß const == static ist, der sollte seine 
Meinung nochmal überdenken.

Gruß,
Michael

von Michael A. (micha54)


Lesenswert?

Peter Dannegger wrote:
> Problem mit AVR-GCC:
>
> Die Tabelle selber darf ich ja nicht ins h-File schreiben, das gibt nen
> bösen Error: multiple definition of `TAB_PWM'
> [c]
>
> prog_uint8_t TAB_PWM[] = {              // logarithmic table
>                 1,
>                 1,
>                 2,
>schnipp-schnapp
>                 202,
>                 255
>                 };
>
> Peter

Hallo,

ich habe derartige Dinge bsiher so gelöst, daß ich mit 0 terminiere und 
zur Laufzeit abzähle. Also

typedef menu_item_t {uint8_t x, y; uint16_t * handler, uint16_t * data} 
menu_item_t;

menu_item_t item1 PROGMEM = {0, 0, dispmenu, &submenu};
menu_item_t item2 PROGMEM = {0, 0, dispmenu, &submenu};
menu_item_t item3 PROGMEM = {0, 0, dispmenu, &submenu};
menu_item_t item4 PROGMEM = {0, 0, dispmenu, &submenu};

menu_item_t * menu1[] PROGMEM = {&item1, &item2, &item3, &item4, 0}

...ein paar casts (uint16_t) habe ich weggelassen.

Entweder ich zähle einmal vorab in eine lokale Variable, oder die 0 wird 
als Terminator in der Schleife abgefragt.

Gruß,
Michael

von Karl H. (kbuchegg)


Lesenswert?

Michael Appelt wrote:

> Wer hier also schreibt, daß const == static ist, der sollte seine
> Meinung nochmal überdenken.

Mein Fehler.
Das ist nur in C++ so, dass const Variablen automatisch internal linkage 
(also static) sind. In C blieb alles beim alten.

Bin wohl schon zu lange von C weg.

von Michael A. (micha54)


Lesenswert?

Hallo Peter,

Ich will ja nix sagen :-X
ich hatte zwar kurz zur Sicherheit gegurgelt, aber in welches Cxy ich 
geraten war habe ich garnicht geprüft. GLück gehabt.

BTW sehe ich eine Lösung für das zweite Problem: const für eine lokale 
Tabelle im Modul und dann nur einen öffentlichen Pointer darauf.

Gruß,
Michael

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.