Игнорируя эффективность, для простоты кода я бы сделал:
#include <numeric>
#include <vector>
#include <cstring>
uint32_t compute_checksum(const char *data, size_t size) {
std::vector<uint32_t> intdata(size/sizeof(uint32_t));
std::memcpy(&intdata[0], data, size);
return std::accumulate(intdata.begin(), intdata.end(), 0);
}
Мне также нравится последний ответ litb, который сдвигает каждый символ по очереди, за исключением того, что, поскольку char может быть подписан, я думаю, что ему нужна дополнительная маска:
checksum += ((data[i] && 0xFF) << shift[i % 4]);
Когда потенциальная проблема связана с типом punning, я предпочитаю не печатать pun, а не пытаться делать это безопасно. Если вы вообще не создаете псевдонимы-указатели разных типов, вам не нужно беспокоиться о том, что компилятор может сделать с псевдонимами, равно как и программист по обслуживанию, который просматривает ваши множественные static_casts через объединение.
Если вы не хотите выделять столько дополнительной памяти, тогда:
uint32_t compute_checksum(const char *data, size_t size) {
uint32_t total = 0;
for (size_t i = 0; i < size; i += sizeof(uint32_t)) {
uint32_t thisone;
std::memcpy(&thisone, &data[i], sizeof(uint32_t));
total += thisone;
}
return total;
}
Достаточная оптимизация полностью избавит от memcpy и дополнительной переменной uint32_t в gcc, и просто прочитает целочисленное значение без выравнивания, каким бы наиболее эффективным способом это ни было на вашей платформе, прямо из исходного массива. Я надеюсь, что то же самое верно и для других «серьезных» компиляторов. Но этот код теперь больше, чем у litb, поэтому сказать о нем немногое, кроме моего, легче превратить в шаблон функции, который будет работать так же хорошо с uint64_t, а мой работает как нативная последовательность, а не выбирает немного -endian.
Это, конечно, не полностью переносимо. Предполагается, что хранилище представления символов sizeof (uint32_t) соответствует хранилищу представления uin32_t так, как мы хотим. Это подразумевается под вопросом, поскольку в нем говорится, что одно можно «рассматривать как» другое. Endian-ness, то есть, является ли char 8-битным, и использует ли uint32_t все биты в своем представлении хранения, может явно помешать, но вопрос подразумевает, что они не будут.