He estado ejecutando el siguiente código a través de diferentes compiladores:
int main()
{
float **a;
void **b;
b = a;
}
Por lo que he podido reunir, novoid **
es un puntero genérico, lo que significa que cualquier conversión desde otro puntero no debe compilarse o al menos lanzar una advertencia. Sin embargo, aquí están mis resultados (todo hecho en Windows):
- gcc : lanza una advertencia, como se esperaba.
- g ++ : arroja un error, como se esperaba (esto se debe a la tipificación menos permisiva de C ++, ¿verdad?)
- MSVC (cl.exe) : no genera ninguna advertencia, incluso con / Wall especificado.
Mi pregunta es: ¿Me estoy perdiendo algo acerca de todo esto y hay alguna razón específica por la cual MSVC no produce una advertencia? MSVC hace producir una advertencia al convertir de void **
a float **
.
Otra cosa a tener en cuenta: si lo reemplazo a = b
con la conversión explícita a = (void **)b
, ninguno de los compiladores arroja una advertencia. Pensé que esto debería ser un reparto no válido, entonces, ¿por qué no habría advertencias?
La razón por la que hago esta pregunta es porque estaba empezando a aprender CUDA y en la Guía de programación oficial ( https://docs.nvidia.com/cuda/cuda-c-programming-guide/index.html#device-memory ) se puede encontrar el siguiente código:
// Allocate vectors in device memory
float* d_A;
cudaMalloc(&d_A, size);
que debe realizar una conversión implícita a void **
for &d_A
, ya que el primer argumento de cudaMalloc
es de tipo void **
. Se puede encontrar un código similar en toda la documentación. ¿Es solo un trabajo descuidado al final de NVIDIA o, de nuevo, me falta algo? Como nvcc
usa MSVC, el código se compila sin advertencias.
void**
no es un puntero genérico. Solovoid*
es(void**)
es un elenco explícito de estilo C. Le dice al compilador que no mire de cerca lo que está haciendo y que confíe en usted. Es una anulación explícita del tipo de sistema de seguridad y se requiere que los compiladores acepten básicamente cualquier tipo de conversión. Se deben evitar los moldes de estilo C, son demasiado poderosos. Use modelos de C ++ como losstatic_cast
que se quejarán si está tratando de hacer algo que no tiene sentido.Respuestas:
Esta asignación sin conversión es una violación de restricción, por lo que un compilador compatible estándar imprimirá una advertencia o error. Sin embargo, MSVC no es totalmente compatible con la implementación de C.
Las conversiones de puntero a través de un reparto están permitidas en algunas situaciones. El estándar C dice lo siguiente en la sección 6.3.2.3p7:
Por lo tanto, puede convertir entre tipos de punteros siempre que no haya problemas de alineación y solo vuelva a convertir (a menos que el objetivo sea a
char *
).Presumiblemente, esta función está desreferenciando el puntero dado y escribiendo la dirección de alguna memoria asignada. Eso significaría que está tratando de escribir a un
float *
como si fuera unvoid *
. Esto no es lo mismo que la conversión típica a / desde avoid *
. Estrictamente hablando, esto parece un comportamiento indefinido, aunque "funciona" porque los procesadores x86 modernos (cuando no están en modo real) usan la misma representación para todos los tipos de puntero.fuente
&d_A
no tienen el tipo requerido?