указатели — приведение uint8_t * к uint32_t * в переполнении стека

Я пытаюсь реализовать функцию интерфейса библиотеки FATFS. Реализация ожидает uint8_t * для данных, которые должны быть записаны на внутреннюю SD-карту другой библиотекой. Данные должны быть записаны в библиотеку с помощью функции BSP_SD_WriteBlocks (uint32_t *, uint64_t, uint32_t, uint32_t). (Увидеть ниже)

/*
* Write data from a specific sector
*/
int SDMMC::disk_write(const uint8_t *buffer, uint32_t sector, uint32_t count)
{
int res = BSP_SD_WriteBlocks((uint32_t*)buffer, (uint64_t)sector, 512, count);

if (res == MSD_OK) {
return RES_OK;
} else {
return RES_ERROR;
}
}

Как видите, я пытаюсь преобразовать 8-битный адрес памяти в 32-битный адрес памяти и не думаю, что это правильный способ сделать это.

К сожалению, я не могу изменить аргументы функции, поэтому функция disk_write должна принимать uint8_t *, а BSP_SD_WriteBlocks просто принимает uint32_t *.

Какой самый лучший и быстрый способ сделать это?

0

Решение

Хитрость заключается в том, чтобы просто использовать изначально uint32_t массив соответствующего размера (он может быть динамически размещен, см. ниже мою первую идею, но не обязательно). Любой объект может быть доступен на уровне байтов, поэтому вы можете uint32_t * к uint8_t * и обработать его обычно как символьный буфер: вы обращаетесь к оригиналу uint32_t массив на уровне байтов, который разрешен строгим правилом алиасинга.

Когда вам нужно uint32_t * просто откинь назад. Поскольку вы обращались только к исходному массиву на уровне байтов, время жизни массива не закончилось, и uint32_t * указать на действительный массив.


Старое и не очень хорошее решение

Хитрость здесь заключается в том, чтобы выделить буфер malloc, Стандарт C говорит в части, ссылающейся на стандартную библиотеку C, доступную из программы на C ++ (*): 7.20.3 Функции управления памятью

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

Это означает, что при условии buffer это возвращаемое значение malloc вызов, стандарт гарантирует, что он может быть безопасно приведен к любому другому типу указателя.

Если вы этого не сделаете, вы рискуете проблема выравнивания, потому что выравнивание для uint32_t выше, чем для uint8_tи использование плохо выровненного указателя является явно неопределенным поведением.

Можно утверждать, что мы нарушаем строгое правило псевдонимов здесь. Но любая обычная реализация будет в порядке (она сломает слишком много существующего кода, чтобы отвергнуть это), и единственный строго совместимый способ — использовать полную memcopy буфера …, чтобы закончить точно такой же последовательностью байтов с совместимое выравнивание!


(*) Я знаю, что C и C ++ — это разные языки, но стандартное справочное руководство по C ++ сказано в 1.2 Нормативные ссылки [intro.refs]

1 Следующие ссылочные документы необходимы для применения этого документа …
— ISO / IEC 9899: 1999, Языки программирования — C

2 Библиотека, описанная в разделе 7 ИСО / МЭК 9899: 1999 и разделе 7 ИСО / МЭК 9899: 1999 / Cor.1: 2001
и пункт 7 ISO / IEC 9899: 1999 / Cor.2: 2003 в дальнейшем называется стандартной библиотекой C.1

1) С квалификациями, отмеченными в пунктах 18-30 и в C.3, стандартная библиотека C является подмножеством стандарта C ++
библиотека.

1

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

Здесь потенциально может быть несколько проблем.

  1. Вы отбрасываете const,

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

В практическом мире, если вы знаете, что BSP_SD_WriteBlocks никогда не пытается писать через этот указатель, вы, вероятно, в порядке. Но это большое дело, поэтому я бы использовал стиль C ++ const_cast<> чтобы было ясно, что вы делаете это намеренно.

  1. Выравнивание.

В педантичном мире актеры из std::uint8_t * в std::uint32_t * может быть небезопасным, по крайней мере, не переносимым, в зависимости от того, знаете ли вы, что исходный указатель правильно выровнен, или ваша платформа допускает не выровненный доступ. Обратите внимание, что копирование, предложенное в # 1, может решить эту проблему, так как вы можете легко убедиться, что ваш временный буфер соответствующим образом выровнен.

В практическом мире, если вы знаете, что исходный буфер всегда будет соответствующим образом выровнен, что кажется вероятным, тогда это не имеет большого значения. Опять же, я бы предложил бросок в стиле C ++. Я также рекомендовал бы утверждение, что адрес буфера кратен размеру std :: uint32_t.

1

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