Der C-Standard stellt in diesem Fall keine Anforderungen an das Verhalten, aber viele Implementierungen spezifizieren das Verhalten der Zeigerarithmetik in vielen Fällen über die vom Standard geforderten Mindestanforderungen hinaus, einschließlich dieses.
Bei jeder konformen C-Implementierung und bei fast allen (wenn nicht allen) Implementierungen von C-ähnlichen Dialekten gelten die folgenden Garantien für jeden Zeiger p
, der entweder ein Objekt identifiziert *p
oder *(p-1)
identifiziert:
- Für jeden ganzzahligen Wert
z
, der gleich Null ist, sind die Zeigerwerte (p+z)
und (p-z)
in jeder Hinsicht gleichwertig p
, mit der Ausnahme, dass sie nur dann konstant sind, wenn beide p
und z
konstant sind.
- Für alle,
q
die äquivalent sind p
, ergeben die Ausdrücke p-q
und q-p
beide Null.
Wenn solche Garantien für alle Zeigerwerte, einschließlich Null, gelten, müssen möglicherweise keine Nullprüfungen im Benutzercode durchgeführt werden. Darüber hinaus wäre es auf den meisten Plattformen einfacher und billiger, Code zu generieren, der solche Garantien für alle Zeigerwerte ohne Rücksicht darauf, ob sie null sind, einhält, als speziell Nullen zu behandeln. Einige Plattformen können jedoch bei Versuchen, eine Zeigerarithmetik mit Nullzeigern durchzuführen, abfangen, selbst wenn Null addiert oder subtrahiert wird. Auf solchen Plattformen würde die Anzahl der vom Compiler generierten Nullprüfungen, die zu Zeigeroperationen hinzugefügt werden müssten, um die Garantie aufrechtzuerhalten, in vielen Fällen die Anzahl der vom Benutzer generierten Nullprüfungen, die als Ergebnis weggelassen werden könnten, erheblich überschreiten.
Wenn es eine Implementierung gäbe, bei der die Kosten für die Aufrechterhaltung der Garantien hoch wären, aber nur wenige, wenn Programme davon profitieren würden, wäre es sinnvoll, zuzulassen, dass "Null + Null" -Berechnungen abgefangen werden und dieser Benutzercode für erforderlich ist Eine solche Implementierung umfasst die manuelle Nullprüfung, die die Garantien möglicherweise unnötig gemacht haben. Es wurde nicht erwartet, dass eine solche Wertberichtigung die anderen 99,44% der Implementierungen betrifft, bei denen der Wert der Aufrechterhaltung der Garantien die Kosten übersteigen würde. Solche Implementierungen sollten solche Garantien aufrechterhalten, aber ihre Autoren sollten die Autoren des Standards nicht brauchen, um ihnen dies mitzuteilen.
Die Autoren von C ++ haben entschieden, dass konforme Implementierungen die oben genannten Garantien um jeden Preis einhalten müssen, selbst auf Plattformen, auf denen sie die Leistung der Zeigerarithmetik erheblich beeinträchtigen könnten. Sie urteilten, dass der Wert der Garantien selbst auf Plattformen, deren Aufrechterhaltung teuer wäre, die Kosten übersteigen würde. Eine solche Einstellung könnte durch den Wunsch beeinflusst worden sein, C ++ als eine höhere Sprache als C zu behandeln. Von AC-Programmierern könnte erwartet werden, dass sie wissen, wann eine bestimmte Zielplattform Fälle wie (null + null) auf ungewöhnliche Weise behandeln würde, aber C ++ - Programmierer Es wurde nicht erwartet, dass sie sich mit solchen Dingen befassen. Die Gewährleistung eines konsistenten Verhaltensmodells wurde daher als die Kosten wert beurteilt.
Fragen, was "definiert" ist, haben heutzutage natürlich selten etwas damit zu tun, welche Verhaltensweisen eine Plattform unterstützen kann. Stattdessen ist es jetzt für Compiler in Mode, im Namen der "Optimierung" zu verlangen, dass Programmierer manuell Code schreiben, um Eckfälle zu behandeln, die Plattformen zuvor korrekt behandelt hätten. Wenn beispielsweise Code, der n
Zeichen ab der Adresse ausgeben p
soll, wie folgt geschrieben wird:
void out_characters(unsigned char *p, int n)
{
unsigned char *end = p+n;
while(p < end)
out_byte(*p++);
}
ältere Compiler würden Code generieren, der zuverlässig nichts ohne Nebenwirkungen ausgeben würde, wenn p == NULL und n == 0, ohne dass ein Sonderfall n == 0 erforderlich wäre. Bei neueren Compilern müsste man jedoch zusätzlichen Code hinzufügen:
void out_characters(unsigned char *p, int n)
{
if (n)
{
unsigned char *end = p+n;
while(p < end)
out_byte(*p++);
}
}
was ein Optimierer möglicherweise loswerden kann oder nicht. Wenn der zusätzliche Code nicht eingeschlossen wird, stellen einige Compiler möglicherweise fest, dass, da p "möglicherweise nicht null sein kann", alle nachfolgenden Nullzeigerprüfungen weggelassen werden können, wodurch der Code an einer Stelle unterbrochen wird, die nicht mit dem tatsächlichen "Problem" zusammenhängt.