Größe von einem "struct Array" bestimmen?!

OP #5292945
Lesenswert?

Moin Freunde,

bekomme Ich irgendwie die Anzahl der Einträge raus?
1
typedef struct
2
{
3
  char *Name;
4
  uint8_t numb;
5
}menue_t;
6

7
menue_t mainMenue[]=
8
{
9
  {"Uhrzeit stellen"  ,0},
10
  {"Datum stellen"  ,1},
11
  {"Bluetooth"    ,2},
12
  {"Neustart"      ,3},
13
  {"Exit"        ,4},      
14
};
15

16
void test(menue_t *m)
17
{
18
  printf("%d",sizeof(m) / sizeof(m[0]));  
19
}
(Firma: RUB) #5292952
Lesenswert?

Nein, in der Funktion nicht, denn da ist es kein Array mehr, sondern 
nur noch ein Zeiger. Wenn du die Groesse brauchst, musst du sie der 
Funktion als extra Parameter mit uebergeben. Die Groesse kannst du nur 
in dem Scope, in dem du das Array erstellt hast, ermitteln.
Gast #5293034
Lesenswert?

Wilhelm M. schrieb:

> Wie schon Bjarne sagen: "Rohe C/C++-Arrays sind so blöd, dass sie ihre
> eigene Länge nicht kennen".

Müssen sie auch nicht. Wenn man das wirklich haben will, kann man sich 
ja problemlos ein struct typedefen, wo die Länge drinsteht. Dann muß man 
halt den Verwaltungs-Overhead mitbezahlen.

Zudem ist die Länge ja auch keineswegs immer eindeutig. Beispielsweise 
habe ich öfter den Fall, wo Funktionen Pointer akzeptieren nebst einer 
Länge. Das ist aber nicht etwa die physikalische Buffergröße des 
dahinterliegenden Arrays bzw. Heap-Bereiches, sondern die 
Nutzdatenlänge, welche im allgemeinen echt kleiner als die Buffergröße 
ist - und sich außerdem erst zur Laufzeit bestimmt.

Der verarbeitenden Funktion kann das aber egal sein. Natürlich hat man 
dann den Overhead, um sicherzustellen, daß die Nutzdaten nicht größer 
als der Buffer werden, aber das ist einmalig bei der Validierung der 
Fall, und danach bei der Verarbeitung entfällt jeder 
Verwaltungs-Overhead.

Genau das scheint der typische Fall zu sein, was auch der wesentliche 
Grund ist, wieso C das so handhabt und nicht etwa nach Art von 
Pascal-Strings die Länge irgendwo unter der Haube mit durchreicht.
#5293055
Lesenswert?

Nop schrieb:
> Wilhelm M. schrieb:
>
>> Wie schon Bjarne sagen: "Rohe C/C++-Arrays sind so blöd, dass sie ihre
>> eigene Länge nicht kennen".

> Zudem ist die Länge ja auch keineswegs immer eindeutig.

Sollte sie aber sein ;-))

> Beispielsweise
> habe ich öfter den Fall, wo Funktionen Pointer akzeptieren nebst einer
> Länge. Das ist aber nicht etwa die physikalische Buffergröße des
> dahinterliegenden Arrays bzw. Heap-Bereiches, sondern die
> Nutzdatenlänge, welche im allgemeinen echt kleiner als die Buffergröße
> ist - und sich außerdem erst zur Laufzeit bestimmt.

Dann hast Du eben einen Buffer mit einer (Maximal)Größe, dass Du für 
eine dynamische Struktur wie etwa eine Queue, Stack, etc. verwendest.

Das Problem des TO ist, dass er erkennen muss, was das "Zerfallen" eines 
Array-Bezeichners zu einem Zeigertyp ist (decay of array designator).
Gast #5293143
Lesenswert?

Wilhelm M. schrieb:

> Dann hast Du eben einen Buffer mit einer (Maximal)Größe, dass Du für
> eine dynamische Struktur wie etwa eine Queue, Stack, etc. verwendest.

Eben, aber zur Verarbeitung ist die meistens kleinere Nutzgröße 
relevant, weil danach Datenmüll folgen kann. Würde damit gearbeitet, 
d.h. bis ans Ende der Buffergröße, dann wären funktionale Fehler die 
Folge.

