Pergunta

O trecho de código a seguir (corretamente) fornece um aviso em C e um erro em C++ (usando gcc & g++ respectivamente, testado com versões 3.4.5 e 4.2.1;MSVC não parece se importar):

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

Posso entender e aceitar isso.
A solução C++ para esse problema é alterar b para ser um const char * const *, o que não permite a reatribuição dos ponteiros e evita que você contorne a correção const (Perguntas frequentes sobre C++).

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

Porém, em C puro, a versão corrigida (usando const char * const *) ainda dá um aviso, e não entendo o porquê.Existe uma maneira de contornar isso sem usar gesso?

Esclarecer:
1) Por que isso gera um aviso em C?Deve ser totalmente seguro para const e o compilador C++ parece reconhecê-lo como tal.
2) Qual é a maneira correta de aceitar esse char** como parâmetro enquanto diz (e faz com que o compilador aplique) que não modificarei os caracteres para os quais ele aponta?Por exemplo, se eu quisesse escrever uma função:

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

E eu queria invocá-lo em um char**, qual seria o tipo correto para o parâmetro?

Editar:Obrigado a todos aqueles que responderam, especialmente aqueles que abordaram a questão e/ou acompanharam minhas respostas.

Aceitei a resposta de que o que eu quero fazer não pode ser feito sem gesso, independentemente de ser ou não possível.

Foi útil?

Solução

Eu tive esse mesmo problema há alguns anos e isso me irritou profundamente.

As regras em C são declaradas de forma mais simples (ou seja,eles não listam exceções como conversão char** para const char*const*).Conseqüentemente, simplesmente não é permitido.Com o padrão C++, eles incluíram mais regras para permitir casos como este.

No final, é apenas um problema no padrão C.Espero que a próxima norma (ou relatório técnico) resolva isso.

Outras dicas

Para ser considerado compatível, o ponteiro da fonte deve estar constante no nível de indireção imediatamente anterior.Então, isso lhe dará o aviso no GCC:

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

Mas isso não vai:

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

Alternativamente, você pode lançá-lo:

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

Você precisaria da mesma conversão para invocar a função f() conforme mencionado.Pelo que eu sei, não há como fazer uma conversão implícita neste caso (exceto em C++).

> Porém, em C puro, isso ainda dá um aviso, e não entendo porque

Você já identificou o problema - este código não está correto."Const correto" significa que, exceto para const_cast e conversões de estilo C que removem const, você nunca pode modificar um objeto const por meio desses ponteiros ou referências const.

O valor da correção const - const existe, em grande parte, para detectar erros do programador.Se você declarar algo como const, estará afirmando que não acha que deveria ser modificado - ou pelo menos aqueles com acesso apenas à versão const não deveriam ser capazes de modificá-lo.Considerar:

void foo(const int*);

Como declarado, foo não tem permissão para modificar o número inteiro apontado por seu argumento.

Se você não tiver certeza do motivo pelo qual o código postado não está correto, considere o código a seguir, apenas um pouco diferente do código do 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;

Os tipos não-const só podem ser convertidos em tipos const de maneiras específicas para evitar qualquer contorno de 'const' em um tipo de dados sem uma conversão explícita.

Os objetos inicialmente declarados como const são particularmente especiais - o compilador pode assumir que eles nunca mudam.No entanto, se 'b' puder receber o valor de 'a' sem conversão, você poderá tentar modificar inadvertidamente uma variável const.Isso não apenas quebraria a verificação que você pediu ao compilador, para impedir que você alterasse o valor das variáveis ​​- também permitiria que você quebrasse as otimizações do compilador!

Em alguns compiladores, isso imprimirá '42', em alguns '43' e em outros, o programa irá travar.

Editar-adicionar:

FelizCara:Seu comentário está correto.A linguagem C ou o compilador C que você está usando trata const char * const * fundamentalmente de maneira diferente da linguagem C++.Talvez considere silenciar o aviso do compilador apenas para esta linha de origem.

Editar apagar: erro de digitação removido

Isso é irritante, mas se você deseja adicionar outro nível de redirecionamento, muitas vezes você pode fazer o seguinte para empurrar para baixo no ponteiro a ponteiro:

char c = 'c';
char *p = &c;
char **a = &p;

const char *bi = *a;
const char * const * b = &bi;

Tem um significado um pouco diferente, mas geralmente é viável e não usa elenco.

Não consigo obter um erro ao converter implicitamente char** para const char * const *, pelo menos no MSVC 14 (VS2k5) e g++ 3.3.3.O GCC 3.3.3 emite um aviso, que não tenho certeza se está correto.

teste.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; 
}

Saída com compilação como código C:cl /TC /W4 /Wp64 teste.c

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

Saída com compilação como código C++:cl /TP /W4 /Wp64 teste.c

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

Saída com gcc:gcc -Teste de parede.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

Saída com g++:g++ -Teste de parede.C

sem saída

Tenho certeza de que a palavra-chave const não implica que os dados não possam ser alterados/são constantes, apenas que os dados serão tratados como somente leitura.Considere isto:

const volatile int *const serial_port = SERIAL_PORT;

que é um código válido.Como volátil e const podem coexistir?Simples.volátil diz ao compilador para sempre ler a memória ao usar os dados e const diz ao compilador para criar um erro quando for feita uma tentativa de gravar na memória usando o ponteiro serial_port.

Const ajuda o otimizador do compilador?Não.De jeito nenhum.Como a constância pode ser adicionada e removida dos dados por meio de conversão, o compilador não consegue descobrir se os dados const são realmente constantes (já que a conversão pode ser feita em uma unidade de tradução diferente).Em C++ você também tem a palavra-chave mutável para complicar ainda mais as coisas.

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

O que acontece quando é feita uma tentativa de gravar em uma memória que realmente é somente leitura (ROM, por exemplo) provavelmente não está definida no padrão.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top