Wann darf man was aus einer Funktion nicht returnen?



  • @EinNutzer0
    Wo wird beim Aufruf eine Kopie erstellt? Nirgends.
    Jede Zuweisung einer Struktur impliziert eine Kopie aller seiner Elemente (außer FAM - ja wer weiß das schon? - naja es reicht ja, wenn ich das weiß).

    Und passend dazu ein Zitat des allwissenden (Vorsicht Ironie) Fachbuchautors Jürgen Wolf (Pfuscher JW) aus "C von A bis Z":

    Folgende Wertzuweisung von Strukturen sollten Sie allerdings vermeiden:

    struct {
       int a1;
       int a2;
       int a3;
    } werte1, werte2;
    
    werte1.a1 = 8;
    werte1.a2 = 16;
    werte1.a3 = 32;
    
    werte2 = werte1;   // Bitte vermeiden Sie solche Zuweisungen.
    

    Das ist in C zwar erlaubt, kann aber zu Fehlern führen, wenn ein Compiler dies nicht unterstützt. Sicherer wäre die folgende Möglichkeit:

    memcpy(&werte2, &wert1, sizeof(werte1));
    

    Zitat Ende.
    Pfuscher JW impliziert hier also, dass ein C-Compilerbauer keine Ahnung vom Standard hat und nicht weiß, wie er solche "komplizierte" Regel fehlerfrei umsetzen soll.

    Ich hoffe, das nimmt dir zukünftig die Laune, C anhand von Büchern,Tutorials,Professoren zu erlernen.

    A_Elem search_max(int *const a, const int count)
    

    Hier ist const komplett überflüssig und suggeriert somit Falsches.



  • @Wutz sagte in Wann darf man was aus einer Funktion nicht returnen?:

    außer FAM - ja wer weiß das schon? - naja es reicht ja, wenn ich das weiß

    Für alle die sich fragen: Flexible Array Members. Der Größe bezüglich offene Array am Ende einer Struct. Der Benutzer ist dafür zuständig genügend Speicherplatz für eine Objekt einer solchen Struct zu beschaffen.


    @EinNutzer0 sagte in Wann darf man was aus einer Funktion nicht returnen?:

    Also doch Call By Value Result...

    Würdest Du bitte mal im Bezug auf die Sprache C mit Calling Conventions aufhören?? Lies den Standard und sag' bescheid wenn Du etwas dazu findest. Viel Glück.


  • Gesperrt

    @Wutz sagte in Wann darf man was aus einer Funktion nicht returnen?:

    Wo wird beim Aufruf eine Kopie erstellt? Nirgends

    Das stimmt nicht, Aktualparameter werden durch Zuweisung an die Formalparameter kopiert.

    Es wäre aber schön zu wissen, was genau bei einer Zuweisung geschieht. Ich muss mir mal den Disassembler anschauen, befürchte ich.



  • Und

    @EinNutzer0 sagte in Wann darf man was aus einer Funktion nicht returnen?:

    Call By Value Result

    in C schonmal garnie-nicht.

    Call By Value Result:

    A ParameterPassing mode, used in AdaLanguage to handle "IN OUT" parameters. In CallByValueResult, the actual parameter supplied by the caller is copied into the callee's formal parameter; the function is run; and the (possibly modified) formal parameter is then copied back to the caller.

    nix "return".



  • @EinNutzer0 sagte in Wann darf man was aus einer Funktion nicht returnen?:

    Es wäre aber schön zu wissen, was genau bei einer Zuweisung geschieht.

    Dann lies den Standart.

    @EinNutzer0 sagte in Wann darf man was aus einer Funktion nicht returnen?:

    Ich muss mir mal den Disassembler anschauen, befürchte ich.

    Nein. As-if.



  • @EinNutzer0
    Du hast hier überhaupt keinen struct-Parameter der kopiert werden könnte.



  • §6.5.2.2 Function calls
    4. An argument may be an expression of any complete object type. In preparing for the call to a function, the arguments are evaluated, and each parameter is assigned the value of the corresponding argument.102)

    102) A function can change the values of its parameters, but these changes cannot affect the values of the arguments. On the other hand, it is possible to pass a pointer to an object, and the function can then change the value of the object pointed to. A parameter declared to have array or function type is adjusted to have a pointer type as described in 6.9.1.


  • Gesperrt

    int f1(int i)
    {
        int a = 5;
        int b = a - i;
        return b;
    }
    
    int main()
    {
        int c = 7;
        int d = f1(c);
        return d + 2;
    }
    

    daraus wird ohne Optimierungen:

    .text:00400496 <f1>:
                               .text:00400496 55                               push   %rbp
                               .text:00400497 48 89 e5                         mov    %rsp,%rbp
                               .text:0040049a 89 7d ec                         mov    %edi,-0x14(%rbp)
                               .text:0040049d c7 45 fc 05 00 00 00             movl   $0x5,-0x4(%rbp)
                               .text:004004a4 8b 45 fc                         mov    -0x4(%rbp),%eax
                               .text:004004a7 2b 45 ec                         sub    -0x14(%rbp),%eax
                               .text:004004aa 89 45 f8                         mov    %eax,-0x8(%rbp)
                               .text:004004ad 8b 45 f8                         mov    -0x8(%rbp),%eax
                               .text:004004b0 5d                               pop    %rbp
                               .text:004004b1 c3                               retq   
    
    .text:004004b2 <main>:
                               .text:004004b2 55                               push   %rbp
                               .text:004004b3 48 89 e5                         mov    %rsp,%rbp
                               .text:004004b6 48 83 ec 10                      sub    $0x10,%rsp
                               .text:004004ba c7 45 fc 07 00 00 00             movl   $0x7,-0x4(%rbp)
                               .text:004004c1 8b 45 fc                         mov    -0x4(%rbp),%eax
                               .text:004004c4 89 c7                            mov    %eax,%edi
                               .text:004004c6 e8 cb ff ff ff                   callq  0x00400496 <f1>
                               .text:004004cb 89 45 f8                         mov    %eax,-0x8(%rbp)
                               .text:004004ce 8b 45 f8                         mov    -0x8(%rbp),%eax
                               .text:004004d1 83 c0 02                         add    $0x2,%eax
                               .text:004004d4 c9                               leaveq 
                               .text:004004d5 c3                               retq   
                               .text:004004d6 66 2e 0f 1f 84 00 00 00 00 00    nopw   %cs:0x0(%rax,%rax,1)
    

    An welcher Stelle wird ein Parameter oder ein Rückgabewert also NICHT kopiert? 🤔

    Und ich kann ja nix dafür, wenn dieses behavior, call by value result(bzw. return) heißt.



  • @EinNutzer0 sagte in Wann darf man was aus einer Funktion nicht returnen?:

    Und ich kann ja nix dafür, wenn dieses behavior, call by value result(bzw. return) heißt.

    Heißt es nicht.

    @EinNutzer0 sagte in Wann darf man was aus einer Funktion nicht returnen?:

    An welcher Stelle wird ein Parameter oder ein Rückgabewert also NICHT kopiert?

    Deine ganzen Überlegungen sind ziemlich sinnlos.

    §5.1.2.3 Program execution

    1. The semantic descriptions in this document describe the behavior of an abstract machine in which issues of optimization are irrelevant.

    Das heißt salopp gesagt: "Es muss nur dasselbe beobachtbare Verhalten dabei rauskommen. Wie ist völlig egal."
    Du bist hier im C-Forum. Hier geht es um die Sprache. Wenn Du über das Verhalten einer bestimmten Implementierung reden willst dann geh' ins Compiler- und IDE-Forum.

    @EinNutzer0 sagte in Wann darf man was aus einer Funktion nicht returnen?:

    int f1(int i)
    {
        int a = 5;
        int b = a - i;
        return b;
    }
    
    int main()
    {
        int c = 7;
        int d = f1(c);
        return d + 2;
    }
    
    int main()
    {
        return 0;
    }
    

    immer noch dasselbe. Da kannst noch so viele Verrenkungen machen (Scherze wie volatile, funktionen mit external linkage (wobei ... Interprocedural optimization) und user input mal ausgenommen.). Einen Compiler der das bei geringsten Optimierungen nicht schnallt kannst getrost in die Tonne treten.

    https://godbolt.org/z/rW64Gq

    main:
            mov     eax, 0
            ret
    

    Wie Du siehst gibt es hier nichtmal einen Funktionsaufruf. Vielleicht verstehst Du jetzt ja endlich "as-if".



  • @Swordfish sagte in Wann darf man was aus einer Funktion nicht returnen?:

    @EinNutzer0 sagte in Wann darf man was aus einer Funktion nicht returnen?:

    Und ich kann ja nix dafür, wenn dieses behavior, call by value result(bzw. return) heißt.

    Heißt es nicht.

    Bevor Du nochmal damit kommst:

    https://condor.depaul.edu/ichu/csc447/notes/wk4/ps.html

    Call by value-result

    A call by value-result, like a call by reference, requires that the argument be a variable. However, the parameters of the function or procedure are not directly bound to the variable's address. Rather, they get their own space within their own scope, and when the function or procedure is terminated, the new values of the parameters are copied back into the caller's variables. Hence, value-result differs from reference only in that the values of the caller's variables are not modified while the function or procedure is still in effect--only when it has finished.

    One advantage of calling by value-result is that the called procedure or function can safely assign to its parameters without fear of confusing side effects. (This, for example, is not true with a call by reference or by name.) On the other hand, making life easier for the callee makes life more confusing for the caller. Consider the following code:

    proc a (int x, int y) {
        x = 1;
        y = 2;
    }
    
    int t;
    a (t,t);
    print t;
    

    The value of t after calling a then depends on the order of parameter copies when the procedure call is finished.

    Soetwas gibt es weder in C noch in C++.


  • Gesperrt

    #include <stdio.h>
    
    typedef struct test_e
    {
        int i;
        char *cp;
    } Test_E;
    
    int main()
    {
        Test_E a = {5, "hallo"};
        Test_E b = a;
        printf("%d %s, %d %d %d\n", b.i, b.cp, &a == &b, &a.i == &b.i, &a.cp == &b.cp);
        printf("%p %p %p %p %p %p\n", (void *)&a, (void *)&b, (void *)&a.i, (void *)&b.i, (void *)&a.cp, (void *)&b.cp);
        return 0;
    }
    
    $ ./TestC2.out 
    5 hallo, 0 0 0
    0x7ffd9baaa950 0x7ffd9baaa940 0x7ffd9baaa950 0x7ffd9baaa940 0x7ffd9baaa958 0x7ffd9baaa948
    

    Hier erschließt sich mir Zeile 12 bis 14 und die Ausgabe überhaupt nicht... Jeder Vergleich ist falsch, und ich hätte angenommen, dass erster und dritter Vergleich true sind... Kopiert das = den kompletten Speicherbereich inkl.. Pointer einer Struktur? Würde selbiges dann auch bei einem return geschehen? Sorry, wenn sich diese Fragen für euch furchtbar anhören, aber ich möchte C (und C++) verstehen.



  • Warum sollten bei dem Vergleich die Adressen von a und b denn gleich sein (in der 3. Zeile der Ausgabe stehen diese doch)?
    Wolltest du die Inhalte vergleichen, also

    printf("%d %s, %d %d %d\n", b.i, b.cp, !memcmp(&a, &b, sizeof(Test_E)), a.i == b.i, a.cp == b.cp);
    

    ?

    PS: memcmp gibt bei "gleich" 0 zurück (daher die Negation mittels !)

    PPS: Bedenke auch, daß memcmp alle Bytes vergleicht (also auch evtl. Padding-Bytes, welche dann nicht unbedingt gleich sein müssen).


  • Gesperrt

    Also, ich habe die Ausgabe nicht manipuliert. Ich bin auch davon ausgegangen, dass &a == &b NICHT 0 liefert (zumal eine Zeile darunter unterschiedliche Adressen stehen) - aber das Gegenteil ist der Fall. Was hab ich falsch gemacht? Oder will mich der Compiler ärgern?

    Bitte beantwortet mir noch diese Frage: Was geschieht bei return a;?



  • @EinNutzer0 sagte in Wann darf man was aus einer Funktion nicht returnen?:

    Also, ich habe die Ausgabe nicht manipuliert. Ich bin auch davon ausgegangen, dass &a == &b NICHT 0 liefert (zumal eine Zeile darunter unterschiedliche Adressen stehen) - aber das Gegenteil ist der Fall. Was hab ich falsch gemacht?

    Was wird denn bei &a == &bverglichen?


  • Gesperrt

    @john-0 sagte in Wann darf man was aus einer Funktion nicht returnen?:

    Was wird denn bei &a == &bverglichen?

    Wenn ich das wüsste, dann würde ich hier nicht fragen...

    Edit: Wird der string "hallo" auch kopiert?



  • @EinNutzer0 sagte in Wann darf man was aus einer Funktion nicht returnen?:

    Wenn ich das wüsste, dann würde ich hier nicht fragen...

    Dir ist die Bedeutung des Operators & bewusst? Der liefert die Adresse einer Variablen zurück! D.h. bei &a == &b werden die Adressen der beiden Variablen verglichen und eben nicht deren Werte. Die beiden Variablen a und b liegen natürlich an unterschiedlichen Stellen auf dem Stack. D.h. solange Du hier die Speicherstellen miteinander vergleichst ist das natürlich zu erwarten, dass da Ungleichheit festgestellt wird. In C gibt es keine Möglichkeit den Operator == für struct zu überladen, d.h. a == b kann hier bei Dir nicht funktionieren. Dazu müsstest Du Dir ein Vergleichsfunktion schreiben, die dann Komponentenweise struct test_e vergleicht. Sinnvoll wäre dann der Test auf a.i == b.i, das vergleicht den Wert der beiden Integer Variablen. Für das Vergleichen von Strings gibt es die Funktion strcmp in der Standard Library.


  • Gesperrt

    Ah, ich denke, jetzt hab ich's herausgefunden:

    #include <stdio.h>
    
    typedef struct test_e
    {
        int i;
        char *cp;
    } Test_E;
    
    Test_E get_test_e()
    {
        Test_E a = {5, "hallo"};
        printf("%p\n", (void *)&a);
        printf("%p\n", (void *)a.cp);
        printf("%p\n", (void *)&(a.cp[2]));
        /*
        0x7ffd1ffb1ee0
        0x400654
        0x400656
    
        a und der Inhalt von a wird flach kopiert!
        */
        return a;
    }
    
    int main()
    {
        Test_E b = get_test_e();
        printf("%p\n", (void *)&b);
        printf("%p\n", (void *)b.cp);
        printf("%p\n", (void *)&(b.cp[2]));
        /*
        0x7ffd1ffb1f10
        0x400654
        0x400656
    
        b und der Inhalt von b wurde flach kopiert!
        */
        return 0;
    }
    

    Wieso haben char s nur so eine kurze Adresse?



  • @EinNutzer0 sagte in Wann darf man was aus einer Funktion nicht returnen?:

    Wieso haben char s nur so eine kurze Adresse?

    Der Zeiger verweist auf einen statischen Codeteil, in dem der konstante String "hallo" abgelegt worden ist. Dagegen befindet sich die Adresse von a und b auf dem Stack, der liegt üblicherweise an einer anderen Stelle im Adressraum.


Anmelden zum Antworten