Deswegen hat man kein Problem, wenn man beim Befüllen sicherstellt, daß 
die Nutzgröße kleiner gleich der Buffergröße ist, aber das ist eben nur 
an einem Punkt nötig und nicht überall danach. Dann aber entfällt auch 
die Notwendigkeit, die physikalische Arraygröße überhaupt 
weiterzureichen.

Ein Problem hat man dann nur, wenn man einen Buffer herumreicht, bei dem 
jeder noch seinen Senf zusätzlich anhängen kann (d.h. nicht an 
vordefinierte Stellen), das riecht dann mit rohen Arrays schnell nach 
Overflow. Das ist aber auch kein besonders glückliches Design und sollte 
nach Möglichkeit vermieden werden.

Abgesehen davon bin ich bei der Ausnutzung statischer Arraygrößen (via 
sizeof-Division) eh vorsichtig, weil einem sowas ganz schnell knallt, 
sobald man das dann z.B. im Mockup auf dem PC doch mal auf dynamisch 
allozierten Speicher umstellt. Dann lieber mit Größen-Defines, denen es 
egal ist, ob sie ein Array deklarieren oder als Futter für calloc 
dienen.

> Das Problem des TO ist, dass er erkennen muss, was das "Zerfallen" eines
> Array-Bezeichners zu einem Zeigertyp ist (decay of array designator).

Ja gut, das muß man einmal verstehen. In dem Fall wäre aber ohnehin die 
Technik mit einem Sentinel-Eintrag am Ende die einfachste, und die wurde 
ja auch schon vorgeschlagen.
#5293226
Lesenswert?

Nop schrieb:
> Wilhelm M. schrieb:
>
> Abgesehen davon bin ich bei der Ausnutzung statischer Arraygrößen (via
> sizeof-Division) eh vorsichtig, weil einem sowas ganz schnell knallt,
> sobald man das dann z.B. im Mockup auf dem PC doch mal auf dynamisch
> allozierten Speicher umstellt. Dann lieber mit Größen-Defines, denen es
> egal ist, ob sie ein Array deklarieren oder als Futter für calloc
> dienen.

Auch wenn ich mal sehr stark vermute, dass es sich hierbei um reinen 
C-Code handelt, so wäre die Lösung in C++ eben std::array für statische 
Arrays: dort ist die Größe Bestandteil des DT, und damit hat man kein 
Problem und keine Overhead.

Und wenn man es eh generisch schreibt, kann man auch std::array durch 
std::vector im Aufrufer austauschen, ohne eine weitere Zeile zu ändern 
...
Gast #5293279
Lesenswert?

Wilhelm M. schrieb:

> so wäre die Lösung in C++ eben std::array für statische
> Arrays: dort ist die Größe Bestandteil des DT, und damit hat man kein
> Problem und keine Overhead.

Nein, das wäre keine Lösung. Siehe Vorposting, wo ich ausgeführt habe, 
daß nicht die Buffergröße entscheidend ist, sondern die Nutzdatengröße, 
welche kleiner gleich der Buffergröße ist.

> Und wenn man es eh generisch schreibt, kann man auch std::array durch
> std::vector im Aufrufer austauschen, ohne eine weitere Zeile zu ändern

Wenn man Arrays nimmt, dann macht man das, weil man keine dynamische 
Speicherverwaltung haben will. Auch nicht hinter dem Rücken des 
Programmierers. Daher ist auch das keine Lösung.

Dann müßte man sich schon Objekte basteln, die eine Art getter/setter 
haben, bei dem die Nutzdatenlänge reingepackt wird. Aber das kann man 
dann auch in C haben, wahlweise per struct oder per Parameter-Übergabe 
an die Funktion, die auch den Pointer bekommt (was das üblichere Idiom 
ist).
Gast #5293281
Lesenswert?

Rufus Τ. F. schrieb:

> Ja. Mit einem simplen Macro:

Für den Fall, daß es sich dabei um eine globale Variable handelt. Aber 
dann braucht man das auch nicht als Parameter an die Funktion zu 
übergeben, sondern kann das gleich mit Globals machen. Einen Pointer zu 
übergeben, aber die Größe als globales Define zu realisieren, ist nicht 
sonderlich wartungsfreundlich. Eigentlich sogar ein Codesmell, finde 
ich.
Moderator #5293351
Lesenswert?

