Warum warnt Visual C ++ bei impliziter Umwandlung von const void ** zu void * in C, aber nicht in C ++?

Zusammenfassung

Der C / C ++ - Compiler in Microsoft Visual Studio gibt eine Warnung ausC4090 Wenn ein C-Programm versucht, einen Zeiger in einen Zeiger in zu konvertierenconst Daten (wieconst void ** oderconst char **) bisvoid * (Auch wenn ein solcher Typ eigentlich kein Zeiger aufconst). Noch seltsamer ist, dass derselbe Compiler im Hintergrund identischen Code akzeptiert, der wie C ++ kompiliert wurde.

Was ist der Grund für diese Inkonsistenz und warum hat Visual Studio (im Gegensatz zu anderen Compilern) ein Problem beim impliziten Konvertieren eines Zeigers zu Zeiger zuconst in einvoid *?

Einzelheiten

Ich habe ein C-Programm, in dem C-Strings, die in einer Liste mit variablen Argumenten übergeben wurden, in ein Array eingelesen werden (durch eine Schleife, in derva_arg aufgerufen wird). Da die C-Saiten vom Typ sindconst char *Das Array, das sie verfolgt, ist vom Typconst char **. Dieses Array von Zeigern auf Zeichenfolgen mitconst Inhalt wird selbst dynamisch vergeben (mitcalloc) und ichfree es, bevor die Funktion zurückkehrt (nachdem die C-Strings verarbeitet wurden).

Als ich diesen Code mit kompiliert habecl.exe (in Microsoft Visual C ++), auch bei niedriger Warnstufe, diefree Anruf ausgelöste WarnungC4090. Schon seitfree nimmt einvoid *Dies sagte mir, dass der Compiler nicht mochte, dass ich eine konvertiert hatteconst char ** zu einemvoid *. Ich habe ein einfaches Beispiel erstellt, um dies zu bestätigen, in dem ich versuche, ein zu konvertierenconst void ** zu einemvoid *:

<code>/* cast.c - Can a const void** be cast implicitly to void* ? */

int main(void)
{
    const void **p = 0;
    void *q;
    q = p;

    return 0;
}
</code>

Ich habe es dann wie folgt kompiliert und bestätigt, dass dies die Warnung ausgelöst hat:

<code>>cl cast.c
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86
Copyright (C) Microsoft Corporation.  All rights reserved.

cast.c
cast.c(7) : warning C4090: '=' : different 'const' qualifiers
Microsoft (R) Incremental Linker Version 10.00.40219.01
Copyright (C) Microsoft Corporation.  All rights reserved.

/out:cast.exe
cast.obj
</code>

MicrosoftDokumentation zur Warnung C4090 sagt:

Diese Warnung wird für C-Programme ausgegeben. In einem C ++ - Programm gibt der Compiler einen Fehler aus: C2440.

Dies ist sinnvoll, da C ++ eine stärker typisierte Sprache als C ist und potenziell gefährliche implizite Umwandlungen, die in C zulässig sind, in C ++ nicht zulässig sind. Die Dokumentation von Microsoft lässt es wie eine Warnung erscheinenC2440 wird in C für denselben Code oder eine Teilmenge des Codes ausgelöst, die einen Fehler auslösen würdeC2440 in C ++.

Zumindest dachte ich, bis ich versuchte, mein Testprogramm als C ++ zu kompilieren/TP flag macht das):

<code>>cl /TP cast.c
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86
Copyright (C) Microsoft Corporation.  All rights reserved.

cast.c
Microsoft (R) Incremental Linker Version 10.00.40219.01
Copyright (C) Microsoft Corporation.  All rights reserved.

/out:cast.exe
cast.obj
</code>

Wenn derselbe Code wie in C ++ kompiliert wird, tritt kein Fehler oder keine Warnung auf. Um sicherzugehen, habe ich neu erstellt und den Compiler angewiesen, so aggressiv wie möglich zu warnen:

<code>>cl /TP /Wall cast.c
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86
Copyright (C) Microsoft Corporation.  All rights reserved.

cast.c
Microsoft (R) Incremental Linker Version 10.00.40219.01
Copyright (C) Microsoft Corporation.  All rights reserved.

/out:cast.exe
cast.obj
</code>

Es gelingt stillschweigend.

Diese Builds wurden mit Microsoft Visual C ++ 2010 Express Edition erstelltcl.exe Auf einem Windows 7-Computer treten jedoch auf einem Windows XP-Computer in beiden Versionen von Visual Studio .NET 2003 dieselben Fehler aufcl.exe und Visual C ++ 2005 Express Editioncl.exe. Es scheint also, dass dies auf allen Versionen passiert (obwohl ich nicht auf jeder möglichen Version getestet habe) und kein Problem mit der Einrichtung von Visual Studio auf meinen Computern darstellt.

Derselbe Code wird problemlos in GCC 4.6.1 auf einem Ubuntu 11.10-System kompiliert (Versionszeichenfolgegcc (Ubuntu/Linaro 4.6.1-9ubuntu3) 4.6.1), um so aggressiv wie möglich zu warnen, wie C89, C99 und C ++:

<code>$ gcc -ansi -pedantic -Wall -Wextra -o cast cast.c
cast.c: In function ‘main’:
cast.c:6:11: warning: variable ‘q’ set but not used [-Wunused-but-set-variable]

$ gcc -std=c99 -pedantic -Wall -Wextra -o cast cast.c
cast.c: In function ‘main’:
cast.c:6:11: warning: variable ‘q’ set but not used [-Wunused-but-set-variable]

$ g++ -x c++ -ansi -pedantic -Wall -Wextra -o cast cast.c
cast.c: In function ‘int main()’:
cast.c:6:11: warning: variable ‘q’ set but not used [-Wunused-but-set-variable]
</code>

Es warnt dasq wird nach der Zuweisung nie gelesen, aber diese Warnung ist sinnvoll und hat nichts damit zu tun.

Abgesehen davon, dass in GCC keine Warnung ausgelöst wird, wenn alle Warnungen aktiviert sind, und dass in C ++ weder in GCC noch in MSVC eine Warnung ausgelöst wird, scheint mir die Konvertierung von Zeiger zu Zeiger zu const zuvoid * sollte überhaupt nicht als problem angesehen werden, denn dabeivoid * ist ein Hinweis aufconstein Zeiger auf einen Zeiger auf const ist auch ein Zeiger aufconst.

In meinem Code der realen Welt (nicht im Beispiel) kann ich dies mit einem#pragma Direktive oder eine explizite Besetzung oder durch Kompilieren als C ++ (heh heh), oder ich kann es einfach ignorieren. Aber ich mache lieber nichts von alledem, zumindest nicht bevor ich verstanden habe, warum das passiert. (Und warum passiert das nicht in C ++!)

Eine mögliche, teilweise Erklärung fällt mir ein: Im Gegensatz zu C ++ erlaubt C das implizite Casting vonvoid * auf einen beliebigen Zeiger auf einen Datentyp. Also könnte ich einen Zeiger implizit konvertieren lassenconst char ** zuvoid *und dann implizit konvertiert vonvoid * zuchar **Dadurch ist es möglich, konstante Daten, auf die es verweist, ohne Umwandlung zu ändern. Das wäre schlimm. Aber ich sehe nicht, wie schlimmer das ist als alle möglichen anderen Dinge, die die schwächere Typensicherheit von C zulässt.

Ich denke, diese Warnung ist sinnvoll, wenn man die Wahl hat, nicht zu warnen, wenn einvoid Zeigertyp wird in konvertiertvoid *:

<code>/* cast.c - Can a const void** be cast implicitly to void* ? */

int main(void)
{
    const void **p = 0;
    void *q;
    q = p;

    return 0;
}</code>
<code>>cl /Wall voidcast.c
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86
Copyright (C) Microsoft Corporation.  All rights reserved.

voidcast.c
Microsoft (R) Incremental Linker Version 10.00.40219.01
Copyright (C) Microsoft Corporation.  All rights reserved.

/out:voidcast.exe
voidcast.obj
</code>

Und doch, wenn das beabsichtigt ist, dann:

Warum gibt die Microsoft-Dokumentation an, dass Code, der diese Warnung in C erzeugt, einen Fehler in C ++ erzeugt?

Abgesehen davon, dass die Warnung ignoriert oder unterdrückt wird, gibt es keine vernünftige Alternative, wenn dies erforderlich istfree ein nicht-const Zeiger auf nichtconst Zeiger aufconst Daten (wie in meiner realen Situation)? Wenn so etwas in C ++ passiert ist, könnte ich die in der Variablenargumentliste übergebenen Zeichenfolgen in einem übergeordneten STL-Container anstelle eines Arrays speichern. Für ein C-Programm ohne Zugriff auf die C ++ - STL, das ansonsten keine übergeordneten Auflistungen verwendet, ist dies keine vernünftige Option.

Einige Programmierer arbeiten nach einer Unternehmens- / Organisationsrichtlinie, bei der Warnungen als Fehler behandelt werden.C4090 ist auch mit aktiviert/W1. Die Leute müssen das schon einmal erlebt haben. Was machen diese Programmierer?

Antworten auf die Frage(2)

Ihre Antwort auf die Frage