it-swarm.com.de

Warum Zeiger erhöhen?

Ich habe erst kürzlich angefangen, C++ zu lernen, und wie die meisten Leute (nach dem, was ich gelesen habe) habe ich Probleme mit Zeigern.

Nicht im herkömmlichen Sinne verstehe ich, was sie sind und warum sie verwendet werden und wie sie nützlich sein können. Ich kann jedoch nicht verstehen, wie nützlich das Inkrementieren von Zeigern wäre. Kann jemand erklären, wie das Inkrementieren eines Zeigers ein ist? nützliches Konzept und idiomatisches C++?

Diese Frage kam, nachdem ich angefangen hatte, das Buch zu lesen Eine Tour durch C++ von Bjarne Stroustrup. Mir wurde dieses Buch empfohlen, weil ich mit Java ziemlich vertraut bin, und die Leute bei Reddit sagten mir, dass es so sein würde sei ein gutes "Umschaltbuch".

25
INdek

Wenn Sie ein Array haben, können Sie einen Zeiger einrichten, der auf ein Element des Arrays zeigt:

int a[10];
int *p = &a[0];

Hier zeigt p auf das erste Element von a, nämlich a[0]. Jetzt können Sie den Zeiger erhöhen, um auf das nächste Element zu zeigen:

p++;

Jetzt zeigt p auf das zweite Element, a[1]. Sie können hier mit *p Auf das Element zugreifen. Dies unterscheidet sich von Java, wo Sie eine ganzzahlige Indexvariable verwenden müssten, um auf Elemente eines Arrays zuzugreifen.

Das Inkrementieren eines Zeigers in C++, wobei dieser Zeiger nicht auf ein Element eines Arrays zeigt, ist ndefiniertes Verhalten.

47
Greg Hewgill

Das Inkrementieren von Zeigern ist idiomatisch in C++, da die Zeigersemantik einen grundlegenden Aspekt der Entwurfsphilosophie hinter der C++ - Standardbibliothek widerspiegelt (basierend auf Alexander Stepanovs [~ # ~] stl [~ # ~] )

Das wichtige Konzept hierbei ist, dass die STL auf Containern, Algorithmen und Iteratoren basiert. Zeiger sind einfach Iteratoren.

Natürlich geht die Fähigkeit zum Inkrementieren (oder Addieren/Subtrahieren) von Zeigern auf C zurück. Viele C-String-Manipulationsalgorithmen können einfach unter Verwendung von Zeigerarithmetik geschrieben werden. Betrachten Sie den folgenden Code:

char string1[4] = "abc";
char string2[4];
char* src = string1;
char* dest = string2;
while ((*dest++ = *src++));

Dieser Code verwendet Zeigerarithmetik, um eine nullterminierte C-Zeichenfolge zu kopieren. Die Schleife wird automatisch beendet, wenn sie auf die Null trifft.

Mit C++ wird die Zeigersemantik auf das Konzept von Iteratoren verallgemeinert. Die meisten Standard-C++ - Container bieten Iteratoren, auf die über die Elementfunktionen begin und end zugegriffen werden kann. Iteratoren verhalten sich wie Zeiger, da sie inkrementiert, dereferenziert und manchmal dekrementiert oder erweitert werden können.

Um über einen std::string Zu iterieren, würden wir sagen:

std::string s = "abcdef";
std::string::iterator it = s.begin();
for (; it != s.end(); ++it) std::cout << *it;

Wir erhöhen den Iterator genauso, wie wir einen Zeiger auf eine einfache C-Zeichenfolge erhöhen würden. Der Grund, warum dieses Konzept leistungsstark ist, liegt darin, dass Sie Vorlagen verwenden können, um Funktionen zu schreiben, die für den Iteratortyp any funktionieren, der die erforderlichen Konzeptanforderungen erfüllt. Und das ist die Kraft der STL:

std::string s1 = "abcdef";
std::vector<char> buf;
std::copy(s1.begin(), s1.end(), std::back_inserter(buf));

Dieser Code kopiert eine Zeichenfolge in einen Vektor. Die Funktion copy ist eine Vorlage, die mit dem Iterator any funktioniert, der das Inkrementieren unterstützt (einschließlich einfacher Zeiger). Wir könnten dieselbe copy -Funktion für eine einfache C-Zeichenfolge verwenden:

   const char* s1 = "abcdef";
   std::vector<char> buf;
   std::copy(s1, s1 + std::strlen(s1), std::back_inserter(buf));

Wir könnten copy für einen std::map Oder einen std::set Oder any benutzerdefinierten Container verwenden, der Iteratoren unterstützt.

Beachten Sie, dass Zeiger eine bestimmte Art von Iterator sind: Iterator mit wahlfreiem Zugriff, dh sie unterstützen das Inkrementieren, Dekrementieren und Vorrücken mit den Operatoren + Und -. Andere Iteratortypen unterstützen nur eine Teilmenge der Zeigersemantik: a bidirektionaler Iterator unterstützt mindestens das Inkrementieren und Dekrementieren; a Forward Iterator unterstützt zumindest das Inkrementieren. (Alle Iteratortypen unterstützen die Dereferenzierung.) Die Funktion copy erfordert einen Iterator, der zumindest das Inkrementieren unterstützt.

Sie können hier über verschiedene Iterator-Konzepte lesen .

Das Inkrementieren von Zeigern ist also eine idiomatische C++ - Methode, um über ein C-Array zu iterieren oder auf Elemente/Offsets in einem C-Array zuzugreifen.

37
Charles Salvia

Zeigerarithmetik ist in C++, weil es in C war. Zeigerarithmetik ist in C, weil es eine normale Redewendung in Assembler ist.

Es gibt viele Systeme, in denen "Inkrementregister" schneller ist als "Konstantenwert 1 laden und zum Register hinzufügen". Darüber hinaus können Sie in einigen Systemen "DWORD von der in Register B angegebenen Adresse in A laden und dann B B Größe von (DWORD) hinzufügen" in einer einzigen Anweisung. Heutzutage könnte man erwarten, dass ein optimierender Compiler dies für Sie regelt, aber dies war 1973 nicht wirklich eine Option.

Dies ist im Grunde der gleiche Grund, warum C-Arrays nicht auf Grenzen überprüft werden und in C-Strings keine Größe eingebettet ist: Die Sprache wurde auf einem System entwickelt, bei dem jedes Byte und jeder Befehl gezählt werden.

18
pjc50