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 / 77852527
Accepted
schuelermine
schuelermine
Asked: 2024-01-21 04:01:32 +0800 CST2024-01-21 04:01:32 +0800 CST 2024-01-21 04:01:32 +0800 CST

Posso mover-me entre sequências contíguas de campos do mesmo tipo em uma estrutura usando aritmética de ponteiro sem alinhamento?

  • 772

Estou ciente de que existem outras questões semelhantes. Eu li isso e não acho que essa pergunta tenha sido respondida.

Posso mover-me entre uma sequência consecutiva (contígua na sequência de declaração) de campos do mesmo tipo de uma estrutura usando aritmética de ponteiro sem consultar _Alignof?

Posso mover-me entre os elementos da matriz sem consultar _Alignofporque o preenchimento final está incluído.

No entanto, é possível que campos consecutivos do mesmo tipo em uma estrutura não estejam alinhados da mesma forma que em uma matriz?

Em outras palavras, é possível que esse código tenha um comportamento indefinido?

struct MyStruct {
    long int field_1;
    int field_2;
    int field_3;
};

int main(void) {
    struct MyStruct my_struct;
    int *field_2_ptr = &my_struct.field_2;
    int field_3_value = *(field_2_ptr + 1);
}

Eu sei que isso é uma má prática. Estou ciente . _ ->Eu sei que o preenchimento pode interferir em geral. Quero saber se o preenchimento pode interferir nesta circunstância específica.

Esta questão é sobre C. Não me importo com C++.

Até agora eu compilei isso com o GCC e experimentei o Valgrind para ver se algo estava errado, e compilei com clang e UBSan. Parece estar bem neste sistema (x86-64 Linux).

c
  • 3 3 respostas
  • 62 Views

3 respostas

  • Voted
  1. Best Answer
    dbush
    2024-01-21T04:08:55+08:002024-01-21T04:08:55+08:00

    A rigor, este é um comportamento indefinido.

    A aritmética de ponteiro é permitida em objetos de matriz (e objetos únicos são tratados como uma matriz de 1 elemento para essa finalidade), desde que o ponteiro original e o ponteiro resultante apontem para o mesmo objeto de matriz (ou um elemento após o final). Além disso, um ponteiro para um após o fim não pode ser desreferenciado, caso contrário, acionará um comportamento indefinido.

    Isso está explicado na seção 6.5.6p8 do padrão C em relação aos operadores aditivos:

    Quando uma expressão do tipo inteiro é adicionada ou subtraída de um ponteiro, o resultado tem o tipo do operando do ponteiro. Se o operando do ponteiro apontar para um elemento de um objeto de matriz, e a matriz for grande o suficiente, o resultado aponta para um deslocamento do elemento do elemento original, de modo que a diferença dos subscritos dos elementos da matriz resultante e original seja igual à expressão inteira. Em outras palavras, se a expressão P aponta para o i-ésimo elemento de um objeto de matriz, as expressões (P)+N (equivalentemente, N+(P)) e (P)-N (onde N tem o valor n) apontam para, respectivamente, os i+n-ésimo e i-n-ésimo elementos do objeto array, desde que existam. Além disso, se a expressão P aponta para o último elemento de um objeto array, a expressão (P)+1 aponta um após o último elemento do objeto array, e se a expressão Q aponta para o último elemento de um objeto de matriz, a expressão (Q)-1 aponta para o último elemento do objeto de matriz. Se tanto o operando do ponteiro quanto o resultado apontarem para elementos do mesmo objeto array, ou um após o último elemento do objeto array, a avaliação não produzirá um overflow; caso contrário, o comportamento será indefinido.Se o resultado apontar para o último elemento do objeto array, ele não deve ser usado como operando de um * operador unário que é avaliado.

    A seção em negrito é exatamente o que está acontecendo aqui:

    int field_3_value = *(field_2_ptr + 1);
    

    Como isso desreferencia um ponteiro para outro elemento de (essencialmente) uma matriz de 1 elemento, isso desencadeia um comportamento indefinido. Isso &my_struct.field_2 + 1 == &my_struct.field_3seria avaliado como verdadeiro, não importa.

    Pela mesma razão, este também é um comportamento indefinido:

    int x[2][2] = {{1,2},{3,4}};
    int y = x[0][2];
    

    Embora o procedimento acima provavelmente funcione, não há garantia disso. Os compiladores modernos otimizam agressivamente e podem e assumem que não existe comportamento indefinido e exploram essa suposição.

    Já vi casos em que, dados ponteiros ae bque apontam para a memória adjacente, (uintptr_t)(a+1) == (uintptr_t)bsão verdadeiros, mas a+1 == bsão falsos.

    • 4
  2. gulpr
    2024-01-21T04:19:51+08:002024-01-21T04:19:51+08:00

    Como @dbush escreveu, é um UB.

    Eu usaria um array para agrupar membros do mesmo tipo:

    struct MyStruct {
        long int field_1;
        int fields[2];
    };
    

    e seu código não invocará um UB

    int main(void) {
        struct MyStruct my_struct;
        int *field_2_ptr = my_struct.fields;
        int field_3_value = *(field_2_ptr + 1);
    
        /* or more simple */
        int field_3_value = my_struct.fields[1];
    
    }
    
    • 0
  3. supercat
    2024-01-21T09:18:14+08:002024-01-21T09:18:14+08:00

    Esse tipo de comportamento foi definido na linguagem que o Padrão C foi designado para descrever, e o Padrão C permite que implementações, como uma forma de "extensão de linguagem conforme", suportem tais construções. Clang e gcc suportarão tais construções quando invocados com o -fno-strict-alisingsinalizador do compilador, embora não se deva esperar que eles suportem tais construções de outra forma.

    • 0

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