hätte wer einen Codeschnippsel um aus 5 Werten in einem uint16_t array den Median zu ermitteln?
uint16_t getMedian(uint16_t* data, uint8_t items)
{
uint16_t tmp;
uint8_t i, j;
for (i = 0; i < items; ++i) {
for (j = 0; j < items - i - 1; ++j) {
if (data[j] > data[j + 1]) {
tmp = data[j];
data[j] = data[j + 1];
data[j + 1] = tmp;
}
}
}
tmp = (items >> 1); // items/2
return data[tmp]; // The result set is odd. Simply return
// the mean value of the set of values.
}
danke.
tut soweit
Ich benutze den Code auf MSP430F2013 Controllern für AD-Wandler Messungen. Darum hatte ich ihn schnell zur Hand.
Möchte mich für den Hinweis zum Wokwi bedanken. Für mal schnell was simulieren offenbar ne feine Sache.
Ich habe gewisse Probleme mit dem alten C Stil.
Nicht, dass er "falsch" ist, aber er birgt Probleme.
(uint16_t* data, uint8_t items)
Hier stehen Array und Anzahl in direkte Beziehung/Abhängigkeit.
-
Ein Irrtum führt zu falschen Ergebnissen.
-
Ein Irrtum kann nicht vom Compiler erkannt werden.
-
Im Fall des
uint16_t getMedian(uint16_t* data, uint8_t items)können sogar (bei einem Irrtum) die falschen Speicherbereiche überschrieben werden. -
Bei einer Erweiterung oder Verkleinerung des Arrays müssen alle Vorkommen der Funktionsaufrufe gesucht und korrigiert werden.
Aus dem Grund hier mein Vorschlag:
template<size_t N>
uint16_t getAverage(uint16_t (&data)[N])
{
uint32_t tmp = 0;
for(uint16_t d:data) tmp += d;
return tmp/N;
}
uint16_t value[] {4, 22, 23, 70, 24};
void setup()
{
Serial.begin(9600);
Serial.println(getAverage(value));
}
void loop()
{
}
Schönes Beispiel. Kannst ja die Referenz noch "const" machen, damit in der Funktion der Inhalt des Arrays nicht "aus Versehen" verändert werden kann.
Hallo,
woher weiß der Funktionsaufruf die Anzahl der Elemente? 5 wird ja nicht übergeben. Ich dachte ein Array zerfällt immer zu einem Zeiger und alle anderen Informationen gehen verloren. Funktionieren tut es.
Das stimmt eben nicht!
Es zerfällt zu einem Zeiger, wenn es implizit oder explizit zu einem Zeiger konvertiert wird. Dann geht auch die Zellen Anzahl N verloren.
(das ist eine der größten C Problemzonen)
Das passiert aber bei dem Template nicht, da das Array per Referenz übergeben wird. Und Array Referenzen behalten den Type und damit die Anzahl N.
Eben diese Anzahl N verwende ich als Template Parameter.
Die Anzahl N wird also aus dem Datentype gezogen.
Das ist genau der Zwecke der Übung.
Wenn keine Anzahl übergeben wird, kann man dabei auch keinen Mist bauen, z.B. sich irren.
Hallo,
Das gilt nur für Templates?
Nöö...
Grundsätzlich.
Außer const, wird nicht immer übernommen, dann wird aber gewarnt.
char test[46];
auto &aliasVonTest1 = test; // ab C++14 oder C++17
decltype(test) &aliasVonTest2 = test; // C++11
Auch hier wird der Type übernommen.
Diese Funktion kann nur mit 5 Zelligen Arays umgehen:
uint16_t getAverage(uint16_t (&data)[5])
{
uint32_t tmp = 0;
for(uint16_t d:data) tmp += d;
return tmp/5;
}
Hallo,
ich präzisiere meine Frage. Das letzte gezeigte nicht Funktion Template funktioniert nur mit Angabe der Anzahl der Elemente [5]. Es gibt ohne Template keine Möglichkeit das das Array nicht zu einem Zeiger zerfällt?
Das hast du nicht geschafft.
Ich verstehe den zugrunde liegenden Gedanken nicht.
Die 5 ist Teil des Datentypes!
Sie ist hier kein Parameter, welcher der Funktion mitgegeben wird.
Auch das verstehe ich nicht!
Der Array Bezeichner zerfällt bei der Konvertierung.
Hier wird nicht Konvertiert.
Also auch kein Zerfall.
Siehe dazu dieses Beispiel:
using ArrayType = uint16_t[5];
// uint16_t value[] {4, 22, 23, 70, 24, 28}; // verursacht Error
uint16_t value[] {4, 22, 23, 70, 24}; // ok
// ArrayType value {4, 22, 23, 70, 24}; // auch ok
uint32_t summe(ArrayType &data)
{
uint32_t tmp = 0;
for(auto d:data) tmp += d;
return tmp;
}
void setup()
{
Serial.begin(9600);
Serial.println(summe(value));
}
void loop()
{
}
Hallo,
also zerfällt ein Array bei Parameterübergabe an eine Funktion doch immer zu einem Zeiger. Das wurde verneint was mich stutzig machte. Einzige Ausnahme ist man schreibt ein Funktion Template wie du es in #6 gezeigt hast.
Nochmal. Gibt es doch noch irgendwie eine Möglichkeit ohne Funktion Template ein Array als Parameter ohne extra Angabe der Anzahl der Elemente an eine Funktion zu übergeben? Eben das man in der Anzahl der Elemente variabel bleibt. Ich will mich nicht auf eine Array Größe festlegen.
Sowas in der Art nur ohne Template. Vermutlich geht das nicht ohne Template.
uint16_t value1[] {4, 22, 23};
uint16_t value2[] {4, 22, 23, 70, 24};
uint32_t summe(uint16_t &data)
{
uint32_t tmp = 0;
for(auto d:data) tmp += d;
return tmp;
}
void setup()
{
Serial.begin(9600);
Serial.println(summe(value1));
Serial.println(summe(value2));
}
void loop()
{
}
Nein!
summe() macht es vor!
Nein, summe() macht es vor.
Die Göße ist immer zur Kompilzeit bekannt, da im Datentype verankert.
Es gibt keine dynamischen Arrays (von der STL mal abgesehen)
Keine variablen Anzahlen.
Auch das Template wird zur Kompilezeit aufgelöst.
uint32_t summe(auto &data)
{
uint32_t tmp = 0;
for(auto d:data) tmp += d;
return tmp;
}
Statt template mit auto.
Geht leider noch nicht mit C++11
Wenn du die Anzahl zur Laufzeit dynamisch halten möchtest, geht nur der C Weg. Mit den bekannten Problemen.
Denn dann MUSS man mit Pointern arbeiten und die Länge der Funktion übergeben.
Aber dann wirds auch nix mehr mit dem "range based for loop"
Hallo,
jetzt ist meine Frage beantwortet. Danke.
combie hat doch gezeigt, wie es ohne template geht.
Dass es so wie du es machen willst, nicht gehen kann, sagt dir der Compiler.
ein uint16_t ist natürlich was anderes als ein uint16_t[5]
Ohne die using Variante ist es knifflig, eine Referenz auf ein Array nicht als Array von Referenzen fehlinterpretiert zu bekommen, geht aber auch:
uint32_t summe(uint16_t (& data) [5] )
{
uint32_t tmp = 0;
for(auto d:data) tmp += d;
return tmp;
}
Wobei mir außerdem die Frage bleibt, ob es evtl. nicht doch sinnvoll sein könnte, gelegentlich die gute alte C-Version mit Zeiger und Länge zu verwenden, und sich zig Template Varianten zu sparen.
Ich programmiere Arduinos weil es Spaß macht, mit wenigen Bytes auszukommen.
Bin aber gern erstaunt, was ein c++ Compiler so alles optimieren kann.
Natürlich hängt der Sinn an den Anforderungen!
Da nur die Templates wirklich erstellt/ausgerollt werden, welche man nutzt, und sich die Anzahl verschiedener Arrays meist in Grenzen hält, ist die Template/auto Variante doch in vielen Fällen nützlich.
Ins Besondere aus dem Grund, weil es so "niemals" zu Array Bereichsüberschreitungen kommen kann. (wenn man sich nicht ganz dämlich anstellt)
Das ist die Kernfrage: Will ich es möglichst flexibel, oder Typsicher?
In Sachen Performance sind die Template Varianten meist weit vorn, weil gut optimierbar.
Vom Flash Verbrauch ist es halt so, dass jedes aufgelöste Template, solcher Art, eben Platz benötigt. Das ist dann die Kröte, welche es zu schlucken gilt.
Interessant ist, dass man die Template/auto Varianten so hinbekommen kann, dass sie nicht nur mit Arrays arbeiten können, sondern auch mit allen STL Containern.
Mein Mantra:
--->> Pointer, nur wenn es nicht anders geht.
Also nur unter Sachzwang.
Als TO kann ich euch das beantworten.
Ich brauch es im konkreten Anwendungsfall weder typsicher noch flexibel. Ich habe eine fixe ungerade Zahl an Werten die ich auf 5 festgelegt habe.
Und ich habe die Gelegenheit genutzt und die beiden betreffenden Funktionen in eine Lib namens Statistics.h verfrachtet.
Zwecks Wiederverwendung!