Wilhelm M. schrieb:
> Auch wenn ich mal sehr stark vermute, dass es sich hierbei um reinen
> C-Code handelt, so wäre die Lösung in C++ eben std::array für statische
> Arrays: dort ist die Größe Bestandteil des DT, und damit hat man kein
> Problem und keine Overhead.

std::array ist nur dann eine Lösung, wenn die Menüs, die an die Funktion
übergeben werden, immer die gleiche Anzahl von Einträgen haben. Das wird
hier wohl eher nicht der Fall sein.
#5293412
Lesenswert?

Yalu X. schrieb:
> Wilhelm M. schrieb:
>> Auch wenn ich mal sehr stark vermute, dass es sich hierbei um reinen
>> C-Code handelt, so wäre die Lösung in C++ eben std::array für statische
>> Arrays: dort ist die Größe Bestandteil des DT, und damit hat man kein
>> Problem und keine Overhead.
>
> std::array ist nur dann eine Lösung, wenn die Menüs, die an die Funktion
> übergeben werden, immer die gleiche Anzahl von Einträgen haben.

Nö, jedes Menu kann eine unterschiedliche, aber zur Laufzeit konstante 
Anzahl von Elementen haben.

> Das wird
> hier wohl eher nicht der Fall sein.

Glaube ich kaum, sonst wären nach Standard-C auch keine rohen Arrays 
möglich.
(Firma: Père Lachaise) #5293414
Lesenswert?

Wilhelm M. schrieb:
> ... und das geht eben nur (s.o.), wenn array nocht nicht zu einem
> Zeigertyp "zerfallen" ist.

Richtig, wie vorher schon angemerkt wurde. In meinen Augen ist es aber 
(in reinem C) akzeptabel wenn man einem kleinen Modul (welches die Daten 
des Arrays auswertet) die statische Definition global verarbeiten kann. 
Ich würde hier nicht von einem Code-Smell reden.
#5293418
Lesenswert?

J. F. schrieb:
> Wilhelm M. schrieb:
>> ... und das geht eben nur (s.o.), wenn array nocht nicht zu einem
>> Zeigertyp "zerfallen" ist.
>
> Richtig, wie vorher schon angemerkt wurde. In meinen Augen ist es aber
> (in reinem C) akzeptabel wenn man einem kleinen Modul (welches die Daten
> des Arrays auswertet) die statische Definition global verarbeiten kann.
> Ich würde hier nicht von einem Code-Smell reden.

Das wollte ich auch nicht damit sagen. Jedoch kann ich mir wiederum 
viele Leute vorstellen, die nun glauben, diese Macro sei universell 
einsetzbar! Und genau das ist das Problem darin.

genau dafür gibt es in C++ std::size<>: das erkennt den potentiellen 
Fehler, weil es für Zeigertypen nicht spezialisiert ist.
(Firma: Père Lachaise) #5293433
Lesenswert?

Wilhelm M. schrieb:
> Das wollte ich auch nicht damit sagen.

Das war auch nicht auf dich bezogen, ging aber aus dem Kontext nicht 
hervor.

> genau dafür gibt es in C++ std::size<>: das erkennt den potentiellen
> Fehler, weil es für Zeigertypen nicht spezialisiert ist.

Richtig, ein Grund warum ich C++ schöner finde. Der Threadersteller läßt 
uns aber nach wie vor im Dunkeln welche Sprache er in welcher Revision 
benutzt.
Moderator #5293655
Lesenswert?

Wilhelm M. schrieb:
> Yalu X. schrieb:
>> Wilhelm M. schrieb:
>>> Auch wenn ich mal sehr stark vermute, dass es sich hierbei um reinen
>>> C-Code handelt, so wäre die Lösung in C++ eben std::array für statische
>>> Arrays: dort ist die Größe Bestandteil des DT, und damit hat man kein
>>> Problem und keine Overhead.
>>
>> std::array ist nur dann eine Lösung, wenn die Menüs, die an die Funktion
>> übergeben werden, immer die gleiche Anzahl von Einträgen haben.
>
> Nö, jedes Menu kann eine unterschiedliche, aber zur Laufzeit konstante
> Anzahl von Elementen haben.

Wie müsste denn die Funktion test() deklariert werden, dass wahlweise
ein std::array<menue_t,5> oder ein std::array<menue_t,6> als Argument
übergeben werden kann?

