¿Por qué Visual C ++ advierte sobre la conversión implícita de const void ** a void * en C, pero no en C ++?

Resumen

El compilador C / C ++ en Microsoft Visual Studio da una advertenciaC4090 cuando un programa de C intenta convertir un puntero a puntero aconst datos (comoconst void ** oconst char **) avoid * (aunque tal tipo no sea en realidad un puntero aconst). Aún más extraño, el mismo compilador acepta silenciosamente código idéntico compilado como C ++.

¿Cuál es la razón de esta inconsistencia y por qué Visual Studio (a diferencia de otros compiladores) tiene un problema con la conversión implícita de un puntero a otro?const en unavoid *?

Detalles

Tengo un programa en C en el que las cadenas en C que se pasan en una lista de argumentos variables se leen en una matriz (por un bucle en el queva_arg se invoca). Dado que las cadenas en C son de tipoconst char *, la matriz que realiza un seguimiento de ellos es de tipoconst char **. Esta matriz de punteros a cadenas conconst El contenido se asigna dinámicamente (concalloc) y yofree antes de que la función regrese (después de que las cadenas C hayan sido procesadas).

Cuando compilé este código concl.exe (en Microsoft Visual C ++), incluso con un nivel de advertencia bajo, elfree advertencia activada por llamadaC4090. Ya quefree toma unavoid *, esto me dijo que al compilador no le gustaba que hubiera convertido unconst char ** a unvoid *. Creé un ejemplo simple para confirmar esto, en el cual intento convertir unconst void ** a unvoid *:

<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>

Luego lo compilé de la siguiente manera, confirmando que esto fue lo que provocó la advertencia:

<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>

De Microsoftdocumentación sobre la advertencia C4090 dice:

Esta advertencia se emite para los programas de C En un programa de C ++, el compilador emite un error: C2440.

Eso tiene sentido, ya que C ++ es un lenguaje más fuertemente tipado que C, y las conversiones implícitas potencialmente peligrosas permitidas en C no están permitidas en C ++. La documentación de Microsoft hace que parezca una advertencia.C2440 se dispara en C para el mismo código, o un subconjunto del código, que generaría un errorC2440 en C ++.

O así lo pensé, hasta que intenté compilar mi programa de prueba como C ++ (el/TP bandera hace esto):

<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>

Cuando el mismo código se compila como C ++, no se produce ningún error o advertencia. Para estar seguro, reconstruí, y le dije al compilador que avisara lo más agresivamente posible:

<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>

Tiene éxito en silencio.

Esas compilaciones fueron con Microsoft Visual C ++ 2010 Express Editioncl.exe en una máquina con Windows 7, pero los mismos errores ocurren en una máquina con Windows XP, tanto en Visual Studio .NET 2003cl.exe y Visual C ++ 2005 Express Edition'scl.exe. Así que parece que esto sucede en todas las versiones (aunque no lo he probado en todas las versiones posibles) y no es un problema con la forma en que Visual Studio está configurado en mis máquinas.

El mismo código se compila sin problemas en GCC 4.6.1 en un sistema Ubuntu 11.10 (cadena de versióngcc (Ubuntu/Linaro 4.6.1-9ubuntu3) 4.6.1), configurado para avisar de la manera más agresiva posible, como C89, C99 y 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>

Advierte queq nunca se lee después de asignarse, pero esa advertencia tiene sentido y no está relacionada.

Además de no activar una advertencia en GCC con todas las advertencias habilitadas, y no activar una advertencia en C ++ en GCC o MSVC, me parece que la conversión de puntero a puntero a constante avoid * No debería considerarse un problema en absoluto, porque mientrasvoid * es un puntero a no-const, un puntero a un puntero a const es también un puntero a no -const.

En mi código del mundo real (no en el ejemplo), puedo silenciar esto con un#pragma directiva, o un reparto explícito, o compilando como C ++ (je je je), o simplemente puedo ignorarlo. Pero prefiero no hacer ninguna de esas cosas, al menos no antes de que entienda por qué sucede esto. (¡Y por qué no sucede en C ++!)

Se me ocurre una posible explicación parcial: a diferencia de C ++, C permite la conversión implícita desdevoid * a cualquier tipo de puntero a datos. Así que podría tener un puntero convertido implícitamente deconst char ** avoid *, y luego se convierte implícitamente devoid * achar **De este modo, es posible modificar los datos constantes a los que apunta a los punteros, sin una conversión. Eso sería malo. Pero no veo que eso sea peor que todo tipo de otras cosas permitidas por la seguridad de tipos más débil de C.

Supongo que tal vez esta advertencia tenga sentido dada la opción de no advertir cuando unvoid el tipo de puntero se convierte avoid *:

<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>

Y sin embargo, si eso es intencional, entonces:

¿Por qué la documentación de Microsoft indica que el código que produce esta advertencia en C produce un error en C ++?

Además de ignorar o suprimir la advertencia, ¿hay alguna alternativa razonable, cuando uno debefree un no-const puntero a no-const puntero aconst datos (como en mi situación del mundo real)? Si algo como esto sucediera en C ++, podría almacenar las cadenas pasadas en la lista de argumentos variables en algún contenedor STL de alto nivel en lugar de una matriz. Para un programa de C sin acceso a la STL de C ++ y que de otra forma no utiliza colecciones de alto nivel, ese tipo de cosas no es una opción razonable.

Algunos programadores trabajan bajo una política corporativa / organizativa de tratar las advertencias como errores.C4090 está habilitado incluso con/W1. La gente debe haber encontrado esto antes. ¿Qué hacen esos programadores?

Respuestas a la pregunta(2)

Su respuesta a la pregunta