Größe einer beliebigen Reihe, auch unterschiedlich viele Elemente, berechnen.

OP #7489792
Lesenswert?

Guten Abend zusammen!

Mit normalen Arrays ist das kein Problem. Leider bekomme ich es nicht mit zweidimensionalen hin. Bitte um Hilfe.

Der einzige Code, über den ich öfter gestolpert bin, ist dieser:

1
#define ROW_SIZE(arr, row) (sizeof(arr[row]) / sizeof(arr[row][0]))

Klingt auch logisch. Klappt nur nicht. Bekomme immer die Anzahl der Spalten wieder.

1
byte Stunden[2][4] = {
2
  {1, 2, 3, 4},
3
  {5, 6, 7}
4
};

Ich Programmiere mit der Arduino IDE(C++)

Gruß

(Firma: Gast) #7489799
Lesenswert?

1
#include <Streaming.h> // die Lib findest du selber ;-)
2
Print &cout = Serial; // cout Emulation für "Arme"
3

4

5
template<typename T, size_t N> size_t arrayCount(T(&)[N])
6
{
7
  return N;
8
}
9

10
byte Stunden[2][4] = {
11
  {1, 2, 3, 4},
12
  {5, 6, 7}
13
};
14

15

16

17
void setup() 
18
{
19
  Serial.begin(9600);
20
  cout << F("Start: ") << F(__FILE__) << endl;
21
  cout << F("Zeilen: ") << arrayCount(Stunden) << endl;
22
  cout << F("Spalten: ") << arrayCount(*Stunden) << endl;
23
}
24

25
void loop() 
26
{
27

28
}
#7489843
Lesenswert?

Arduino F. schrieb:

1
> template<typename T, size_t N> size_t arrayCount(T(&)[N])
2
> {
3
>   return N;
4
> }
5
> 
6
> byte Stunden[2][4] = {
7
>   {1, 2, 3, 4},
8
>   {5, 6, 7}
9
> };
10
>

Gute Güte, jetzt quälst Du den TO auch noch mit einem Funktionstemplate ;-) Dann mache es wenigstens noch constexpr und gibt ihm den Namen size(...), damit man wenigstens etwas näher an die StdLibC++ heranrückt. Da ich stark vermute, dass er mit avr-g++ arbeitet, und der keine StdLibC++ mitbringt, darf man das auch ruhig in den Namensraum std packen.

Und das dieses unsäglich byte für vorzeichenlose Ganzzahlen. Nimm doch wenigstens uint8_t.

#7489845
Lesenswert?

Es wird immer der maximal nötige Speicherplatz belegt, anders kann der Compiler nicht drauf zugreifen. Wenn Du den Zugriff auf ungültige Elemente verhindern willst, geht das z.B. über einen reservierten Wert (0, 0xFF, NAN). Oder Du definierst eine Struct, wo zusätzlich die Länge mit abgelegt wird (Array of Structs). Wenn die Länge schon zur Compilezeit feststeht, geht auch ein Array von Pointern auf Arrays. Kostet natürlich zusätzlichen Speicher für die Pointerliste und etwas Laufzeit zum aufdröseln der Pointer.

#7489852
Lesenswert?

Peter D. schrieb:

Wenn Du den Zugriff auf ungültige Elemente verhindern willst, geht das z.B. über einen reservierten Wert (0, 0xFF, NAN).

Warum sollen 0x00 oder 0xff reservierte Werte bei einem Byte sein, wobei der DT byte bei Arduino ja dasselbe wie uint8_t ist. Weder für byte noch für uint8_t gibt es "ungültige" Werte. Dein Ratschlag ist so, wie man das Jahr-2000-Problem vielerorten gelöst hat: einfach Mist.

Und NaN hat byte schon mal gar nicht. Das gibt es bei float oder double.

Auch Pointer-Typen (T*) haben einen definitiv ungültigen Wert: nullptr. Alle anderen primitiven DT nicht.

Will man so etwas erreich, dann braucht man einen additiven DT wie etwa std::optional<>.

OP #7489866
Lesenswert?

Peter D. schrieb:

Es wird immer der maximal nötige Speicherplatz belegt, anders kann der Compiler nicht drauf zugreifen. Wenn Du den Zugriff auf ungültige Elemente verhindern willst, geht das z.B. über einen reservierten Wert (0, 0xFF, NAN). Oder Du definierst eine Struct, wo zusätzlich die Länge mit abgelegt wird (Array of Structs). Wenn die Länge schon zur Compilezeit feststeht, geht auch ein Array von Pointern auf Arrays. Kostet natürlich zusätzlichen Speicher für die Pointerliste und etwas Laufzeit zum aufdröseln der Pointer.

