Vorteile von Pointern?

Lesezeit: 9 Minuten

Benutzer-Avatar
Nick

Ich habe kürzlich ein Interesse an C-Programmierung entwickelt, also habe ich mir ein Buch (K&R) besorgt und angefangen zu studieren.

Aus einem Universitätskurs in Java (Grundlagen) stammend, sind Zeiger ein völlig neues Kapitel, und nach dem, was ich online gelesen habe, ist es ein ziemlich schwieriges Konzept, sich zurechtzufinden. Bevor ich zum Zeigerkapitel kam, hatte ich den Eindruck, dass Zeiger ein Hauptbestandteil von C sind und große Vorteile bieten.

Nachdem ich das Kapitel gelesen und eine grundlegende Vorstellung davon bekommen habe, was Pointer sind und wie sie funktionieren, sind die Vorteile für mich nicht offensichtlich.

Zum Beispiel (bitte korrigieren Sie mich, wenn ich das völlig falsch verstanden habe) heißt es in der Einführung von Zeigern im K&R-Buch, dass wir, da wir nach Wert aufrufen, beim Übergeben einer Variablen in einem Funktionsaufruf ziemlich genau eine Kopie der Variablen für die übergeben zu handhabende Funktion und daher kann die Funktion nichts mit der ursprünglichen Variable tun, und wir können dies mit Zeigern überwinden.

In einem späteren Beispiel, das einen char-Zeiger verwendet, sagt das Buch, dass das Erhöhen des char-Zeigers legal ist, da die Funktion eine private Kopie des Zeigers hat. Sind „Privatkopien“ nicht ein Grund, stattdessen Zeiger zu verwenden?

Ich glaube, ich bin ein bisschen verwirrt über die ganze Verwendung von Zeigern. Wenn ich gefragt werde, kann ich zum Beispiel Zeiger anstelle von Array-Indizes verwenden, aber ich bezweifle, dass dies die Hauptverwendung von Zeigern ist.

Linux und Open-Source-Programmierung waren der Hauptgrund, warum ich zu C kam. Ich bekam den Quellcode eines C-Projekts zum Studieren (Geany IDE) und ich kann sehen, dass im gesamten Quellcode Zeiger verwendet werden.

Ich habe auch ein bisschen im Forum gesucht und ein paar Beiträge mit ähnlichen Fragen gefunden. Eine Antwort war (ich zitiere):

Wenn Sie nicht wissen, wann Sie Zeiger verwenden sollten, verwenden Sie sie einfach nicht.

Es wird deutlich, wann Sie sie verwenden müssen, jede Situation ist anders.

Kann ich die Verwendung von Zeigern derzeit vermeiden und sie nur in bestimmten Situationen verwenden (wo die Notwendigkeit von Zeigern offensichtlich ist?)

Benutzer-Avatar
Alex

Ein Vorteil von Zeigern besteht darin, dass Sie, wenn Sie sie in Funktionsargumenten verwenden, keine großen Speicherblöcke kopieren müssen und außerdem den Zustand ändern können, indem Sie den Zeiger dereferenzieren.

Zum Beispiel haben Sie vielleicht eine riesige struct MyStructund Sie haben eine Funktion a().

void a (struct MyStruct* b) {
   // You didn't copy the whole `b` around, just passed a pointer.
}

Wenn Sie von Java kommen, haben Sie eine etwas andere Perspektive als das, was in K&R präsentiert wird (K&R geht nicht davon aus, dass der Leser irgendeine andere moderne Programmiersprache kennt).

Ein Zeiger in C ist wie eine etwas leistungsfähigere Version einer Referenz in Java. Sie können diese Ähnlichkeit durch die benannte Java-Ausnahme erkennen NullPointerException. Ein wichtiger Aspekt von Zeigern in C ist, dass Sie es können Rückgeld worauf sie durch Inkrement und Dekrement zeigen.

In C können Sie eine Reihe von Dingen im Speicher in einem Array speichern, und Sie wissen, dass sie nebeneinander im Speicher sitzen. Wenn Sie einen Zeiger auf einen von ihnen haben, können Sie diesen Zeiger auf den “nächsten” zeigen lassen, indem Sie ihn erhöhen. Zum Beispiel:

int a[5];

int *p = a; // make p point to a[0]
for (int i = 0; i < 5; i++) {
    printf("element %d is %d\n", i, *p);
    p++; // make `p` point to the next element
}

Der obige Code verwendet den Zeiger p um auf jedes nachfolgende Element im Array zu zeigen a der Reihe nach und druckt sie aus.