Man könnte test() zwar als Template-Funktion mit der Array-Größe als
Parameter realisieren, das bläht aber ganz ordentlich, wenn man es mit
vielen verschiedenen Array-Größen zu tun hat und test() eine etwas
größere Funktion ist.
Gast #5293690
Lesenswert?

Das Problem in C ist eigenlich, dass der Pointer zwei verschiedene 
Bedeutungen haben kann:

a) Eine Liste von Menüeinträgen. Hier bieten sich 0-Terminierte Listen 
an, wenn man über genügend C-Erfahrung verfügt, statische Analysetools 
hat und weiss, was man tut. Der meiste konservative Code zerfällt dann 
in fast nichts, analog zu den einfachen STringroutinen.

b) Einen konkreten Menüeintrag. Hier ist die Gesamtliste aber egal.

Von daher hast Du das Problem nicht, wenn Du einigermaßen Erfahrung 
hast. Fall nicht, musst Du Dir die Informationen der Arrays wrappen. Das 
geht auch elegant, aber Du brauchst jede Menge Code mehr.
#5293766
Lesenswert?

Yalu X. schrieb:
> Wilhelm M. schrieb:
>> Yalu X. schrieb:
>>> Wilhelm M. schrieb:
>>>> Auch wenn ich mal sehr stark vermute, dass es sich hierbei um reinen
>>>> C-Code handelt, so wäre die Lösung in C++ eben std::array für statische
>>>> Arrays: dort ist die Größe Bestandteil des DT, und damit hat man kein
>>>> Problem und keine Overhead.
>>>
>>> std::array ist nur dann eine Lösung, wenn die Menüs, die an die Funktion
>>> übergeben werden, immer die gleiche Anzahl von Einträgen haben.
>>
>> Nö, jedes Menu kann eine unterschiedliche, aber zur Laufzeit konstante
>> Anzahl von Elementen haben.
>
> Wie müsste denn die Funktion test() deklariert werden, dass wahlweise
> ein std::array<menue_t,5> oder ein std::array<menue_t,6> als Argument
> übergeben werden kann?
>
> Man könnte test() zwar als Template-Funktion mit der Array-Größe als
> Parameter realisieren, das bläht aber ganz ordentlich, wenn man es mit
> vielen verschiedenen Array-Größen zu tun hat und test() eine etwas
> größere Funktion ist.

Genau!
Wir sind hier im Unterforum PC-Programmierung, und da ist die Code-Größe 
dann wohl egal. Bei µC hätte ich diese meine Antwort so nicht gegeben.

Und außerhalb des µC sollte man dann, also bei variablen Größen, eben 
besser auf den fast immer optimalen Container std::vector<> gehen.

Wobei der Code-Bloat eigentlich nur dann eintritt, wenn man fürchterlich 
ungeschickt vorgeht: also etwa eine Funktion test() schreibt, die 
sehr(!) lang ist und man sie nicht weiter untergliedert. Meistens ist es 
ja so, dass die öffentliche Schnittstelle als template gestaltet wird, 
intern dann aber was anderes abläuft, was oft nicht von den 
template-Parametern abhängt. Aber wie gesagt: das sollte einen 
eigentlich nur auf dem µC tangieren, wobei die heutigen Flash-Größen 
auch das Argument regelmäßig ad absurdum führen. Oft ist der 
RAM-Verbrauch auf µC der limitierende Faktor.
(Firma: RUB) #5293878
Lesenswert?

Jan H. schrieb:
> Hatte gedacht das man die Größe von dem
> Pointer Array raus bekommt. Von nem normalen Array das ich übergebe
> klappt das ja weil es eben nicht als pointer zerfällt.
Du redest wirr. Jedes Array zerfaellt zu einem Pointer, wenn es an eine 
Funktion uebergeben wird. Egal ob es ein Array von Pointern ist, oder 
nicht.

Ja, du bekommst die Groesse des Arrays raus, aber nur bevor du es 
der Funktion uebergibst. In der Funktion geht das dann nicht mehr.

Geht:
1
void bar(irgendein_datentyp* p, size_t len)
2
{
3
    ...
4
}
5

6
void foo(void)
7
{
8
    irgendein_datentyp array[] = {...};
9

10
    bar(array, sizeof(array)/sizeof(array[0]));
11
}


Geht nicht:
1
void bar(irgendein_datentyp* p)
2
{
3
    size_t len = sizeof(p)/sizeof(p[0]);
4
}
5