Ich möchte über eine Funktion alle Arrays über eine for-Schleife ausgeben. Mit normalen Arrays klappt das auch wunderbar. Die Größe wird dabei bequem über ein Makro ermittelt. Die Größe wird benötigt, damit die Zählschleife weiß, wann sie fertig ist.

1
  #define SIZEOF(arr) sizeof(arr) / sizeof(*arr)
2

3
  {OutputArray(Array, SIZEOF(Array));} 
4

5
  void OutputArray(byte Arr[], byte ArrSize)
6
  {     
7
      for(byte i = 0; i < ArrSize; i++)
8
     {
9
      //do something - Arr[i]
10
     }
11
  }
#7489878
Lesenswert?

Tim schrieb:

Ich möchte über eine Funktion alle Arrays über eine for-Schleife ausgeben. Mit normalen Arrays klappt das auch wunderbar. Die Größe wird dabei bequem über ein Makro ermittelt. Die Größe wird benötigt, damit die Zählschleife weiß, wann sie fertig ist.

1
>   #define SIZEOF(arr) sizeof(arr) / sizeof(*arr)
2
> 
3
>   {OutputArray(Array, SIZEOF(Array));}
4
> 
5
>   void OutputArray(byte Arr[], byte ArrSize)
6
>   {
7
>       for(byte i = 0; i < ArrSize; i++)
8
>      {
9
>       //do something - Arr[i]
10
>      }
11
>   }
12
>

Hoffentlich weißt Du auch, dass man diese üble Größenberechnung nicht innerhalb(!) einer Funktion durchführen darf.

Zudem: werden Deine Arrays nie größer als 256 Elemente?

OP #7489884
Lesenswert?

Wilhelm M. schrieb:

Hoffentlich weißt Du auch, dass man diese üble Größenberechnung nicht innerhalb(!) einer Funktion durchführen darf.

Wie meinst du das und wieso? Es funktioniert.

Zudem: werden Deine Arrays nie größer als 256 Elemente?

Das ist mir bewusst. Ich kenne auch die gängigen anderen Datentypen und nutze byte, da ich weiß, dass ich an dieser Stelle nicht mehr benötige.

#7489891
Lesenswert?

Tim schrieb:

Wilhelm M. schrieb:

Hoffentlich weißt Du auch, dass man diese üble Größenberechnung nicht innerhalb(!) einer Funktion durchführen darf.

Wie meinst du das und wieso? Es funktioniert.

Ja. Aber nicht mehr, wenn Du die Größenberechnung mittels dieses dummen Macros innerhalb der Funktion machst, und auf den zweiter Parameter der Funktion verzichtest. Denn der Array-Bezeichner an der Aufrufstelle zerfällt zu einem Zeiger in der Funktion. Ein Zeiger-Typ bei AVR hat 16 Bit. Damit ist also die Größe um den Faktor 2 falsch.

Zudem: werden Deine Arrays nie größer als 256 Elemente?

Das ist mir bewusst. Ich kenne auch die gängigen anderen Datentypen und nutze byte, da ich weiß, dass ich an dieser Stelle nicht mehr benötige.

Na dann ... warten wir es ab ;-)

Moderator #7489893
Lesenswert?

Tim schrieb:

byte Stunden[2][4] = { {1, 2, 3, 4}, {5, 6, 7} };

Wenn du ein mit 4 Elementen deklariertes Array nur mit 3 Werten initialisierst, wird das Array deswegen nicht kleiner, sondern der nicht explizit initialisierte Teil des Arrays wird implizit mit 0 initialisiert.

Dein Code ist somit äquivalent zu diesem:

1
byte Stunden[2][4] = {
2
  {1, 2, 3, 4},
3
  {5, 6, 7, 0}
4
};
OP #7489903
Lesenswert?

Wilhelm M. schrieb:

Na dann ... warten wir es ab ;-)

Im Praxistest hats geklappt.

Einen Teil der Arrays hatte ich in einer Switch-Case-Anweisung gepackt. Nun wollte ich den Code "optimieren"/verkürzen, indem ich diesen Block in ein 2D-Array packe, da man auf diese Weise, die Elemente(Reihen) nacheinander ausgeben lassen könnte. Vielleicht ein Irrglaube. Was wäre die Alternative? Für jedes Arrayelement(Reihe) eine Variable deklarieren? Diese dann vielleicht auch noch in einen Block packen, zwecks Durchnummerierung?! Bspw.:

