Hi,
ich habe gerade einen Knoten im Kopf, obwohl das Problem an sich wohl
nicht irre schwer ist denke ich:
Ich habe zwei Arrays mit 16 Bit-Variablen und Pointer die auf diesen
umherwandern. Das eine Array ist aufsteigend sortiert (PWMtable), das
andere mit verschiedenen Werten.
Ich möchte nun einen Pointer von dem unsortierten Array auf das Element
des sortierten Arrays umsetzen, das dem Vorherigen Wert am nächsten kam.
Ich hoffe die Beschreibung ist nicht zu konfus. :) Meine Idee war
folgende:
1
voidBuffer_SetTableVal(uint16_t**p_ToSet){
2
3
uint16_ttemp=**p_ToSet;
4
*p_ToSet=PWMtable;
5
while(temp>**p_ToSet)*p_ToSet++;
6
}
Nun schmeißt mir der Compiler noch die Warnung "value computed is not
used" für den Befehl hinter dem while.
Achja: Die Zeiger die verändert werden sollen sind deklariert als
"uint16_t *pointername".
Funktioniert das so überhaupt? Ich verzwirbele mich da grade mit den
Pointern auf Pointern. :) Wäre für Hilfe dankbar.
Frederik N. schrieb:> Ich hoffe die Beschreibung ist nicht zu konfus.
Ich habe es ehrlich gesagt nicht verstanden. Da du aber von Arrays
schreibst, könnte der Zugiff über den Index einfacher sein. Anstatt
Pointer auf Pointer mit anderen Pointern zu vereinigen, speichert man
die Indizes
Da fehlen mindestens noch Klammern um das letzte *p_ToSet.
Frederik N. schrieb:> Ich verzwirbele mich da grade mit den Pointern auf Pointern. :) Wäre> für Hilfe dankbar.
Deswegen wäre es vielleicht sinnvoll, eine temporäre Variable p_temp =
*p_ToSet einzuführen, alle *p_ToSet durch p_temp zu ersetzen und erst am
Ende der Funktion mit *p_ToSet=p_temp das Ergebnis zu schreiben.
Du könntest den Ergebnis-Pointer auch als Funktionswert zurückgeben,
dann ersparst du dir die doppelte Verpointerung des Funktionsarguments.
Danke schonmal - wenn ich es so programmiere schmeißt der Compiler keine
Warnung. Vereinfachung wären warscheinlich sinnvoll, ja. Aber da die
Funktion nur so kurz ist dachte ich mir ich schaffe es auch so.
:)
Also vllt nochmal verständlicher was sie tun soll:
Ich habe einen Pointer der zeigt auf einen Wert. und nun will ich diesen
Pointer nehmen und ihn stattdessen auf das Element in einem Array zeigen
lassen, dass dem Wert auf den er vorher gezeigt hat am nächsten kommt
(bzw auf das erste, das nicht mehr größer ist). Das Array ist dabei der
größe nach sortiert.
Frederik N. schrieb:> Vereinfachung wären warscheinlich sinnvoll, ja. Aber da die> Funktion nur so kurz ist dachte ich mir ich schaffe es auch so.> :)
Willkommen in der 2-Sterne Programmierung :-)
Bei den ersten paar mal kommt es einem noch ein wenig seltsam vor, aber
man gewöhnt sich daran.
Was du aber ins Auge fassen solltest:
Du hast bei einer Funktion immer 2 grundsätzliche Möglichkeiten, wie du
Werte rein und raus kriegst.
Das eine ist die Argumentliste
das andere ist der Returnwert
Deine Funktion ist eine void Funktion. D.h. du lässt die Möglichkeit,
das ganze über den Return Wert zu regeln links liegen.
Ja mit den return-Werten hast du durchaus recht. Ich hatte es nicht
gemacht, weil man dann ja quasi mehr schreiben muss für die gleiche
Funktionalität - man muss die Zuweisung machen und trotzdem den Pointer
übergeben, oder sehe ich da was falsch? Hat das noch andere Vorteile?
Das werde ich mich aber auf jeden Fall für die Zukunft merken. Es
funktioniert nun soweit und ich habe wieder ein bisschen mehr C
verstanden. :) Danke auf jeden Fall für die Hilfe!
Frederik N. schrieb:> Hat das noch andere Vorteile?
Funktionsargumente ohne Not verändern ist bäh ;-)
Vorteil ist hier vorallem, das der Aufrufer sich entscheiden kann seinen
"orginal" Pointer nicht zu zerstören, außerdem kannst du z.B. durch
rückgabe des NULL pointers signalisieren, dass garnichts gefunden wurde.
Folgendes wäre auch denkbar:
Läubi .. schrieb:> Frederik N. schrieb:>> Hat das noch andere Vorteile?> Funktionsargumente ohne Not verändern ist bäh ;-)>> Vorteil ist hier vorallem, das der Aufrufer sich entscheiden kann seinen> "orginal" Pointer nicht zu zerstören
sofern er überhaupt einen hat.
Spricht ja nichts dagegen das so zu benutzen
pPtr = SearchNearest( Numbers, 8 );
wenn also im Original beim Aufrufer sowas steht (die 8 sind nur
Platzhalter für eine Datenquelle)
1
*pPtr=8;
2
Buffer_SetTableVal(&pPtr);
3
if(pPtr!=NULL)
4
...
dann ersetzt sich das durch
1
pPtr=SearchNearest(Numbers,8);
2
if(pPtr!=NULL)
3
....
Oder: Ich kann den Funktionsaufruf direkt in der Argumentliste eines
weiteren Funktionsaufrufs benutzen
1
PrintValue(SearchNearest(Numbers,8));
oder mit konstanten Daten (die zb im Flash stehen)
1
constuint16_tValues[]={3,8,12,2,25,8};
2
3
for(i=0;i<sizeof(Values)/sizeof(*Values);i++)
4
{
5
OutputPWM(SearchNearest(PWMtable,Values[i]));
6
...
7
}
oder ....
1
constuint16_tValues[]={3,8,12,2,25,8};
2
uint16_tMappedValues[sizeof(Values)/sizeof(*values)];// same size as Values
// oder die Abfrage des Ergebnisses etwas kompakter.
18
MappedValues[i]=pMapped?*pMapped:0;
19
20
21
}
Im Laufe der Zeit kommt man eigentlich meistens drauf, dass diese
impliziten Manipulationen an Argumenten nicht so toll sind. Eine
Funktion ist oft besser verwendbar, wenn sich der Aufrufer aussuchen
kann, was er mit dem Ergebnis machen will, anstelle das ihm das die
Funktion aufs Auge drückt. Man sollte das nicht unterschätzen. Kleine
Änderungen bewirken oft an ganz andere Stellen mehr Möglichkeiten bzw.
eine ganz andere Art der Programmierung. Wenn man also wissen möchte, ob
das Interface einer Funktion gut oder schlecht ist, sollte man sich die
aufrufenden Stellen ansehen und sich überlegen, wie man die anders,
besser, einfacher schreiben könnte. Daraus gewinnt man dann einen
Eindruck darüber, wie das Funktionsinterface aussehen könnte/sollte um
gut verwendbar zu sein. Es gibt nichts schlimmeres als Programmierer,
die an einem einmal geschriebenen Funktionsinterface auf Biegen und
Brechen festhalten, selbst dann wenn die Funktion nur unter Mühen
verwendbar ist.
Und wenn man das wirklich per Argument regeln will, dann kann man ja
immer noch schreiben
1
voidBuffer_SetTableVal(uint16_t**p_ToSet)
2
{
3
*p_ToSet=SearchNearest(PWMtable,**p_toSet);
4
}
Dann ist diese Funktion leicht verständlich und die SearchNearest ist es
ebenfalls. Und dank inlining zahlt man dafür noch nicht mal einen
Penalty ausser ein wenig Tipparbeit, die sich aber auf lange Sicht
gesehen meistens auszahlt.
Frederik N. schrieb:> btw: Was ist "inlining"? ^^
Wenn der Compiler den Aufruf einer Funktion durch den Funktionsrumpf
selbst ersetzt.
Du schreibst
1
intDoIt(intj)
2
{
3
return2*j;
4
}
5
6
7
voidfoo(intk)
8
{
9
intl;
10
11
l=DoIt(k);
12
}
und der Compiler ersetzt den Funktionsaufruf von DoIt durch den
Funktionsrumpf:
1
voidfoo(intk)
2
{
3
intl;
4
5
{
6
intj=k;
7
l=2*j;
8
}
9
}
wodurch dir der im Original vorhandene, vermeintlich 'teure'
Funktionsaufruf nichts mehr kostet. Zusätzlich eröffnen sich dadurch für
den Compiler oft neue Optimierungsmöglichkeiten (wie zb hier das
Eleminieren von j)
Ooops. Tippfehler.
Es heißt natürlich 'inlineing' und nicht 'inlining'.
'to inline'
Eine eigene Frage?
Dieser Thread ist über 14 Jahre alt.
Stell sie in einem eigenen Thread – dort lesen mehr Leute mit, und sie wird öfter und schneller beantwortet.