Какое наименьшее смещение, для которого я могу безопасно пропустить проверку переполнения при добавлении его в указатель? - PullRequest
0 голосов
/ 05 апреля 2009

Можно ли ожидать, что любой указатель «данных» в программах пользовательского пространства находится на безопасном расстоянии от адресов 0 и 0xffffffff ..., так что я могу безопасно добавить небольшое смещение к указателю без проверки на переполнение? Какой самый большой положительный n, для которого я могу с уверенностью предположить, что p + n не переполняется, когда p - это символьный указатель на постоянный символьный буфер или динамически размещаемую строку (в современной> = 32-битной операционной системе)?

Чтобы избежать путаницы: я говорю о проверке переполнения, а не о проверке границ . Например: если у вас есть указатель p на начало строки с m символами, и вы хотите получить доступ к символу с положительным смещением i, то вам нужно либо проверить, что i = p.

Обновление: ОК, p + i не является допустимым стандартом C, если i> m, независимо от того, является ли p + i фактической разыменовкой или переполняется. Однако вопрос, который меня действительно интересует, заключается в том, существует ли маленький n, для которого p + n не будет переполняться на практике . Ответ на этот вопрос, очевидно, требует некоторых знаний о том, как современные операционные системы организуют адресное пространство.

Update2: было бы очень интересно услышать о какой-либо конкретной платформе, даже если она не обобщаема. Желательно не какой-то непонятный встроенный. 32-битный Win на базе x86 или Power, Linux и Mac были бы наиболее интересны.

Ответы [ 8 ]

8 голосов
/ 05 апреля 2009

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

В любом случае, гарантируется, что память существует (и что один конец памяти является разумным адресом), в противном случае вы бы получили исключение из new, указатель NULL из malloc или неопределенное поведение, если вы попытались неправильно распределить в стеке. Ни в коем случае не нужно проверять наличие переполнения.

4 голосов
/ 05 апреля 2009

Строго говоря, ответ равен 0, потому что p уже может указывать на точку за концом массива, и это то, что стандарт говорит, что действует.

В конкретной реализации вы можете избежать некоторой суммы, но это полностью определяется реализацией. Было аппаратное обеспечение, и, возможно, все еще, которое проверяет операции над указателями в инструкциях ЦПУ: если p указывает на массив из 2-х точек, выполнение p+3 приведет к сбою инструкции ЦПУ. С другой стороны, на большинстве современных аппаратных средств вы можете сойти с рук.

3 голосов
/ 05 апреля 2009

Учитывая предоставленную вами информацию, ответ 0. 0 - единственный ответ, действительный в соответствии со стандартом C ++ .

Если вы готовы пойти на неопределенное поведение (подсказка: нет), вам придется предоставить нам некоторую информацию для конкретной платформы и прощаются с любыми гарантиями о действительности вашего заявления. Ваше приложение может все еще работать, но вы полагаетесь на произвольные и, возможно, изменяющиеся решения, принимаемые разработчиками ОС и компиляторов.

Если нам известны точные сведения о вашей платформе (какой процессор, ОС, какой компилятор, в первую очередь), то может быть возможно дать вам ответ, который будет обычно работать, если ничего не изменится. в компиляторе или в ОС.

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

Строго говоря, добавление что-нибудь к указателю, который указывает на тот же блок памяти, на который он указывал ранее, не определено. Это может сработать, или это может на самом деле выглядеть очень забавно на некоторых архитектурах. Он может переполниться в неожиданное время, в некоторых случаях это может привести к серьезным сбоям. Вот почему язык просто говорит «это запрещено», и поэтому мы не можем сказать, что произойдет на практике, если вы не сказали нам платформу, на которой он работает.

Указатель C ++ не является адресом памяти . Именно так обычно и реализуют компиляторы, но они подчиняются другим правилам. В соответствии с набором инструкций процессора некоторые вещи допустимы с адресами памяти, что недопустимо с указателями.

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

2 голосов
/ 05 апреля 2009

В C арифметика указателей и относительные сравнения определяются только внутри «объектов» (не путать с объектами C ++). «Объект» - это переменная (любого типа) или область памяти, выделенная с помощью malloc / calloc / realloc. Вы можете вычислить указатель на «одно прошлое» объекта, и это всегда будет работать в стандартной соответствующей реализации.

Глядя на вещи на более низком уровне, указатель обычно реализуется как целое число без знака. Размер целого числа достаточно велик, чтобы вместить адрес любой ячейки памяти. Единственный способ указателя на переполнение - это если вы превышаете адресное пространство, что невозможно сделать при соответствии стандарту C.

Однако, если вы пишете низкоуровневый код и игнорируете ограничение на то, что арифметика указателей действительна только внутри объекта, лучший способ сделать это - воспользоваться знанием того, как представлены указатели. В большинстве современных сред это становится тем же, что и проверка на переполнение целочисленной арифметикой без знака.

(Исключениями являются такие вещи, как архитектуры с сегментированной памятью, типа 8086 или Multics, и, возможно, другие вещи, которые я, возможно, подавил из своей памяти сохрани мое здравомыслие.)

1 голос
/ 05 апреля 2009

В верхней части моей головы кажется, что это очень зависит от:

  • ОС
  • базовый HW
  • компилятор

Как правило, и только, насколько я помню, стек размещается в верхней части линейного адресного пространства. Учитывая, что у вас работают всевозможные библиотеки времени выполнения, ваши фактические данные, скорее всего, не работают в верхней части этого пространства. В любом случае, если вы превысите область, выделенную на malloc, у вас возникнут другие проблемы, и если вы превысите свой кадр стека, вы также столкнетесь с проблемами. Суть в том, что я не думаю, что вам нужно беспокоиться о переносе с 0xffffffffffffffff до 0x0, но вы все равно должны убедиться, что вы не выходите за пределы статической, автоматической или выделенной вручную памяти.

0 голосов
/ 05 апреля 2009

Для переполнения адресов, как я знаю, полезный прием, используемый для временного лагеря, также должен работать в вашей ситуации.

Если у вас 32-битная платформа, то каждый адрес имеет ширину 32 бита с (тип long без знака), вы можете попробовать следующий макрос:

# определить address_after (a, b) ((длинный) (b) - (длинный) (a) <0)) </p>

# определить address_before (a, b) address_after (b, a)

Тогда вы можете безопасно сравнивать адрес как address_after (p + i, p + m), только если | m-i | <0x7FFFFFFF (что является обычной ситуацией). Этот макрос очень хорошо справляется с проблемой переполнения. </p>

0 голосов
/ 05 апреля 2009

Обычно выделение памяти должно осуществляться в блоках, например, даже если вы хотите использовать 1 байт, минимальное выделение всегда должно быть в степени 2, например, CString в MFC использует 128 байтов или 64 байта, таким образом, вы можете потратить некоторое пространство наверняка, но при этом уменьшить объем вычислений. Если ваше распределение было сделано на основе блоков, то вы можете использовать размер блока и текущее значение указателя, чтобы использовать максимальное количество смещений, чтобы избежать переполнений.

0 голосов
/ 05 апреля 2009

Это зависит от двух вещей

  • Архитектура
  • Компилятор

Таким образом, единственный способ убедиться в этом - обратиться к справочной документации по вашему компилятору и архитектуре.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...