byte Stunden[2][4] = { {1, 2, 3, 4}, {5, 6, 7} };

byte row_size_0 = 4; byte row_size_1 = 3;

OP #7489911
Lesenswert?

Udo S. schrieb:

Vielleicht erzählst du einfach was für ein Problem du lösen willst statt immer nur nach der Lösung für ein Scheibchen deiner angedachten Lösung zu fragen.

Es geht nicht um eine explizite Anwendung, sondern darum, eine Reihe von 2D-Array nacheinander ausgeben zu können. Vielleicht verfolge ich ja auch den falschen Ansatz...

Es geht darum, wie beim Array, eine Reihe von Werten zu deklarieren - 53, 102, 162, diese vielleicht noch thematisch zusammenzufassen und dann, nach Bedarf, abzurufen.

Moderator #7489937
Lesenswert?

Tim schrieb:

Für jedes Arrayelement(Reihe) eine Variable deklarieren? Diese dann vielleicht auch noch in einen Block packen, zwecks Durchnummerierung?! Bspw.:

byte Stunden[2][4] = { {1, 2, 3, 4}, {5, 6, 7} };

byte row_size_0 = 4; byte row_size_1 = 3;

Das ist eine von vielen Möglichkeiten. Statt dir für die Größen jeweils einen eigenen Variablennamen auszudenken, kannst du sie auch direkt im Array Stunden unterbringen, bspw. so:

1
struct row {
2
  byte size;
3
  byte elements[4];
4
};
5

6
row Stunden[] = {
7
  {4, {1, 2, 3, 4}},
8
  {3, {5, 6, 7}}
9
};
10

11
const byte NROWS = (sizeof Stunden / sizeof Stunden[0]);

Dann kannst du folgendermaßen über die gewünschten Elemente von Stunden iterieren:

1
  for (byte i=0; i<NROWS; i++) {
2
    for (byte j=0; j<Stunden[i].size; j++)
3
      do_something_with(Stunden[i].elements[j]);
4
  }

Andere Möglichkeiten wurden in diesem und dem anderen Thread bereits genannt, bspw. die Markierung des Endes jeder Reihe durch einen speziellen Wert, den als normales Reihenelement nicht vorkommt.

Welche der vielfältigen Möglichkeiten die geeignetste ist, hängt vom konkreten Anwendungsfall ab.

OP #7489939
Lesenswert?

Tim schrieb:

Dein Code ist somit äquivalent zu diesem:

Also ist das eigentlich Platzverschwendung, wenn man kürzere Arrays verwendet, als angegeben?! Wenn ich aber eine Liste von Zusammengefassten Werten, die der Reihe nach abgerufen werden sollen, möchte, ist es ein notwendiges Übel?

Oder ist das einfach nicht relevant?

#7489943
Lesenswert?

Wilhelm M. schrieb:

Du hast es leider nicht verstanden: uint8_t alias byte hat in seinem Wertebereich keine "ungültigen" Werte.

Hat ja auch niemand behauptet. Nochmal: "Natürlich muß der Entwickler darauf achten, daß der reservierte Code nicht regulär enthalten sein darf." Wenn aber der gesamte Wertebereich erlaubt sein soll, muß man es eben als struct mit extra Längenelement definieren.

OP #7489947
Lesenswert?

Yalu X. schrieb:

1
> struct row {
2
>   byte size;
3
>   byte elements[4];
4
> };
5
> row Stunden[] = {
6
>   {4, {1, 2, 3, 4}},
7
>   {3, {5, 6, 7}}
8
> };
9
> const byte NROWS = (sizeof Stunden / sizeof Stunden[0]);
10
> 
11
> Dann kannst du folgendermaßen über die gewünschten Elemente von Stunden
12
> iterieren:
13
>   for (byte i=0; i<NROWS; i++) {
14
>     for (byte j=0; j<Stunden[i].size; j++)
15
>       do_something_with(Stunden[i].elements[j]);
16
>   }

Das sieht sehr gut aus! Zumindest durch meine bescheidenen Augen betrachtet :D Arraystructs sind mir bekannt und verstehe diese. Habe damit aber tatsächlich noch nicht wirklich gearbeitet. Werde ich später mal ausführlich testen. Derzeit hatten mich Arrays sehr begeistert, da ich darin viele Möglichkeiten sehen. Was soll ich sagen, bin halt noch ein noob.

Vielen Dank!

#7489949
Lesenswert?

Peter D. schrieb:

Hat ja auch niemand behauptet. Nochmal: "Natürlich muß der Entwickler darauf achten, daß der reservierte Code nicht regulär enthalten sein darf."

