Ist es undefiniertes Verhalten, Nullzeiger mit dem %p
Konvertierungsspezifizierer zu drucken ?
#include <stdio.h>
int main(void) {
void *p = NULL;
printf("%p", p);
return 0;
}
Die Frage bezieht sich auf den C-Standard und nicht auf C-Implementierungen.
c
language-lawyer
c99
undefined-behavior
c11
Dror K.
quelle
quelle
Antworten:
Dies ist einer dieser seltsamen Eckfälle, in denen wir den Einschränkungen der englischen Sprache und der inkonsistenten Struktur im Standard unterliegen. Im besten Fall kann ich ein überzeugendes Gegenargument vorbringen, da es unmöglich ist , es zu beweisen :) 1
Der Code in der Frage zeigt ein genau definiertes Verhalten.
Da [7.1.4] die Grundlage der Frage ist, beginnen wir dort:
Das ist ungeschickte Sprache. Eine Interpretation ist, dass die Elemente in der Liste für alle Bibliotheksfunktionen UB sind, sofern sie nicht durch die einzelnen Beschreibungen überschrieben werden. Die Liste beginnt jedoch mit "wie", was darauf hinweist, dass sie illustrativ und nicht erschöpfend ist. Beispielsweise wird die korrekte Nullterminierung von Zeichenfolgen nicht erwähnt (kritisch für das Verhalten von z
strcpy
. B. ).Somit ist klar, dass die Absicht / der Umfang von 7.1.4 einfach darin besteht, dass ein "ungültiger Wert" zu UB führt ( sofern nicht anders angegeben ). Wir müssen uns die Beschreibung jeder Funktion ansehen, um festzustellen, was als "ungültiger Wert" gilt.
Beispiel 1 -
strcpy
[7.21.2.3] sagt nur folgendes:
Nullzeiger werden nicht explizit erwähnt, aber auch Nullterminatoren werden nicht erwähnt. Stattdessen schließt man aus "Zeichenfolge, auf die gezeigt wird
s2
", dass die einzigen gültigen Werte Zeichenfolgen sind (dh Zeiger auf nullterminierte Zeichenarrays).In der Tat ist dieses Muster in den einzelnen Beschreibungen zu sehen. Einige andere Beispiele:
Beispiel 2 -
printf
[7.19.6.1] sagt dies über
%p
:Null ist ein gültiger Zeigerwert, und in diesem Abschnitt wird weder ausdrücklich erwähnt, dass Null ein Sonderfall ist, noch dass der Zeiger auf ein Objekt zeigen muss. Somit ist es definiertes Verhalten.
1. Es sei denn, ein Standardautor meldet sich oder wir finden etwas Ähnliches wie ein Begründungsdokument , das die Dinge klarstellt.
quelle
Die kurze Antwort
Ja . Das Drucken von Nullzeigern mit dem
%p
Konvertierungsspezifizierer hat ein undefiniertes Verhalten. Trotzdem ist mir keine vorhandene konforme Implementierung bekannt, die sich schlecht verhalten würde.Die Antwort gilt für alle C-Standards (C89 / C99 / C11).
Die lange Antwort
Der
%p
Konvertierungsspezifizierer erwartet, dass ein Argument vom Typ Zeiger ungültig wird. Die Konvertierung des Zeigers in druckbare Zeichen ist implementierungsdefiniert. Es wird nicht angegeben, dass ein Nullzeiger erwartet wird.Die Einführung in die Standardbibliotheksfunktionen besagt, dass Nullzeiger als Argumente für (Standardbibliotheks-) Funktionen als ungültige Werte betrachtet werden, sofern nicht ausdrücklich anders angegeben.
C99
/.C11
§7.1.4 p1
Beispiele für (Standardbibliotheks-) Funktionen, die Nullzeiger als gültige Argumente erwarten:
fflush()
verwendet einen Nullzeiger zum Löschen "aller zutreffenden Streams".freopen()
verwendet einen Nullzeiger, um die Datei anzuzeigen, die dem Stream "aktuell zugeordnet" ist.snprintf()
Ermöglicht die Übergabe eines Nullzeigers, wenn 'n' Null ist.realloc()
verwendet einen Nullzeiger zum Zuweisen eines neuen Objekts.free()
ermöglicht die Übergabe eines Nullzeigers.strtok()
verwendet einen Nullzeiger für nachfolgende Aufrufe.Wenn wir den Fall für annehmen
snprintf()
, ist es sinnvoll, die Übergabe eines Nullzeigers zuzulassen, wenn 'n' Null ist, aber dies ist nicht der Fall für andere (Standardbibliotheks-) Funktionen, die eine ähnliche Null 'n' zulassen. Zum Beispiel:memcpy()
,memmove()
,strncpy()
,memset()
,memcmp()
.Es wird nicht nur in der Einführung in die Standardbibliothek angegeben, sondern auch noch einmal in der Einführung in diese Funktionen:
C99 §7.21.1 p2
/.C11 §7.24.1 p2
Ist es beabsichtigt?
Ich weiß nicht, ob die UB von
%p
mit einem Nullzeiger tatsächlich beabsichtigt ist, aber da der Standard explizit angibt, dass Nullzeiger als ungültige Werte als Argumente für Standardbibliotheksfunktionen betrachtet werden, werden die Fälle explizit angegeben, in denen eine Null vorliegt Zeiger ist ein gültiges Argument (snprintf, frei, etc.), und dann geht es und einmal wiederholt erneut die Forderung nach den Argumenten gelten sogar in Null ‚n‘ Fälle (memcpy
,memmove
,memset
), dann denke ich , es vernünftig anzunehmen , dass die Das C-Normungskomitee ist nicht allzu besorgt darüber, dass solche Dinge nicht definiert sind.quelle
%p
nicht undefiniertes Verhalten sein sollDie Autoren des C-Standards haben keine Anstrengungen unternommen, um alle Verhaltensanforderungen, die eine Implementierung erfüllen muss, um für einen bestimmten Zweck geeignet zu sein, vollständig aufzulisten. Stattdessen erwarteten sie, dass Leute, die Compiler schreiben, einen gewissen gesunden Menschenverstand üben würden, unabhängig davon, ob der Standard dies erfordert oder nicht.
Die Frage, ob etwas UB aufruft, ist an und für sich selten nützlich. Die wirklichen Fragen von Bedeutung sind:
Sollte jemand, der versucht, einen Qualitätscompiler zu schreiben, dafür sorgen, dass er sich vorhersehbar verhält? Für das beschriebene Szenario lautet die Antwort eindeutig Ja.
Sollten Programmierer das Recht haben zu erwarten, dass sich Qualitätscompiler für alles, was normalen Plattformen ähnelt, vorhersehbar verhalten? In dem beschriebenen Szenario würde ich sagen, dass die Antwort ja ist.
Könnten einige stumpfe Compiler-Autoren die Interpretation des Standards erweitern, um zu rechtfertigen, etwas Seltsames zu tun? Ich würde nicht hoffen, aber es nicht ausschließen.
Sollten Desinfektions-Compiler über das Verhalten kreischen? Das würde vom Paranoia-Level ihrer Benutzer abhängen; Ein Desinfektions-Compiler sollte wahrscheinlich nicht standardmäßig über ein solches Verhalten kreischen, sondern möglicherweise eine Konfigurationsoption für den Fall bereitstellen, dass Programme auf "clevere" / dumme Compiler portiert werden, die sich seltsam verhalten.
Wenn eine vernünftige Interpretation des Standards implizieren würde, dass ein Verhalten definiert ist, aber einige Compiler-Autoren die Interpretation erweitern, um etwas anderes zu rechtfertigen, spielt es dann wirklich eine Rolle, was der Standard sagt?
quelle
printf("%p", (void*) 0)
undefiniertes Verhalten nach dem Standard oder nicht? Tief verschachtelte Funktionsaufrufe sind dafür ebenso relevant wie der Preis für Tee in China. Und ja, UB ist in realen Programmen sehr verbreitet - was ist damit?%p
für jede mögliche Bedeutung der Frage beantwortet.