Wozu braucht man Pointer in C?

5 Antworten

Wie Kelec schon gesagt hat werden Pointer vorallem dann verwendet, wenn man um Pass by Reference zu realisieren. Hier mal ein praktische Beispiel für die Benutzung von Pointern, wenn du den Wert von zwei Variablen tauschen möchtest

#include <stdio.h>

void swap(int *a, int *b)
{
    *a = *a ^ *b;
    *b = *a ^ *b;
    *a = *a ^ *b;
}

int main()
{
    int num1 = 69;
    int num2 = 420;

    /* num1 = 69, num2 = 420 */
    printf("Bevor dem Tausch: num1 = %d, num2 = %d\n", num1, num2);


    /* Hier werden die Speicheradressen der Variablen übergeben */
    swap(&num1, &num2) // pass by reference

    /* num1 = 420, num2 = 69 */
    printf("Nach dem Tausch: num1 = %d, num2 = %d\n", num1, num2);

Da hier die Speicheradressen übergeben werden, werden die Variablen geändert, da direkt auf den Speicher zugegriffen wird und nicht der Wert der Variablen kopiert wird, wie bei einer Funktion die keine Pointer benutzt.

#include <stdio.h>

void swap(int a, int b)
{
    a = a ^ b;
    b = a ^ b;
    a = a ^ b;
}

int main()
{
    int num1 = 69;
    int num2 = 420;

    /* num1 = 69, num2 = 420 */
    printf("Bevor dem Tausch: num1 = %d, num2 = %d\n", num1, num2);


    /* Hier werden nur die Werte der Variablen übergeben */
    swap(num1, num2) // pass by value

    /* num1 = 69, num2 = 420 */
    printf("Nach dem Tausch: num1 = %d, num2 = %d\n", num1, num2);

Hier würde nichts passieren, da der Wert von num1 und num2 nur kopiert wird, von daher ändern sich auch die Variablen nicht, sonder nur die kopierten Werte innerhalb der funktion

kabik334 
Fragesteller
 09.11.2023, 14:59

Danke nur das verstehe ich nicht mit dem ^ operator hab ich bis jetzt nie gearbeitet .Ich verstehe die logik dahinter nicht warum wird hier duch :

*a = *a ^ *b;

    *b = *a ^ *b;

    *a = *a ^ *b;

a und b vertauscht ? wieso nicht einfach *a=*b bzw anderes rum ?

0

Hauptsächlich werden Pointer verwendet um sogenannte Pass by Reference Funktionsaufrufe zu realisieren.

Sagen wir wir haben einen Speichertype namens mytype_t dann wäre so eine Funktion:

void function(mytype_t* t);

Der Aufruf wäre dann eben:

mytype_t t;
function(&t);

Es gibt dann im wesentlichen 3 Gründe warum man das macht.

Nummer 1 ist Geschwindigkeit:

Bei einem Funktionsaufruf wird die Variable immer auf den Stack gelegt und danach wieder vom Stack genommen. Wenn nun mytype_t ein sehr großer Datentyp ist dann ist es effizienter nur die Speicheradresse auf den Stack zu schreiben und zu lesen anstatt die komplette Variable. Das bringt dir ab dann etwas ab wann der Datentyp größer ist als ein Pointer also auf einem 64 Bit System bringt es dann etwas wenn mytype_t größer als 64 Bit ist.

Nummer 2 die Funktion will etwas an der Variable ändern:

Nehmen wir jetzt an die Funktion function ändert irgendwelche Werte in t, dann würden diese Änderung bei einem normalen Funktionsaufruf (Pass by Copy) nicht außerhalb der Funktion gelten. Wenn du einen Pointer übergibst tun sie das schon.

Das führt dann direkt auch zu Punkt 3 Opaque Handle:

Ein Opaque Handle ist eine Art und weise wie man zB C++ Objekte in C verwenden kann und es ist eine Möglichkeit um bei einer Bibliothek die innere Implementierung zu verschleiern. Der Opaque Handle ist dabei ein Pointer über den das Programm nur weiß, dass es ein Pointer ist, das Programm weiß aber nicht was in diesem Pointer nun genau steht.

Das Handle dient dabei lediglich der Library als Instanz und beinhaltet alle wesentlichen Informationen der Library wie zB Einstellungen etc. Da das C Programm darüber aber nichts weiß kann es die Eigenschaften dieses Handle nur über Funktionen beeinflussen und damit sich diese Änderungen nun wirklich durchschlagen muss das Opaque Handle ein Pointer sein.

Wenn man nun bedenkt, dass "this" in C++ (die Instanzvariable) auch nur ein Pointer ist wird klar wie so ein C++ C Interface aussieht. Man definiert nun C Funktionen welche die Instanzvariable als Opaque Handle verwenden und reicht damit die C++ Instanz an C weiter und das obwohl C gar nicht mal weiß, dass so etwas wie Klassen und Instanzen überhaupt existieren.

Kelec hat schon eine gute Antwort vorgelegt, ich möchte daran anknüpfen und noch 'Generics' in den Raum werfen, an einem Beispiels aus der libc:

void qsort(void base[.size * .nmemb], size_t nmemb,
                     size_t size, int (*compar)(const void [.size], const void [.size]));

qsort() sortiert Arrays und um diese Aufgabe generisch erledigen zu können, muß es wissen an welcher Adresse das Array liegt, wieviele Elemente es lang ist, wie groß das einzelne Element ist und muß die Elemente vergleichen können.

Die Basisadresse ist ein Pointer, die Vergleichsfunktion wird auch über ihre Adresse bestimmt, wobei diese wiederum die generischen Adressen von zwei Elementen erhält, was auch wieder Pointer benötigt.

----

Ein zweiter Punkt der mir bisher fehlt sind dynamische Datenstrukturen. Der Pointer ist eine Form des Referenztyps und viele Datenstrukturen wie Graphen (Bäume), Listen, ... nutzen 'Verweise' innerhalb der Datenstruktur, also Referenzen auf ein anderen Element.

Nullterminierte Zeichenketten sind auch Felder beziehungsweise Zeiger.

Außerdem brauchst du Zeiger um eine C-API zu benutzen, wie die WinApi.

Auch Funktionszeiger, wie zum Beispiel bei GetProcAdress, sind Zeiger.

Woher ich das weiß:Hobby
Aber was will ich mit der Speicheradresse ?

Die Speicheradresse teilt dir mit, ab wo bestimmte Daten stehen oder bestimmte Programmteile (Methoden, Funktionen) beginnen.