Nochmal: wenn ich ein Objekt vom DT byte oder uint8_t habe, wie sehe ich ihm allgemein(!) an, dass es ungültig sein soll? Das ist nicht möglich, weil diese DT das einfach nicht hergeben. Damit ist es dann unmöglich, guten Code zu schreiben, der allgemeingültig ist, etwa als Rückgabewert oder auch als Argument einer Funktion. Entweder hat der DT in seinem Wertebereich einen ungültigen Wert, der das "Nichts" ausdrückt, wie etwa Zeigertypen oder andere UDT, oder man verwendet dafür einen additiven DT, wie std::optional<>.

#7489950
Lesenswert?

Tim schrieb:

Wenn ich aber eine Liste von Zusammengefassten Werten, die der Reihe nach abgerufen werden sollen, möchte, ist es ein notwendiges Übel?

Nö. Kann man auch anders machen, aber die Längeninformation muss man halt separat transportieren.

1
struct krempel
2
{
3
  int len;
4
  int *data;
5
};
6

7
int x0[] = {1, 3, 5, 7};
8
int x1[] = {1};
9
int x2[] = {2, 7, 56, 12, 1, 78};
10
int x3[] = {2, 3, 4};
11

12
struct krempel y[] =
13
{
14
  {sizeof (x0) / sizeof (x0[0]), x0},
15
  {sizeof (x1) / sizeof (x1[0]), x1},
16
  {sizeof (x2) / sizeof (x2[0]), x2},
17
  {sizeof (x3) / sizeof (x3[0]), x3},
18
};

Das entspricht dem Beispiel von yalu, mit dem Unterschied, daß für jedes Teilarray wirklich nur soviel Platz verwendet wird, wie benötigt wird.

Achte bei der Definition auf die leeren eckigen Klammern! In diesem Fall, der nur bei einer Dimension von Array angewandt werden kann, bestimmt der Compiler die Arraygröße aus der Größe des Initialisierers.

In der Hilfsstruktur (struct krempel) ist die Anzahl der Arrayelemente und ein Pointer auf das Array abgelegt, und das Array y besteht --wie bei yalu-- aus mehreren dieser Hilfsstrukturen.

Bei dieser Vorgehensweise aber ist es nicht vermeidbar, die einzelnen Arrays mit jeweils einem Variablennamen (x0 .. x3) anzulegen.

#7489958
Lesenswert?

Harald K. schrieb:

In der Hilfsstruktur (struct krempel) ist die Anzahl der Arrayelemente und ein Pointer auf das Array abgelegt, und das Array y besteht --wie bei yalu-- aus mehreren dieser Hilfsstrukturen.

Dann wird der TO sicher noch Zeiger auf Stack-Objekte dort ablegen, und sich wundern, warum es nicht funktioniert.

Leute, der TO verwendet C++. Ihr bringe ihm gerade C bei.

Gibt es denn in diesem tollen Arduino-Zeug nicht auch so etwas wie einen FixedVector<>, den man ggf. aus FixedVector<byte, 10> verwenden kann. Also ein Array mit maximal 10 Elementen.

Persönliche Seite #7489960
Lesenswert?

Wilhelm M. schrieb:

Gute Güte, jetzt quälst Du den TO auch noch mit einem Funktionstemplate ;-)

Seit C++17 ist das auch schon in der Standardbibliothek mitgeliefert:

https://en.cppreference.com/w/cpp/iterator/size Overload 3

Man kann also einfach std::size(Stunden) schreiben. Aber das ist jetzt hier natürlich nicht die Frage vom OP, vermeidet nur lediglich dieses hässliche ROW_SIZE Makro. Und fehlersicherer ist es auch, denn wenn man versehentlich nur einen Pointer übergibt, meckert der Compiler, anstatt einfach was falsches zurückzugeben.

#7489963
Lesenswert?

Niklas G. schrieb:

Wilhelm M. schrieb:

Gute Güte, jetzt quälst Du den TO auch noch mit einem Funktionstemplate ;-)

Seit C++17 ist das auch schon in der Standardbibliothek mitgeliefert:

https://en.cppreference.com/w/cpp/iterator/size Overload 3

Hatten wir schon:

Beitrag "Re: Größe einer beliebigen Reihe, auch unterschiedlich viele Elemente, berechnen."

Allerdings scheint es hier um avr-g++ zu gehen ...

#7489990
Lesenswert?

Wilhelm M. schrieb:

Leute, der TO verwendet C++. Ihr bringe ihm gerade C bei.

