Прямой доступ к членам структуры
Вопрос
У меня есть определение структуры тестирования следующим образом:
struct test{
int a, b, c;
bool d, e;
int f;
long g, h;
};
И где-то я использую это так:
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
В моей Windows это работает правильно — печатает 15, как и ожидалось, но безопасно ли это?Могу ли я быть уверен, что переменная находится в том месте памяти, где она должна быть, особенно в случае таких комбинированных структур (например, f в моем компиляторе является пятым словом, но это шестая переменная)?
Если нет, есть ли другой способ напрямую манипулировать членами структуры без фактического наличия конструкции struct->member в коде?
Решение
Похоже, вы задаете два вопроса
Безопасно ли рассматривать &test как массив int длиной 3 длины?
Наверное, лучше этого избегать.Это может быть определенное действие в стандарте C++, но даже если это так, маловероятно, что все, с кем вы работаете, поймут, что вы здесь делаете.Я считаю, что это не поддерживается, если вы читаете стандарт, из-за возможности заполнения структур, но я не уверен.
Есть ли лучший способ получить доступ к участнику без его имени?
Да.Попробуйте использовать смещение макрос/оператор.Это обеспечит смещение памяти для конкретного элемента внутри структуры и позволит вам правильно расположить точку на этом элементе.
size_t offset = offsetof(mystruct,c);
int* pointerToC = (int*)((char*)&someTest + offset);
Другой способ - просто взять адрес c напрямую.
int* pointerToC = &(someTest->c);
Другие советы
Нет, ты не можешь быть уверен. Компилятор может вводить отступы между членами структуры. Р>
Добавить в ответ JaredPar , еще один вариант только в C ++ (не просто C) создать объект указатель на член:
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
}
Возможно, вы ищете макрос offsetof
. Это даст вам смещение байта члена. Затем вы можете манипулировать членом с этим смещением. Обратите внимание, что этот макрос зависит от реализации. Включите stddef.h
, чтобы заставить его работать.
Вероятно, это небезопасно и на 100% не читается; что делает такой код неприемлемым в реальном производственном коде.
используйте методы set и boost :: bind для функтора create, который изменит эту переменную.
Помимо проблем с отступами / выравниванием, которые возникли в других ответах, ваш код нарушает строгие правила создания псевдонимов, что означает, что он может нарушаться для оптимизированных сборок (не уверен, как MSVC делает это, но GCC -O3
будет перерыв на этот тип поведения). По сути, поскольку test * t
и int * ptr
имеют разные типы, компилятор может предположить, что они указывают на разные части памяти, и он может переупорядочивать операции.
Рассмотрим эту незначительную модификацию:
test* t = new test;
int* ptr = (int*) t;
t->c = 13;
ptr[2] = 15;
cout << t->c;
Выход в конце может быть 13
или 15
, в зависимости от порядка операций, которые использует компилятор.
В соответствии с пунктом 9.2.17 стандарта фактически допустимо приводить указатель на структуру к указателю на его первый член, при условии, что структура является POD :
Указатель на объект POD-struct, соответствующим образом преобразованы с использованием reinterpret_cast, указывает на его первоначальный член (или если этот член битовое поле, то к единице, в которой оно проживает) и наоборот. [Заметка: Поэтому может быть безымянным заполнение внутри объекта POD-struct, но не в начале, по необходимости добиться соответствующего выравнивания. ] Р>
Однако стандарт не дает никаких гарантий относительно структуры структур - даже структур POD - за исключением того, что адрес более позднего элемента будет больше, чем адрес более раннего элемента, при условии, что нет спецификаторов доступа (< code> private: , protected:
или public:
) между ними. Таким образом, обработка начальной части вашего struct test
как массива из 3 целых чисел является технически неопределенным поведением.