Подтвердить что ты не робот

Может ли массив С++ заканчиваться на границе памяти?

Стандарт С++ (и C в этом случае) позволяет создавать (а не разыменовывать) указатель на один элемент за конец массива. Означает ли это, что массив никогда не будет выделен в таком месте, где его последний элемент заканчивается на границе памяти? Я понимаю, что на практике некоторые/все реализации могут следовать этому соглашению, но какое из них справедливо:

  • Это фактически false, и массив может закончиться на границе памяти, OR
  • Стандарт С++ имеет мандат для завершения, по крайней мере, одного значения ценности памяти перед границей, OR
  • Ни 1, ни 2, но в реальных компиляторах это все равно, потому что упрощает реализацию.

Что-нибудь другое для случая C?

Update: Кажется, что 1 - правильный ответ. См. Ответ от Джеймса Канзе ниже, а также см. efence (http://linux.die.net/man/3/efence - спасибо Майклу Частинэю за указатель на него)

4b9b3361

Ответ 1

Реализация должна позволять указателю на один конец существовать. Как это происходит в этом бизнесе. На многих машинах вы может безопасно помещать любое значение в указатель, без риска (если только вы разыгрываете его); на таких системах, один за концом указатель может указывать на unmapped memory — на самом деле столкнулся с корпусом под Windows, где он и был.

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

Повторите свой последний вопрос: C и С++ в этом отношении точно идентичны; С++ просто взял на себя правила из C.

Ответ 2

Там интересный отрывок из § 3.9.2/3 [Тип соединения]:

Тип указателя на void или указатель на тип объекта называется типом указателя объекта. [...] Допустимое значение типа указателя объекта представляет собой либо адрес байта в памяти (1.7), либо нулевой указатель (4.10).

Вместе с текстом в §5.7/5 [Аддитивные операторы]:

[...] Кроме того, если выражение P указывает на последний элемент объекта массива, выражение (P) +1 указывает один за последним элементом объекта массива, а если выражение Q точек один за последним элементом объекта массива, выражение (Q) -1 указывает на последний элемент массива объект.

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

В разделе §3.7.4.2/4 [Функции освобождения] указано, что:

Эффект использования недопустимого значения указателя (включая передачу его функции освобождения) undefined.

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

Основываясь на полученных комментариях, я предполагаю, что реализация может выделять массив, не заботясь о том, можно ли использовать указатель массива one-past-end, или нет. Однако я хотел бы узнать соответствующие отрывки в стандарте для этого.

Ответ 3

Ты наполовину прав. Предположим, что гипотетическая реализация использует линейно адресуемую память и указатели, которые представлены как 16-разрядные целые числа без знака. Предположим также, что нулевой указатель представлен как ноль. И, наконец, предположим, что вы запрашиваете 16 байт памяти, с char *p = malloc(16);. Затем он гарантирует, что вы получите указатель, числовое значение которого меньше 65520. Значение 65520 само по себе недействительно, потому что, как вы справедливо указываете, если предположение о том, что выделение выполнено успешно, p + 16 является допустимым указателем, который должен не быть нулевым указателем.

Однако предположим теперь, что гипотетическая реализация использует линейно адресуемую память и указатели, которые представлены как 32-разрядные целые числа без знака, но имеет только адресное пространство 16 бит. Предположим также, что нулевой указатель представлен как ноль. И, наконец, предположим, что вы запрашиваете 16 байт памяти, с char *p = malloc(16);. Тогда это только гарантирует, что вы получите указатель, числовое значение которого меньше или равно 65520. Значение 65520 само по себе было бы действительно, пока реализация гарантирует, что добавление 16 к этому даст вам значение 65536 и что вычитание 16 возвращает вас к 65520. Это справедливо даже в том случае, если нет памяти (физической или виртуальной) вообще по адресу 65536.

Ответ 4

В стандартном состоянии явно указывается, что происходит, когда вы увеличиваете указатель на последний элемент. Он дает вам значение, которое можно использовать только для сравнения, чтобы проверить, находитесь ли вы до или после конца массива или нет. Указатель может указывать на достоверно выделенную память для какого-либо другого объекта, но это полное поведение undefined (реализация определено?) И использование этого указателя как такового, безусловно, поведение undefined.

То, что я получаю, заключается в том, что указатель "один-на-конец" - это только то: это указатель, который вы получаете, когда вы увеличиваете указатель на последний элемент, чтобы пометить конец массива в очень дешевый способ. Но обратите внимание, что сравнение указателей несвязанных объектов совершенно бессмысленно (и даже undefined поведение, если я не ошибаюсь). Таким образом, тот факт, что может быть перекрытие в "значениях" указателя для разных объектов, не является проблемой, так как при использовании этого вы введете Землю undefined Behavior..

Ответ 5

Это зависит от реализации. По крайней мере, в Visual С++ без использования какой-либо из проверки привязки массива вы можете создать указатель на любое количество элементов, находящихся за концом массива. Если вы разыщите его, он будет работать до тех пор, пока адрес памяти, к которому вы обращаетесь, находится в пределах выделенной кучи/стека вашей программы. Вы будете читать/изменять любое значение в этой ячейке памяти. Если адрес находится за пределами выделенного пространства памяти, это приведет к ошибке.

Отладчики имеют проверки для их обнаружения, так как этот вид кодирования создает ошибки, которые очень трудно отслеживать.