C-Grundlagenwissen ist nicht verkehrt, um zu verstehen, was einem ein C++-Compiler an Arbeit abnimmt (und möglicherweise an Komplexität draufschaufelt) - wenn man im Embedded-Bereich unterwegs ist, kann es längerfristig besser sein, zu wissen, woraus die bunten Bauklötze sind, statt sie nur zusammendrücken zu können.

Die teilweise sehr akademischen Diskussionen, die hier in letzter Zeit von einigen Protagonisten geführt wurden, sind jedenfalls auch nicht hilfreich.

Moderator #7489992
Lesenswert?

Wilhelm M. schrieb:

Tim schrieb:

Wilhelm M. schrieb:

Gibt es denn in diesem tollen Arduino-Zeug nicht auch so etwas wie einen FixedVector<>, den man ggf. aus FixedVector<byte, 10> verwenden kann. Also ein Array mit maximal 10 Elementen.

Bitte was? :D

Besorge Dir ein C++-Buch oder mach einen Online-Kurs.

Der Datentyp FixedVector gibt es IMHO weder in der C++-Standard- noch in der Arduino-Bibliothek, weswegen man ihn auch in keinem C++-Buch finden wird.

Natürlich kann man diesen Typ selber schreiben. Er entspräche dann in etwa diesem Typ

Yalu X. schrieb:

struct row { byte size; byte elements[4]; };

nur mit dem Unterschied, dass die Maximalgröße als Template-Argument übergeben wird, der Zugriff auf die Elemente direkt mit dem []-Operator erfolgt kann und /size/ automatisch aus der Initialiererliste bestimmt wird.

Um das zu tun (oder auch nur zu verstehen), sind aber Kenntnisse über

  • Templates,

  • Operatorüberladung und

  • Initialisiererlisten

erforderlich, die der TE, der sich selbst als "Noob" bezeichnet, definitiv (noch) nicht hat.

Deswegen sollten wir erst einmal versuchen, sein Problem mit den Sprachelementen zu lösen, die er aus seiner Arduino-Praxis schon kennt. Den von dir vorgeschlagenen FixedVector kann man danach Schritt für Schritt darauf aufbauen.

Trotzdem ist es natürlich für einen Arduino-Programmierer kein Fehler, ein C++-Buch durchzuarbeiten. Konkrete Vorschläge dafür habe ich leider nicht, Mein letztes C++-Buch ist das von Breymann, wobei das schon ein ziemlicher Schinken ist. Wie es sich im Vergleich zu anderen Büchern schlägt, kann ich nicht sagen.

#7490007
Lesenswert?

Tim schrieb:

Es geht nicht um eine explizite Anwendung, sondern darum, eine Reihe von 2D-Array nacheinander ausgeben zu können.

Suchst du sowas? Wenn ja machste dir daraus eine Funktion.

1
int a2D [2][4] {
2
  {1,2,3,4,}, 
3
  {5,6,7,8}
4
};
5
     
6
void setup (void)
7
{
8
  Serial.begin(9600);
9
  Serial.println("\nuC Reset ####");
10

11
  for (auto &row : a2D) {
12
    for (auto &col : row){
13
      Serial.println(col);
14
    }
15
  }
16
  Serial.println();
17
}
18

19
void loop (void)
20
{ 
21
}
(Firma: Gast) #7490013
Lesenswert?

Tim schrieb:

Die Konsole gibt mir jetzt die Anzahl der Reihen und Spalten aus.

So soll es auch sein!

Tim schrieb:

Möchte aber die Anzahl der Elemente jeder Reihe. Also für dieses Beispiel: row_0 = 4, row_1 = 3.

Wie soll das möglich sein?

Solche Arrays sind "rechteckig"! Haben keine zackeligen Ränder.

byte Stunden[2][4]

Ein solches Array hat exakt 2*4=8 Zellen. Das ist genau die Zellenzahl die du erzwungen hast. Völlig unabhängig davon, was du dir wünscht oder was sich in deiner Fantasie abspielt.

Tim schrieb:

Besorge Dir ein C++-Buch oder mach einen Online-Kurs.

Kannst du da was empfehlen?

z.B. dieses: http://www.cppbuch.de/ Veraltete Versionen gibts gerade deutlich billiger. Warnung: Arduino wird da drin mit keinem Wort erwähnt.

#7490098
Lesenswert?

Yalu X. schrieb:

Der Datentyp FixedVector gibt es IMHO weder in der C++-Standard- noch in der Arduino-Bibliothek, weswegen man ihn auch in keinem C++-Buch finden wird.

