Acessando os membros da estrutura diretamente
Pergunta
Eu tenho uma definição de estrutura de teste da seguinte maneira:
struct test{
int a, b, c;
bool d, e;
int f;
long g, h;
};
E em algum lugar que eu o uso desta maneira:
test* t = new test; // create the testing struct
int* ptr = (int*) t;
ptr[2] = 15; // directly manipulate the third word
cout << t->c; // look if it really affected the third integer
Isso funciona corretamente nas minhas janelas - imprime 15 como esperado, mas é seguro? Posso ter certeza de que a variável está no local na memória que eu quero que seja - especialmente no caso de tais estruturas combinadas (por exemplo, f está no meu compilador a quinta palavra, mas é uma sexta variável)?
Caso contrário, existe outra maneira de manipular os membros da Struct diretamente sem realmente ter o construto de estrutura no código?
Solução
Parece que você está fazendo duas perguntas
É seguro tratar e testar como um 3 comprimento int arrray?
Provavelmente é melhor evitar isso. Essa pode ser uma ação definida no padrão C ++, mas mesmo que seja, é improvável que todos com quem você trabalha entenda o que está fazendo aqui. Acredito que isso não é suportado se você ler o padrão devido ao potencial das estruturas de almofada, mas não tenho certeza.
Existe uma maneira melhor de acessar um membro sem seu nome?
Sim. Tente usar o Offsetf macro/operador. Isso fornecerá o deslocamento da memória de um membro específico dentro de uma estrutura e permitirá que você posicione corretamente um ponto para esse membro.
size_t offset = offsetof(mystruct,c);
int* pointerToC = (int*)((char*)&someTest + offset);
Outra maneira, porém, seria apenas tomar o endereço de C diretamente
int* pointerToC = &(someTest->c);
Outras dicas
Não, você não pode ter certeza. O compilador é gratuito para introduzir preenchimento entre os membros da estrutura.
Para adicionar Resposta de Jaredpar, outra opção apenas em C ++ (não na lisão C) é criar um objeto de ponteiro para membro:
struct test
{
int a, b, c;
bool d, e;
int f;
long g, h;
};
int main(void)
{
test t1, t2;
int test::*p; // declare p as pointing to an int member of test
p = &test::c; // p now points to 'c', but it's not associating with an object
t1->*p = 3; // sets t1.c to 3
t2->*p = 4; // sets t2.c to 4
p = &test::f;
t1->*p = 5; // sets t1.f to 5
t2->*p = 6; // sets t2.f to 6
}
Você provavelmente está procurando o offsetof
macro. Isso fará com que você o compensará o membro do membro. Você pode manipular o membro nesse deslocamento. Nota Porém, essa macro é específica da implementação. Incluir stddef.h
Para fazer isso funcionar.
Provavelmente não é seguro e é 100% não legível; tornando esse tipo de código inaceitável no código de produção da vida real.
Use Métodos Definir e Boost :: Bind for Create Functor, que alterará essa variável.
Além de questões de preenchimento/alinhamento que outras respostas trouxeram, seu código viola regras estritas de alias, o que significa que pode quebrar para construções otimizadas (não tenho certeza de como o MSVC faz isso, mas o GCC -O3
irá quebrar esse tipo de comportamento). Essencialmente, porque test *t
e int *ptr
são de tipos diferentes, o compilador pode assumir que aponta para diferentes partes da memória e pode reordenar operações.
Considere esta pequena modificação:
test* t = new test;
int* ptr = (int*) t;
t->c = 13;
ptr[2] = 15;
cout << t->c;
A saída no final pode ser 13
ou 15
, dependendo da ordem das operações que o compilador usa.
According to paragraph 9.2.17 of the standard, it is in fact legal to cast a pointer-to-struct to a pointer to its first member, providing the struct is POD:
A pointer to a POD-struct object, suitably converted using a reinterpret_cast, points to its initial member (or if that member is a bit-field, then to the unit in which it resides) and vice versa. [Note: There might therefore be unnamed padding within a POD-struct object, but not at its beginning, as necessary to achieve appropriate alignment. ]
However, the standard makes no guarantees about the layout of structs -- even POD structs -- other than that the address of a later member will be greater than the address of an earlier member, provided there are no access specifiers (private:
, protected:
or public:
) between them. So, treating the initial part of your struct test
as an array of 3 integers is technically undefined behaviour.