Compiler-Fehler Wieso muss bei Methode immer erst am Ende returned werden?

Zrebna

Bekanntes Mitglied
Hi!

Hier erstmal folgender funktionierender Code für die Berechnung des größten gemeinsamen Teils von 2 Zahlen:

Java:
package ex1;

public class Ggt {
    
    static int ggt(int x, int y) {
        int i, higher, lower;
        if(x > y) {
            higher = x;
            lower = y;
        } else {
            higher = y;
            lower = x;
        }
        
        for(i = lower; i > 0; i--) {
            if((lower % i == 0) && (higher % i == 0)) {
                break;
            }
        }
        return i;
    }

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        
        int num1 = 1071;
        int num2 = 1029;
        
        System.out.println("The greatest common divisor of " + num1 + " and " + num2 + " is " + ggt(num1, num2));
    }

}

Wieso darf man bei Java nicht direkt in Zeile 17 returnen - also dort 'return i', und somit aus der Methode zurück in die main-Methode zurückspringen?

Ich glaube bei C ginge das - dort halt bei Funktionen, weil wohl Funktionen bei C in java effektiv die Methoden sind?

Lg,
Zrebna
 

mrBrown

Super-Moderator
Mitarbeiter
Du kannst nicht nur in der Zeile returnen, weil es passieren kann, dass diese Zeile nie ausgeführt wird.
 

mihe7

Top Contributor
@mrBrown Spielverderber :)

Ich glaube bei C ginge das - dort halt bei Funktionen, weil wohl Funktionen bei C in java effektiv die Methoden sind?
Ja, in C... da darfst Du auch aus Fußpilz Mozzarella machen.

Wenn Du in C das return weglässt, dann funktioniert das max. zufällig. Bei x86 werden int-Ergebnisse per cdecl-Aufrufkonvention über das eax-Register zurückgegeben. Je nachdem, wofür dieses Register zuvor verwendet wurde, stimmt das Ergebnis - oder eben auch nicht :)
 

Zrebna

Bekanntes Mitglied
1.) Hier Java-Code, bei dem es nun nicht funzt sofort zu returnen, wenn man das Ergebnis hat: Siehe Zeile 19

Java:
package test;



public class Testing {

    static int ggt(int x, int y) {
        int i, higher, lower;
        if(x > y) {
            higher = x;
            lower = y;
        } else {
            higher = y;
            lower = x;
        }
       
        for(i = lower; i > 0; i--) {
            if((lower % i == 0) && (higher % i == 0)) {
                return i;
                //break;
            }
        }
        // return i;
    }

    public static void main(String[] args) {
        // TODO Auto-generated method stub
       
        int num1 = 1071;
        int num2 = 1029;
       
        System.out.println("The greatest common divisor of " + num1 + " and " + num2 + " is " + ggt(num1, num2));
    }

}

2.) Hier C-Code - siehe Zeile 30 - es funktioniert und sieht mir nicht nach Zufall aus:

C:
#include <stdio.h>
void swap(int* num1, int* num2);
int ggT(int* num1, int* num2);

int main() {
    int zahl1 = 1029, zahl2 = 1071;

    printf("Der groesste gemeinsame Teiler von den Zahlen %d und %d ist: %d.\n", zahl1, zahl2, ggT(&zahl1, &zahl2));

        return 0;
}

void swap(int* n1, int* n2) {
    int temp;

    temp = *n1;
    *n1 = *n2;
    *n2 = temp;
}

int ggT(int* num1, int* num2) {
    int i;

    if(*num1 > *num2) {
        swap(num1, num2);
    }

    for(i = *num1; i > 0; i--) {
        if((*num1 % i == 0) && (*num2 % i == 0)) {
            return i;
        }
    }
   // return ggT;
}

Nebenfrage:
@Zeile 6 beim C-code:
Ich deklariere und initialisiere 2 Variablen in einer Zeile.
Ist sowas bei Java nicht Konvention oder gerne gesehen? Habe das nämlich als blutiger Java-Anfänger noch nicht gesehen.
Nur Deklaration (ohne Initialisierung) in einer Zeile von mehreren Variablen habe ich dagegen gesehen.

Lg,
Zrebna
 
K

kneitzel

Gast
Also wie schon beschrieben wurde:
Der Java Compiler meckert, da du nur dann einen Wert zurück gibst, wenn in der Schleife einmal die if Bedingung wahr war.
Nun kannst Du evtl. belegen, dass dies immer der Fall sein wird, weil spätestens bei i = 1 die if-Abfrage immer zutreffen wird. Das ist toll, aber solche Gedankengänge stellt der Compiler nicht an. Also meckert er dies an. (Vollkommen zu Recht! So etwas ist ja nicht klar im Code erkenntlich und daher klar schlecht lesbarer Code!)

Der Java Compiler definiert aber in der JLS, dass sicher gestellt sein muss, dass eine Funktion einen definierten Wert zurück gibt. Also musst Du am Ende auch noch ein return haben. Also ein Java Code könnte so aussehen:
Java:
    static int ggt(int x, int y) {
        int i, higher, lower;
        if(x > y) {
            higher = x;
            lower = y;
        } else {
            higher = y;
            lower = x;
        }
      
        for(i = lower; i > 1; i--) {
            if((lower % i == 0) && (higher % i == 0)) {
                return i;
                //break;
            }
        }
        return 1; // Warum auch die 1 prüfen und dann zurück geben?
    }

Dem C-Compiler ist es komplett egal. Am Ende der Funktion ist das Ergebnis in einem Register oder an einer Stelle im Stack. Der Wert wird genommen, egal ob Du da etwas rein geschrieben hast oder nicht...
 

mrBrown

Super-Moderator
Mitarbeiter
X

Xyz1

Gast
lustigerweise gibt C die Variable zurück, die zuletzt in der Methode beschrieben wurde, wenn diese kein "natürliches" Ende (also return) findet :D

C:
#include <stdio.h>

int
ggt (int x, int y)
{
  int i, higher, lower, rubbish=-1;
  if (x > y)
    {
      higher = x;
      lower = y;
    }
  else
    {
      higher = y;
      lower = x;
    }

  for (i = lower; i > 100; i--)
    {
      if ((lower % i == 0) && (higher % i == 0))
	{
	  return rubbish;
	}
    }
}

int
main ()
{
  printf ("Hello World\n");
  printf ("%d\n", ggt (50, 10));
  return 0;
}
 

Thallius

Top Contributor
Ich denke es wird eher die zuletzt erzeugte lokale Variable zurück geben, denn diese liegt oben auf dem Stack und C gibt nunmal immer obersten Eintrag vom Stack zurück.
 
X

Xyz1

Gast
Das wäre demnach rubbisch und stimmt nicht, probieres mal aus, es ist in dem Fall immer lower
 

mrBrown

Super-Moderator
Mitarbeiter
@mihe7 hat's doch schon gesagt:
Bei x86 werden int-Ergebnisse per cdecl-Aufrufkonvention über das eax-Register zurückgegeben. Je nachdem, wofür dieses Register zuvor verwendet wurde, stimmt das Ergebnis - oder eben auch nicht :)

Was in eax drin steht, ist mehr oder weniger Zufall, und hängt nicht von der letzten Benutzung oder Deklaration ab.

Probier das testweise mal mit -O3 und x=0,y=-1 aus, dann sollte wirklich Unsinn drin stehen.
 

mihe7

Top Contributor
C:
#include<stdio.h>
int a() {
}

int b() {
    printf("Test\n");
}

int main(int argc, char *argv[]) {
    printf("a(): %d\n", a());
    printf("b(): %d\n", b());
}
Liefert bei mir mit -O1 z. B. 1 und 5, mit -O2 dagegen 0 und 5 :)
 

Zrebna

Bekanntes Mitglied
Ich denke es wird eher die zuletzt erzeugte lokale Variable zurück geben, denn diese liegt oben auf dem Stack und C gibt nunmal immer obersten Eintrag vom Stack zurück.

