Funktionen

CarotinBombe

Grünschnabel
Hallo liebe Community,

mal wieder... habe ich ein Problem, welches ich aus irgendeinen Grund nicht lösen kann. Eventuell könntet Ihr mir helfen. Da Programm startet mit der Main, im Header wird pi.h schon als const double deklariert.
C++:
#include <iostream>
#include "pi.h"

using std::cout;
using std::endl;
using std::cin;

int zylinder();
int rechnen(int&, int&);


int main()
{
    zylinder();
    return 0;
}

int zylinder(int r,int h)
{
    cout << "Geben Sie den Radius ein: ";
    cin >> r;

    if (cin.fail())
    {
        std::cerr << "Fehler bei Eingabe!" << endl;
        return 0;
    }
    cout << "Geben Sie die Höhe ein: ";
    cin >> h;
    if (cin.fail())
    {
        std::cerr << "Fehler bei Eingabe!" << endl;
        return 0;
    }

    rechnen(r, h);
    system("pause");
    return 0 ;
}

int rechnen(int &r, int &h)
{


    cout << "Das Volumen betraegt: " << pi *(r * r)* h << " kubikmeter" << endl;
    return 1;
}

Vorher habe ich das, was in Funktion "Zylinder" steht nur in die main gepackt. Ich dachte mir, das man als Berechnung eines Zylinders, eine eigenständige Funktion entwickeln kann. Problem nur dabei ist, das sich die Funktion nicht aufrufen lässt. Ob wohl ich sie doch vor der main initialisiert habe!?
 

sheel

I love Asm
Hi

der Fehler ist nicht, dass die Funktion nicht gefunden wird (was durch Lesen meistens ersichtlich ist...).
Probleme sind:

