C99: Будут ли когда-нибудь заканчиваться массивы или выделенные в куче буферы в UINTPTR_MAX? - PullRequest
2 голосов
/ 15 июля 2011

Могу ли я предположить следующий инвариант?

void foo(char *buf, size_t len) {
  // "buf" points to either an array or memory allocated with malloc().
  assert((uintptr_t)(buf + len) < UINTPTR_MAX);
}

В синтаксическом анализаторе, который я пишу, я хочу пометить определенные смещения с помощью указателей: например, у меня может быть char *end_of_submessage, где end_of_submessage относительномой текущий буфер.Но если суб-сообщение не заканчивается внутри текущего буфера, я хочу использовать значение, которое больше, чем любое смещение в текущем буфере.Поэтому я бы сделал:

void parse(char *buf, size_t len, uintptr_t end_of_submessage) {
  // Some parsing that might increment "buf"
  // ...

  // If end_of_submessage == UINTPTR_MAX, processing will not be
  // triggered even if we have processed our entire current buffer.
  if ((uintptr_t)buf >= end_of_submessage)
    process_submsg_end();
}

Но эта схема была бы сорвана, если бы malloc () вернул память так, что ptr + len == UINTPTR_MAX, или массив имел такое же свойство.Можно ли предположить, что этого никогда не произойдет?Это безопасно в соответствии со стандартом?Если нет, безопасно ли это на практике?

1 Ответ

5 голосов
/ 15 июля 2011

Единственная гарантия, которую предоставляет стандарт C, заключается в следующем:

ISO / IEC 9899: 1999 (E) §7.18.1.4 / 1

Следующий тип обозначает подписанныйЦелочисленный тип со свойством, что любой действительный указатель на void может быть преобразован в этот тип, затем преобразован обратно в указатель на void, и результат будет сравниваться равным исходному указателю:

intptr_t

Следующий тип обозначаетцелочисленный тип без знака со свойством, что любой действительный указатель на void может быть преобразован в этот тип, затем преобразован обратно в указатель на void, и результат будет сравниваться равным исходному указателю:

uintptr_t

Эти типыявляются необязательными.

Нет никаких гарантий относительно точного содержания этих преобразованных целых чисел.В частности, если указатель на символ p, (uintptr_t)(p + 1) == ((uintptr_t)p) + 1 не гарантированно будет истинным.

Если вы хотите пометить смещения, вы должны использовать смещение ptrdiff_t от другого указателя, или вы можете простоиспользуйте указатель, чтобы отметить конец.Например:

void parse(char *buf, size_t len, char *end_of_submessage)
{
// ...
    if (buf >= end_of_submessage)
        process_submsg_end();
}

Если end_of_submessage может находиться в другом буфере, вы можете использовать что-то вроде:

char *buf_start = buf;
// ...
if (buf_start <= end_of_submessage && buf >= end_of_submessage)
  process_submsg_end();
...