(Hinweis: Der obige Code ist nur ein erläuterndes Beispiel, und Sie würden normalerweise keine einfache Schleife auf diese Weise schreiben. Es wäre einfacher, auf die Elemente des Arrays zuzugreifen als a[i]und verwenden Sie dort keinen Zeiger.)

  • +1 für den Punkt, dass das nächste Analogon von C-Zeigern Java-Referenzen sind.

    – Billy ONeal

    19. Dezember 2010 um 0:11 Uhr

Benutzer-Avatar
Enabren Tane

Ihre hervorgehobene Regel ist sehr weise. Es wird Sie vor Ärger bewahren, aber früher oder später müssen Sie Hinweise lernen.

Warum wollen wir Zeiger verwenden?

Angenommen, ich habe eine Textdatei geöffnet und sie in eine riesige Zeichenfolge eingelesen. Ich kann Ihnen die riesige Zeichenfolge nicht nach Wert übergeben, da sie zu groß ist, um auf den Stapel zu passen (z. B. 10 MB). Also sage ich dir, wo die Saite ist und sage: “Geh und sieh dort drüben nach meiner Saite”.

Ein Array ist ein Zeiger ( fast ).

int[] und int* unterscheiden sich geringfügig, sind aber größtenteils austauschbar.

int[] i = new int[5]; // garbage data
int* j = new int[5]   // more garbage data but does the same thing
std::cout << i[3] == i + 3 * sizeof(int); // different syntax for the same thing

Eine fortgeschrittenere Verwendung von Zeigern, die äußerst nützlich ist, ist die Verwendung von Funktionszeigern. In C und C++ sind Funktionen keine erstklassigen Datentypen, aber Zeiger sind es. Sie können also einen Zeiger auf eine Funktion übergeben, die Sie aufrufen möchten, und sie können dies tun.

Hoffentlich hilft das, aber höchstwahrscheinlich wird es verwirrend sein.

  • Stimmt, aber der Punkt ist immer noch gültig. C würde malloc() anstelle von new verwenden. und printf() statt std::cout

    – EnabrenTane

    19. Dezember 2010 um 0:12 Uhr

  • Ich habe nie gesagt, dass die Antwort falsch war. Aber die Frage ist getaggt c.

    – Billy ONeal

    19. Dezember 2010 um 0:12 Uhr

  • Alle Informationen über Arrays und Zeiger sind falsch, und der präsentierte Code ist überhaupt nicht C++.

    – QUentin

    13. Januar 2016 um 22:12 Uhr

Benutzer-Avatar
Salzbank

In einem späteren Beispiel, das einen char-Zeiger verwendet, sagt das Buch, dass das Erhöhen des char-Zeigers legal ist, da die Funktion eine private Kopie des Zeigers hat.

Ich würde sagen, dass dies bedeutet, dass sie den Zeiger selbst erhöhen, was bedeutet, dass die Adresse geändert wird (und sie daher auf einen anderen Wert zeigt). Dies kann nützlich sein, wenn ihnen das erste Element eines Arrays übergeben wurde und sie im Array fortfahren, aber die Werte nicht ändern möchten.

Denken Sie daran, dass C (und das K&R-Buch) sehr alt ist, wahrscheinlich älter als alles, was Sie zuvor gelernt haben (definitiv Alter als Java). Zeiger sind kein zusätzliches Feature von C, sie sind ein sehr grundlegender Bestandteil der Funktionsweise von Computern.

Die Pointer-Theorie ist nicht besonders schwer zu meistern, nur dass sie sehr leistungsfähig ist, sodass ein Fehler höchstwahrscheinlich Ihre Anwendung zum Absturz bringen wird und Compiler Schwierigkeiten haben, zeigerbezogene Fehler abzufangen. Eine der großen Neuerungen bei Java war, dass es „fast“ so viel Leistung wie C ohne Zeiger hat.

Meiner Meinung nach ist der Versuch, C zu schreiben und dabei Zeiger zu vermeiden, wie der Versuch, ein Fahrrad ohne ein Pedal zu fahren. Ja, es ist machbar, aber Sie werden doppelt so hart arbeiten.

Benutzer-Avatar
Gemeinschaft

Ignorieren Sie diese Antwort bitte.

Wenn Sie nicht wissen, wie man Zeiger verwendet, lernen wie man sie benutzt. Einfach.

Zeiger, wie Sie sagen, ermöglichen es Ihnen, mehr als eine Variable über einen Zeiger darauf an eine Funktion zu übergeben, wie Sie zu Recht bemerkt haben. Eine weitere Verwendung von Zeigern ist die Bezugnahme auf Datenarrays, die Sie mithilfe von Zeigerarithmetik schrittweise durchlaufen können. Schließlich können Sie mit Zeigern Speicher dynamisch zuweisen. Daher wird der Ratschlag, keine Zeiger zu verwenden, Ihre Möglichkeiten stark einschränken.

