Почему C поддерживает отрицательные индексы массива?

Из этого поста в SO ясно, что C поддерживает отрицательные индексы.

  1. Зачем поддерживать такое потенциальное нарушение памяти в программе?

  2. Разве компилятор не должен выдавать предупреждение "Отрицательный индекс" хотя бы? (использую GCC)

  3. Или этот расчет выполняется во время выполнения?

РЕДАКТИРОВАТЬ 1: Кто-нибудь может намекнуть на его использование?

РЕДАКТИРОВАНИЕ 2: для 3.) Использование счетчиков циклов в [] массивов/указателей указывает расчет индексов во время выполнения.

Ответ 1

Вот пример использования.

Фильтр Infinite Impulse Response частично вычисляется из последних предыдущих выходных значений. Как правило, будет существовать некоторый массив входных значений и массив, где должны быть размещены выходные значения. Если текущий выходной элемент y i, тогда y i может быть рассчитан как y i= a 0 • x i + a 1 • x i-1 + a 2 • y i-1 + a 3 • y i-2.

Естественным способом написания кода для этого является что-то вроде:

void IIR(float *x, float *y, size_t n)
{
    for (i = 0; i < n; ++i)
        y[i] = a0*x[i] + a1*x[i-1] + a2*y[i-1] + a3*y[i-2];
}

Заметим, что когда i равно нулю, y[i-1] и y[i-2] имеют отрицательные индексы. В этом случае вызывающий отвечает за создание массива, установив исходные два элемента на "стартовые значения" для вывода (часто либо нулевые, либо значения, удерживаемые из предыдущего буфера), и передачу указателя туда, где первое новое значение должен быть написан. Таким образом, эта процедура, IRR, обычно получает указатель на середину массива и использует отрицательные индексы для адресации некоторых элементов.

Ответ 2

Расчет выполняется во время выполнения.

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

Например, скажем, у вас есть указатель, который в настоящее время указывает на 10-й элемент в массиве. Теперь, если вам нужно получить доступ к 8-му элементу без изменения указателя, вы можете сделать это легко, используя отрицательный индекс -2.

char data[] = "01234567890123456789";
char* ptr = &data[9];
char c = ptr[-2]; // 7

Ответ 3

Зачем поддерживать такое потенциальное нарушение памяти в программе?

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

Не должен ли компилятор выдавать предупреждение об отрицательном индексе? (я использую GCC)

По той же причине компилятор не будет предупреждать вас, когда вы обращаетесь к array[10], когда массив имеет только 10 элементов. Потому что это оставляет работу программистам.

Или этот расчет выполняется во время выполнения?

Да, расчет выполняется во время выполнения.

Ответ 4

Разрабатывая ответ Таймона:

float arr[10];
float *p = &arr[2];

p[-2]

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

Ответ 5

Индексы массива - это просто синтаксический сахар для разыменования указателей на произвольные места в памяти. Компилятор не может предупредить вас об отрицательных индексах, потому что он не знает во время компиляции, на который указывает указатель. Любое заданное арифметическое выражение указателя может привести или не привести к допустимому адресу для доступа к памяти.

Ответ 6

OP: Почему поддержка... потенциального нарушения памяти?

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

OP:... вывести предупреждение об отрицательном индексе...

Если это касается, используйте индекс unsigned или еще лучше, используйте size_t.

OP... расчет, выполненный во время выполнения?

Да, довольно часто, как в a[i], где i не является константой.

OP: намек на его использование?

Пример: один обрабатывает точку в массиве точек (Pt) и хочет определить, является ли промежуточная точка кандидатом для удаления, поскольку она является совместно инцидентом. Предположим, что вызывающая функция уже определила, что Mid не является ни первой, ни последней точкой.

static int IsCoincident(Pt *Mid) {
  Pt *Left = &Mid[-1];   // fixed negative index
  Pt *Right = &Mid[+1]; 
  return foo(Left, Mid, Right);
} 

Ответ 7

a[b] делает то же самое, что и *(a+b). Так как последний допускает отрицательный b, то и первый.

Ответ 8

Пример использования отрицательных индексов массива.

Я использую отрицательные индексы для проверки протоколов сообщений. Например, один формат протокола выглядит так:

<nnn/message/f>

или, в равной степени действительный:

<nnn/message>

Параметр f является необязательным и должен содержать один символ, если он указан.

Если я хочу получить значение символа f, я сначала получаю указатель на символ > и уменьшаю его один раз, чтобы получить значение f:

char * f_ptr = strchr(msg, '>');
char f_char = '1';  /* default value */

f_ptr--;  /* point to 'f', if supplied */

Теперь я проверяю, введено ли f (здесь используется отрицательный индекс массива):

if (f_ptr[-1] == '/')
{
    f_char = *f_ptr;
}

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