Возьмите адрес one-past-the-end элемента массива через нижний индекс: законный по Стандарту C++ или нет?

Если то, что вы говорите о диспетчере SDK в OP, правильно, и если у вас есть доступ к SDK Manager, то оно показывает, где в настоящее время находится SDK.

enter image description here

Прямо под меню «Пакет и инструменты» отображается путь SDK.

Однако, если вы не можете получить доступ к SDK Manager, скорее всего, вы скачали либо ADT Bundle, либо SDK Installer.

Если это более ранняя версия, то загруженный вами файл называется: adt-bundle-windows-x86.zip . Если это позже, и если вы помните, что устанавливали его, то путь по умолчанию для установки находится в вашем Program Files\Android..... Программа установки называется примерно так: installer_r21.1-windows.exe (это текущая доступная версия)

Если ни одно из предложений на этой странице не поможет, то в крайнем случае это загрузить SDK снова и снова. При хорошем соединении, вы должны сделать около 15 с лишним минут. Скачать исходный код

Удачи в кодировании. ; -)

75
задан M.M 13 February 2016 в 00:02
поделиться

12 ответов

Ваш пример является допустимым, но только потому, что вы на самом деле не используете указатель за пределами границ.

Давайте сначала разберемся с указателями за пределами границ (потому что именно так я изначально интерпретировал ваш вопрос, прежде чем я заметил, что пример вместо этого используется указатель на один конец):

В общем, вам даже не разрешено создавать указатель за пределами границ. Указатель должен указывать на элемент в массиве или на элемент за концом . Нигде больше.

Указателю даже не разрешено существовать, что означает, что вам, очевидно, не разрешено разыменовать его.

Вот что стандарт должен сказать по этому поводу:

5.7: 5:

Когда выражение, имеющее интеграл тип добавляется или вычитается из указатель, результат имеет тип указатель-операнд. Если указатель операнд указывает на элемент объект массива, и массив большой достаточно, результат указывает на смещение элемента от оригинала элемент такой, что разница индексы результирующих и исходные элементы массива равны интегральное выражение. Другими словами, если выражение P указывает на i-й элемент объекта массива, выражения (P) + N (эквивалентно, N + (P)) и (P) -N (где N имеет значение n) указывают, соответственно, на i + n-й и i − n-й элементы объект массива, если они существуют. Более того, если выражение P указывает до последнего элемента массива объект, выражение (P) +1 балл один за последним элементом массива объект, и если выражение Q указывает один за последним элементом массива объект, выражение (Q) -1 указывает на последний элемент объекта массива. Если и операнд-указатель, и результат указывает на элементы того же объект массива или один за последним элемент объекта массива, оценка не должна давать переполнение; в противном случае поведение будет undefined .

(выделено мной)

Конечно, это для оператора +. Итак, на всякий случай, вот что стандарт говорит о индексировании массивов:

5.2.1: 1:

Выражение E1 [E2] идентично (по определению) * ((E1) + (E2))

Конечно, есть очевидная оговорка: в вашем примере на самом деле не отображается указатель выхода за пределы. он использует указатель «один за концом», который отличается. Указателю разрешено существовать (как сказано выше), но стандарт, насколько я понимаю, ничего не говорит о разыменовании его. Ближайшее, что я могу найти, это 3.9.2: 3:

[Примечание: например, адрес, следующий за концом массива (5.7), будет считаться указывают на несвязанный объект типа элемента массива, который может находиться по этому адресу. —End note]

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

