¿Por qué no puedo convertir ‘char **’ en ‘const char * const *’ en C?

El siguiente fragmento de código (correctamente) da una advertencia en C y un error en C ++ (usando gcc & g ++ respectivamente, probado con las versiones 3.4.5 y 4.2.1; MSVC no parece importar):

char **a; const char** b = a; 

Puedo entender y aceptar esto.
La solución de C ++ a este problema es cambiar b para que sea un const char * const *, que no permite la reasignación de los punteros y evita que eluda la corrección de const ( preguntas frecuentes de C ++ ).

 char **a; const char* const* b = a; 

Sin embargo, en C pura, la versión corregida (usando const char * const *) todavía da una advertencia, y no entiendo por qué. ¿Hay alguna forma de evitar esto sin utilizar un elenco?

Para aclarar:
1) ¿Por qué esto genera una advertencia en C? Debería ser completamente seguro, y el comstackdor de C ++ parece reconocerlo como tal.
2) ¿Cuál es la forma correcta de aceptar este char ** como parámetro mientras digo (y hacer que el comstackdor haga cumplir) que no voy a modificar los caracteres a los que apunta? Por ejemplo, si quisiera escribir una función:

 void f(const char* const* in) { // Only reads the data from in, does not write to it } 

Y quería invocarlo en un char **, ¿cuál sería el tipo correcto para el parámetro?

Editar: Gracias a quienes respondieron, especialmente a los que respondieron la pregunta y / o dieron seguimiento a mis respuestas.

He aceptado la respuesta de que lo que quiero hacer no se puede hacer sin un yeso, independientemente de si es posible o no.

Tuve el mismo problema hace unos años y me molestó muchísimo.

Las reglas en C son más simples (es decir, no enumeran excepciones como convertir char** a const char*const* ). Consecuencia, simplemente no está permitido. Con el estándar C ++, incluyeron más reglas para permitir casos como este.

Al final, es solo un problema en el estándar C. Espero que la próxima norma (o informe técnico) aborde esto.

> Sin embargo, en C pura, esto todavía da una advertencia, y no entiendo por qué

Ya ha identificado el problema: este código no es correcto. “Const correct” significa que, a excepción de los moldes const_cast y C-style que eliminan const, nunca se puede modificar un objeto const a través de esos punteros o referencias const.

El valor de const-correctness – const está ahí, en gran parte, para detectar errores del progtwigdor. Si declaras algo como const, estás diciendo que no crees que deba modificarse, o al menos, aquellos con acceso a la versión const no deberían poder modificarlo. Considerar:

 void foo(const int*); 

Como declarado, foo no tiene permiso para modificar el entero al que apunta su argumento.

Si no está seguro de por qué el código que publicó no es correcto, tenga en cuenta el siguiente código, solo ligeramente diferente del código de HappyDude:

 char *y; char **a = &y; // a points to y const char **b = a; // now b also points to y // const protection has been violated, because: const char x = 42; // x must never be modified *b = &x; // the type of *b is const char *, so set it // with &x which is const char* .. // .. so y is set to &x... oops; *y = 43; // y == &x... so attempting to modify const // variable. oops! undefined behavior! cout << x << endl; 

Los tipos non-const solo pueden convertir a tipos const de maneras particulares para evitar cualquier elusión de 'const' en un tipo de datos sin un molde explícito.

Los objetos inicialmente declarados const son particularmente especiales: el comstackdor puede suponer que nunca cambian. Sin embargo, si a 'b' se le puede asignar el valor de 'a' sin un molde, entonces podría intentar modificar una variable const sin querer. Esto no solo rompería el control que le pediste al comstackdor, sino que te impediría cambiar el valor de las variables, ¡también te permitiría romper las optimizaciones del comstackdor!

En algunos comstackdores, esto imprimirá '42', en algunos '43', y otros, el progtwig se bloqueará.

Editar-agregar:

HappyDude: Tu comentario es perfecto. O bien, el C langauge, o el comstackdor de C que está utilizando, trata const char * const * fundamentalmente de manera diferente que el lenguaje C ++ lo trata. Tal vez considere silenciar la advertencia del comstackdor solo para esta línea fuente.

Editar-eliminar: error eliminado

Para que se considere compatible, el puntero fuente debe estar const en el nivel de direccionamiento indirecto anterior. Entonces, esto te dará la advertencia en GCC:

 char **a; const char* const* b = a; 

Pero esto no:

 const char **a; const char* const* b = a; 

Alternativamente, puedes lanzarlo:

 char **a; const char* const* b = (const char **)a; 

Necesitarías el mismo molde para invocar la función f () como mencionaste. Hasta donde yo sé, no hay forma de hacer una conversión implícita en este caso (excepto en C ++).

Esto es molesto, pero si está dispuesto a agregar otro nivel de redirección, a menudo puede hacer lo siguiente para presionar hacia abajo en el puntero a puntero:

 char c = 'c'; char *p = &c; char **a = &p; const char *bi = *a; const char * const * b = &bi; 

Tiene un significado ligeramente diferente, pero por lo general es viable y no usa un molde.

No puedo obtener un error al convertir implícitamente char ** a const char * const *, al menos en MSVC 14 (VS2k5) y g ++ 3.3.3. GCC 3.3.3 emite una advertencia, que no estoy exactamente seguro de si es correcto.

test.c:

 #include  #include  void foo(const char * const * bar) { printf("bar %s null\n", bar ? "is not" : "is"); } int main(int argc, char **argv) { char **x = NULL; const char* const*y = x; foo(x); foo(y); return 0; } 

Salida con comstackción como código C: cl / TC / W4 / Wp64 test.c

 test.c(8) : warning C4100: 'argv' : unreferenced formal parameter test.c(8) : warning C4100: 'argc' : unreferenced formal parameter 

Salida con comstackción como código C ++: cl / TP / W4 / Wp64 test.c

 test.c(8) : warning C4100: 'argv' : unreferenced formal parameter test.c(8) : warning C4100: 'argc' : unreferenced formal parameter 

Salida con gcc: gcc -Wall test.c

 test2.c: In function `main': test2.c:11: warning: initialization from incompatible pointer type test2.c:12: warning: passing arg 1 of `foo' from incompatible pointer type 

Salida con g ++: g ++ -Wall test.C

ninguna salida

Estoy bastante seguro de que la palabra clave const no implica que los datos no se puedan cambiar / sean constantes, solo que los datos se tratarán como de solo lectura. Considera esto:

 const volatile int *const serial_port = SERIAL_PORT; 

que es un código válido ¿Cómo pueden convivir volátiles y const? Sencillo. volátil le dice al comstackdor que siempre lea la memoria cuando usa los datos y const le dice al comstackdor que cree un error cuando se intenta escribir en la memoria usando el puntero serial_port.

¿Const ayuda al optimizador del comstackdor? No, en absoluto. Debido a que la constness se puede agregar y eliminar de los datos a través del casting, el comstackdor no puede determinar si los datos de const realmente son constantes (dado que el molde se puede hacer en una unidad de traducción diferente). En C ++ también tiene la palabra clave mutable para complicar aún más las cosas.

 char *const p = (char *) 0xb000; //error: p = (char *) 0xc000; char **q = (char **)&p; *q = (char *)0xc000; // p is now 0xc000 

Lo que sucede cuando se intenta escribir en la memoria que realmente es de solo lectura (ROM, por ejemplo) probablemente no está definido en el estándar.