... einfach einen Zeiger außerhalb des zugewiesenen Bereichs zu dekrementieren, erscheint mir sehr lückenhaft. Ist dieses "erlaubte" Verhalten in C?
Dürfen? Ja. Gute Idee? Nicht gewöhnlich.
C ist eine Abkürzung für Assemblersprache, und in Assemblersprache gibt es keine Zeiger, nur Speicheradressen. Cs Zeiger sind Speicheradressen, die ein Nebenverhalten aufweisen, bei dem sie um die Größe dessen, worauf sie zeigen, inkrementiert oder dekrementiert werden, wenn sie einer Arithmetik unterzogen werden. Dies macht aus Syntaxperspektive Folgendes gut:
double *p = (double *)0xdeadbeef;
--p; // p == 0xdeadbee7, assuming sizeof(double) == 8.
double d = p[0];
Arrays sind in C nicht wirklich eine Sache; Sie sind nur Zeiger auf zusammenhängende Speicherbereiche, die sich wie Arrays verhalten. Der []
Operator ist eine Abkürzung für Zeigerarithmetik und Dereferenzierung, a[x]
bedeutet also eigentlich *(a + x)
.
Es gibt gute Gründe , die oben, wie einige E / A - Gerät mit ein paar zu tun double
s in die kartierte 0xdeadbee7
und 0xdeadbeef
. Sehr wenige Programme müssten das tun.
Wenn Sie die Adresse von etwas erstellen, z. B. mithilfe des &
Operators oder durch Aufrufen malloc()
, möchten Sie den ursprünglichen Zeiger intakt halten, damit Sie wissen, dass das, worauf er verweist, tatsächlich etwas Gültiges ist. Das Dekrementieren des Zeigers bedeutet, dass ein Teil des fehlerhaften Codes versuchen könnte, ihn zu dereferenzieren, fehlerhafte Ergebnisse zu erhalten, etwas zu blockieren oder, abhängig von Ihrer Umgebung, eine Segmentierungsverletzung zu begehen. Dies gilt insbesondere für malloc()
, weil Sie die Last auf free()
denjenigen legen, der anruft , sich daran zu erinnern, den ursprünglichen Wert zu übergeben, und nicht auf eine geänderte Version, die dazu führt, dass alles zum Teufel losbricht.
Wenn Sie in C 1-basierte Arrays benötigen, können Sie dies sicher tun, indem Sie ein zusätzliches Element zuweisen, das niemals verwendet wird:
double *array_create(size_t size) {
// Wasting one element, so don't allow it to be full-sized
assert(size < SIZE_MAX);
return malloc((size+1) * sizeof(double));
}
inline double array_index(double *array, size_t index) {
assert(array != NULL);
assert(index >= 1); // This is a 1-based array
return array[index];
}
Beachten Sie, dass dies keinen Schutz gegen das Überschreiten der Obergrenze bietet, aber das ist einfach genug zu handhaben.
Nachtrag:
Einige Kapitel und Verse aus dem C99-Entwurf (Entschuldigung, das ist alles, worauf ich verlinken kann):
§6.5.2.1.1 besagt, dass der zweite ("andere") Ausdruck, der mit dem Indexoperator verwendet wird, vom Typ Integer ist. -1
ist eine ganze Zahl, und das macht p[-1]
gültig und macht daher auch den Zeiger &(p[-1])
gültig. Dies bedeutet nicht, dass der Zugriff auf Speicher an dieser Stelle ein definiertes Verhalten erzeugen würde, aber der Zeiger ist immer noch ein gültiger Zeiger.
§6.5.2.2 besagt, dass der Array-Indexoperator das Äquivalent zum Hinzufügen der Elementnummer zum Zeiger p[-1]
ergibt und daher äquivalent zu ist *(p + (-1))
. Immer noch gültig, aber möglicherweise nicht erwünscht.
§6.5.6.8 sagt (Hervorhebung von mir):
Wenn ein Ausdruck mit einem ganzzahligen Typ zu einem Zeiger hinzugefügt oder von diesem subtrahiert wird, hat das Ergebnis den Typ des Zeigeroperanden.
... wenn der Ausdruck P
auf das i
-te Element eines Array-Objekts zeigt, zeigen die Ausdrücke (P)+N
(äquivalent N+(P)
) und (P)-N
(wo N
der Wert ist n
) jeweils auf das i+n
-te und
i−n
-te Element des Array-Objekts, sofern sie vorhanden sind .
Dies bedeutet, dass die Ergebnisse der Zeigerarithmetik auf ein Element in einem Array zeigen müssen. Es heißt nicht, dass die Arithmetik auf einmal durchgeführt werden muss. Deshalb:
double a[20];
// This points to element 9 of a; behavior is defined.
double d = a[-1 + 10];
double *p = a - 1; // This is just a pointer. No dereferencing.
double e = p[0]; // Does not point at any element of a; behavior is undefined.
double f = p[1]; // Points at element 0 of a; behavior is defined.
Empfehle ich, die Dinge so zu machen? Ich nicht, und meine Antwort erklärt warum.