6
void foo(void)
7
{
8
    irgendein_datentyp array[] = {...};
9

10
    bar(array);
11
}
Mach doch einfach mal ein vollstaendiges minimal Beispiel, damit wir 
nachvollziehen koennen was du willst/meinst.
OP #5293892
Lesenswert?

Kaj G. schrieb:
> Jan H. schrieb:
> Hatte gedacht das man die Größe von dem
> Pointer Array raus bekommt. Von nem normalen Array das ich übergebe
> klappt das ja weil es eben nicht als pointer zerfällt.
>
> Du redest wirr. Jedes Array zerfaellt zu einem Pointer, wenn es an eine
> Funktion uebergeben wird. Egal ob es ein Array von Pointern ist, oder
> nicht.
>
> Ja, du bekommst die Groesse des Arrays raus, aber nur bevor du es der
> Funktion uebergibst. In der Funktion geht das dann nicht mehr.
 }
>
> Mach doch einfach mal ein vollstaendiges minimal Beispiel, damit wir
> nachvollziehen koennen was du willst/meinst.

Ich habe es aber schon mal mit nem Array hin bekommen..

CheckSize (char a[])
return (sizeof(a) /siueof(a|0]))
Beitrag #5293904 wurde von einem Moderator gelöscht.
(Firma: RUB) #5293912
Lesenswert?

Jan H. schrieb:
> Ich habe es aber schon mal mit nem Array hin bekommen..
Nein, hast du nicht.
1
#include <stdio.h>
2

3
size_t check_size(char array[]);
4

5

6
int main(void)
7
{
8
    char text[] = "Was auch immer...";
9

10
    printf("Laenge: %ld\n", check_size(text));
11
    printf("Und jetzt richtig: %ld\n", sizeof(text)/sizeof(text[0]));
12
    return 0;
13
}
14

15

16
size_t check_size(char array[])
17
{
18
    return sizeof(array)/sizeof(array[0]);
19
}
1
$ gcc -Wall -Wextra -std=c99 -o main test.c
2
test.c: In function 'check_size':
3
test.c:18:18: warning: 'sizeof' on array function parameter 'array' will return size of 'char *' [-Wsizeof-array-argument]
4
     return sizeof(array)/sizeof(array[0]);
5
                  ^
6
test.c:16:24: note: declared here
7
 size_t check_size(char array[])
8
                        ^~~~~
Der Compiler sagt dir schon, dass das Unfug ist.
1
$ ./main 
2
Laenge: 8
3
Und jetzt richtig: 18
Haette das Array jetzt zufaellig genau 8 Elemente, dann wuerde es fuer 
Dich so aussehen, als ob es klappen wuerde. Tut es aber nicht.
#5293928
Lesenswert?

Yalu X. schrieb:
>> Nö, jedes Menu kann eine unterschiedliche, aber zur Laufzeit konstante
>> Anzahl von Elementen haben.
>
> Wie müsste denn die Funktion test() deklariert werden, dass wahlweise
> ein std::array<menue_t,5> oder ein std::array<menue_t,6> als Argument
> übergeben werden kann?
> Man könnte test() zwar als Template-Funktion mit der Array-Größe als
> Parameter realisieren, das bläht aber ganz ordentlich, wenn man es mit
> vielen verschiedenen Array-Größen zu tun hat und test() eine etwas
> größere Funktion ist.

Außerdem kann ich dann nicht mehr zur Laufzeit das Menü auswählen, denn 
ich muss ja dann bereits zur Compilezeit festelegen, welche Menügröße 
für diesen konkreten Aufruf gilt.

Jan H. schrieb:
> Ich habe es aber schon mal mit nem Array hin bekommen..

Dann war das kein C.

> CheckSize (char a[])
> return (sizeof(a) /siueof(a|0]))

a ist hier, auch wenn es irreführenderweise so aussieht, kein Array, 
sondern ein Zeiger. Du hast also die Größe eines Zeigers durch die Größe 
von dem, worauf er zeigt, dividiert.
Merke: Arrays ohne Größe gibt es nicht, also kann char a[] keine 
Array-Definition sein. Die einzige Stelle, wo man die Größe weglassen 
kann, ist beim Definieren einer Array-Variable mit gleichzeitiger 
Initialisierung. Dann ergibt sich die Größe aus dem Initializer.

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren