Неопределенное поведение в RE2, которое заявлено как четко определенное

Недавно я обнаружил, что библиотека RE2 использует эта техника для быстрых поисков. Во время поиска он использует значения из неинициализированного массива, который, насколько я знаю, является неопределенным поведением.

Я даже нашел Эта проблема с valgrind предупреждениями об использовании неинициализированной памяти. Но проблема была закрыта с комментарием, что это поведение с отступом.

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

Вопрос в том, является ли законным использование неинициализированных данных в C ++?

0

Решение

Когда C был первоначально разработан, если arr был массив какого-то типа T занимающий N байты, выражение как arr[i] имел в виду «взять базовый адрес arr, добавлять i*N к нему, принеси N байтов в результирующий адрес, и интерпретировать их как T«. Если каждая возможная комбинация N байты будут иметь значение при интерпретации как тип Tвыборка неинициализированного элемента массива может привести к произвольному значению, но в противном случае поведение будет предсказуемым. Если T является 32-битным типом, попытка прочитать неинициализированный элемент массива типа T приведет к одному из не более 4294967296 возможных вариантов поведения; такое действие будет безопасным, если и только если каждое из этих 4294967296 поведений будет соответствовать требованиям программы. Как вы заметили, бывают ситуации, когда такая гарантия полезна.

Стандарт C, однако, описывает семантически более слабый язык, который не гарантирует, что попытка чтения неинициализированного элемента массива будет вести себя в соответствии с любым битовым шаблоном, который может содержать хранилище. Авторы компиляторов хотят обрабатывать этот более слабый язык, а не тот, который придумал Деннис Ритчи, потому что он позволяет им применять ряд оптимизаций, независимо от того, как они взаимодействуют. Например, если код выполняет a=x; и позже выполняет b=a; а также c=a;и если компилятор не может «видеть» что-либо между исходным назначением и последующими, которые могут измениться a или же x, он может опустить первое назначение и изменить последние два назначения на b=x; а также c=x;, Однако если между двумя последними заданиями что-то случится, это изменится x, что может привести к b а также c получать разные значения — то, что должно быть невозможно, если ничего не меняется a,

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

float *fp = malloc(4);
...
*fp = slowCalculation();
somethingElse = *fp;
free(fp);
int *ip = malloc(4);
...
a=*ip;
b=a;
...
c=a;

переписать как:

float *fp = malloc(4);
...
startSlowCalculation(); // Use some pipelined computation unit
int *ip = (int*)fp;
...
b=*ip;

*fp = resultOfSlowCalculation();  // ** Moved from up above
somethingElse = *fp;

...
c=*ip;

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

Лично я расцениваю философию авторов компиляторов как строго ошибочную: если программист в определенной ситуации знает, что гарантия будет полезной, то требование, чтобы программист обходил ее, будет стоить значительных затрат с уверенностью 100%. Напротив, требование о том, чтобы компилятор воздерживался от оптимизаций, основанных на отсутствии этой гарантии, редко стоило бы чего-либо (поскольку код, обходящий его отсутствие, почти наверняка заблокировал бы «оптимизацию» в любом случае). К сожалению, некоторые люди, похоже, больше заинтересованы в оптимизации производительности тех исходных текстов, которые не нуждаются в гарантиях, выходящих за рамки требований Стандарта, чем в оптимизации эффективности, с которой компилятор может генерировать код для выполнения полезных задач.

0

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]