AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • Início
  • system&network
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • Início
  • system&network
    • Recentes
    • Highest score
    • tags
  • Ubuntu
    • Recentes
    • Highest score
    • tags
  • Unix
    • Recentes
    • tags
  • DBA
    • Recentes
    • tags
  • Computer
    • Recentes
    • tags
  • Coding
    • Recentes
    • tags
Início / coding / Perguntas / 79311888
Accepted
Evgeny Ilyin
Evgeny Ilyin
Asked: 2024-12-27 21:13:06 +0800 CST2024-12-27 21:13:06 +0800 CST 2024-12-27 21:13:06 +0800 CST

Conversão implícita e explícita de ponteiros para objetos const em C

  • 772

O padrão afirma claramente que tentar acessar um objeto const por meio de um ponteiro para um tipo não const desse objeto causará Comportamento Indefinido. O mesmo se aplica a objetos qualificados com a palavra-chave volátil e ponteiros para tais objetos.

Mas o que significa acesso? Por exemplo, é uma sequência arbitrariamente longa de precasts de ponteiros contendo um cast "incorreto", mas finalmente conversível para o tipo correto, uma expressão

int const i = 0;
int const *p = &i; // ok
int const *t = (int const *)(float *)(int *)&i; // is this OK or UB?

Sempre fui atormentado por esse ponto de acordo com a rigidez do padrão da linguagem C. Em particular, tenho muitos dos seguintes casos especiais de passagem de argumentos para funções, onde eles devem ser explicitamente convertidos:

void send(unsigned int const buf[], unsigned int size);

typedef struct {
  unsigned int hdr;
  float temp;
  unsigned int data[5];
} Str;

void func(void) {
  Str const s = {
    0, 1.5f, {0, 1, 2, 3, 4}
  };
  ...
  send((unsigned int const *)&s, sizeof(s));
}

De acordo com as regras do padrão, posso converter com segurança um ponteiro para uma estrutura em um ponteiro para seu primeiro elemento (com o tipo apropriado), mas nada é dito sobre obrigações adicionais com qualificadores (ou eu não vi). Embora eu entenda formalmente que é correto deixar const em uma conversão explícita, isso geralmente confunde muito o código, e eu gostaria de omitir o uso de const nesta expressão. Quero ter certeza de que chamar

send((unsigned int *)&s, sizeof(s));

é tão seguro quanto ligar

send((unsigned int const *)&s, sizeof(s));

já que o protótipo da função send() forçará explicitamente o compilador a converter implicitamente (unsigned int *) para (unsigned int const *) para mim.

Mas se qualquer perda intermediária do qualificador const em conversões de tipo (em conversões de tipo), apesar do fato de que até mesmo o tipo final terá o qualificador correto (uma conversão do formato (type const *) -> (type *) -> ... -> (type const *)), for proibida pelo padrão, então uma chamada no estilo de send((unsigned int *)&s, sizeof(s)); será incorreta.

Por favor, confirme ou negue os pontos do padrão C.

c
  • 3 3 respostas
  • 74 Views

