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 | const uint16_t Values[] = { 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 | const uint16_t Values[] = { 3, 8, 12, 2, 25, 8 };
|
2 | uint16_t MappedValues[ sizeof(Values)/sizeof(*values) ]; // same size as Values
|
3 |
|
4 | for( i = 0; i < sizeof(Values)/sizeof(*Values); i++ )
|
5 | {
|
6 | // wenn sicher gestellt ist
|
7 | // das immer was gefunden wird
|
8 | MappedValues[i] = *SearchNearest( PWMtable, Values[i] ) );
|
9 |
|
10 | // sonst eben mit einer Abfrage
|
11 | uint16_t* pMapped = SearchNearest( PWMTable, Values[i] );
|
12 | if( pMapped )
|
13 | MappedValues[i] = *pMapped;
|
14 | else
|
15 | MappedValues[i] = 0;
|
16 |
|
17 | // 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 | void Buffer_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.