Да, это может вызвать проблемы.
4-выравнивание просто означает, что указатель, если рассматривать его как числовой адрес, кратен 4. Если указатель не кратен требуемому выравниванию, то он не выровнен. Есть две причины, по которым компиляторы накладывают ограничения на выравнивание для определенных типов:
- Поскольку аппаратное обеспечение не может загрузить этот тип данных из невыровненного указателя (по крайней мере, без использования инструкций, которые компилятор хочет выдать для загрузки и сохранения).
- Поскольку оборудование загружает этот тип данных быстрее из выровненных указателей.
Если вы в случае (1) и double выровнен по 4, и вы попробуете свой код с указателем char *
, который не выровнен по 4, то вы, скорее всего, получите аппаратную ловушку. Некоторое оборудование не ловит. Он просто загружает ерунду и продолжает. Однако стандарт C ++ не определяет, что может произойти (неопределенное поведение), поэтому этот код может поджечь ваш компьютер.
На x86 вы никогда не подходите (1), потому что стандартные инструкции загрузки могут обрабатывать не выровненные указатели. В ARM нет невыровненных загрузок, и если вы попытаетесь выполнить одну из них, ваша программа вылетает (если вам повезет. Некоторые ARM молча терпят неудачу).
Возвращаясь к вашему примеру, вопрос в том, почему вы пытаетесь сделать это с char *
, который не выровнен по 4. Если вы успешно написали двойную запись через double *
, вы сможете прочитать ее обратно. Поэтому, если у вас изначально был «правильный» указатель на удвоение, которое вы приводите к char *
, а теперь вы отбрасываете назад, вам не нужно беспокоиться о выравнивании.
Но вы сказали произвольно char *
, так что я думаю, что это не то, что у вас есть. Если вы читаете порцию данных из файла, который содержит сериализованное двойное число, то вы должны убедиться, что требования к выравниванию для вашей платформы выполнены для выполнения этого приведения. Если у вас есть 8 байтов, представляющих двойное число в каком-либо формате файла, то вы не можете просто прочитать его произвольно в буфер char * с любым смещением и затем привести к double *
.
Самый простой способ сделать это - убедиться, что вы читаете данные файла в подходящую структуру. Вам также помогает тот факт, что выделение памяти всегда выравнивается по требованию максимального выравнивания любого типа, который они достаточно велики, чтобы вместить. Таким образом, если вы выделяете буфер, достаточно большой, чтобы содержать double, тогда начало этого буфера имеет то выравнивание, которое требуется для double. Тогда вы можете прочитать 8 байтов, представляющих двойное число, в начало буфера, привести (или использовать объединение) и прочитать двойное число.
В качестве альтернативы, вы можете сделать что-то вроде этого:
double readUnalignedDouble(char *un_ptr) {
double d;
// either of these
std::memcpy(&d, un_ptr, sizeof(d));
std::copy(un_ptr, un_ptr + sizeof(d), reinterpret_cast<char *>(&d));
return d;
}
Это гарантированно будет действительным (при условии, что un_ptr действительно указывает на байты действительного двойного представления для вашей платформы), потому что double - это POD и, следовательно, может быть скопировано побайтово. Возможно, это не самое быстрое решение, если вам нужно загрузить много пар.
Если вы читаете из файла, на самом деле это немного больше, чем если вы беспокоитесь о платформах с двойным представлением не-IEEE, или с 9-битными байтами, или некоторыми другими необычными свойствами, где могут быть не значащие биты в сохраненном представлении double. Но вы на самом деле не спрашивали о файлах, я просто придумал это в качестве примера, и в любом случае эти платформы гораздо реже, чем вопрос, о котором вы спрашиваете, что для double требуется требование выравнивания.
Наконец, ничего общего с выравниванием нет, вам также нужно беспокоиться о строгом псевдониме, если вы получили это char *
через приведение от указателя, который не совместим с псевдонимом double *
. Однако псевдоним действителен между char *
и всем остальным.