Funktion mit Argument "const float *a[]" aufrufen



  • Hallo

    Ich will von C++ aus eine C-Funktion aufrufen, deren Deklaration einen "float *a[]" und einen "const float *b[]" enthält - z.B.

    void theFunction(float *a[], const float *b[], int arraySize);
    

    Meine "aktive" C/C++-Zeit ist eine Weile her, deswegen bin ich mir da an einigen Stellen unsicher: Ist es OK, an eine Funktion, die einen Array von Pointern erwartet, einen Pointer auf einen Pointer zu übergeben?

    float **a = new float*[arraySize];
    ...
    theFunction(a, b, arraySize);
    

    Falls ja, gibt es trotzdem noch das Problem mit dem const, und ich frage mich, wie man regelkonform aus dieser kleinen const/array/pointer-Falle rauskommt. Ich glaubte erst, http://www.parashift.com/c++-faq-lite/const-correctness.html#faq-18.17 hätte etwas damit zu tun, aber wenn ich das richtig sehe, will die Funktion ja nur einen array von pointern zu const floats, und ich sehe im Moment nicht, warum ich nicht einen "array" von pointern zu floats erstellen könnte, und der Funktion durch ein zusätzliches 'const' sagen, dass sie die floats nicht verändern soll.

    Die einzige Möglichkeit, die ich bisher in Visual Studio zum Compilieren bringen konnte, war

    ...
    typedef const float * POINTER_TO_CONST_FLOAT;
    const float **b = new POINTER_TO_CONST_FLOAT[arraySize];
    b[0] = ...
    theFunction(a, b, arraySize);
    

    Allerdings sieht das so seltsam aus, dass ich mal nachfragen wollte, ob das so OK wäre, und ob es ggf. auch eine Lösung ohne das typedef gibt...


  • Mod

    Ein Gast namens M schrieb:

    Meine "aktive" C/C++-Zeit ist eine Weile her, deswegen bin ich mir da an einigen Stellen unsicher: Ist es OK, an eine Funktion, die einen Array von Pointern erwartet, einen Pointer auf einen Pointer zu übergeben?

    Ja. Das ist sogar genau das was diese Funktion erwartet. Wenn da nur [] ohne Größe steht, dann ist das so als ob da float **a stünde.

    Zum const: Da hast du dich ja in was reingeritten. Das ist alles unnötig!

    Du kannst einer Funktion die etwas konstantes erwartet ganz wunderbar etwas nicht-konstantes übergeben. Bloß umgekehrt geht das nicht. Denk mal kurz drüber nach, dann sollte dir klar werden, warum: Die Funktion macht durch das const bekannt, dass sie nichts an den Werten ändert. Daher kann man ihr auch Konstanten übergeben. Aber auch Variablen, denn Variablen muss man schließlich nicht andern, bloß weil man es kann. Wenn da aber kein const steht, dann macht die Funktion bekannt, dass sie etwas ändern möchte. Und dann muss der Wert variabel sein.



  • Hallo SeppJ,

    Danke für die schnelle Antwort - um diese Uhrzeit 🙂

    Dass das, was man dort übergibt, bei der Deklaration eigentlich nicht als 'const' bezeichnet werden müßte, leuchtet ein - und das hatte ich auch ursprünglich versucht, weil ich davon ausgegangen bin, dass das 'const' an diser Stelle ja eher ein "Versprechen" der Funktion an den Aufrufer ist, und nicht umgekehrt.

    Aber bei dem Versuch, dort auch einen einfachen "float **" zu übergeben, hat mir Visual Studio gesagt

    cannot convert parameter 2 from 'float **' to 'const float *[]'
        Conversion loses qualifiers
    

    Ich habe dann verzweifelt im Web gesucht, mehrmals den verlinkten FAQ-Beitrag auf der Suche nach einer Erklärung gelesen (aber keinen direkten Zusammenhang zu diesem Problem erkannt - auch wenn ich mir nicht 100% sicher war), irgendwann sämtliche Permutationen von const,float,new und * durchprobiert, und bin am Ende bei diesem schräg-verzweifelten Lösungsversuch mit dem typedef gelandet...

    Hast du eine Idee, warum sich VS dort beschwert und was man dagegen machen kann?


  • Mod

    Ach, da gab's ja diese eine Sache. Schon zu lange nicht mehr mit mehreren Ebenen Indirektion jongliert, da ist es mir nicht sofort eingefallen. Es ist genau das was du selber schon rausgefunden hattest:
    http://www.parashift.com/c++-faq-lite/const-correctness.html#faq-18.17



  • Hm... wie gesagt, das hatte mir nicht ganz eingeleuchtet... aber es stimmt schon: mehrfache array/pointer-Indirektionen mit eingestreuten const's werden schnell unübersichtlich... Ich werd' wohl nochmal drüber schlafen, und schauen, wie ich das Problem im konkreten Fall lösen kann.

    Nochmal danke für die Hilfe!



  • Sorry, Doppelpost, aber...: Die Frage, warum es mit dem typedef zu funktionieren scheint, und ob das "legitim" ist (oder nur ein compilerspezifischer Seiteneffekt) wäre schon interessant... Aber nicht mehr heute 😉



  • Ein Gast namens M schrieb:

    ...
    typedef const float * POINTER_TO_CONST_FLOAT;
    const float **b = new POINTER_TO_CONST_FLOAT[arraySize];
    b[0] = ...
    theFunction(a, b, arraySize);
    

    Also wenn dann wohl eher

    typedef const float * POINTER_TO_CONST_FLOAT;
    POINTER_TO_CONST_FLOAT*b = new POINTER_TO_CONST_FLOAT[arraySize];
    

    bzw. korrektes C

    #define ARRAYSIZE 123
    typedef const float * POINTER_TO_CONST_FLOAT;
    POINTER_TO_CONST_FLOAT b[ARRAYSIZE];
    

  • Mod

    Ein Gast namens M schrieb:

    Hm... wie gesagt, das hatte mir nicht ganz eingeleuchtet...

    const float konstante=3.14;
    
    void foo(const float **a)
    {
    
     *a = &konstante; // Absolut legitim, den const float* auf einen const float zeigen zu lassen
    }
    
    int main()
    {
     float *pointer;
     float **bar = &pointer;  // Auch erlaubt.
     foo(bar);    // Das hier verbietet der Compiler normalerweise. Mal angenommen, es wäre erlaubt
     // Dann zeigt *bar nun auf die Konstante. 
     // Das ist aber gerade pointer, welcher damit auch auf die konstante zeigt.
    
     // Das heißt, sowohl:
     *pointer = 3;
     // als ach
     **bar = 3;
     // Würden die Konstante ändern!
    }
    

    Und versuch das bitte nicht durch irgendwelche Casts den Mechanismus auszuhebeln. Der ist zu deinem eigenen Schutz da. Klar kannst du den Compiler dazu zwingen, aber du nimmst dir dadurch die Warnung/Fehler für den Fall, dass du dann wirklich die Konstante änderst. Das Problem ist das Interface der Funktion. Entweder ist dies mit Absicht so gewählt und wenn du den Compiler mit Casts zu dieser Aktion zwingst geht es schief. Oder es ist schlecht gewählt und du solltest es ändern.


Anmelden zum Antworten