0

Here установлено, что незаконно обрабатывать плотно упакованные последовательные элементы структуры типа T в виде массива T.std :: memcpy struct с плотно упакованными членами TriviallyCopyable типа T в массив из T и наоборот

Но как насчет копирования основного представления?

Дано:

struct vec { 
    float x, y, z; 
}; 

с теми же ограничениями:

static_assert(sizeof(vec) == 3 * sizeof(float)); 

является следующее:

int main() { 
    vec v = {1.9f, 2.5f, 3.1f}; 

    float a[3]; 
    std::memcpy(&a, &v, 3 * sizeof(float)); 
    assert(a[0] == v.x); 
    assert(a[1] == v.y); 
    assert(a[2] == v.z); 

    vec u; 
    std::memcpy(&u, &a, 3 * sizeof(float)); 
    assert(u.x == a[0]); 
    assert(u.y == a[1]); 
    assert(u.z == a[2]); 
} 

законно?

+0

Связанный (или, возможно, дубликат): http://stackoverflow.com/questions/37211298/accessing-an-array-as-a-struct-vs-undefined-behavior – Barmar

+0

@Barmar - та же проблема, которая обсуждалась в вопрос, который я связал. –

+0

Они оба говорят, что это неопределенное поведение. Почему, по-вашему, ваше дело по-другому? – Barmar

ответ

1

Как долго, как ваша структура типа не имеет отступов, нет никакой явной поддержки для этого в стандарте , но можно предположить, что поддержка чего-то очень близкого к ней.

Учитывая тривиальным Copyable типа T, что явно разрешено, чтобы скопировать его представление в массив char (или unsigned char) и обратно.

Нет необходимости хранить содержимое массива в самом массиве. Содержимое может храниться в файле и перечитываться при последующем выполнении программы. Или хранится в объекте другого типа, если только этот тип допускает это. Чтобы это сработало, реализации должны допускать представления memcpy ing в объекты, если эти представления не возникли из объектов типа T в этом же запуске.

В результате, по крайней мере,

int main() { 
    vec v = {1.9f, 2.5f, 3.1f}; 

    float a[3]; 

    assert(sizeof v == sizeof a); 

    { char tmp[3 * sizeof(float)]; 
     std::memcpy(tmp, &v, 3 * sizeof(float)); 
     std::memcpy(a, tmp, 3 * sizeof(float)); } 
    assert(a[0] == v.x); 
    assert(a[1] == v.y); 
    assert(a[2] == v.z); 

    vec u; 
    { char tmp[3 * sizeof(float)]; 
     std::memcpy(tmp, a, 3 * sizeof(float)); 
     std::memcpy(&u, tmp, 3 * sizeof(float)); } 
    assert(u.x == a[0]); 
    assert(u.y == a[1]); 
    assert(u.z == a[2]); 
} 

должен либо не на первом assert, или передать. Для любого представления, где он потерпел неудачу, тривиально создать функцию, которая, как представляется, придумывает это точное представление в однозначно допустимых способах, поэтому она не должна терпеть неудачу.

Теперь, опуская tmp, здесь немного в порядке.

std::memcpy - это просто повторные присвоения отдельных байтов и могут быть явно указаны. Из семантики оператора = следует, что для трех типов, подлежащих копированию, a = b; и { auto tmp = b; a = tmp; } эквивалентны. То же самое с a = b; c = d; и { auto tmp1 = b; auto tmp2 = d; a = tmp1; c = tmp2; } и так далее. Первый - это то, что делает прямой memcpy, последний - это то, что два memcpy с до tmp do.

С другой стороны, разрешение на копирование в и из массива char может считаться требующим фактического массива char, а не только его функциональный эквивалент.

Лично я, вероятно, не стал бы беспокоиться об этом, если бы я не натолкнулся на реализацию, которая использует эту интерпретацию, но если вы хотите играть в нее безопасно, вы можете ввести такой временный массив и убедиться, что ваш компилятор сумел оптимизировать это прочь.

-3

Проблема, почему вы не должны всегда доверять тому, что структура из трех членов того же типа эквивалентна массиву того же типа, в основном из-за выравнивания памяти.

https://en.wikipedia.org/wiki/Data_structure_alignment

Ваш код может работать хорошо на C++ компилятора и не на другом или даже на тот же компилятор с различной конфигурацией.

Также обратите внимание, что неправильно указатель массива.

std::memcpy(a, &v, 3 * sizeof(float)); 

и не

std::memcpy(&a, &v, 3 * sizeof(float)); 

а является уже постоянным указателем плавать

+1

Нет проблем с выравниванием, так как 'float' всегда имеют одинаковые требования к выравниванию независимо от того, где они хранятся. Однако есть проблемы с запасом, но я защищаю те, у кого есть «static_assert». Что касается объекта «указатель-массив» и «элемент-указатель-первым-массив-элемент», это не имеет значения, поскольку они одинаковы при преобразовании в указатель-на-void. –

+0

@yurikilochek Другие типы имеют различное требование выравнивания в зависимости от того, являются ли они частью структуры ('long long' на системах x86 GNU/Linux), почему нельзя« плавать »? Но выравнивание не должно вызывать проблем здесь, если ваша проверка заполнения работает. – hvd

+0

@hvd Я был под впечатлением, которое было верно для всех типов, а не просто плавает. Но вы правы, неважно, не введен ли прокладка. –

 Смежные вопросы

  • Нет связанных вопросов^_^