Deswegen habe ich auch Arduino-Zeug geschrieben und nicht StdCppLib. Denn es gibt ja hier Leute, die sehr "stolz" auf Arduino sind, und ich hätte es für möglich gehalten, dass es da so etwas gibt. Das macht nämlich Sinn! So wie auch ein std::array<> (ohne std ;-).

Yalu X. schrieb:

Um das zu tun (oder auch nur zu verstehen), sind aber Kenntnisse über

  • Templates,

  • Operatorüberladung und

  • Initialisiererlisten

erforderlich, die der TE, der sich selbst als "Noob" bezeichnet, definitiv (noch) nicht hat.

Sinnerfassendes Lesen ist schon wichtig: er soll das nicht selbst schreiben, sondern nur benutzen, so wie er den restlichen Arduino-Kram auch einfach nur benutzt. Ich vermute, er wird auch Serial und String benutzen.

#7490129
Lesenswert?

Harald K. schrieb:

Veit D. schrieb:

Suchst du sowas?

Nee. Das genau sucht er nicht.

Dann funktioniert das was Tim eigentlich will mit Mehrdimensionalen Arrays nicht. Das was Tim möchte funktioniert nur mit EIN-Dimensionalen Arrays. Das ist die aktuelle Situation. Nur dann muss man sich keine komplizierten durch die Brust ins Auge Lösungen ausdenken, denn die Anzahl der Elemente ist zur Compiletime bekannt. Genau das deckt Range-based for loop ab.

#7490162
Lesenswert?

Wilhelm M. schrieb:

Nochmal: wenn ich ein Objekt vom DT byte oder uint8_t habe, wie sehe ich ihm allgemein(!) an, dass es ungültig sein soll?

Man kann natürlich alles super eng sehen und alles auf die Goldwaage legen, aber man muß nicht. Ich nehme sehr gern einen Leerstring oder Nullpointer als Endekennzeichen eines Arrays oder einer Liste. Rein formal sind das natürlich gültige Einträge. Ihre Sonderbedeutung erhalten sie durch den Kontext. Das vermeidet Schusselfehler, wenn ich stattdessen die Länge extra mit übergeben würde und sich nachträglich die Länge ändert. Oft fügt man Elemente später hinzu oder löscht welche.

Auch kann man Werten außerhalb des Nutzungsbereiches eine Sonderbedeutung geben, z.B. in meinem Scheduler:

1
#define MAX_SCHEDULE    30
2

3
enum
4
{
5
  T_FREE = MAX_SCHEDULE,
6
  T_LAST,
7
};
#7490168
Lesenswert?

Peter D. schrieb:

Ich nehme sehr gern einen Leerstring oder Nullpointer als Endekennzeichen eines Arrays oder einer Liste.

Was soll das denn sein? Einen Leerstring als sentinel? Für ein Array-of-byte? Oder noch schlimmer, einen nullptr.

Du meinst wahrscheinlich das Stringendezeichen '\0' als sentinel in einem Array von char bzw. byte. Das ist die Konvention (!) für C-Strings. Kannst Du aber nicht in einem Array von byte anwenden, wenn das numerische Bedeutung (wie bei Arduino als uint8_t) hat.

nullptr oder (void*)0 (aka NULL in C) passt doch vom Dt nur in eine Array von Pointern. Und ja, genau das habe ich doch geschrieben: Pointer DT wie T* haben einen ungültigen Wert, hier nullptr aka NULL in C

Moderator #7490185
Lesenswert?

Wilhelm M. schrieb:

Du meinst wahrscheinlich das Stringendezeichen '\0' als sentinel in einem Array von char bzw. byte. Das ist die Konvention (!) für C-Strings. Kannst Du aber nicht in einem Array von byte anwenden, wenn das numerische Bedeutung (wie bei Arduino als uint8_t) hat.

Das geht schon, wenn man man bereit ist, ein einzelnes Element des Wertebereichs für das Vorhaben zu opfern. Ob und für welches Element dies möglich ist, hängt von der Anwendung ab. Gerade bei der Interpretation von byte bzw. uint8_t als Zahlenwert wird der Wertebereich oft nicht voll ausgeschöpft, so dass entweder 255 oder 0 problemlos als Endemarkierung verwendet werden kann.

Bei den C-Strings wurde ähnlich verfahren: Eigentlich sollten darin mindestens alle ASCII erlaubt sein. Da aber das ASCII-Zeichen NUL nur selten verwendet wird, hat man sich dafür entschieden, dieses dem zulässigen Wertebereich zu entnehmen und ihm stattdessen die Bedeutung als Endezeichen zukommen zu lassen.

Entsprechendes gilt auch für den NULL-Pointer als Endemarkierung.