Alles klar, vielen Dank!
Interessehalber,
wie ist es den hardwaretechnisch in Java - also bei C gibt es ja das LastInFirstOut-Prinzip bzgl. Push/Pop_Stack.
Wie kann man sich das in java vorstellen?
 

mrBrown

Super-Moderator
Mitarbeiter
Alles klar, vielen Dank!
Interessehalber,
wie ist es den hardwaretechnisch in Java - also bei C gibt es ja das LastInFirstOut-Prinzip bzgl. Push/Pop_Stack.
Wie kann man sich das in java vorstellen?
Die Antwort von @Thallius, die du zitiert hast, ist in diesem Kontext Unsinn, der Stack spielt da keine Rolle.

Aber generell funktionieren Stacks immer nach LIFO-Prinzip, das ist die Definition eines Stacks.
 
K

kneitzel

Gast
Also wenn die Rückgabe über den Stack erfolgt, dann kommt die nach meiner Erfahrung zuerst auf den Stack, dann kommt die Rücksprungadresse.
Im Anschluss baut die Funktion lokale Variablen auf dem Stack auf um dann über den Stack Pointer darauf zuzugreifen.

Am Ende wird die Funktion ihre lokalen Variablen vom Stack nehmen, dann die Rücksprungadresse + Rücksprung.

Dann ist es Aufgabe des aufrufenden Codes, das Resultat vom Stack zu nehmen.

So erinnere ich mich zumindest. Aber da dies schon länger her ist, kann ich mich evtl. irren. Und ganz wichtig: So eine Rückgabe kann auch über ein Register erfolgen wie man ja auch am Beispiel erkennen kann.
 

mihe7

Top Contributor
Um nochmal auf "zufällig" zurück zu kommen. Damit war gemeint, dass dieses Verhalten nicht durch die Sprache spezifiziert ist. Es ist dem Beispiel, dem Compiler und der Architektur geschuldet, dass das hier (zufällig) funktioniert.
 
X

Xyz1

Gast
Genau, das ist compilerspezifisch bzw ein Compiler-Detail - jedoch nicht sprach- oder ISO-spezifisch. Jedoch ging mihe7 einfach keck davon aus, das "immer" die in der Methode zuletzt beschriebene oder gelesene Variable returned wird insofern kein Return erreicht wurde. ;)
 

mrBrown

Super-Moderator
Mitarbeiter
Jedoch ging mihe7 einfach keck davon aus, das "immer" die in der Methode zuletzt beschriebene oder gelesene Variable returned wird insofern kein Return erreicht wurde.
Nö, @mihe7 hat nichts von Variablen gesagt, hat damit auch nichts zu tun.

C:
#include<stdio.h>
 
int c() {
    int a = 5;
}


int b() {
    c();
}

int main(int argc, char *argv[]) {
    printf("%d\n",b());
}


Liefert mit -O0 0 und mit -O3 irgendeine beliebige Zahl, zB -301901784
 

mrBrown

Super-Moderator
Mitarbeiter
Und ohne Dein seltsames O? Tu doch nicht so als läge ich falsch.
gleich mal vorweg: dumme Pöbeleien will ich hier nicht sehen...



-O setzt das Optimierungslevel des Compilers, Erklärungen findest du z.B. hier. Da ist nichts seltsam dran, das sind einfach nur Standardoptionen. -O0 ist Standard, das gleiche Ergebnis erhältst du ohne Angabe.

Ich muss auch nicht so tun als ob du falsch liegst, dass kannst du sowohl ausprobieren als auch einfach überprüfen, in dem du dir den generierten Code anguckst.
Beim ausprobieren wirst du sehen, dass es nicht die "zuletzt beschriebene oder gelesene Variable" ist, um Code wirst du sehen, das keine Variable einfach so in eax landet.
 

mihe7

Top Contributor
C:
#include<stdio.h>

int b() {
    asm("movl $125, %eax");
}

int main(int argc, char *argv[]) {
    printf("%d\n", b());
}
 

Neue Themen


Oben