3 respostas

  • Voted
  1. Best Answer
    Eric Postpischil
    2024-12-27T22:28:58+08:002024-12-27T22:28:58+08:00

    Mas o que significa acesso?

    C 2024 3.1 define acesso :

    ⟨ação em tempo de execução⟩ ler ou modificar o valor de um objeto

    (Versões anteriores do padrão diziam “ ler ou modificar”.)

    Por exemplo, é uma sequência arbitrariamente longa de precasts de ponteiros contendo uma conversão "incorreta", mas finalmente convertível para o tipo correto…

    A conversão de ponteiros é apenas uma operação em valores e não acessa os objetos apontados.

    int const *t = (int const *)(float *)(int *)&i; // is this OK or UB?

    Elas envolvem regras sobre conversões de ponteiros, o que não acredito ser o cerne da sua pergunta. Conforme C 2024 6.3.3.3, conversões entre ponteiros para tipos de objeto são geralmente definidas desde que o alinhamento seja adequado para o tipo de destino. &ié necessariamente alinhado adequadamente para um int, porque foi definido com int const i, então as conversões para int *e int const *são definidas. A conversão para float *é definida se ifor alinhado adequadamente para um float. (Um inté sempre alinhado adequadamente para floatna maioria das implementações comuns de C.)

    Dado alinhamento satisfatório, a conversão para um ponteiro diferente para um tipo de objeto e de volta para o tipo original é garantida para produzir um valor igual ao original. Cadeias de conversões não são explicitamente especificadas, mas pode-se presumir razoavelmente que restauram o valor original.

    O padrão afirma claramente que tentar acessar um objeto const por meio de um ponteiro para um tipo não const desse objeto causará um comportamento indefinido.

    Não diz isso. Como vemos acima, “acesso” inclui leitura, e ler um objeto definido com consté definido. O que o padrão diz que é indefinido a esse respeito é, por 6.7.4.1, modificar um objeto const-qualificado:

    Se for feita uma tentativa de modificar um objeto definido com um tipo qualificado const por meio do uso de um lvalue com tipo não qualificado const, o comportamento será indefinido.

    Observe também que “comportamento indefinido” não é um substantivo próprio e não deve ser escrito com letra maiúscula.

    Quero ter certeza de que ligar send((unsigned int *)&s, sizeof(s)); é tão seguro quanto ligar para send((unsigned int const *)&s, sizeof(s));…

    Ambos são equivalentes em termos do que o padrão C especifica para execução em sua máquina abstrata; dois programas que diferem apenas neste código terão o mesmo comportamento de execução (exceto fatores de composição externos a isso, como alguém examinando o código-fonte e inserindo entradas diferentes em programas diferentes).

    No entanto, um compilador poderia avisar que o primeiro rejeita constenquanto não avisaria para o último. Isso é permitido pelo padrão (5.2.1.3: “É claro que uma implementação é livre para produzir qualquer número de mensagens de diagnóstico, frequentemente chamadas de avisos, desde que um programa válido ainda esteja corretamente traduzido.”), então não é garantido que os dois programas se comportarão de forma idêntica a esse respeito.

    Além disso, uma grande parte da programação não é meramente como os programas interagem com compiladores e computadores, mas como eles interagem com humanos. Um pedaço de código pode confundir futuros leitores humanos mais do que o outro.

    • 3
  2. gnasher729
    2024-12-27T21:30:28+08:002024-12-27T21:30:28+08:00

    Não é acesso, é uma tentativa de modificar um objeto const que causa comportamento indefinido.

    Você pode ter implementações onde casts entre ponteiros para pontos diferentes são problemáticos, mas eu não conheço nenhuma implementação assim agora. Mas conversões entre const / non const ou volátil / não volátil são sempre boas. Então 99,999% só importa que você tenha um objeto T const e o modifique por meio de um ponteiro T*. Se você cast o ponteiro mais adiante para um ponteiro T* const, o compilador não permite nenhuma modificação. Então, para casts múltiplos, apenas o resultado final importa.

    (Observação: converter T* para um tipo inteiro muito pequeno e vice-versa causará problemas).

    • 0
  3. 0___________
    2024-12-27T21:22:09+08:002024-12-27T21:22:09+08:00
    int const *t = (int const *)(float *)(int *)&i; // is this OK or UB?
    

    IMO é um UB, pois o padrão apenas garante que qualquer ponteiro pode ser convertido para ponteiro para voide void *pode ser convertido para qualquer tipo de ponteiro. Mas não garante a conversão de ou para quaisquer outros tipos de ponteiro. Também inclui conversão "indireta" via voidponteiro

    • -1

relate perguntas

  • Multiplicação mais rápida que *

  • Usando uma macro para comprimento de string no especificador de formato scanf () em C

  • Como você pode definir o tipo de dados de #define para long double?

  • Ponteiros const incompatíveis

  • Mudança de cor não gradual no OpenGL