Спасибо ilproxyil за исправление последнего бита здесь, отвечая на последняя часть вашего вопроса:

  • array + 5 на самом деле не разыменовать что-либо, это просто создает указатель на один за концом из разыменований массива .
  • & array [4] + 1 array + 4 (что совершенно безопасно), берет адрес этого lvalue, и добавляет к этому адресу единицу, которая приводит к указателю на один конец (но этот указатель никогда не получает разыменовано.
  • & array [5] разыменовывает массив + 5 (что, насколько я понимаю, законно, и приводит к "несвязанному объекту типа элемента массива ", поскольку выше сказано), а затем берет адрес того элемента, который также кажется достаточно законным.

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

38
ответ дан 24 November 2019 в 11:39
поделиться

Если ваш пример - НЕ общий случай, а конкретный, то он разрешен. Вы можете законно , AFAIK, переместить один за пределы выделенного блока памяти. Это не работает для общего случая, то есть когда вы пытаетесь получить доступ к элементам дальше на 1 от конца массива.

Только что выполнен поиск C-Faq: текст ссылки

-1
ответ дан 24 November 2019 в 11:39
поделиться

Стандарт C ++, 5.19, параграф 4:

Выражение константы адреса - это указатель на lvalue .... Указатель должен быть создан явно, используя унарный оператор & ... или выражение типа array (4.2) ... Оператор индексации [] ... может использоваться при создании выражения константы адреса, но к значению объекта нельзя получить доступ с помощью этих операторов. Если используется оператор индексации, один из его операндов должен быть целочисленным константным выражением.

Мне кажется, & array [5] является допустимым C ++, будучи адресным константным выражением.

0
ответ дан 24 November 2019 в 11:39
поделиться

Рабочий черновик ( n2798 ) :

"Результатом унарного оператора & является указатель на его операнд. Операнд должно быть lvalue или квалифицированным идентификатором. В первом случае, если тип выражение - «Т», тип результат - «указатель на T.» «(стр. 103)

array [5] не является квалифицированным идентификатором, насколько я могу судить (список находится на стр. 87); ближайшим, казалось бы, был идентификатор, но в то время как массив является идентификатором, array [5] - нет. Это не lvalue, потому что «lvalue относится к объекту или функции. "(стр. 76). array [5], очевидно, не является функцией, и не гарантируется, что он будет ссылаться на допустимый объект (поскольку array + 5 находится после последнего выделенного элемента массива).

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

Примечание. Допускается добавление, чтобы пройти за массивом (стр. 113):

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

Но это незаконно с помощью &.

2
ответ дан 24 November 2019 в 11:39
поделиться

Даже если это законно, зачем отступать от условностей? array + 5 в любом случае короче и, на мой взгляд, более читабелен.

Изменить: если вы хотите, чтобы он был симметричным, вы можете написать

int* array_begin = array; 
int* array_end = array + 5;
2
ответ дан 24 November 2019 в 11:39
поделиться

В дополнение к приведенным выше ответам я укажу оператор &, который можно переопределить для классов. Так что, даже если бы это было действительным для POD, вероятно, не рекомендуется делать это для объекта, который, как вы знаете, недействителен (во многом как переопределение оператора & () в первую очередь).

7
ответ дан 24 November 2019 в 11:39
поделиться

Я не считаю, что это незаконно, но я считаю, что поведение & array [5] не определено.

  • 5.2.1 [expr.sub] E1 [E2] идентичен (по определению) * ((E1) + (E2))

  • 5.3.1 [expr.unary.op] унарный оператор * ... результатом является lvalue, относящееся к объекту или функции, к которой точки выражения.

На данный момент у вас неопределенное поведение, потому что выражение ((E1) + (E2)) на самом деле не указывает на объект, и стандарт действительно говорит, каким должен быть результат, если это не так.

  • 1.3.12 [defns.undefined] Неопределенное поведение также может ожидаться, когда этот международный стандарт опускает описание любого явного определения поведения.

Как отмечено в другом месте, array + 5 and &array [0] + 5 - допустимые и четко определенные способы получения указателя, находящегося за пределами конца массива.

8
ответ дан 24 November 2019 в 11:39
поделиться

Я считаю, что это законно и зависит от того, как происходит преобразование «lvalue в rvalue». Последняя строка Core issue 232 содержит следующее:

Мы согласились, что подход в стандарте кажется нормальным: p = 0; *п; не является ошибкой по своей сути. Преобразование lvalue-to-rvalue дало бы ему поведение undefined

Хотя это немного другой пример, он показывает, что '*' не приводит к преобразованию lvalue в rvalue, и поэтому, учитывая, что выражение является непосредственным операнд '&', который ожидает lvalue, тогда поведение определяется.

9
ответ дан 24 November 2019 в 11:39
поделиться

Это допустимо.

Согласно документации gcc для C ++ , и array [5] являются допустимыми. И в C ++ , и в C вы можете безопасно адресовать элемент, находящийся за концом массива - вы получите действительный указатель. Таким образом, выражение & array [5] является допустимым.

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

На практике, я полагаю, это обычно не вызывает сбоя, хотя.

Изменить: Кстати, обычно так и реализуется итератор end () для контейнеров STL (как указатель на один за концом), так что это довольно хорошее свидетельство того, что практика является законной .

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

17
ответ дан 24 November 2019 в 11:39
поделиться

Это допустимо:

int array[5];
int *array_begin = &array[0];
int *array_end = &array[5];

Раздел 5.2.1 Индексы в выражении E1 [ E2] идентичен (по определению) * ((E1) + (E2))

Таким образом, мы можем сказать, что array_end также эквивалентен:

int *array_end = &(*((array) + 5)); // or &(*(array + 5))

Раздел 5.3.1.1 Унарный оператор '*': Унарный * оператор выполняет косвенное обращение: выражение, к которому он применяется, должно быть указателем на тип объекта, или указатель на тип функции и результатом является lvalue, относящееся к объекту или функции , на которые указывает выражение. Если тип выражения - «указатель на T», тип результата - «T.» [Примечание: указатель на неполный тип (другое чем cv void) можно разыменовать. Полученное таким образом lvalue можно использовать ограниченными способами (для инициализации ссылки, для пример); это lvalue не должно преобразовываться в rvalue, см. 4.1. - конец примечания]

Важная часть вышесказанного:

'результатом является lvalue, относящееся к объекту или функции'.

Унарный оператор '*' возвращает lvalue, относящийся к int (no де-референция). Унарный оператор '&' затем получает адрес lvalue.

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

Тот факт, что многие алгоритмы STL зависят от четко определенного поведения, является своего рода намеком на то, что комитет по стандартам уже об этом подумал, и я уверен, что есть что-то, что охватывает это явно.

В разделе комментариев ниже представлены два аргумента:

(пожалуйста, прочтите: но он длинный, и мы оба в конечном итоге троллишь)

Аргумент 1

это незаконно из-за параграфа 5 раздела 5.7

Когда выражение, имеющее целочисленный тип, добавляется к или вычитаемый из указателя, результат имеет тип операнда указателя. Если операнд-указатель указывает на элемент объекта массива, а массив достаточно велик, результат указывает на смещение элемента относительно исходного элемента, так что разность индексов результирующего и исходного элементов массива равна интегральному выражению. Другими словами, если выражение P указывает на i-й элемент объекта массива, выражения (P) + N (эквивалентно N + (P)) и (P) -N (где N имеет значение n) указывают соответственно к i + n-му и i - n-му элементам объекта массива, если они существуют. Более того, если выражение P указывает на последний элемент объекта массива, выражение (P) +1 указывает на один за последним элементом объекта массива, а если выражение Q указывает на один за последним элементом объекта массива, выражение (Q) -1 указывает на последний элемент объекта массива. Если и операнд-указатель, и результат указывают на элементы одного и того же объекта массива или на один прошлый последний элемент объекта массива, оценка не должна вызывать переполнения; в противном случае поведение не определено.

И хотя раздел актуален; он не показывает неопределенного поведения. Все элементы в массиве, о котором мы говорим, находятся либо внутри массива, либо на один за концом (что хорошо определено в предыдущем абзаце).

Аргумент 2:

Второй аргумент, представленный ниже: * - оператор отмены ссылки.
И хотя это общий термин, используемый для описания оператора '*'; этот термин намеренно избегается в стандарте, так как термин «отсылка» не очень хорошо определен с точки зрения языка и того, что он означает для базового оборудования.

Хотя доступ к памяти за пределами конца массива определенно неопределенное поведение. Я не уверен, что унарный оператор * обращается к памяти (читает / записывает в память) в этом контексте (не так, как это определяет стандарт). В этом контексте (как определено стандартом (см. П. 5.3.1.1)) унарный * оператор возвращает значение l, относящееся к объекту . В моем понимании языка это не доступ к основной памяти. Результат этого выражения затем немедленно используется унарным & operator Оператор, который возвращает адрес объекта, на который ссылается lvalue, относящийся к объекту .

Представлено много других ссылок на Википедию и неканонические источники. Все это я считаю неуместным. C ++ определяется стандартом .

Заключение:

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

  1. оставлю ответ.
  2. Введите заглавными буквами, это глупо, и я не прав, чтобы все читали.

Это не аргумент:

Не все во всем мире определяется Стандарт C ++. Открой свой разум.

4
ответ дан 24 November 2019 в 11:39
поделиться

Да, это законно. Из проекта стандарта C99 :

§6.5.2.1, параграф 2:

Постфиксное выражение, за которым следует выражение в квадратных скобках [] , является нижним индексом обозначение элемента объекта массива. Определение оператора индекса [] таково, что E1 [E2] идентично (* ((E1) + (E2))) . Из-за правил преобразования, которые применяется к двоичному оператору + , если E1 является объектом массива (эквивалентно указателем на начальный элемент объекта массива) и E2 является целым числом, E1 [E2] обозначает E2 -го элемент E1 (отсчет с нуля).

§6.5.3.2, параграф 3 (выделено мной):

Унарный оператор & возвращает адрес его операнда. Если операнд имеет тип " тип ", результат имеет тип «указатель на тип » ». Если операнд является результатом унарного оператора * , ни этот оператор, ни оператор & не оцениваются, и результат такой, как если бы оба были опущено, за исключением того, что ограничения на операторы все еще применяются, и результат не является lvalue. Аналогично, , если операнд является результатом оператора [] , ни оператор &, ни унарный * , подразумеваемый [] , не оцениваются, и результат такой, как если бы & оператор были удалены, а оператор [] заменен на оператор + . В противном случае результат будет указатель на объект или функцию, обозначенную его операндом.

§6.5.6, параграф 8:

Когда выражение, имеющее целочисленный тип, добавляется к указателю или вычитается из него, Результат имеет тип операнда-указателя. Если операнд указателя указывает на элемент объект массива, и массив достаточно велик, результат указывает на смещение элемента от исходный элемент такой, что разница индексов результирующего и исходного элементы массива равны целочисленному выражению. Другими словами, если выражение P указывает на i -й элемент объекта массива, выражения (P) + N (эквивалентно, N + (P) ) и (P) -N (где N имеет значение n ) указывают, соответственно, на i + n -й и i − n -ые элементы объект массива, если они существуют. Более того, если выражение P указывает на последний элемент объекта массива, выражение (P) +1 указывает на один за последним элементом объект массива, и если выражение Q указывает на один за последним элементом объекта массива, выражение (Q) -1 указывает на последний элемент объекта массива. Если оба указателя операнд и результат указывают на элементы одного и того же объекта массива или на один за последним элемент объекта массива, оценка не должна вызывать переполнения; в противном случае поведение не определено. Если результат указывает на один за последним элементом объекта массива, он не должны использоваться в качестве операнда унарного оператора * , который оценивается.

Обратите внимание, что стандарт явно разрешает указателям указывать на один элемент за концом массива, при условии, что они без разыменования . Согласно 6.5.2.1 и 6.5.3.2 выражение & array [5] эквивалентно & * (array + 5) , что эквивалентно (array + 5) , который указывает на один за концом массива. Это не приводит к разыменованию (по 6.5.3.2), поэтому это допустимо.

40
ответ дан 24 November 2019 в 11:39
поделиться

Это совершенно законно.

Класс шаблона vector <> из stl делает именно это, когда вы вызываете myVec.end (): он получает указатель (здесь как итератор), который указывает на один элемент за концом массива.

-2
ответ дан 24 November 2019 в 11:39
поделиться