C++:
int zylinder(int r,int h)
Hier verlangst du, dass Werte für r und h aus main übergeben werden (sonst ist die Funktion einfach nciht aufrufbar). Im main werden aber keine Werte übergeben. Außerdem willst du anscheinend keien übergebenen Werte, weil sie in zylinder mit cin eingelesen werden. Daher, die Variablen einfach lokal in der Funktion machen:
C++:
int zylinder()
{
    int r,;
    int h;
    //und wie vorher weiter

Außerdem muss das
C++:
int zylinder();
darüber immer zu der Funktion selber passen. Wenn man wirklich zwei int-Paramter will müssten die auch oben stehen.


Dann, nur mal angenommen es gibt noch int-Parameter, ein paar Zeilen darüber ist
C++:
int rechnen(int&, int&);
Das müsste aber lauten
C++:
int rechnen(int, int);
um gleich wie die Funktion selber zu sein.
Und da es jetzt ohne Parameter ist
C++:
int rechnen();

zylinder returned immer 0, und nie etwas anderes (und rechnen immer 1). Warum? Es ist kein Rechenergebnis, man hat damit keine Möglichkeit Fehler in der Funktion zu erkennnen, usw. ... => sinnlos. Zwar kein wirklicher Fehler, aber man könnte die ganzen return's wegmachen und den Returntyp der Funktion auf void ("nichts") ändern.
C++:
void zylinder();

Und zu rechnen noch was: Warum sind die Parameter Referenzen (int& statt int)?

Falls das nicht klar ist, eine kurze Beschreibung was es ist (nur die Basics, es gibt noch viel mehr spezielle Sachen rund um Referenzen):
Beim Übergeben eines Werts an eine Funktion wird dieser Wert normalerweise kopiert. Am Ende der Funktion wird die Kopie dann wieder gelöscht. Ausiwkrungen sind zB. mehr Speicherverbrauch während der Funktion usw. (aber das stört bei einem einzelnen int nicht). Hier wichtig ist aber, dass Änderungen der Variable in der Funktion nur die Kopie ändern, die dann am Funktionsende eben wieder gelöscht wird. Die "Originalvariable" bleibt unverändert.
C++:
void testfunktion(int b)
{
    b = 2;
}

int main()
{
    int a = 1;
    testfunktion(a);
    cout << a; //das gibt 1 aus
}
Eine Referenz zu nehmen verhindert die Kopie, sondern nimmt auch in der Funktion die Originalvariable für alles her
C++:
void testfunktion(int &b)
{
    b = 2;
}

int main()
{
    int a = 1;
    testfunktion(a);
    cout << a; //das gibt 2 aus
}
Was man aber mit (dieser Art von) Referenz nicht mehr machen kann: Einer Funktion fixe Werte wie 123, statt Variablen, zu übergeben. Grund: Welche Variable "außerhalb" der Funktion soll bei Änderungen dann geändert werden? Der Zahl 123 die Zahl 2 zuweisen macht nicht viel Sinn.

Da du in rechnen keine Änderungen an den Variablen machst ist die Sache mit den Änderungen egal, und du verhinderst nur dass man fixe Werte übergeben kann
 

CarotinBombe

Grünschnabel
Hi

der Fehler ist nicht, dass die Funktion nicht gefunden wird (was durch Lesen meistens ersichtlich ist...).
Probleme sind:

C++:
int zylinder(int r,int h)
Hier verlangst du, dass Werte für r und h aus main übergeben werden (sonst ist die Funktion einfach nciht aufrufbar). Im main werden aber keine Werte übergeben. Außerdem willst du anscheinend keien übergebenen Werte, weil sie in zylinder mit cin eingelesen werden. Daher, die Variablen einfach lokal in der Funktion machen:
C++:
int zylinder()
{
    int r,;
    int h;
    //und wie vorher weiter

Außerdem muss das
C++:
int zylinder();
darüber immer zu der Funktion selber passen. Wenn man wirklich zwei int-Paramter will müssten die auch oben stehen.


Dann, nur mal angenommen es gibt noch int-Parameter, ein paar Zeilen darüber ist
C++:
int rechnen(int&, int&);
Das müsste aber lauten
C++:
int rechnen(int, int);
um gleich wie die Funktion selber zu sein.
Und da es jetzt ohne Parameter ist
C++:
int rechnen();

zylinder returned immer 0, und nie etwas anderes (und rechnen immer 1). Warum? Es ist kein Rechenergebnis, man hat damit keine Möglichkeit Fehler in der Funktion zu erkennnen, usw. ... => sinnlos. Zwar kein wirklicher Fehler, aber man könnte die ganzen return's wegmachen und den Returntyp der Funktion auf void ("nichts") ändern.
C++:
void zylinder();

Und zu rechnen noch was: Warum sind die Parameter Referenzen (int& statt int)?

Falls das nicht klar ist, eine kurze Beschreibung was es ist (nur die Basics, es gibt noch viel mehr spezielle Sachen rund um Referenzen):
Beim Übergeben eines Werts an eine Funktion wird dieser Wert normalerweise kopiert. Am Ende der Funktion wird die Kopie dann wieder gelöscht. Ausiwkrungen sind zB. mehr Speicherverbrauch während der Funktion usw. (aber das stört bei einem einzelnen int nicht). Hier wichtig ist aber, dass Änderungen der Variable in der Funktion nur die Kopie ändern, die dann am Funktionsende eben wieder gelöscht wird. Die "Originalvariable" bleibt unverändert.
C++:
void testfunktion(int b)
{
    b = 2;
}

int main()
{
    int a = 1;
    testfunktion(a);
    cout << a; //das gibt 1 aus
}
Eine Referenz zu nehmen verhindert die Kopie, sondern nimmt auch in der Funktion die Originalvariable für alles her
C++:
void testfunktion(int &b)
{
    b = 2;
}

int main()
{
    int a = 1;
    testfunktion(a);
    cout << a; //das gibt 2 aus
}
Was man aber mit (dieser Art von) Referenz nicht mehr machen kann: Einer Funktion fixe Werte wie 123, statt Variablen, zu übergeben. Grund: Welche Variable "außerhalb" der Funktion soll bei Änderungen dann geändert werden? Der Zahl 123 die Zahl 2 zuweisen macht nicht viel Sinn.

Da du in rechnen keine Änderungen an den Variablen machst ist die Sache mit den Änderungen egal, und du verhinderst nur dass man fixe Werte übergeben kann

Hallo!

ich danke dir für deine ausführliche Hilfe. Es stimmt. Bei den Rückgabenwerten könnte ich auch einfach eine void Funktion nutzen. Wie du sicherlich bemerkst, bin ich noch am Anfang von C++. Ich versuche den Kontext mehr zu verstehen. Mir war eigentlich klar, das ich immer außerhalb einer Funktion einen Zeiger oder eine Referenz nutze, aufgrund das die Variable nur ein bestimmten Gültigkeitsbereich hat. Zeiger kopieren den "Datensatz", Referenzen sind wie eine Art Spiegelbild. Ich dachte man nutzt fast nur Referenzen, weil Zeiger bei großen Datensatz sich überhaupt nicht lohnen, bzw zu viel Speicheraufwand wäre. Ich kann mich auch irren, wie gesagt ist der Unterschied zw. Referenzen und Zeiger noch nicht ganz klar. Bei deinem Beispiel heißt es ja, das die Referenz von b nicht mehr mit a verändert werden kann. Heißt das, sobald ich eine Referenz einer Variable habe, lässt sich diese nicht mehr nachträglich verändern? Heißt im Endeffekt ich müsste die original Variable wieder verändern, weil eine Referenz nur den Wert "kopiert", welcher in der Funktion zugewiesen wurde? b=2
 

cwriter

Erfahrenes Mitglied
Mir war eigentlich klar, das ich immer außerhalb einer Funktion einen Zeiger oder eine Referenz nutze, aufgrund das die Variable nur ein bestimmten Gültigkeitsbereich hat.
Das stimmt, allerdings tut das hier nichts zur Sache. Du nutzt nur den Stack und keine Variable ist weniger lang gültig als der main()-Frame.

Zeiger kopieren den "Datensatz", Referenzen sind wie eine Art Spiegelbild.
Nein. (Und das, @sheel, meinte ich mit der C++-Verwirrung).
Eine Referenz, typ&, ist ein Zeiger. Genau genommen ist es eine echte Teilmenge von Zeigern.
C:
void incRef(int& i)
{
   i++;
}

void incPtr(int* i)
{
    (*i)++;
}

int main()
{
    int i = 42;
    std::cout << i << std::endl;
    incRef(i);
    std::cout << i << std::endl;
    incPtr(&i);
    std::cout << i << std::endl;
    return 0;
}
Die Ausgabe wird sein:
42
43
44

incRef und incPtr sind somit semantisch identisch (übrigens auch in Assembly, der Compiler wird höchstwahrscheinlich exakt denselben Code erstellen). Zeiger kopieren NICHT.

Der Unterschied liegt in der Syntax, und im Verhalten in Grenzfällen.
Ich kann incPtr zum Abstürzen bringen, incRef aber nicht.
C:
incRef(nullptr); //Syntax error: void* (bzw. nullptr) ist kein int-typ.
incPtr(nullptr); //syntax ok.
//Aber in incPtr wird (*i) gemacht. (*(0)) ist ein Fehler, da 0 die reservierte ungültige Adresse ist -> Absturz.
Das Gegenteil von Referenz und Pointer ist schlicht die Kopie, pass-by-value.
Ich dachte man nutzt fast nur Referenzen, weil Zeiger bei großen Datensatz sich überhaupt nicht lohnen, bzw zu viel Speicheraufwand wäre.
Du scheinst Kopien und Zeiger zu verwechseln.
Im Übrigen stimmt das bezogen auf die Kopie, allerdings nicht bei primitiven Typen. Diese sind wesentlich schneller mit pass-by-value, da keine Indirektion notwendig ist.

Bei deinem Beispiel heißt es ja, das die Referenz von b nicht mehr mit a verändert werden kann. Heißt das, sobald ich eine Referenz einer Variable habe, lässt sich diese nicht mehr nachträglich verändern?
Welches Beispiel meinst du?
Das hier?
Eine Referenz zu nehmen verhindert die Kopie, sondern nimmt auch in der Funktion die Originalvariable für alles her
Was man aber mit (dieser Art von) Referenz nicht mehr machen kann: Einer Funktion fixe Werte wie 123, statt Variablen, zu übergeben. Grund: Welche Variable "außerhalb" der Funktion soll bei Änderungen dann geändert werden? Der Zahl 123 die Zahl 2 zuweisen macht nicht viel Sinn.
Sheel meint damit, dass eine Referenz einen Kopiervorgang umgeht und schlicht die Adresse des Parameters an die Funktion übergibt. Konstanten haben i.d.R. keine Adresse, sondern werden direkt in den Code eingebaut (für Interessierte: "Immediate instructions" googlen). Daher kann man keine Referenz auf sie aufbauen. Dass man Konstanten nicht ändern kann, tut dabei wenig zur Sache. Z.B. kann man auch eine Stringkonstante haben, welche nicht als Immediate behandelt wird: (oder sogar noch einfacher: Ein const int i = 42; als Konstante. Was nicht ginge, wäre dann constexpr int i = 42;, aber da sind wir schon weit im C++-Dschungel)
C:
#include <cstdio>

int test(const char* const & text)
{
    printf("%s\n", text);
    return 0;
}

int main()
{

    test("testtext");
    return 0;
}
(Doofes Beispiel, ja. Man wird ja noch gemein sein dürfen :p)

Eine Referenz funktioniert sonst genauso wie Pointer:
C:
int a = 0;
int& b = a;
b++;
//a == b == 1

//In Pointern
int a = 0;
int* b = &a;
(*b)++;
//a == *b == 1

Heißt im Endeffekt ich müsste die original Variable wieder verändern, weil eine Referenz nur den Wert "kopiert", welcher in der Funktion zugewiesen wurde? b=2
Nein, das ist genau umgekehrt.
Als Ergänzung zum incRef/incPtr-Teil oben:
C:
void incCopy(int i)
{
    i++;
}

int main()
{
    int i = 42;
    incCopy(i);
    //i == 42: 42 wurde als Kopie übergeben, sodass die Funktion nur die Kopie verändern kann.
    return 0;
}

Mein Tipp für alle, die mit C++ anfangen: Unterschied Pointer / Kopie verstehen, dann Referenzen als Pointer vorstellen. Es wird sehr viel einfacher, da auch die Syntax unterschiedlich ist (Referenzen und Kopien lassen sich syntaktisch nur in der Definition, nicht in der Verwendung unterscheiden).

Gruss
cwriter
 
Zuletzt bearbeitet: