Указатель на один перед первым элементом массива - PullRequest
8 голосов
/ 11 февраля 2020

В C сказано, что когда указатели ссылаются на один и тот же массив или один элемент после конца этого массива, арифметика и сравнения хорошо определены. Тогда как насчет одного перед первым элементом массива? Это нормально, если я не разыменую это?

Учитывая

int a[10], *p;
p = a;

(1) Законно ли писать --p?

(2) законно писать p-1 в выражении?

(3) Если (2) нормально, могу ли я утверждать, что p-1 < a?

Существует некоторая практическая проблема для этого. Рассмотрим функцию reverse(), которая переворачивает строку C, оканчивающуюся на '\0'.

#include <stdio.h>

void reverse(char *p)
{
    char *b, t;

    b = p;
    while (*p != '\0')
        p++;
    if (p == b)      /* Do I really need */
        return;      /* these two lines? */
    for (p--; b < p; b++, p--)
        t = *b, *b = *p, *p = t;
}

int main(void)
{
    char a[] = "Hello";

    reverse(a);
    printf("%s\n", a);
    return 0;
}

Действительно ли мне нужно выполнить проверку в коде?

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

Ответы [ 2 ]

6 голосов
/ 11 февраля 2020

(1) Допустимо ли писать --p?

Это «законно», так как в синтаксисе C это позволяет, но вызывает неопределенное поведение. Для нахождения соответствующего раздела в стандарте --p эквивалентно p = p - 1 (за исключением того, что p оценивается только один раз). Тогда:

C17 6.5.6 / 8

Если и операнд-указатель, и результат указывают на элементы одного и того же объекта массива или один после последнего элемента объекта массива оценка не должна приводить к переполнению; в противном случае поведение не определено.

Оценка вызывает неопределенное поведение, то есть не имеет значения, отменяете ли вы ссылку на указатель или нет - вы уже вызывали неопределенное поведение.

Более того:

C17 6.5.6 / 9:

Когда вычитаются два указателя, оба должны указывать на элементы одного и того же объекта массива или одного последнего последний элемент объекта массива;

Если ваш код нарушает «должен» в стандарте ISO, он вызывает неопределенное поведение.

(2) Это законно написать p-1 в выражении?

То же, что (1), неопределенное поведение.


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

0 голосов
/ 11 февраля 2020

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

Мне приходилось использовать подобные вещи только один раз в более 20 лет. Я писал функцию обратного вызова, но у меня не было доступа к нужным данным. Вызывающая функция предоставляла указатель внутри правильного массива, и мне был нужен байт непосредственно перед этим указателем.

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

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

Итак, a[-1] возможен, но должен использоваться ТОЛЬКО с очень хорошей заботой в очень специфических ситуациях. В противном случае, нет веских оснований для такого рода причинения вреда Вуду.


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


Ссылаясь на предоставленный код:

  • это НЕ ОК, чтобы использовать p [-1] с reverse(a);;
  • Нормально (-i sh) использовать его с reverse(a+1);, потому что вы остаетесь внутри массива.
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...