#7490191
Lesenswert?

Veit D. schrieb:

Dann funktioniert das was Tim eigentlich will mit Mehrdimensionalen Arrays nicht.

Funktionieren tut es schon.

Man könnte sich vorstellen:

[¢]

template<size_t... L> struct RowLengths;

template<typename RowLengths> struct Fluttering { ... };

[/c]

Oder wenn nur eine begrenzte Anzahl von verschiedenen Zeilenlängen vorkommt:

1
std::array<std::variant<std::array<byte, 10>, std::array<byte, 19>>, 4> flutter;

Nur: der TO wird es nicht erstellen können bzw. Arduino stellt es nicht zur Verfügung bzw. die StdLibC++ ist für avr-g++ nicht vorhanden und std::variant<> und std::array<> wird er auch nicht schreiben können.

Gäbe es das oben vorgeschlagene FixedVector<> sähe das schon besser aus. Wobei natürlich dabei Speicher "verschwendet" wird.

#7490199
Lesenswert?

Yalu X. schrieb:

Das geht schon, wenn man man bereit ist, ein einzelnes Element des Wertebereichs für das Vorhaben zu opfern. Ob und für welches Element dies möglich ist, hängt von der Anwendung ab. Gerade bei der Interpretation von byte bzw. uint8_t als Zahlenwert wird der Wertebereich oft nicht voll ausgeschöpft, so dass entweder 255 oder 0 problemlos als Endemarkierung verwendet werden kann.

Das ist doch jetzt echt nicht Dein Ernst. Das ist wie im letzten Jahrtausend, sorry. Und so etwas gibst Du einem Anfänger mir. Fahrlässig.

Die durch C-Strings motivierte Ausnahme mit '\0' als Sentinel ist schon schlimm genug. Sie wäre nur dann gerechtfertigt, wenn der DT char definitiv nur für (ASCII)-Zeichen reserviert wäre. Aber er ist ein normaler arithmetischer Typ. Deswegen ist auch das großer Mist.

Zeiger-Typen sind die einzigen primitiven DT, die einen ungültigen Wert in ihrem Wertebereich haben. Und die Verwendung (Indirektion) für definitiv zu einem Programmabbruch (eingebaute "Assertion"). Das ist gut so.

In C++ schreibt man sich dafür einen kleinen UDT, der genau das zum Ausdruck bringt, was man will. Von mir aus mit 255 als interne Codierung für den ungültigen Wert. Aber dann bitte mit einer Assertion bei der Initialisierung und einem Typumwandlungsoperator nach bool.

#7490218
Lesenswert?

Wilhelm M. schrieb:

Zeiger-Typen sind die einzigen primitiven DT, die einen ungültigen Wert in ihrem Wertebereich haben.

Ich hatte mal ein 8051 Programm, was sich merkwürdig verhielt. Beim Debuggen bin ich dann an eine Stelle gekommen, wo ein Pointer auf 0 geprüft wurde. Nun hat der 8051 aber Harward-Architektur, d.h. die XDATA Adresse 0x0000 ist gültiger SRAM, die dann der Linker auch brav einer Struct zugewiesen hat. Ich hab dann per Linkeroption den XDATA an Adresse 0x0001 definiert und es lief einwandfrei.

#7490278
Lesenswert?

Wilhelm M. schrieb:

Veit D. schrieb:

Dann funktioniert das was Tim eigentlich will mit Mehrdimensionalen Arrays nicht.

Funktionieren tut es schon.

Hallo Wilhelm,

wenn sein 2D Array mit der Anzahl von 5 Elementen definiert ist je Zeile, er aber nur 3 Werte reinschreibt zur Initialisierung, dann möchte er auch nur diese 3 Werte auslesen und nicht 5. Das sehe ich bei dir nicht. Und das geht auch ohne Trickserei nicht.

#7490338
Lesenswert?

Veit D. schrieb:

Wilhelm M. schrieb:

Veit D. schrieb:

Dann funktioniert das was Tim eigentlich will mit Mehrdimensionalen Arrays nicht.

Funktionieren tut es schon.

Hallo Wilhelm,

wenn sein 2D Array mit der Anzahl von 5 Elementen definiert ist je Zeile, er aber nur 3 Werte reinschreibt zur Initialisierung, dann möchte er auch nur diese 3 Werte auslesen und nicht 5. Das sehe ich bei dir nicht. Und das geht auch ohne Trickserei nicht.

Welchen meiner Vorschläge meinst Du?

#7490823
Lesenswert?

Veit D. schrieb:

Hallo Wilhelm,

