Ich versuche nur zu verstehen, wie man ein einzelnes Zeichen in einem String-Array anspricht. Dies ermöglicht mir natürlich auch, Zeiger auf Subskribierung von Zeigern im Allgemeinen zu verstehen.
Wenn ich char **a
habe und das 3. Zeichen der 2. Zeichenkette erreichen möchte, funktioniert das: **((a+1)+2)
? Scheint so, als müsste es ...
Fast, aber nicht ganz. Die richtige Antwort lautet:
%Vor%, weil Sie zuerst auf einen der tatsächlichen String-Zeiger aufheben und dann den ausgewählten String-Zeiger auf das gewünschte Zeichen zurücksetzen müssen. (Beachten Sie, dass ich zur besseren Übersichtlichkeit in der Reihenfolge der Operationen zusätzliche Klammern hinzugefügt habe.)
Alternativ, dieser Ausdruck:
%Vor%wird auch funktionieren! .... und vielleicht wäre es vorzuziehen, weil die Absicht dessen, was Sie zu tun versuchen, selbstverständlicher ist und die Notation selbst prägnanter ist. Diese Form ist für Personen, die neu in der Sprache sind, möglicherweise nicht sofort offensichtlich, aber verstehen, dass der Grund, warum die Array-Notation funktioniert, darin liegt, dass in C eine Array-Indizierungsoperation eigentlich nur eine Abkürzung für die äquivalente Pointeroperation ist. dh: * (a + x) ist gleich wie a [x]. Durch Erweiterung dieser Logik auf die ursprüngliche Frage gibt es also zwei separate Pointer-De-Referencing-Operationen, die miteinander kaskadiert sind, wobei der Ausdruck a [x] [y] der allgemeinen Form von * ((* (a + x)) + entspricht y).
Sie müssen keine Zeiger verwenden.
int main (int argc, char ** argv) {
printf ("Das dritte Zeichen von argv [1] ist [% c]. \ n ", argv [1] [2]);
}
Dann:
$ ./main hallo Das dritte Zeichen von argv [1] ist [l].
Das ist eins und eins.
Sie könnten Zeiger verwenden, wenn Sie möchten ...
* (argv [1] +2)
oder sogar
* ((* (a + 1)) + 2)
Wie jemand oben hingewiesen hat.
Dies liegt daran, dass Array-Namen Zeiger sind.
Zitat aus dem Wikipedia Artikel zu C-Zeigern -
In C ist die Array-Indizierung formal in Form von Zeigerarithmetik definiert; das ist, Die Sprachspezifikation erfordert, dass Array [i] äquivalent zu * (Array + i) ist. So können Arrays in C als Zeiger auf aufeinanderfolgende Bereiche des Speichers (ohne Lücken) gedacht werden, und die Syntax für den Zugriff auf Arrays ist identisch für das, was zur Dereferenzierung verwendet werden kann Zeiger. Ein Array kann beispielsweise wie folgt deklariert und verwendet werden:
%Vor%Als Antwort auf Ihre Frage - yes können Zeiger auf Zeiger als Array ohne irgendeine andere Deklaration verwendet werden!
Versuchen Sie a[1][2]
. Oder *(*(a+1)+2)
.
Arrayreferenzen sind im Grunde genommen syntaktischer Zucker für Zeiger-Dereferenzierung. a [2] ist dasselbe wie a + 2 und auch dasselbe wie 2 [a] (wenn Sie wirklich unlesbaren Code wollen). Ein Array von Strings ist dasselbe wie ein Doppelzeiger. Sie können also die zweite Zeichenfolge entweder mit [1] oder mit *(a+1)
extrahieren. Sie können dann das dritte Zeichen in dieser Zeichenfolge finden (nennen Sie es vorerst "b") entweder mit b [2] oder mit *(b + 2)
. Ersetzen wir die ursprüngliche zweite Zeichenfolge für 'b', erhalten wir entweder eine [1] [2] oder *(*(a+1)+2)
.
Theres eine brillante C-Programmierung Erklärung in dem Buch Hacking die Kunst der Ausbeutung 2. Ausgabe von Jon Erickson, die Zeiger, Strings, eine Erwähnung für die Programmierung Erklärung Abschnitt allein https://leaksource.files.wordpress.com/2014 diskutiert /08/hacking-the-art-of-exploitation.pdf.
Obwohl die Frage bereits beantwortet wurde, könnte jemand anderes, der mehr darüber wissen möchte, die folgenden Highlights aus Ericksons Buch nützlich finden, um etwas von der Struktur hinter der Frage zu verstehen.
Header
Beispiele für Header-Dateien, die für die Manipulation von Variablen verfügbar sind, werden Sie wahrscheinlich verwenden.
stdio.h - http://www.cplusplus.com/reference/cstdio/
stdlib.h - http://www.cplusplus.com/reference/cstdlib/
string.h - http://www.cplusplus.com/reference/cstring/
limits.h - http://www.cplusplus.com/reference/climits/
Funktionen
Beispiele für allgemeine Funktionen, die Sie wahrscheinlich verwenden werden.
malloc () - http://www.cplusplus.com/reference/cstdlib/malloc/
calloc () - http://www.cplusplus.com/reference/cstdlib/calloc/
strcpy () - http://www.cplusplus.com/reference/cstring/strcpy/
Speicher
" Der Speicher eines kompilierten Programms ist in fünf Segmente unterteilt: text, data, bss, heap und stack. Jedes Segment stellt einen speziellen Teil des Speichers dar, der für einen bestimmten Zweck reserviert ist. Das Textsegment ist auch manchmal auch als Codesegment bezeichnet. Hier befinden sich die zusammengesetzten Maschinensprachenbefehle des Programms ".
" Die Ausführung von Anweisungen in diesem Segment ist nichtlinear, dank der oben erwähnten übergeordneten Kontrollstrukturen und Funktionen, die kompilieren in Verzweigung, Sprung und Anweisungen in Assemblersprache aufrufen. Als ein Programm ausgeführt wird, wird der EIP auf die erste Anweisung im Textsegment gesetzt. Das Der Prozessor folgt dann einer Ausführungsschleife, die Folgendes ausführt:
" 1. Liest die Anweisung, auf die EIP zeigt "
" 2. Addiert die Byte-Länge der Anweisung zu EIP "
" 3. Führt die Anweisung aus, die in Schritt 1 gelesen wurde "
" 4. Geht zurück zu Schritt 1 "
" Manchmal wird die Anweisung ein Sprung oder eine Aufrufanweisung sein, die ändert den EIP in eine andere Speicheradresse. Der Prozessor nicht kümmern Sie sich um die Änderung, weil sie erwartet, dass die Ausführung nichtlinear ist sowieso. Wenn EIP in Schritt 3 geändert wird, kehrt der Prozessor einfach zu Schritt 1 zurück und liest die Anweisung, die sich unter der Adresse des geänderten EIP befindet, in ".
."" Die Schreibberechtigung ist im Textsegment deaktiviert, da es nicht zum Speichern von Variablen verwendet wird, sondern nur von Code. Dadurch wird verhindert, dass Benutzer den Programmcode tatsächlich ändern. Jeder Versuch, in dieses Speichersegment zu schreiben, führt dazu Programm, um den Benutzer zu warnen, dass etwas Schlimmes passiert ist, und das Programm wird getötet. Ein weiterer Vorteil dieses Segments ist das Lesen kann zwischen verschiedenen Kopien des Programms geteilt werden, so dass mehrere Ausführungen des Programms zur gleichen Zeit ohne Probleme. Es sollte Beachten Sie auch, dass dieses Speichersegment eine feste Größe hat, seit nichts mehr ändert sich darin ".
" Die Daten- und bss-Segmente werden zum Speichern von globalen und statischen Programmen verwendet Variablen. Das Datensegment ist mit den initialisierten globalen und statischen Variablen gefüllt, während das BSS-Segment mit ihren nicht initialisierten Gegenstücken gefüllt ist. Obwohl diese Segmente beschreibbar sind, haben sie auch eine feste Größe. Denken Sie daran, dass globale Variablen trotz des funktionalen Kontexts bestehen bleiben (wie die Variable j in den vorherigen Beispielen). Sowohl globale als auch statische Variablen können beibehalten werden, da sie in ihren eigenen Speichersegmenten gespeichert sind ".
" Das Heap-Segment ist ein Speichersegment, das ein Programmierer direkt verwenden kann Steuerung. Speicherblöcke in diesem Segment können zugewiesen und verwendet werden für was auch immer der Programmierer braucht. Ein bemerkenswerter Punkt über den Haufen Segment ist, dass es nicht von fester Größe ist, so kann es nach Bedarf größer oder kleiner werden ".
" Der gesamte Speicher innerhalb des Heapspeichers wird durch Allocator- und Deallocator-Algorithmen verwaltet, die jeweils einen Speicherbereich im Heap zur Verwendung reservieren und Reservierungen entfernen, damit dieser Speicherbereich für spätere Reservierungen wiederverwendet werden kann. Der Heap wird abhängig davon wachsen und schrumpfen viel Speicher ist für den Gebrauch reserviert. Dies bedeutet, dass ein Programmierer den Heap verwendet Zuweisungsfunktionen können Speicherplatz reservieren und freigeben. Das Wachstum von der Heap bewegt sich nach unten zu höheren Speicheradressen ".
" Das Stack-Segment hat auch eine variable Größe und wird als temporärer Notizblock verwendet, um lokale Funktionsvariablen und den Kontext während Funktionsaufrufen zu speichern. Dies ist der Backtrace-Befehl von GDB.Wenn ein Programm eine Funktion aufruft, hat diese Funktion ihre eigenen übergebenen Variablen, und der Code der Funktion befindet sich an einem anderen Speicherort im Text- (oder Code-) Segment. Da sich der Kontext und die EIP ändern müssen, wenn eine Funktion aufgerufen wird, werden im Stack alle übergebenen Variablen, die Position, an die die EIP nach Beendigung der Funktion zurückkehren soll, und alle von dieser Funktion verwendeten lokalen Variablen gespeichert. All diese Informationen werden zusammen auf dem Stapel in einem Stapelrahmen gespeichert. Der Stapel enthält viele Stapelrahmen ".
" Ein Stapel ist in der allgemeinen Informatik eine abstrakte Datenstruktur, die häufig verwendet wird. Sie hat die Reihenfolge" First-in-last-out "(FILO) , was bedeutet, dass der erste Gegenstand, der in einen Stapel gelegt wird, der letzte Gegenstand ist, der daraus hervorgeht. Stellen Sie sich vor, Sie setzen Perlen auf ein Stück Schnur, das an einem Ende einen Knoten hat - Sie können die erste Perle nicht entfernen, bis Sie alle anderen Perlen entfernt haben. Wenn ein Gegenstand in einen Stapel gelegt wird, nennt man das Schieben, und wenn ein Gegenstand von einem Stapel entfernt wird, nennt man das Popping ".
" Wie der Name andeutet, ist das Stapelsegment des Speichers tatsächlich eine Stapeldatenstruktur, die Stapelrahmen enthält. Das ESP-Register wird verwendet, um die Adresse des Endes des Stapels zu verfolgen, Das ändert sich ständig, wenn Elemente in und aus dem Objekt gedrückt werden.Da dies ein sehr dynamisches Verhalten ist, macht es Sinn, dass der Stapel auch keine feste Größe hat, im Gegensatz zum dynamischen Wachstum des Heap, wenn sich der Stack ändert s in der Größe, wächst es in einer visuellen Auflistung von Speicher nach oben, zu niedrigeren Speicheradressen ".
" Der FILO-Charakter eines Stapels mag seltsam erscheinen, aber da der Stapel verwendet wird um Kontext zu speichern, ist es sehr nützlich. Wenn eine Funktion aufgerufen wird, werden mehrere Dinge zusammen in einem Stapelrahmen auf den Stapel geschoben. Das EBP-Register - manchmal als Rahmenzeiger (FP) oder lokaler Basiszeiger (LB) bezeichnet - Wird verwendet, um lokale Funktionsvariablen im aktuellen Stapelrahmen zu referenzieren. Jeder Stapelrahmen enthält die Parameter für die Funktion, ihre lokalen Variablen und zwei Zeiger, die notwendig sind, um die Dinge wieder so zu gestalten, wie sie waren: der gespeicherte Rahmenzeiger (SFP) und die Rücksprungadresse. Das SFP wird verwendet, um EBP auf seinen vorherigen Wert und die Rücksendeadresse zurückzusetzen wird verwendet, um EIP für den nächsten Befehl nach dem Funktionsaufruf wiederherzustellen. Dies stellt den funktionalen Kontext des vorherigen Stapels wieder her Rahmen ".
Strings
" Ein Array ist in C einfach eine Liste von n Elementen eines bestimmten Datentyps. Ein 20-stelliges Array ist einfach 20 benachbarte Zeichen im Speicher. Arrays werden auch als Puffer bezeichnet ".
%Vor%" Im vorhergehenden Programm ist ein 20-Elemente-Zeichen-Array definiert als str_a, und jedes Element des Arrays wird einzeln beschrieben. Beachten Sie, dass die Zahl bei 0 statt 1 beginnt. Beachten Sie auch, dass das letzte Zeichen eine 0 ist.
" (Dies wird auch als Null-Byte bezeichnet.) Das Zeichen-Array wurde definiert, also werden ihm 20 Byte zugewiesen, aber nur 12 dieser Bytes werden tatsächlich verwendet. Das Null-Byte-Programming am Ende wird verwendet als ein Trennzeichen, um jeder Funktion, die sich mit der Zeichenkette befaßt, anzuweisen, Operationen genau dort anzuhalten Die restlichen zusätzlichen Bytes sind nur Müll und werden ignoriert Wenn ein Null-Byte in das fünfte Element des Zeichen-Arrays eingefügt wird, nur die Zeichen Hallo würde von der printf () Funktion "gedruckt werden."
" Da es sehr mühsam ist, jedes Zeichen in einem Zeichenfeld zu setzen und Zeichenfolgen relativ häufig zu verwenden, wurde eine Reihe von Standardfunktionen für die Zeichenfolgenbearbeitung erstellt. Die Funktion strcpy () kopiert beispielsweise eine Zeichenfolge aus einer Quelle an ein Ziel, durchläuft die Quellzeichenfolge und kopiert jedes Byte zum Ziel (und stoppt, nachdem es das Null-Beendigungsbyte kopiert hat) ".
" Die Reihenfolge der Funktionsargumente ähnelt der Intel-Assembly-Syntax zuerst und dann der Quelle. Das Programm char_array.c kann mit strcpy () neu geschrieben werden, um dasselbe mit der String-Bibliothek zu erreichen. Die nächste Version des unten gezeigten char_array-Programms enthält string.h, da es eine String-Funktion verwendet ".
%Vor%Weitere Informationen zu C-Zeichenfolgen
Ссылка
Ссылка
Zeiger
" Das EIP-Register ist ein Zeiger, der während einer Programmausführung auf den aktuellen Befehl" zeigt ", indem er seine Speicheradresse enthält. Die Idee der Zeiger wird auch in C verwendet. Da der physikalische Speicher nicht wirklich verschoben werden kann Die darin enthaltenen Informationen müssen kopiert werden.Es kann sehr rechenintensiv sein, große Teile des Speichers zu kopieren, die vonverschiedenen Funktionen oder an verschiedenen Orten verwendet werden sollen.Dies ist auch vom Standpunkt des Speichers her teuer, da Platz für die neueZielkopie sein muss gespeichert oder zugewiesen, bevor die Quelle kopiert werden kann. Zeiger sind eine Lösung für dieses Problem.Anstatt einen großen Speicherblock zu kopieren, ist es viel einfacher, die Adresse des Anfangs dieses Speicherblocks zu übergeben ".
" Zeiger in C können wie jeder andere Variablentyp definiert und verwendet werden. Da Speicher in der x86-Architektur 32-Bit-Adressierung verwendet, sind Zeiger auch 32 Bits groß (4 Bytes). Zeiger werden durch Voranstellen definiert Ein Stern (*) für den Variablennamen Anstatt eine Variable dieses Typs zu definieren, wird ein Zeiger als etwas definiert, das auf Daten dieses Typs verweist Das Programm pointer.c ist ein Beispiel für einen Zeiger, der mit den char-Daten verwendet wird Typ, der nur 1 Byte groß ist ".
%Vor%" Wie die Kommentare im Code anzeigen, wird der erste Zeiger am Anfang des Zeichen-Arrays gesetzt. Wenn das Zeichen-Array wie folgt referenziert wird, ist es tatsächlich ein Zeiger selbst. So war dieser Puffer als Zeiger auf die Funktionen printf () und strcpy () früher übergeben, der zweite Zeiger wird auf die erste Zeigeradresse plus zwei gesetzt, und dann werden einige Dinge gedruckt (in der Ausgabe unten gezeigt). p> %Vor%
" Der address-of-Operator wird oft in Verbindung mit Zeigern verwendet, da Zeiger Speicheradressen enthalten. Das Programm addressof.c demonstriert dies Der Address-of-Operator wird verwendet, um die Adresse einer Integer-Variablen zu setzen in einen Zeiger. Diese Zeile wird unten fett dargestellt. ".
%Vor%" Ein zusätzlicher unärer Operator namens Dereferenzierungsoperator existiert zur Verwendung mit Zeigern. Dieser Operator gibt die Daten zurück, die in der Adresse gefunden werden, auf die der Zeiger zeigt, anstelle der Adresse selbst. Er hat die Form eines Sternchens Vor dem Variablennamen, ähnlich der Deklaration eines Zeigers, existiert der Dereferenzierungsoperator sowohl in GDB als auch in C .
" Einige Zusätze zum addressof.c-Code (gezeigt in addressof2.c) werden demonstrieren all diese Konzepte. Die hinzugefügten printf () -Funktionen verwenden Format Parameter, die ich im nächsten Abschnitt erklären werde. Konzentrieren Sie sich vorerst nur auf die Programmausgabe ".
%Vor%" Wenn die unären Operatoren mit Zeigern verwendet werden, kann man sich den Adressenoperator so vorstellen, dass er sich rückwärts bewegt, während sich der Dereferenzierungsoperator in die Richtung bewegt, in die der Zeiger zeigt " . p>
Erfahren Sie mehr über Pointer & amp; Speicherzuweisung
Professor Dan Hirschberg, Abteilung für Informatik, Universität von Kalifornien über Computerspeicher https://www.ics.uci.edu/~dan/class/165/notes/memory.html
Ссылка
Ссылка
Arrays
Theres ein einfaches Tutorial auf mehrdimensionalen Arrays von einem Kerl namens Alex Allain hier verfügbar http://www.cprogramming.com/tutorial/c/lesson8.html
Theres Informationen über Arrays von einem Kerl namens Todd A Gibson hier verfügbar http://www.augustcouncil.com/~tgibson/tutorial/arr.html
Iteriere ein Array
%Vor%Verknüpfte Listen im Vergleich zu Arrays
Arrays sind nicht die einzige verfügbare Option, Informationen zur Linked List.
Ссылка
Fazit
Diese Information wurde einfach geschrieben, um etwas von dem weiterzugeben, was ich während meiner Forschung über das Thema gelesen habe, die anderen helfen könnte.