Zeiger und Vektoren



  • Hallo Leute,

    beim Erlernen der Grundlagen zum Thema Zeiger und Vektoren hat sich eine Frage ergeben.
    Und zwar ist ein Vektorname ja zugleich ein Zeiger auf das erste Vektorelement, weshalb
    baum (char baum[] = "Eiche") und &baum[0] äquivalent zueinander sind. Der Vektorname kann
    deshalb auch einer Zeigervariablen zugewiesen werden (char * cptr = baum).
    Warum wird, wenn ich cptr mit cout ausgebe, (cout << cptr) in dem Fall das Wort Eiche ausgegeben und
    mit cout << cptr der erste Buchstabe von Eiche?
    Eigentlich steht in einem Zeiger doch immer die Adresse bzw. der Wert des adressierten Objektes, wenn ich den
    Dereferenzierungsoperator (
    ) anwende:

    int* test;
    int a=2;
    test = &a;
    cout << test << " " << *test << endl << endl;

    Ausgabe:
    012FFD28 2

    Vielen Dank für eure Hilfe!



  • @C-Sepp sagte in Zeiger und Vektoren:

    Warum wird, wenn ich cptr mit cout ausgebe, (cout << cptr) in dem Fall das Wort Eiche ausgegeben und
    mit cout << *cptr der erste Buchstabe von Eiche?

    cptr zeigt auf das 'E' von Eiche, und *cptr dereferenziert das und gibt das 'E' zurück.



  • Genau...allerdings wird wenn ich cptr mit cout ausgebe trotzdem das komplette Wort ausgegeben.
    Wenn ich einen normalen Zeiger vom Typ int mit einer Integer-Variablen intialisieren und dann ohne Anwendung
    des Derenferenzierungsoperators (*) mit cout ausgebe (so wie in meinen Code-Beispiel) wird mir die
    Adresse ausgegeben. Warum da die Adresse und bei Vektoren das ganze Objekt?



  • Weil std::cout für verschiedene Typen verschiedene Dinge tut. Ganz einfach. Wenn du eine Variable vom Typ char* übergibst, dann weiß cout (bzw. operator<< weiß), dass du einen nullterminierten String ausgeben willst. Bei anderen Pointer-Typen gibt es keine solche Spezialbehandlung und dann kommt eben die Adresse raus.

    Im Prinzip liegt es also daran, dass der operator<< vom ostream für char* überladen ist. Siehe hier: https://en.cppreference.com/w/cpp/io/basic_ostream/operator_ltlt2

    Man hat also einen Spezialfall, der sehr nützlich ist, eingebaut.

    Edit: Bitte sprich lieber von einem Array, wenn du Arrays meinst. In C++ versteht man unter "Vector" eigentlich immer std::vector.



  • Alles klar...dann weis ich Becheid. Vielen Dank!
    Im Kapitel Zeiger und Arrays habe ich allerdings eine weitere Funktion vor mir, bei mir ich nicht
    verstehe was da gemacht wurde/wie das möglich ist:

    int strlen(char * str)
    {
    char * p = str;
    for (p=str; *p != '\0'; ++p)
    ;
    return (p -str)
    }

    Warum wurde hier str ohne Verwendung des Adressoperators & den Zeiger p zugewiesen. Wären nicht
    die beiden Varianten char* p = &str bzw. char *p = *str nur möglich?
    Was drückt dann der Ausruck p=str bzw. ++p aus? Müsste nicht eigentlich der Verweisoperator * angewendet werden
    wenn dort Werte zugewiesen werden bzw. in dem String weitergesprungen wird?



  • @C-Sepp hmm, steht in deinem Buch wirklich

    int strlen(char * str)
    

    ?
    Falls ja, würde ich das Lehrmaterial wechseln.

    size_t strlen(const char *str)
    {
    	const char *p;
    	for (p = str; *p; ++p)
    		;
    	return (p - str);
    }
    


  • Innerhalb von strlen ist str eine lokale Zeiger-Variable.
    Ebenso wie p

    Bei &str bekommst du die Adresse, wo str selber liegt. dies wäre auch vom Typ char** (Ein Zeiger auf einen Zeiger auf char).
    Mit *str bekommst du den Wert an der Stelle str. das wäre ein char.

    Beide Typen passen nicht zu p (was ja ein char* ist)

    @C-Sepp sagte in Zeiger und Vektoren:

    Was drückt dann der Ausruck p=str

    Eine Zuweisung: p bekommt den Inhalt von str. Also die Adresse die in str abgelegt ist.
    (ist hier überflüssig, da dies in der Zeile davor (Definition von *p mit Initialisierung) schon gemacht wird.

    bzw. ++p aus?

    Das ist Pointer-Arithmetik. der Inhalt von p wird erhöht. p Zeigt jetzt auf das nächste Element.
    Dabei wird die Größe von *ptr (hier char) berücksichtigt.

    p-str liefert die Differenz (Abstand) der Adressen.
    Da p auf das Ende com String zeigt, bekommt man somit die Länge.



  • @DirkB sagte in Zeiger und Vektoren:

    p-str liefert die Differenz (Abstand) der Adressen.

    Um das noch zu präzisieren: es ist der Abstand in Einheiten der Größe des Typen, auf den man zeigt. Wenn du z.B. einen Pointer auf einen 32-bit Integer hast (und char 8 Bit hat), dann gehst du mit int_pointer + 1 gleich 4 Bytes weiter, weil eben der Integer 4 Bytes groß ist. Würdest du den Pointer vorher nach char* casten und dann 1 addieren, würdest du noch "in der Mitte" des integers landen. Das heißt, die Differenz hängt von der Pointee-Größe ab. Oder anders: wenn der Typ korrekt ist, bekommst du mit *(pointer+1) immer das nächstfolgende Element im Array.



  • Okay...das habe ich soweit verstanden.
    Müsste im Programmbeispiel:

    double *pv, v[5] = {5.5,4.4,3.3,2.2,1.1}
    for (pv = v+4; pv >=v; --p)
    *pv *=2;

    Warum wird dann der Schleifenrumpf 5 mal durchlaufen?
    Müsste es nicht nur viermal sein, dass heißvon 1.1 zu 2.2, 2.2 -> 3.3, 3.3 -> 4.4, 4.4 -> 5.5?



  • @C-Sepp sagte in Zeiger und Vektoren:

    Warum wird dann der Schleifenrumpf 5 mal durchlaufen?

    Weil bei pv == v (pv zeigt auf den Anfang vom Array) die Bedingung pv >=v noch wahr ist. Die Schleife wird dann nochmal ausgeführt.



  • @C-Sepp sagte in Zeiger und Vektoren:

    double *pv, v[5] = {5.5,4.4,3.3,2.2,1.1}
    for (pv = v+4; pv >=v; --p)
    *pv *=2;
    Warum wird dann der Schleifenrumpf 5 mal durchlaufen?

    Der Code funktionier gar nicht. Oder woher kommt "p"? Wenn du mit p pv meintest, dann verstehe ich dir Frage.

    Müsste es nicht nur viermal sein, dass heißvon 1.1 zu 2.2, 2.2 -> 3.3, 3.3 -> 4.4, 4.4 -> 5.5?

    Aber ich komme auf 5:

    • v + 4
    • v + 4 - 1
    • v + 4 - 2
    • v + 4 - 3
    • v + 4 - 4

    Ich weiß nicht so recht, was du mit "von 1.1 zu 2.2" etc. meinst. Es läuft der Pointer pv (=v + 4) herunter, solange der Wert >= v ist.



  • Ja genau mit p meinte ich pv. Sorry! Okay!!



  • Arbeite gerade die Thematik zu Zeiger und Arrays durch. Darin steht, dass der ++-Operator einen
    höheren Vorrang als der Verweisoperator * hat, weshalb *pv++ mit *(pv++) äquivalent ist.
    In dem Codebeispiel steht wiederrum:

    float v[6] = {0.0F, 0.1F, 0.2F, 0.3F, 0.F, 0.5F}, *pv, x;
    pv= v+4;
    pv -=2;
    ++pv;
    x= *pv++; //v[3] an x zuweisen, dann pv ein Element weiter

    als Kommentar "v[3] an x zuweisen, dann pv ein Element weiter". Müsste es nach der oben aufgeführten Regel
    nicht genau andersrum sein....sprich pv ein Element weiter, dann v[4] an x zuweisen? Vielen Dank!



  • @C-Sepp sagte in Zeiger und Vektoren:

    Darin steht, dass der ++-Operator einen
    höheren Vorrang als der Verweisoperator * hat, weshalb *pv++ mit *(pv++) äquivalent ist.

    Das bedeutet, dass du den Pointer nach dem Zugriff auf *p erhöhst.

    (*pv)++ inkrementiert den Wert, auf den p zeigt. Du bekommst aber noch den Wert vor dem inkrement.



  • Okay...ich hätte erwartet, dass die Operation, welche einen höheren Vorrang hat, in Klammern gesetzt wird und
    auch zuwerst ausgeführt wird. Irgendwie ein bisschen unlogisch



  • @C-Sepp sagte in Zeiger und Vektoren:

    und
    auch zuwerst ausgeführt wird.

    Du bekommst trotzdem den Pointer von vor dem Inkrement. Du denkst an *(++p)



  • Hallo Leute,

    ich habe ein weiteres interssantes Codebeispiel gefunden , in dem sich eine Frage ergeben hat:

    char arr[] = "open sea";

    char* p = arr, * q = arr + 4;
    
    cout << q - p << endl;
    cout << *q << endl;
    
    p = ++q;
    while (p < q + 3)
        cout << *p++ << endl;
    
    cout << *q << endl;
    cout << *arr << endl;
    cout << *(q - 2) << endl;
    cout << *q << endl; 
    

    Ausgabe:
    4

    s
    e
    s
    o
    n
    s

    Ich verstehen was das Programm gemacht hat, um auf dieses Ergebnis zu kommen. Jedoch verstehe
    ich nicht, warum der Zeiger *q ab der While-Schleife auf das "s" im Array zeigt. Müsste er nicht auf
    das Leerzeichen zwischen open und sea zeigen? Der zweite Ausgabewert ist ja ein Leerziechen.
    Nochmals vielen Dank!



  • Entschuldige die Ausgabe ist:

    4

    s
    e
    a
    s
    o
    n
    s


  • Mod

    Bei p = ++q wird q nebenher um 1 erhöht. Vorher stand q auf dem Leerzeichen, wie du mit der zweiten Ausgabe gesehen hast, danach eben eins weiter auf dem 's'.



  • Ahh okay...also wird mit p=++q auch q gleichzeitig um 1 erhöht. Vielen Dank!


Log in to reply