Forum: Compiler & IDEs C: Pointer in Funktion verändern


von Frederik N. (freddy2287)


Lesenswert?

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
void Buffer_SetTableVal (uint16_t **p_ToSet){
2
  
3
  uint16_t temp = **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.

von [ ] (Gast)


Lesenswert?

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

von Yalu X. (yalu) (Moderator)


Lesenswert?

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.

von Frederik N. (freddy2287)


Lesenswert?

1
void Buffer_SetTableVal (uint16_t **p_ToSet){
2
  
3
  uint16_t temp = **p_ToSet;
4
  *p_ToSet = PWMtable;
5
  while (temp>**p_ToSet)(*p_ToSet)++;  
6
}

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.

von ohne Garantie (Gast)


Lesenswert?

1
uint16_t* Buffer_SetTableVal (uint16_t *p_ToSet){
2
  for (uint16_t i = 0;i < sizeof (PWMtable / 2);i++)
3
    if (*p_ToSet > PWMtable[i]){
4
      p_ToSet = &(PWMtable[i]);
5
      return (p_ToSet);
6
    }
7
      p_ToSet = 0;
8
      return (p_ToSet);
9
}

von Karl H. (kbuchegg)


Lesenswert?

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.
1
uint16_t * SearchNearest(uint16_t * pWhere, uint16_t What )
2
{
3
  while( *pWhere < What )
4
    pWhere++;
5
6
  return pWhere;
7
}


und verwendet wird es dann eben so
1
uint16_t Numbers[] = { 1, 5, 8, 12, 28 };
2
3
...
4
5
  uint16_t someVar = 8;
6
  uint16_t somPtr = &someVar;    // um gleiche Voraussetzungen zu schaffen
7
                                 // zu deinem Code
8
9
10
  somePtr = SearchNearest( Numbers, *somePtr );
11
12
  ...

da kann man jetzt noch meckern. zb. darüber wie du den Fall handhaben 
willst, dass die gesuchte Zahl nicht zu finden ist.

von Frederik N. (freddy2287)


Lesenswert?

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!

von Läubi .. (laeubi) Benutzerseite


Lesenswert?

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:
1
if(SearchNearest( Numbers, *somePtr ) != NULL) {
2
 //found in Numbers
3
} else if (SearchNearest( otherNumbers, *somePtr ) != NULL) {
4
 //found in otherNumbers
5
} else {
6
  //Alles doof!
7
}

von Karl H. (kbuchegg)


Lesenswert?

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.

von Frederik N. (freddy2287)


Lesenswert?

Ja die Erklärung ist einleuchtend -ich werde es mir auf jeden Fall 
merken.

btw: Was ist "inlining"? ^^

von Karl H. (kbuchegg)


Lesenswert?

Frederik N. schrieb:

> btw: Was ist "inlining"? ^^


Wenn der Compiler den Aufruf einer Funktion durch den Funktionsrumpf 
selbst ersetzt.

Du schreibst
1
int DoIt( int j )
2
{
3
  return 2*j;
4
}
5
6
7
void foo( int k )
8
{
9
  int l;
10
11
  l = DoIt( k );
12
}

und der Compiler ersetzt den Funktionsaufruf von DoIt durch den 
Funktionsrumpf:
1
void foo( int k )
2
{
3
  int l;
4
5
  {
6
    int j = 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'

von Frederik N. (freddy2287)


Lesenswert?

:-D ah okay danke. Wieder was gelernt.

von Klugscheisser (Gast)


Lesenswert?

Ooops. Tippfehler.

Es heißt natürlich trotzdem 'inlining' und nicht 'inlineing'.
'to inline'

von Karl H. (kbuchegg)


Lesenswert?

:-)

Ist heute nicht mein Tag

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.