todas - ¿Por qué Visual C++ advierte sobre la conversión implícita de const void** a void*en C, pero no en C++?
void c++ ejemplo (2)
Al igual que 6502 dice que esto parece ser un error en el compilador. Sin embargo, también pregunta qué debe hacer al respecto.
Mi respuesta es que debe agregar un reparto explícito a la llamada gratuita, y luego un comentario explicando por qué es necesario. Se producen errores en el compilador, utilice la solución más fácil y agregue una nota tal que se pueda probar si el error se resolvió más tarde.
Puntos extra por informar el error al proveedor del compilador.
En cuanto a 1. Parece que se refiere a convertir implícitamente una const T *
en un void *
, que debería ser una advertencia en C y un error en C ++.
Resumen
El compilador C / C ++ en Microsoft Visual Studio da advertencia C4090 cuando un programa C intenta convertir un puntero a puntero a datos const
(como const void **
o const char **
) a void *
(aunque ese tipo no es en realidad un puntero a const
). Aún más extraño, el mismo compilador acepta silenciosamente código idéntico compilado como C ++.
¿Cuál es el motivo de esta incoherencia y por qué Visual Studio (a diferencia de otros compiladores) tiene un problema al convertir implícitamente un puntero a puntero para formar un void *
?
Detalles
Tengo un programa C en el que las cadenas C pasadas en una lista de argumentos variables se leen en una matriz (mediante un ciclo en el que se invoca va_arg
). Como las C-series son de tipo const char *
, la matriz que las sigue es de tipo const char **
. Esta matriz de punteros a cadenas con contenido const
está asignada dinámicamente (con calloc
) y la calloc
antes de que la función regrese (después de que se hayan procesado las cadenas C).
Cuando compilé este código con cl.exe
(en Microsoft Visual C ++), incluso con un nivel de advertencia bajo, la llamada free
activó la advertencia C4090 . Como free
toma un void *
, esto me dice que al compilador no le gustó que haya convertido un const char **
a un void *
. Creé un ejemplo simple para confirmar esto, en el cual intento convertir un const void **
a un void *
:
/* cast.c - Can a const void** be cast implicitly to void* ? */
int main(void)
{
const void **p = 0;
void *q;
q = p;
return 0;
}
Luego lo compilé de la siguiente manera, confirmando que esto fue lo que desencadenó la advertencia:
>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
La documentación de Microsoft sobre la advertencia C4090 dice:
Esta advertencia se emite para los programas C. En un programa C ++, el compilador emite un error: C2440.
Eso tiene sentido, ya que C ++ es un lenguaje más fuertemente tipado que C, y los moldes implícitos potencialmente peligrosos permitidos en C no se permiten en C ++. La documentación de Microsoft hace que parezca que la advertencia C2440 se activa en C para el mismo código, o un subconjunto del código, que desencadenaría el error C2440 en C ++.
O eso creía, hasta que intenté compilar mi programa de prueba como C ++ (la bandera /TP
hace esto):
>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
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 me advirtiera de la forma más agresiva posible:
>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
Lo logra silenciosamente.
Esas compilaciones se realizaron con cl.exe
Microsoft Visual C ++ 2010 Express Edition en una máquina con Windows 7, pero los mismos errores ocurren en una máquina con Windows XP, en cl.exe
Visual Studio .NET 2003 y en cl.exe
Visual C ++ 2005 Express Edition cl.exe
. 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 (versión de cadena gcc (Ubuntu/Linaro 4.6.1-9ubuntu3) 4.6.1
), configurado para advertir de la forma más agresiva posible, como C89, C99 y C ++:
$ 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]
Advierte que q
nunca se lee después de ser asignado, 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 const a void *
no debe considerarse un problema en absoluto, porque mientras que void *
es un puntero a non- const
, un puntero a un puntero a const también es un puntero a non- const
.
En mi código del mundo real (no en el ejemplo), puedo silenciar esto con una directiva #pragma
, o un molde explícito, o compilando como C ++ (heh heh), o simplemente puedo ignorarlo. Pero prefiero no hacer ninguna de esas cosas, al menos no antes de entender 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 de void *
a cualquier tipo de puntero a datos. De modo que podría convertir un puntero implícitamente de const char **
a void *
, y luego convertirlo implícitamente de void *
a char **
, lo que posibilitaría modificar los datos constantes a los que señala punteros, sin un molde. Eso sería malo. Pero no veo cómo eso sea peor que todo tipo de otras cosas que están permitidas por la seguridad de tipo más débil de C.
Supongo que tal vez esta advertencia tenga sentido dada la opción de no advertir cuando un tipo de puntero no void
se convierte a void *
:
/* cast.c - Can a const void** be cast implicitly to void* ? */
int main(void)
{
const void **p = 0;
void *q;
q = p;
return 0;
}
>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
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 debe
free
un puntero noconst
puntero noconst
paraconst
data (como en mi situación del mundo real)? Si algo así 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 C sin acceso a C ++ STL y que no utiliza colecciones de alto nivel, ese tipo de cosas no es una opción razonable.Algunos programadores trabajan bajo una política corporativa / organizacional de tratar las advertencias como errores. C4090 está habilitado incluso con
/W1
. La gente debe haber encontrado esto antes. ¿Qué hacen esos programadores?
Aparentemente, esto es simplemente un error en VC ++.
Si declara const char **x;
el resultado es un puntero a un puntero "de solo lectura" para los caracteres, y no es un puntero de "solo lectura" (uso el término "solo lectura" porque el término constness empuja el concepto incorrecto de que el carácter es apuntado a es constante mientras que esto es falso en general ... const
con referencias y punteros es una propiedad de la referencia o del puntero y no dice nada acerca de la concordancia de los datos apuntados o referenciados).
Cualquier puntero de lectura / escritura se puede convertir en void *
y VC ++ no tiene una razón real para emitir una advertencia al compilar ese código, ni en C
ni en el modo C++
.
Tenga en cuenta que esto no es formalmente un problema porque la norma no ordena qué advertencias deben o no emitirse y, por lo tanto, un compilador puede emitir advertencias libremente para que el código perfectamente válido siga siendo compatible. VC ++ en realidad emite una plétora de esas advertencias para un código C ++ válido ...