okay, wir drehen das jetzt rum. Zeige bitte dein Programm für den Desktop PC mit Ausgabe, da haben alle die StdLibC++. Bin gespannt ob das den Vorgaben vom TO entspricht.

Für ein System mit voller Unterstützung der StdLibC++?

Auf die Schnelle würde ich das vermutlich so machen:

1
template<typename C>
2
concept Container = requires(C c) {
3
    std::begin(c);
4
    std::end(c);
5
};
6

7
template<Container C>
8
std::ostream& operator<<(std::ostream& out, const C& c) {
9
    std::cout << '{';
10
    for(const auto& element: c) {
11
        if (&element != &c[0]) {
12
            out << ", ";
13
        }
14
        out << element;
15
    }
16
    std::cout << '}';
17
    return out;    
18
}
19

20
int main(){
21
    std::vector<std::vector<int>> vv{{
22
        {1}, 
23
        {2, 3}, 
24
        {4, 5, 6}
25
    }};
26
    std::cout << vv << '\n';
27
}

Das ergibt:

1
{{1}, {2, 3}, {4, 5, 6}}

Wie schon vorgeschlagen, könnte man, wenn man z.B. auf Head-Allocation verzichten möchte, sich ein Klassen-Template

1
template<typename T, size_t Size = 10>
2
struct FixedVector {
3
   // ...
4
};

was man statt std::vector<> einsetzen könnte:

1
   FixedVector<FixedVector<int>> vv{{
2
        {1}, 
3
        {2, 3}, 
4
        {4, 5, 6}
5
    }};

Wie oben auch schon gesagt, hat das natürlich den Nachteil der "Speicherverschwendung".

Wenn man das auch nicht möchte und ggf. bereit ist, Speicherverbrauch gegen Laufzeit einzutauschen, dann wäre auch das möglich (s.a. oben Fluttering).

Bevor jetzt alle wieder aufschreien: dieser Beitrag war nicht an den TO gerichtet! Für den TO hätte ich mir nach wie vor ein FixedVector o.ä. aud irgendeiner Arduino-Lib gewünscht.

#7490846
Lesenswert?

Hallo,

Danke für die Demo. Ich habe das natürlich ausprobiert. Eine Frage bleibt. Woher kommt die Ausgabe der Allerersten und Allerletzten Klammer?

Kommentiere ich std::cout << '{'; std::cout << '}'; aus werden gar keine Klammern ausgegeben.

Wenn ich den Code lese erwarte ich eigentlich das: {1}, {2, 3}, {4, 5, 6} und nicht: {{1}, {2, 3}, {4, 5, 6}} Wo liegt der Zauber der Allerersten und Allerletzten Klammer?

OP #7491714
Lesenswert?

Veit D. schrieb:

Hallo Wilhelm,

okay, wir drehen das jetzt rum. Zeige bitte dein Programm für den Desktop PC mit Ausgabe, da haben alle die StdLibC++. Bin gespannt ob das den Vorgaben vom TO entspricht.

Hatte es, von Yalu X., gerade ins Programm eingesetzt und werde es später testen. Sieht aber erst einmal gut aus.

Bin gespannt :)

1
  struct row
2
  {
3
    byte size;
4
    byte hour[6];
5
  };
6

7
  row hours[12] = //1-12
8
  {
9
    {4, { 62,  63,  64,  65        }}, //eins
10
    {4, { 55,  56,  57,  58        }}, //zwei
11
    {4, { 66,  67,  68,  69        }}, //drei
12
    {4, { 73,  74,  75,  76        }}, //vier
13
    {4, { 51,  52,  53,  54        }}, //fünf
14
    {5, { 83,  84,  85,  86, 87    }}, //sechs
15
    {6, { 88,  89,  90,  91, 92, 93}}, //sieben
16
    {4, { 77,  78,  79,  80        }}, //acht
17
    {4, {103, 104, 105, 106        }}, //neun
18
    {4, {109, 108, 107, 106        }}, //zehn
19
    {3, { 49,  50,  51             }}, //elf
20
    {5, { 94,  95,  96,  97, 98    }}  //zwölf
21
  };
22

23
void OutputArrayHours()
24
{
25
const byte NROWS = (sizeof hours / sizeof hours[0]);
26

27
  for (byte i=0; i < NROWS; i++)
28
  {
29
    for (byte j=0; j < hours[i].size; j++)
30
    {
31
      pixels.setPixelColor(hours[i].hour[j], pixels.Color(COLOR, COLOR, COLOR));
32
      pixels.show();
33
    }
34
  }
35
}

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