Ответ 1
Насколько я могу судить, это переносимая реализация функции, которой я пользуюсь для всех возможных реализаций:
#ifdef UINTPTR_MAX
bool points_into_buffer(std::uintptr_t p, std::uintptr_t buf, std::size_t len)
{
const auto diff = p + 0u - buf;
if (diff < len)
// #1
if (reinterpret_cast<char *>(p) == reinterpret_cast<char *>(buf) + diff)
return true;
for (std::size_t n = 0; n != len; n++)
if (reinterpret_cast<char *>(p) == reinterpret_cast<char *>(buf) + n)
// #2
if (reinterpret_cast<char *>(p) - reinterpret_cast<char *>(buf) != diff)
return true;
return false;
}
template <typename T>
bool points_into_buffer(T *p, T *buf, std::size_t len)
{
return points_into_buffer(reinterpret_cast<std::uintptr_t>(p),
reinterpret_cast<std::uintptr_t>(buf),
len * sizeof(T));
}
#else
template <typename T>
bool points_into_buffer(T *p, T *buf, std::size_t len)
{
for (std::size_t n = 0; n != len; n++)
if (p == buf + n)
return true;
return false;
}
#endif
В общем случае diff
не имеет значимого значения. Но все в порядке: функция возвращает true
тогда и только тогда, когда она находит n
такую, что reinterpret_cast<char *>(p) == reinterpret_cast<char *>(buf) + n
. Он использует diff
как подсказку, чтобы быстрее найти значение n
.
Он опирается на условия оптимизации компилятора, которые не всегда известны во время компиляции, но известны во время компиляции для конкретной платформы. Условия для операторов if
, помеченные как #1
и #2
, определяются GCC во время компиляции, всегда должны быть true
и false
соответственно, из-за того, как diff
определено, что позволяет GCC видеть, что никакое полезное действие не выполняется внутри цикла и позволяет отбросить весь цикл.
Сгенерированный код для points_into_buffer<char>
и points_into_buffer<int>
выглядит следующим образом:
bool points_into_buffer(char*, char*, unsigned int): movl 4(%esp), %edx movl $1, %eax movl 12(%esp), %ecx subl 8(%esp), %edx cmpl %edx, %ecx ja L11 xorl %eax, %eax L11: rep ret bool points_into_buffer(int*, int*, unsigned int): movl 4(%esp), %edx movl 12(%esp), %eax subl 8(%esp), %edx leal 0(,%eax,4), %ecx movl $1, %eax cmpl %edx, %ecx ja L19 xorl %eax, %eax L19: rep ret
В системах, где std::uintptr_t
недоступен или где адреса более сложны, чем простые целые числа, вместо этого используется цикл.