Ich bereite einige Folien für eine einführende C-Klasse vor, und ich versuche, gute Beispiele (und Motivation) für die Verwendung von Zeigerarithmetik über Array-Subskriptionen zu präsentieren.
Viele Beispiele, die ich in Büchern sehe, sind ziemlich gleichwertig. Zum Beispiel zeigen viele Bücher, wie man die Groß- und Kleinschreibung aller Werte in einer Zeichenkette umkehrt, aber mit der Ausnahme, dass a [i] durch ein * p ersetzt wird, ist der Code identisch.
Ich suche nach einem guten (und kurzen) Beispiel mit eindimensionalen Arrays, bei denen die Zeigerarithmetik wesentlich eleganteren Code erzeugen kann. Irgendwelche Ideen?
Erneut einen Zeiger anstelle eines Werts abrufen:
Normalerweise verwendet man Zeigerarithmetik, wenn sie wieder einen Zeiger bekommen wollen. Um einen Zeiger zu erhalten, während ein Array-Index verwendet wird: 1) Berechnen des Zeiger-Offsets, dann 2) Abrufen des Wertes an diesem Speicherort, dann 3) Sie müssen & amp; um die Adresse wieder zu bekommen. Das ist mehr Eingabe und weniger saubere Syntax.
Beispiel 1: Angenommen, Sie benötigen einen Zeiger auf das 512. Byte in einem Puffer
%Vor%Ist sauberer als:
%Vor%Beispiel 2: Effizienter strcat
%Vor%Das ist sauberer als:
%Vor%Verwenden der Zeigerarithmetik ++ als Iterator:
Das Inkrementieren von Zeigern mit ++ und das Dekrementieren mit - ist nützlich, wenn über jedes Element in einem Array von Elementen iteriert wird. Es ist sauberer als eine separate Variable zu verwenden, um den Offset zu verfolgen.
Zeiger-Subtraktion:
Sie können die Zeiger-Subtraktion mit Zeigerarithmetik verwenden. Dies kann in einigen Fällen nützlich sein, um das Element vor demjenigen zu erhalten, auf das Sie zeigen. Es kann auch mit Array-Indizes gemacht werden, aber es sieht wirklich schlecht und verwirrend aus. Vor allem für einen Python-Programmierer, wo ein negativer Index angegeben wird, um etwas vom Ende der Liste zu indizieren.
Warum möchten Sie solch eine Schönheit mit einem Index verwöhnen? (Siehe K & amp; R, und wie sie auf diesem Stil aufbauen.) Es gibt einen Grund, warum ich die obige Signatur so verwendet habe, wie sie ist. Beende die Bearbeitung, ohne vorher um eine Klarstellung zu bitten. Für diejenigen, die denken, dass sie es wissen, schauen Sie sich die aktuelle Unterschrift an - Sie haben einige restrict
Qualifikationen verpasst.
Strukturausrichtungstest und die offsetof
Makroimplementierung.
Warum möchten Sie solch eine Schönheit mit einem Index verwöhnen? (Siehe K & amp; R, und wie sie auf diesem Stil aufbauen.) Es gibt einen Grund, warum ich die obige Signatur so verwendet habe, wie sie ist. Beende die Bearbeitung, ohne vorher um eine Klarstellung zu bitten. Für diejenigen, die denken, dass sie es wissen, schauen Sie sich die aktuelle Unterschrift an - Sie haben einige %code% Qualifikationen verpasst.
Strukturausrichtungstest und die %code% Makroimplementierung.
Ich bereite einige Folien für eine einführende C-Klasse vor, und ich versuche, gute Beispiele (und Motivation) für die Verwendung von Zeigerarithmetik über Array-Subskriptionen zu präsentieren.
Viele Beispiele, die ich in Büchern sehe, sind ziemlich gleichwertig. Zum Beispiel zeigen viele Bücher, wie man die Groß- und Kleinschreibung aller Werte in einer Zeichenkette umkehrt, aber mit der Ausnahme, dass a [i] durch ein * p ersetzt wird, ist der Code identisch.
Ich suche nach einem guten (und kurzen) Beispiel mit eindimensionalen Arrays, bei denen die Zeigerarithmetik wesentlich eleganteren Code erzeugen kann. Irgendwelche Ideen?
Wenn Sie einen alten Compiler oder irgendeinen spezialisierten Compiler für eingebettete Systeme verwenden, könnte es kleine Leistungsunterschiede geben, aber die meisten modernen Compiler würden wahrscheinlich diese (winzigen) Unterschiede optimieren.
Der folgende Artikel könnte etwas sein, auf das Sie zurückgreifen könnten - hängt vom Niveau Ihrer Schüler ab:
Zeigerarithmetik mag schick und "hackerisch" aussehen, aber ich habe nie einen Fall gefunden, der SCHNELLER war als die Standardindexierung. Im Gegenteil, ich bin oft auf Fälle gestoßen, in denen der Code stark verlangsamt wurde.
Beispielsweise ist eine typische sequenzielle Schleife durch ein Array mit einem Zeiger möglicherweise weniger effizient als eine Schleife mit einem klassischen Index auf einem modernen Prozessor, der SSE-Erweiterungen unterstützt. Die Pointer-Arithmetik in einer Schleife blockiert Compiler ausreichend, um eine Loop-Vektorisierung durchzuführen, was eine typische 2x-4x-Leistungsverstärkung ergeben kann. Außerdem kann die Verwendung von Zeigern anstelle von einfachen Ganzzahlvariablen zu unnötigen Speicheroperationen aufgrund von Zeigeraliasbildung führen.
Daher sollte im Allgemeinen die Zeigerarithmetik anstelle des standardmäßigen indizierten Zugriffs NIE empfohlen werden.
Sie fragen spezifisch nach C, aber C ++ baut auch darauf auf:
Die meisten Zeigerarithmetik verallgemeinert sich natürlich auf das Forward-Iterator-Konzept. Das Durchlaufen des Speichers mit %code% kann für jeden Sequenz-Container (verknüpfte Liste, Skip-Liste, Vektor, Binärbaum, B-Baum usw.) verwendet werden, da der Operator überlastet ist.
Oft ist die Wahl nur eine von Stil - man sieht oder fühlt sich natürlicher als die anderen für einen bestimmten Fall.
Es gibt auch das Argument, dass die Verwendung von Indizes dazu führen kann, dass der Compiler Offsets innerhalb einer Schleife wiederholt neu berechnen muss - ich bin nicht sicher, wie oft das der Fall ist (anders als in nicht optimierten Builds), aber ich stelle es mir vor passiert, aber es ist wahrscheinlich selten ein Problem.
Ein Bereich, der meiner Meinung nach auf lange Sicht wichtig ist (was vielleicht nicht für eine einführende C-Klasse gilt - aber lerne sie früh, sage ich), ist, dass Zeigerarithmetik auf die in C ++ STL verwendeten Idiome angewendet wird. Wenn Sie sie dazu bringen, die Zeigerarithmetik zu verstehen und sie zu verwenden, werden sie, wenn sie mit der STL fortfahren, einen Anhaltspunkt dafür haben, wie man Iteratoren richtig einsetzt.
Etwas Spaß, ich hoffe, Sie müssen nie damit umgehen: Zeiger können Alias, während Arrays nicht können. Aliasing kann alle Arten von nicht-idealen Codegenerierung verursachen, von denen die häufigste einen Zeiger als out-Parameter für eine andere Funktion verwendet. Grundsätzlich kann der Compiler nicht davon ausgehen, dass der von der Funktion verwendete Zeiger sich selbst oder irgendetwas anderes in diesem Stapelrahmen nicht aliasiert, so dass er den Wert jedes Mal, wenn er verwendet wird, vom Zeiger neu laden muss. Oder besser gesagt, um sicher zu gehen.
Erneut einen Zeiger anstelle eines Werts abrufen:
Normalerweise verwendet man Zeigerarithmetik, wenn sie wieder einen Zeiger bekommen wollen. Um einen Zeiger zu erhalten, während ein Array-Index verwendet wird: 1) Berechnen des Zeiger-Offsets, dann 2) Abrufen des Wertes an diesem Speicherort, dann 3) Sie müssen & amp; um die Adresse wieder zu bekommen. Das ist mehr Eingabe und weniger saubere Syntax.
Beispiel 1: Angenommen, Sie benötigen einen Zeiger auf das 512. Byte in einem Puffer
%Vor%Ist sauberer als:
%Vor%Beispiel 2: Effizienter strcat
%Vor%Das ist sauberer als:
%Vor%Verwenden der Zeigerarithmetik ++ als Iterator:
Das Inkrementieren von Zeigern mit ++ und das Dekrementieren mit - ist nützlich, wenn über jedes Element in einem Array von Elementen iteriert wird. Es ist sauberer als eine separate Variable zu verwenden, um den Offset zu verfolgen.
Zeiger-Subtraktion:
Sie können die Zeiger-Subtraktion mit Zeigerarithmetik verwenden. Dies kann in einigen Fällen nützlich sein, um das Element vor demjenigen zu erhalten, auf das Sie zeigen. Es kann auch mit Array-Indizes gemacht werden, aber es sieht wirklich schlecht und verwirrend aus. Vor allem für einen Python-Programmierer, wo ein negativer Index angegeben wird, um etwas vom Ende der Liste zu indizieren.
Wenn Sie einen alten Compiler oder irgendeinen spezialisierten Compiler für eingebettete Systeme verwenden, könnte es kleine Leistungsunterschiede geben, aber die meisten modernen Compiler würden wahrscheinlich diese (winzigen) Unterschiede optimieren.
Der folgende Artikel könnte etwas sein, auf das Sie zurückgreifen könnten - hängt vom Niveau Ihrer Schüler ab:
Sie fragen spezifisch nach C, aber C ++ baut auch darauf auf:
Die meisten Zeigerarithmetik verallgemeinert sich natürlich auf das Forward-Iterator-Konzept. Das Durchlaufen des Speichers mit *p++
kann für jeden Sequenz-Container (verknüpfte Liste, Skip-Liste, Vektor, Binärbaum, B-Baum usw.) verwendet werden, da der Operator überlastet ist.
Zeigerarithmetik mag schick und "hackerisch" aussehen, aber ich habe nie einen Fall gefunden, der SCHNELLER war als die Standardindexierung. Im Gegenteil, ich bin oft auf Fälle gestoßen, in denen der Code stark verlangsamt wurde.
Beispielsweise ist eine typische sequenzielle Schleife durch ein Array mit einem Zeiger möglicherweise weniger effizient als eine Schleife mit einem klassischen Index auf einem modernen Prozessor, der SSE-Erweiterungen unterstützt. Die Pointer-Arithmetik in einer Schleife blockiert Compiler ausreichend, um eine Loop-Vektorisierung durchzuführen, was eine typische 2x-4x-Leistungsverstärkung ergeben kann. Außerdem kann die Verwendung von Zeigern anstelle von einfachen Ganzzahlvariablen zu unnötigen Speicheroperationen aufgrund von Zeigeraliasbildung führen.
Daher sollte im Allgemeinen die Zeigerarithmetik anstelle des standardmäßigen indizierten Zugriffs NIE empfohlen werden.
Oft ist die Wahl nur eine von Stil - man sieht oder fühlt sich natürlicher als die anderen für einen bestimmten Fall.
Es gibt auch das Argument, dass die Verwendung von Indizes dazu führen kann, dass der Compiler Offsets innerhalb einer Schleife wiederholt neu berechnen muss - ich bin nicht sicher, wie oft das der Fall ist (anders als in nicht optimierten Builds), aber ich stelle es mir vor passiert, aber es ist wahrscheinlich selten ein Problem.
Ein Bereich, der meiner Meinung nach auf lange Sicht wichtig ist (was vielleicht nicht für eine einführende C-Klasse gilt - aber lerne sie früh, sage ich), ist, dass Zeigerarithmetik auf die in C ++ STL verwendeten Idiome angewendet wird. Wenn Sie sie dazu bringen, die Zeigerarithmetik zu verstehen und sie zu verwenden, werden sie, wenn sie mit der STL fortfahren, einen Anhaltspunkt dafür haben, wie man Iteratoren richtig einsetzt.
Etwas Spaß, ich hoffe, Sie müssen nie damit umgehen: Zeiger können Alias, während Arrays nicht können. Aliasing kann alle Arten von nicht-idealen Codegenerierung verursachen, von denen die häufigste einen Zeiger als out-Parameter für eine andere Funktion verwendet. Grundsätzlich kann der Compiler nicht davon ausgehen, dass der von der Funktion verwendete Zeiger sich selbst oder irgendetwas anderes in diesem Stapelrahmen nicht aliasiert, so dass er den Wert jedes Mal, wenn er verwendet wird, vom Zeiger neu laden muss. Oder besser gesagt, um sicher zu gehen.
Tags und Links c pointer-arithmetic