2009-03-28 5 views
8

Ich bereite einige Folien für eine Einführung C-Klasse vor, und ich versuche, gute Beispiele (und Motivation) für die Verwendung von Zeigerarithmetik über Array-Subskriptionen zu präsentieren.Was sind überzeugende Beispiele, bei denen Zeigerarithmetik der Array-Subskribierung vorzuziehen ist?

Viele der 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 a * p ersetzt wird, ist der Code identisch.

Ich bin auf der Suche nach einem guten (und kurzen) Beispiel mit eindimensionalen Arrays, wo Zeigerarithmetik deutlich eleganter Code erzeugen kann. Irgendwelche Ideen?

Antwort

15

Wie Sie einen Zeiger wieder anstelle eines Wertes:

Ein in der Regel Pointer-Arithmetik verwendet, wenn sie wollen wieder einen Zeiger zu bekommen. 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 & verwenden, um die Adresse erneut zu erhalten. Das ist mehr Eingabe und weniger saubere Syntax.

Beispiel 1: Angenommen, Sie haben in einem Puffer

char buffer[1024] 
char *p = buffer + 512; 

ist sauberer als einen Zeiger auf das 512. Byte benötigen:

char buffer[1024]; 
char *p = &buffer[512]; 

Beispiel 2: Effizientere strcat

char buffer[1024]; 
strcpy(buffer, "hello "); 
strcpy(buffer + 6, "world!"); 

Dies ist sauberer als:

char buffer[1024]; 
strcpy(buffer, "hello "); 
strcpy(&buffer[6], "world!"); 

Verwendung Zeigerarithmetik ++ als Iterator:

Zeiger mit ++ Inkrementieren und Dekrementieren mit - ist nützlich, wenn sie über jedes Element in Iterieren eine Reihe von Elementen. Es ist sauberer als eine separate Variable zu verwenden, um den Offset zu verfolgen.


Zeiger Subtraktion:

Sie können Zeiger Subtraktion mit Pointer-Arithmetik 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.

+0

Aber Sie haben immer noch die Gesamtzahl der Variablen. – Uri

+1

Sie wissen, ein even ** cleaner ** für das zweite Beispiel wäre 'sprintf (buffer,% s% s", "hallo", "world!") '. –

+1

@sgm, und teuer –

3
char *my_strcpy(const char *s, char *t) { 
    char *u = t; 
    while (*t++ = *s++); 
    return u; 
} 

Warum möchten Sie solch eine Schönheit mit einem Index verwöhnen? (Siehe K & R, und wie sie bis zu 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 wissen, suchen Sie die aktuelle Unterschrift - Sie haben ein paar restrict Qualifikationen verpasst.

Strukturausrichtungstest und die offsetof Makroimplementierung.

+1

Ich dachte darüber nach, das zu verwenden, aber ich habe immer noch das Gefühl, dass es die Studenten nicht überzeugen wird, es ist immer noch ein sehr ähnlicher Code mit Indizes. – Uri

+0

Aber das ist eine zusätzliche Variable, die Sie nicht verwenden müssen. – dirkgently

+0

Ich bin nicht wer es bearbeitet und warum. – Uri

1

durch einen 2-dimensionalen Array iterieren, wo die Position eines Datums nicht wirklich wichtig
, wenn Sie verwenden Zeiger nicht, würden Sie Spur von zwei Indizes
mit Zeigern zu halten haben, können Sie auf den oberen Punkt von Ihr Array und mit einer einzigen Schleife, zip durch die ganze Sache

0

Oft ist die Wahl nur eine von Stil ist - man sieht oder fühlt sich natürlicher, als der andere 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 mir vor Es 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 der 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.

meisten Zeigerarithmetik verallgemeinert natürlich Forward Iterator-Konzept: speziell, aber C++ baut auf diese als auch

1

Sie über C zu fragen. 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.

0

Etwas Spaß, ich hoffe, Sie müssen nie damit umgehen: Zeiger können Alias, während Arrays nicht können. Aliasing kann alle Arten der 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 sein.

1

Zeiger Arithmetik kann schick und "hackerisch" aussehen, aber ich habe nie einen Fall gefunden, es war SCHNELLER als die Standard-Indizierung. Im Gegenteil, ich bin oft auf Fälle gestoßen, in denen der Code stark verlangsamt wurde.

Zum Beispiel ist die typische sequenzielle Schleife durch ein Array mit einem Zeiger möglicherweise weniger effizient als das Schleifen 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.

Also, in der Regel sollte Zeigerarithmetik anstelle von standardmäßigen indizierten Zugriff NIE empfohlen werden.

1
#include ctype.h 
void skip_spaces(const char **ppsz) 
{ 
    const char *psz = *ppsz; 
    while(isspace(*psz)) 
    psz++; 
    *ppsz = psz; 
} 

void fn(void) 
{ 
    char a[]=" Hello World!"; 
    const char *psz = a; 
    skip_spaces(&psz); 
    printf("\n%s", psz); 
} 
Verwandte Themen