Zeiger sind die Art von C, über Speicheradressen zu sprechen. Aus diesem Grund sind sie kritisch. Da Sie K&R haben, lesen Sie das.

Ein Beispiel für die Verwendung finden Sie in meiner Antwort darauf. Wie ich in dieser Antwort sage, ist es angesichts der Frage nicht unbedingt so, wie Sie es tun würden.

Diese Technik ist jedoch genau die Art und Weise, wie Bibliotheken wie MPIR und GMP funktionieren. libgmp, wenn Sie es noch nicht kennen, es unterstützt Mathematica, Maple usw. und ist das Bibliothek für Arithmetik mit beliebiger Genauigkeit. Du wirst es finden mpn_t ist ein Typedef auf einen Zeiger; Je nach Betriebssystem hängt es davon ab, worauf es zeigt. Sie finden auch viel Zeigerarithmetik in den langsamen C-Versionen dieses Codes.

Schließlich erwähnte ich die Speicherverwaltung. Wenn Sie ein Array von etwas zuweisen möchten, das Sie benötigen malloc und free die sich mit Zeigern auf Speicherplätze befassen; speziell malloc gibt einen Zeiger auf einen einmal zugewiesenen Speicher zurück oder NULL auf Versagen.

Eine meiner Lieblingsverwendungen von Zeigern ist es, eine C++-Klassenmitgliedsfunktion unter Windows mithilfe der Win32-API als Thread fungieren zu lassen, dh die Klasse einen Thread enthalten zu lassen. Leider CreateThread unter Windows akzeptiert aus offensichtlichen Gründen keine C++-Klassenfunktionen – CreateThread ist eine C-Funktion, die keine Klasseninstanzen versteht; Sie müssen also eine statische Funktion übergeben. Hier ist der Trick:

DWORD WINAPI CLASSNAME::STATICFUNC(PVOID pvParam)
{
    return ((CLASSNAME*)pvParam)->ThreadFunc();
}

(PVOID ist ein void *)

Was passiert, ist, dass es zurückkehrt ThreadFunc was “statt” ausgeführt wird (eigentlich STATICFUNC ruft es auf) STATICFUNC und kann tatsächlich auf alle privaten Member-Variablen von zugreifen CLASSNAME. Genial, oder?

Wenn das nicht ausreicht, um Sie irgendwie zu überzeugen sind C, ich weiß nicht, was ist. Oder vielleicht gibt es in C keinen Sinn ohne Zeiger. Oder…

In Anbetracht dessen, dass Sie von einem Java-Hintergrund kommen, ist dies der einfachste Weg, um sich mit dem Nutzen von Zeigern vertraut zu machen.

Nehmen wir an, Sie haben eine Klasse wie diese in Java:

public class MyClass {
    private int myValue;

    public int getMyValue() { return myValue; }
    public void setMyValue(int value) { myValue = value; }
}

Hier ist Ihre Hauptfunktion, die eines Ihrer Objekte erstellt und eine Funktion darauf aufruft.

public static void Main(String[] args) {
    MyClass myInstance = new MyClass();

    myInstance.setMyValue(1);
    System.out.printLn(myInstance.getMyValue()); // prints 1

    DoSomething(myInstance);
    System.out.printLn(myInstance.getMyValue()); // prints 2
}

public static void DoSomething(MyClass instance) {
    instance.setMyValue(2);
}

Das myInstance Variable, in der Sie deklariert haben Main ist eine Referenz. Es ist im Grunde ein Handle, das die JVM verwendet, um Ihre Objektinstanz im Auge zu behalten. Jetzt machen wir dasselbe in C.

typedef struct _MyClass
{
    int myValue;
} MyClass;

void DoSomething(MyClass *);

int main()
{
    MyClass myInstance;

    myInstance.myValue = 1;
    printf("i", myInstance.myValue); // prints 1

    MyClass *myPointer = &myInstance; // creates a pointer to the address of myInstance

    DoSomething(myPointer);
    printf("i", myInstance.myValue); // prints 2

    return 0;
}

void DoSomething(MyClass *instance)
{
    instance->myValue = 2;
}

Natürlich sind Zeiger in C viel flexibler, aber das ist das Wesentliche, wie sie auf Java-Art funktionieren.

  • (*instance).myValue sollte wohl sein instance->myValue.

    – Billy ONeal

    19. Dezember 2010 um 0:23 Uhr

  • Ah ja, für eine Sekunde dachte ich, das sei C++-Syntax.

    – Adam Maras

    19. Dezember 2010 um 0:25 Uhr

1178970cookie-checkVorteile von Pointern?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy