Pregunta

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

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

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

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

Sin embargo, en C puro, la versión corregida (usando const char * const *) todavía da una advertencia y no entiendo por qué.¿Hay alguna manera de solucionar esto sin usar un yeso?

Para aclarar:
1) ¿Por qué esto genera una advertencia en C?Debería ser completamente seguro y el compilador de C++ parece reconocerlo como tal.
2) ¿Cuál es la forma correcta de aceptar este char** como parámetro y al mismo tiempo decir (y hacer que el compilador haga cumplir) que no 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 carácter**, ¿cuál sería el tipo correcto para el parámetro?

Editar:Gracias a quienes respondieron, particularmente a quienes abordaron 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 debería ser posible o no.

¿Fue útil?

Solución

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

Las reglas en C están expresadas de manera más simple (es decir,no enumeran excepciones como la conversión char** a const char*const*).En consecuencia, simplemente no está permitido.Con el estándar C++, incluyeron más reglas para permitir casos como este.

Al final, es sólo un problema en el estándar C.Espero que el próximo estándar (o informe técnico) aborde esto.

Otros consejos

Para que se considere compatible, el puntero de la fuente debe estar constante en el nivel de dirección indirecta inmediatamente anterior.Entonces, esto le dará la advertencia en GCC:

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

Pero esto no lo hará:

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

Alternativamente, puedes transmitirlo:

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

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

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

Ya ha identificado el problema: este código no es correcto de manera constante."Const correcto" significa que, a excepción de const_cast y conversiones de estilo C que eliminan const, nunca se puede modificar un objeto constante a través de esos punteros o referencias constantes.

El valor de const-correctness - const está ahí, en gran parte, para detectar errores del programador.Si declara algo como constante, está indicando que no cree que deba modificarse, o al menos, aquellos con acceso solo a la versión constante no deberían poder modificarlo.Considerar:

void foo(const int*);

Como se declaró, foo no tiene permiso para modificar el número entero señalado por su argumento.

Si no está seguro de por qué el código que publicó no es correcto, considere el siguiente código, que es 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 no constantes solo se pueden convertir a tipos constantes de maneras particulares para evitar cualquier elusión de 'const' en un tipo de datos sin una conversión explícita.

Los objetos inicialmente declarados const son particularmente especiales: el compilador puede asumir que nunca cambian.Sin embargo, si a 'b' se le puede asignar el valor de 'a' sin una conversión, entonces, sin darse cuenta, podría intentar modificar una variable constante.Esto no solo rompería la verificación que le pidió al compilador que hiciera, para no permitirle cambiar el valor de esa variable, ¡sino que también le permitiría romper las optimizaciones del compilador!

En algunos compiladores, esto imprimirá '42', en algunos '43' y en otros, el programa fallará.

Editar-añadir:

FelizDude:Tu comentario es acertado.Ya sea el lenguaje C, o el compilador de C que estás usando, trata const char * const * fundamentalmente diferente a como lo trata el lenguaje C++.Quizás considere silenciar la advertencia del compilador solo para esta línea fuente.

Editar Borrar: error tipográfico eliminado

Esto es molesto, pero si está dispuesto a agregar otro nivel de redirección, a menudo puede hacer lo siguiente para presionar 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 generalmente es viable y no utiliza yeso.

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 seguro de si es correcta.

prueba.c:

#include <stdlib.h> 
#include <stdio.h>
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 compilación como código C:cl /TC /W4 /Wp64 prueba.c

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

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

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

Salida con gcc:gcc -Prueba de pared.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++ -Prueba de pared.C

ninguna salida

Estoy bastante seguro de que la palabra clave const no implica que los datos no se puedan cambiar o 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 coexistir volátiles y constantes?Simple.volatile le dice al compilador que siempre lea la memoria cuando usa los datos y const le dice al compilador que cree un error cuando se intenta escribir en la memoria usando el puntero serial_port.

¿Const ayuda al optimizador del compilador?No.De nada.Debido a que se puede agregar y eliminar constancia de los datos mediante la conversión, el compilador no puede determinar si los datos constantes realmente son constantes (ya que la conversión se podría realizar en una unidad de traducción diferente).En C++ también tienes 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 una memoria que realmente es de sólo lectura (ROM, por ejemplo) probablemente no esté definido en absoluto en el estándar.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top