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.
Du kannst auch als letzten Eintrag im Array
{NULL , 0xff}
nehmen
Andererseits ist es in C eigentlich immer gut, die Arraygröße mit an die
Funktionen zu übergeben.
Dirk B. schrieb:> Du kannst auch als letzten Eintrag im Array> {NULL , 0xff}> nehmen>> Andererseits ist es in C eigentlich immer gut, die Arraygröße mit an die> Funktionen zu übergeben.
Wie schon Bjarne sagen: "Rohe C/C++-Arrays sind so blöd, dass sie ihre
eigene Länge nicht kennen".
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.
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).
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.
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
...
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).
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.
Rufus Τ. F. schrieb:> Ja. Mit einem simplen Macro:> #define MENUEANZAHL ((sizeof mainMenue) / sizeof menue_t)
Ich würde diese Schreibweise wählen:
#define SIZE_OF_ARRAY(array) (sizeof(array)/sizeof((array)[0]))
Aber das ist eine Feinheit.
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.
J. F. schrieb:> Rufus Τ. F. schrieb:>> Ja. Mit einem simplen Macro:>> #define MENUEANZAHL ((sizeof mainMenue) / sizeof menue_t)>> Ich würde diese Schreibweise wählen:>> #define SIZE_OF_ARRAY(array) (sizeof(array)/sizeof((array)[0]))>> Aber das ist eine Feinheit.
... und das geht eben nur (s.o.), wenn array nocht nicht zu einem
Zeigertyp "zerfallen" ist.
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.
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.
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.
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.
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.
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.
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.
Also das sollte für C sein. 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.
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
voidbar(irgendein_datentyp*p,size_tlen)
2
{
3
...
4
}
5
6
voidfoo(void)
7
{
8
irgendein_datentyparray[]={...};
9
10
bar(array,sizeof(array)/sizeof(array[0]));
11
}
Geht nicht:
1
voidbar(irgendein_datentyp*p)
2
{
3
size_tlen=sizeof(p)/sizeof(p[0]);
4
}
5
6
voidfoo(void)
7
{
8
irgendein_datentyparray[]={...};
9
10
bar(array);
11
}
Mach doch einfach mal ein vollstaendiges minimal Beispiel, damit wir
nachvollziehen koennen was du willst/meinst.
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]))
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.