Прямой доступ к членам структуры - PullRequest
1 голос
/ 08 марта 2009

У меня есть определение структуры тестирования следующим образом:

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 в коде?

Ответы [ 8 ]

13 голосов
/ 08 марта 2009

Похоже, вы задаете два вопроса

Безопасно ли обрабатывать и тестировать как массив длины 3?

Вероятно, лучше этого избежать. Это может быть определенное действие в стандарте C ++, но даже если это так, вряд ли все, с кем вы работаете, поймут, что вы здесь делаете. Я полагаю, что это не поддерживается, если вы читаете стандарт из-за потенциальной возможности дополнить структуры, но я не уверен.

Есть ли лучший способ получить доступ к участнику без его имени?

Да. Попробуйте использовать offsetof макрос / оператор. Это обеспечит смещение в памяти конкретного элемента в структуре и позволит вам правильно расположить точку на этом элементе.

size_t offset = offsetof(mystruct,c);
int* pointerToC = (int*)((char*)&someTest + offset);

Другим способом было бы просто взять адрес c напрямую

int* pointerToC = &(someTest->c);
5 голосов
/ 08 марта 2009

Нет, ты не можешь быть уверен. Компилятор может вводить отступы между членами структуры.

4 голосов
/ 08 марта 2009

Чтобы добавить к ответ 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
}
2 голосов
/ 08 марта 2009

Возможно, вы ищете макрос offsetof. Это даст вам смещение байта члена. Затем вы можете манипулировать членом с этим смещением. Обратите внимание, что этот макрос зависит от реализации. Включите stddef.h, чтобы заставить его работать.

1 голос
/ 08 марта 2009

Это, вероятно, небезопасно и на 100% не читается; что делает такой код неприемлемым в реальном производственном коде.

0 голосов
/ 10 марта 2009

Согласно пункту 9.2.17 стандарта, фактически допустимо приводить указатель на структуру к указателю на его первый член, при условии, что структура имеет вид POD :

Указатель на объект POD-struct, соответствующим образом преобразованы с использованием reinterpret_cast, указывает на его первоначальный член (или если этот член битовое поле, то к единице, в которой оно проживает) и наоборот. [Заметка: Поэтому может быть безымянным заполнение внутри объекта POD-struct, но не в начале, по необходимости добиться соответствующего выравнивания. ]

Однако, стандарт не дает никаких гарантий относительно структуры структур - даже структур POD - за исключением того, что адрес более позднего элемента будет больше, чем адрес более раннего элемента, при условии, что нет спецификаторов доступа (private:, protected: или public:) между ними. Таким образом, обработка начальной части вашего struct test как массива из 3 целых чисел является технически неопределенным поведением.

0 голосов
/ 08 марта 2009

Помимо проблем с дополнением / выравниванием, которые возникли в других ответах, ваш код нарушает строгие правила псевдонимов, что означает, что он может нарушаться для оптимизированных сборок (не уверен, как 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, в зависимости от порядка операций, используемых компилятором.

0 голосов
/ 08 марта 2009

используйте методы set и boost :: bind для функтора create, который изменит эту переменную.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...