Я могу взять адрес one-past-the-end элемента массива? [дубликат]

16
задан Community 23 May 2017 в 11:54
поделиться

5 ответов

Да, вы можете взять адрес за пределами конца массива, но вы не можете его разыменовать. Для вашего массива из 10 элементов подойдет массив + 10 . Несколько раз (в том числе комитетом) утверждалось, действительно ли & array [10] вызывает неопределенное поведение или нет (и если да, то действительно ли должно). Суть в том, что, по крайней мере, в соответствии с текущими стандартами (как C, так и C ++) он официально вызывает неопределенное поведение, но если есть один компилятор, для которого он фактически не работает, никто ни по одному из аргументов не смог найти или процитировать его.

Править: На этот раз я наполовину правильно помнил - это был (часть) официального отчета о дефектах для комитета, и, по крайней мере, некоторые члены комитета (например, Том Плам) подумали, что формулировка была был изменен таким образом, чтобы не приводили к неопределенному поведению. OTOH, DR датируется 2000 годом, и статус по-прежнему «черновой», поэтому остается открытым вопрос, действительно ли он исправлен или когда-либо будет (я не просматривал N3090 / 3092, чтобы понять).

Однако в C99 это явно не неопределенное поведение.

24
ответ дан 30 November 2019 в 16:57
поделиться

Нет. Это не определено. массив [10] составляет * (массив + 10) . Другими словами, вы разыменовали недопустимый указатель.

1
ответ дан 30 November 2019 в 16:57
поделиться

массив [10] эквивалентен * (массив + 10) (а также эквивалентен 10 [массив] ), поэтому & array [10] действует как удаление * из * (array + 10) . Любой достойный компилятор должен выдавать один и тот же код (и такое же предупреждение).

2
ответ дан 30 November 2019 в 16:57
поделиться

Как указывалось в других ответах, выражение array [10] эквивалентно * (array + 10) , что, по-видимому, приводит к неопределенное разыменование элемента сразу за концом массива. однако выражение & array [10] эквивалентно & * (array + 10) , а в стандарте C99 ясно сказано (6.5.3.2 Операторы адресации и косвенного обращения):

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

Итак, в и array [10] нет ничего неопределенного - операции разыменования не происходит.

10
ответ дан 30 November 2019 в 16:57
поделиться

На самом деле ответить на этот вопрос довольно просто.

Все, что вы делаете, это математика указателя, в этом нет ничего недействительного. Если вы каким-то образом попытаетесь ИСПОЛЬЗОВАТЬ полученные указатели, это будет зависеть от того, имеет ли ваш процесс доступ к памяти, на которую указывает этот указатель, и если да, то какие у него разрешения?

foo.cc:

#include <iostream>

using namespace std;
int main() {
        int array[10];
        int *a = array + 10;
        int *b = &array[10];
        int *c = &array[3000];

        cerr << "Pointers values are: " << a << " " << b << " " << c << endl;
        return 0;
}

Для компиляции:

g++ -Werror -Wall foo.cc -o foo

не должно выдавать НИКАКИХ предупреждений, потому что int * b = & array [10] действителен

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

foo не только скомпилируется, но и будет работать без сбоев:

 ./foo
  Pointers are: 0x7fff1d356e68 0x7fff1d356e68 0x7fff1d359d20

В основном синтаксис массива foo [idx] является сокращенным и чистым представлением математики указателя.

Если есть некоторая путаница относительно c99, стандарт НЕ ВЛИЯЕТ на эту математику в любом случае и четко определен.

То же самое в C99:

#include <stdio.h>

int main() {
        int array[10];
        int *a = array + 10;
        int *b = &array[10];
        int *c = &array[3000];

        fprintf(stderr, "Pointers are: %p, %p, %p\n" , a , b , c );
        return 0;
}

Затем:

gcc -std=c99 -Wall -Werror -o foo foo.c

./foo

Выводит:

Pointers are: 0x7fff2c7d22c8, 0x7fff2c7d22c8, 0x7fff2c7d5180
-2
ответ дан 30 November 2019 в 16:57
поделиться
Другие вопросы по тегам:

Похожие вопросы: