скажем у меня есть:
int test[10];
на 32-битной машине. Что делать, если я делаю:
int b = test[-1];
Очевидно, что это большой отказ, когда дело доходит до доступа к массиву (вне границ), но что на самом деле происходит? Просто любопытно
Я получаю доступ к 32-битному слову «до» моего массива?
int b = *(test - 1);
или просто адрес очень далекого слова (начиная с «тестовой» ячейки памяти)?
int b = *(test + 0xFFFFFFFF);
0xFFFFFFFF — это двоичное представление в виде десятичного числа -1
Поведение вашей программы не определено, так как вы пытаетесь получить доступ к элементу за пределами массива.
Это может произойти так: если у вас 32-битный тип int, вы обращаетесь к 32-битной памяти в стеке (если есть) перед тестом [0] и приводите его к типу int. Ваш процесс может даже не владеть этой памятью. Нехорошо.
Что бы ни случилось, вы получите неопределенное поведение, поскольку арифметика указателя определяется только в массиве (включая позицию «один за другим»).
Лучший вопрос может быть:
int test[10];
int * t1 = test+1;
int b = t1[-1]; // Is this defined behaviour?
Ответ на это да. Определение подписки (C ++ 11 5.2.1):
Выражение E1 [E2] идентично (по определению) * ((E1) + (E2))
так что это эквивалентно *((t1)+(-1))
, Определение добавления указателя (C ++ 11 5.7 / 5) предназначено для всех целочисленных типов, со знаком или без знака, поэтому ничего не вызовет -1
быть преобразованным в неподписанный тип; поэтому выражение эквивалентно *(t1-1)
, который четко определен, так как t1-1
находится в пределах массива.
Стандарт C ++ говорит, что это неопределенное поведение и незаконно. На практике это означает, что что-нибудь может случиться, и все, что вы можете придумать, может варьироваться в зависимости от аппаратного обеспечения, компилятора, параметров и всего, что вы можете себе представить. Поскольку что-то может произойти, нет большого смысла рассуждать о том, что может произойти с конкретной комбинацией аппаратного обеспечения / компилятора.
Официальный ответ: поведение не определено. Неофициально вы пытаетесь получить доступ к целому числу до начала массива. Это означает, что вы указываете компьютеру рассчитывать адрес, который предшествует началу массива, на 4 байта (в вашем случае). Будет ли эта операция успешной или нет, зависит от нескольких факторов. Некоторые из них связаны с тем, будет ли массив размещаться в сегменте стека или в сегменте статических данных, где, в частности, будет расположение этого адреса. На машине общего назначения (windows / linux) вы, вероятно, получите в результате значение мусора, но это также может привести к ошибке нарушения памяти, если адрес окажется где-то, к которому у процесса нет доступа. Что может случиться на специализированном оборудовании, можно только догадываться.