Verändertes Objekt - keine Warnung? Auch in diesem Fall ist es UB?

9

Warum gibt es im folgenden Code keine Warnung?

%Vor%

So habe ich es genannt:

%Vor%

Auch ich habe folgende Frage: Ist das, was ich undefiniertes Verhalten getan habe? Hinweis:

  • Ich denke, es ist in Ordnung, students zu übergeben, weil die Funktion const Student* erwartet und ich nicht const weitergeben kann. Richtig?
  • Aber wenn ich dieses Objekt in fscanf ändere, habe ich UB ausgelöst? Oder hängt es davon ab, ob students an erster Stelle (außerhalb dieser Funktion) als const deklariert wurde?
Grzegorz Szpetkowski 21.08.2015, 07:05
quelle

4 Antworten

2

Es gibt kein undefiniertes Verhalten in Ihrem Code.

Was der Anrufer übergeben hat, ist ein veränderbares Objekt. Also ist es in Ordnung, es entweder direkt oder durch explizite Umwandlung zu modifizieren:

%Vor%

ist in Ordnung (wahrscheinlich unklug, aber legal), solange das an func() übergebene Objekt veränderbar ist.

Aber wenn das übergebene Objekt const qualifiziert war, wäre es ein undefiniertes Verhalten gewesen. In deinem Fall ist es also nicht undefiniert.

Das Qualifikationsmerkmal const ist nur ein Vertrag, den die Funktion nicht ändern soll dest . Es hat keinen Einfluss auf die tatsächliche Änderbarkeit eines Objekts. Also hängt die Änderung eines const-qualified-Aufrufs von UB oder nicht davon ab, ob das übergebene Objekt über ein solches Qualifikationsmerkmal verfügt.

Was die Warnung betrifft, warnt GCC (5.1.1) vor:

%Vor%

mit:

%Vor%

Wahrscheinlich erkennt VS nicht, dass fscanf() das Objekt modifiziert. Aber der C-Standard sagt nur, dass es undefiniert ist, wenn Sie ein Const-qualifiziertes Objekt ändern:

(C11 Entwurf, 6.7.3, 6 Typqualifikatoren)

  

Wenn versucht wird, ein mit a definiertes Objekt zu ändern   const-qualified type durch Verwendung eines lvalue mit nicht-const-qualified   Typ, das Verhalten ist nicht definiert.

Der C-Standard erfordert keine Diagnose, wenn der Code undefiniertes Verhalten aufruft. Im Allgemeinen sind Sie allein, wenn Ihr Code UB verursacht und ein Compiler Ihnen möglicherweise nicht in allen Fällen helfen kann.

    
P.P. 21.08.2015 08:07
quelle
1

IMHO ist es formale UB.

Die Funktion deserialize_students deklariert einen const Student *dest -Parameter. Von da an ist dest[i].id ein Const int und &dest[i].id ist ein const int * .

Sie erhalten keine Warnung, weil fscanf eine Variadic-Funktion ist und der Compiler keine Constance steuern kann (auch wenn gcc dies als Sonderfall verwendet), aber wenn Sie eine temporäre Zwischenvariable verwenden, erhalten Sie einen Fehler:

%Vor%

Sie übergeben also einen const-Zeiger an eine Funktion, die den Pointee ( fscanf ) ändert, und IMHO genügt es, ihn als formale UB zu qualifizieren. Man könnte sich eine Implementierung eines Compilers vorstellen, der einen Zeiger auf eine Kopie des Wertes an fscanf übergibt, da Sie versprochen haben, dass es const ist. Oder das hätte einen Zeiger auf eine Kopie des students -Arrays übergeben, da deserialize_students seinen Parameter als const deklariert.

Gibt es ein echtes Risiko? IMHO nein, weil, wenn Sie eine modifizierbare dest an die Funktion übergeben, normale Compiler-Implementierung wird nur die ursprüngliche Adresse übergeben, und wird das gleiche übergeben die Adresse von dest[i].id zu fscanf . Die ganze Sache wird damit enden, das ursprüngliche Array korrekt zu modifizieren. Aber wie bereits von Peter gesagt, wie alle Fälle von undefiniertem Verhalten, funktioniert ein mögliches Ergebnis wie erwartet , so dass die Arbeit mit allen getesteten Compilern keine Versicherung für nicht undefiniertes Verhalten ist.

NB: Da das ursprüngliche Array nicht const ist, wurde das Objekt nicht als definiert als const, ich bin also nicht sicher, ob 6.7.3, § 6 hier gilt. Aber 6.7.3 § 9 sagt immer noch: Damit zwei qualifizierte Typen kompatibel sind, müssen beide die identisch qualifizierte Version eines kompatiblen Typs haben also int * (benötigt von fscanf) und const int * ( tatsächlich bestanden) sind nicht.

    
Serge Ballesta 21.08.2015 09:08
quelle
0

Der Standard ist in dieser Angelegenheit unklar. Ab C11 7.21.6.2/12:

  

Die Conversion-Spezifizierer und ihre Bedeutungen sind:

     

d Entspricht einer optional vorzeichenbehafteten dezimalen Ganzzahl, deren Format dem für die Betrefffolge der strtol-Funktion erwarteten Wert mit dem Wert 10 entspricht   für das Basisargument. Das entsprechende Argument soll ein Zeiger auf sein   Ganzzahl mit Vorzeichen .

Nun könnten Sie argumentieren, dass, weil es nicht "einen Zeiger auf nicht-const signed integer" sagt, es bedeutet, dass int * und const int * beide OK sind. Oder Sie könnten argumentieren, dass sie nur Zeiger auf nicht-const signed Integer bedeutet.

Beachten Sie, dass die Definition von vararg passing (7.16) explizit besagt, dass es nicht definiert ist, va_arg(int *) zum Abrufen eines Arguments vom Typ const int * zu verwenden. Die Funktion fscanf ist jedoch NICHT so angegeben, dass sie sich so verhält, als wäre va_arg verwendet worden. Das ist also nicht direkt relevant.

Meinung folgt: Die gesamte Spezifikation von printf und scanf ist ein schlampiges Durcheinander, das unter der üblichen Qualität der Spezifikation liegt, die im Rest des Standards gefunden wird. Es gibt viele andere solche Beispiele, z.B. Gemäß dem genauen Wortlaut des Standards ist printf("%lx", 1L); ein undefiniertes Verhalten, während printf("%lx", -1L); kein undefiniertes Verhalten ist.

In der Realität haben die Implementierungen ihre eigenen Entscheidungen darüber getroffen, was zu tun ist, und niemand möchte die Standardformulierung mit einem Barge-Pol anfassen. Also würde ich sagen, dass es auf diese Frage keine richtige Antwort gibt.

    
M.M 21.11.2017 06:19
quelle
-1

Es ist undefiniertes Verhalten.

Wie Michael Walz in den Kommentaren erwähnt hat, ist der Grund, warum der Compiler das akzeptiert hat, dass fscanf() variadisch ist, so dass bestimmte Compiler-Prüfungen - einschließlich der Konstanz - gelockert werden.

Wie bei allen Instanzen von undefiniertem Verhalten funktioniert ein mögliches Ergebnis wie erwartet - mit dem ausgewählten Compiler und der Bibliothek, aber nicht unbedingt mit einem anderen. Daher ist das Testen kein zuverlässiges Mittel, um das Vorhandensein von undefiniertem Verhalten zu erkennen - das Bestehen eines Tests ist ebenfalls erlaubt.

    
Peter 21.08.2015 07:56
quelle