c ++ 11 — структурные смещения и безопасность указателей в переполнении стека

Этот вопрос касается указателей, полученных с использованием арифметики указателей со структурными смещениями.

Рассмотрим следующую программу:

#include <cstddef>
#include <iostream>
#include <new>

struct A {
float a;
double b;
int c;
};

static constexpr auto off_c = offsetof(A, c);

int main() {
A * a = new A{0.0f, 0.0, 5};
char * a_storage = reinterpret_cast<char *>(a);
int * c = reinterpret_cast<int *>(a_storage + off_c));

std::cout << *c << std::endl;

delete a;
}

Эта программа работает и дает ожидаемые результаты на протестированных мной компиляторах, используя настройки по умолчанию и стандарт C ++ 11.

(Тесно связанная программа, где мы используем void * вместо char * а также static_cast вместо reinterpret_cast,
не является общепринятым. gcc 5.4 выдает предупреждение об арифметике указателей с пустыми указателями, и clang 6.0 говорит, что указатель
арифметика с void * это ошибка.)

Имеет ли эта программа четко определенное поведение в соответствии со стандартом C ++?

Зависит ли ответ от того, имеет ли реализация ослабленная или строгая безопасность указателя ([basic.stc.dynamic.safety])?

5

Решение

В вашем коде нет фундаментальных ошибок.

Если A это не простые старые данные, выше UB (до C ++ 17) и условно поддерживается (после C ++ 17).

Вы можете заменить char* а также int* с auto*но это вещь стиля.

Обратите внимание, что указатели на члены делают это точно так же безопасным для типов способом. Большинство компиляторов реализуют указатель на член … как смещение члена в типе. Они, однако, работают везде, даже на структурах, не являющихся стручками.

В сторону: я не вижу гарантии, что offsetof является constexpr в стандарте. 😉

В любом случае замените:

static constexpr auto off_c = offsetof(A, c);

с

static constexpr auto off_c = &A::c;

а также

  auto* a_storage = static_cast<char *>(a);
auto* c = reinterpret_cast<int *>(a_storage + off_c));

с

  auto* c = &(a->*off_c);

сделать это способом C ++.

8

Другие решения

Это безопасно в вашем конкретном примере, но только потому, что ваша структура стандартное расположение, который вы можете перепроверить, используя std::is_standard_layout<>,

Попытка применить это к структуре, такой как:

struct A {
float a;
double b;
int c;
std::string str;
};

Было бы недопустимо, даже если строка прошла часть соответствующей структуры.

редактировать

Вот что меня интересует abt: в 3.7.4.3 [basic.stc.dynamic.safety] говорится, что указатель безопасно получен, только если (условия) и если у нас строгая безопасность указателя, тогда указатель недействителен, если он не прибыл из такого места , В 5.7 арифметике указателей говорится, что я могу выполнять обычную арифметику в массиве, но я не вижу там ничего, говорящего мне, что арифметика смещения структуры в порядке. Я пытаюсь выяснить, не относится ли это к делу так, как я думаю, или если арифметика смещения структуры не подходит для гипотетических «строгих» имплов, или если я прочитал 5.7 неправильно (n4296)

Когда вы делаете арифметический указатель, вы выполняете его на массиве charразмером не менее sizeof(A)так что все в порядке.

Затем, когда вы возвращаетесь ко второму участнику, вы попадаете под (2.4):

— результат четко определенного преобразования указателя (4.10, 5.4) из безопасного значения указателя;

4

Вы должны проверить свои предположения.

Предположение № 1) offsetof дает правильное смещение в байтах. Это гарантируется только в том случае, если класс считается «стандартным макетом», который имеет ряд ограничений, таких как отсутствие виртуальных методов, исключает множественное наследование и т. Д. В этом случае все должно быть в порядке, но в целом вы не можете быть уверенным.

Предположение № 2) Символ имеет тот же размер, что и байт. В C это по определению, так что вы в безопасности.

Предположение № 3) Параметр offsetof дает правильное смещение от указателя на класс, а не от начала данных. Это в основном то же самое, что и # 1, но vtable, безусловно, может быть проблемой. Опять же, работает только со стандартным макетом.

1
По вопросам рекламы [email protected]