Sidebar

Stats

  • Perguntas 205573
  • respostas 270741
  • best respostas 135370
  • utilizador 68524
  • Highest score
  • respostas
  • Marko Smith

    Vue 3: Erro na criação "Identificador esperado, mas encontrado 'import'" [duplicado]

    • 1 respostas
  • Marko Smith

    Por que esse código Java simples e pequeno roda 30x mais rápido em todas as JVMs Graal, mas não em nenhuma JVM Oracle?

    • 1 respostas
  • Marko Smith

    Qual é o propósito de `enum class` com um tipo subjacente especificado, mas sem enumeradores?

    • 1 respostas
  • Marko Smith

    Como faço para corrigir um erro MODULE_NOT_FOUND para um módulo que não importei manualmente?

    • 6 respostas
  • Marko Smith

    `(expression, lvalue) = rvalue` é uma atribuição válida em C ou C++? Por que alguns compiladores aceitam/rejeitam isso?

    • 3 respostas
  • Marko Smith

    Quando devo usar um std::inplace_vector em vez de um std::vector?

    • 3 respostas
  • Marko Smith

    Um programa vazio que não faz nada em C++ precisa de um heap de 204 KB, mas não em C

    • 1 respostas
  • Marko Smith

    PowerBI atualmente quebrado com BigQuery: problema de driver Simba com atualização do Windows

    • 2 respostas
  • Marko Smith

    AdMob: MobileAds.initialize() - "java.lang.Integer não pode ser convertido em java.lang.String" para alguns dispositivos

    • 1 respostas
  • Marko Smith

    Estou tentando fazer o jogo pacman usando apenas o módulo Turtle Random e Math

    • 1 respostas
  • Martin Hope
    Aleksandr Dubinsky Por que a correspondência de padrões com o switch no InetAddress falha com 'não cobre todos os valores de entrada possíveis'? 2024-12-23 06:56:21 +0800 CST
  • Martin Hope
    Phillip Borge Por que esse código Java simples e pequeno roda 30x mais rápido em todas as JVMs Graal, mas não em nenhuma JVM Oracle? 2024-12-12 20:46:46 +0800 CST
  • Martin Hope
    Oodini Qual é o propósito de `enum class` com um tipo subjacente especificado, mas sem enumeradores? 2024-12-12 06:27:11 +0800 CST
  • Martin Hope
    sleeptightAnsiC `(expression, lvalue) = rvalue` é uma atribuição válida em C ou C++? Por que alguns compiladores aceitam/rejeitam isso? 2024-11-09 07:18:53 +0800 CST
  • Martin Hope
    The Mad Gamer Quando devo usar um std::inplace_vector em vez de um std::vector? 2024-10-29 23:01:00 +0800 CST
  • Martin Hope
    Chad Feller O ponto e vírgula agora é opcional em condicionais bash com [[ .. ]] na versão 5.2? 2024-10-21 05:50:33 +0800 CST
  • Martin Hope
    Wrench Por que um traço duplo (--) faz com que esta cláusula MariaDB seja avaliada como verdadeira? 2024-05-05 13:37:20 +0800 CST
  • Martin Hope
    Waket Zheng Por que `dict(id=1, **{'id': 2})` às vezes gera `KeyError: 'id'` em vez de um TypeError? 2024-05-04 14:19:19 +0800 CST
  • Martin Hope
    user924 AdMob: MobileAds.initialize() - "java.lang.Integer não pode ser convertido em java.lang.String" para alguns dispositivos 2024-03-20 03:12:31 +0800 CST
  • Martin Hope
    MarkB Por que o GCC gera código que executa condicionalmente uma implementação SIMD? 2024-02-17 06:17:14 +0800 CST

Hot tag

python javascript c++ c# java typescript sql reactjs html

Explore

  • Início
  • Perguntas
    • Recentes
    • Highest score
  • tag
  • help

Footer

AskOverflow.Dev

About Us

  • About Us
  • Contact Us

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve