Принудительно разрешить разыменование нулевого указателя

У меня есть очень старый (и огромный) проект Win32, который использует массовые проверки с указателем NULL путем преобразования указателя разыменования. Нравится:

int* x = NULL; //somewhere
//... code
if (NULL == &(*(int*)x) //somewhere else
    return;

И да, я знаю, что этот код глупый и требует рефакторинга. Но это невозможно из-за огромного количества кода. Прямо сейчас мне нужно скомпилировать этот проект под MacOS Sierra в Xcode, что приводит к большим проблемам ... Оказывается, в режиме выпуска (с оптимизацией кода) условие выполняется с некорректным поведением (так называемое поведение undefined из-за разыменования NULL указатель).

Согласно в этом документе для GCC есть опция -fno -delete-null-pointer-tests, но кажется, что он не работает для LLVM, когда включена оптимизация O1, O2 или O3. Возникает вопрос: как заставить компилятор LLVM 8.0 разрешить такое разыменование?

ОБНОВЛЕНИЕ. Реальный рабочий пример для проверки проблемы.

//somewhere 1
class carr
{
public:
    carr(int length)
    {
        xarr = new void*[length];

        for (int i = 0; i < length; i++)
            xarr[i] = NULL;
    }

    //some other fields and methods

    void** xarr;
    int& operator[](int i)
    {
        return *(int*)xarr[i];
    }
};

//somewhere 2
carr m(5);

bool something(int i)
{
    int* el = &m[i];
    if (el == NULL)
        return FALSE; //executes in debug mode (no optimization)

    //other code
    return TRUE; //executes in release mode (optimization enabled)
}

В -O0 и -O1 something сохраняет нулевую проверку, и код "работает":

something(int):                          # @something(int)
    pushq   %rax
    movl    %edi, %eax
    movl    $m, %edi
    movl    %eax, %esi
    callq   carr::operator[](int)
    movb    $1, %al
    popq    %rcx
    retq

Но на -O2 и выше оптимизирована проверка:

something(int):                          # @something(int)
    movb    $1, %al
    retq

person Community    schedule 02.12.2016    source источник
comment
Соответствующий отчет об ошибке. Это не многообещающе: флаг действительно пока игнорируется (сначала он не был распознан).   -  person Quentin    schedule 02.12.2016
comment
-fno-delete-null-pointer-checks не должен влиять на &*(int*)x, это должно быть разрешено NULL. Проверка с помощью clang на gcc.godbolt.org с помощью простого bool b(short *p) { return 0 == &*(int*)p; }, clang генерирует правильный код. Пожалуйста, опубликуйте минимально полную программу, в которой ваш компилятор генерирует неправильный код.   -  person    schedule 02.12.2016
comment
@hvd Я выложил реальный пример. Я не уверен, связана ли эта проблема с GCC, я видел это только в Apple LLVM 8.0   -  person    schedule 02.12.2016
comment
@hvd то, что возвращает &, не должно быть нулевым - это адрес чего-то. Разыменование нулевого указателя запускает UB, поэтому bool b(short *p) { return true; } будет допустимой оптимизацией вашей функции в соответствии со стандартом.   -  person Quentin    schedule 02.12.2016
comment
@Quentin Для C явно указано, что &*p разрешено, даже если p равно NULL, а для C ++ заявлено, что намерение совпадает с тем, что делают компиляторы. Другое дело со ссылками, но здесь ссылок нет. См. open-std.org/jtc1/sc22/wg21 /docs/cwg_active.html#232 Изменить: теперь в отредактированном вопросе есть ссылки. Что объясняет его.   -  person    schedule 02.12.2016
comment
@hvd Я запомнил это для C, но пропустил обсуждение этого в C ++. Не уверен, хочу ли я сейчас погрузиться в это, но спасибо: p   -  person Quentin    schedule 02.12.2016
comment
@Quentin, есть идеи, как исправить эту проблему, не переписывая код?   -  person    schedule 02.12.2016
comment
@AlekDepler Я немного покопался в документации, но решения не нашел. Если этот шаблон всегда один и тот же, вы можете обнаружить и заменить его с помощью регулярного выражения, но это не гарантирует, что ничего не останется. Изменить: подождите, нет, в вашем реальном случае вы не можете ...   -  person Quentin    schedule 02.12.2016
comment
Был бы дубликат, если бы вопрос OP не состоял в том, как мне все равно разрешить: Стандарт C ++: разыменование указателя NULL для получения ссылки? < / а>   -  person GSerg    schedule 02.12.2016
comment
Дублируется ли функция something () в различных формах в кодовой базе или только в нескольких местах? Как насчет класса carr? Ответ повлияет на возможные (практические) средства правовой защиты.   -  person Jeremy    schedule 02.12.2016
comment
Это похоже на вчерашний вопрос. Подавляющее большинство согласились с тем, что ваш код нужно переписать. Не бойтесь переписывать плохой код! Это важный шаг в любом процессе разработки.   -  person djgandy    schedule 02.12.2016
comment
Честно говоря, вам лучше исправить код. Это займет меньше времени, чем все время, которое вы потратите на отладку этого беспорядка. Код в примере можно исправить без изменения места вызова, если m[i] вернет прокси, который перегружает operator&.   -  person M.M    schedule 02.12.2016


Ответы (1)


Выполните текстовый поиск по NULL. Затем запустите компилятор в режиме предупреждения и распечатайте все предупреждения на бумаге (если такая технология у вас еще есть). Теперь для каждого нуля, это проблемный или хороший? Если проблематично, переименуйте его в XNULL.

Теперь вполне вероятно, что проверки C ++ могут потерпеть неудачу в небольшой системе с установленным, скажем, 640 КБ, потому что 640 КБ достаточно для всех, но не в вашей современной системе с большим количеством ГБ. Так что просто удалите их после повторной маркировки. Если это не так. сделать XNULL «фиктивным объектом» с действительным адресом в глазах C ++.

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

person Malcolm McLean    schedule 02.12.2016
comment
Это не решение - person ; 01.03.2017