Подтвердить что ты не робот

Означает ли С++ стандартное описание оператора косвенного управления памятью запись не оптимизирована?

Это в основном продолжение этого вопроса. Пока это выглядит так, если у меня есть такая функция:

void SecureZeroMemory( void* ptr, size_t cnt )
{
   volatile char *vptr = (volatile char *)ptr;
   while (cnt) {
       *vptr = 0;
       vptr++;
       cnt--;
   }
}

и назовите его следующим образом:

{
    char buffer[size];
    SecureZeroMemory( buffer, size );
}

так как buffer не объявляется volatile, не имеет значения, что используется указатель на volatile - сами данные не являются изменчивыми, поэтому запись в переменную не является наблюдаемым поведением (1.9/6) и компилятором разрешено оптимизировать их.

Однако недавно я столкнулся с утверждением, что это имеет значение только для указателя указателя. В частности, С++ 03 5.3.1/1 описывает косвенность (*) следующим образом:

Унарный * оператор выполняет косвенное обращение [...] Если тип выражения является "указателем на T", то тип результата будет "T."

Таким образом, утверждение состоит в том, что из-за использования косвенности на volatile char* мы получаем volatile char, и записи на них составляют наблюдаемое поведение, и уже не имеет значения, как объявляются фактические данные.

Является ли описание косвенности С++ 03 5.3.1/1 действительно гарантией того, что перезапись памяти с помощью указателя volatile T*, как в примере выше, является наблюдаемым поведением и запрещена для оптимизации?

4b9b3361

Ответ 1

Я уверен, что все, что добавляет "новая" цитата, заключается в том, что *vptr является выражением lvalue с типом volatile char.

Тип lvalue не влияет на тип объекта, к которому относится это выражение lvalue, по той же причине, что указатель-на-const, который указывает на объект, не являющийся объектом const, не делает каким-либо образом объект Уст. Таким образом, исходный анализ не повлиял на эту цитату - объект по-прежнему не имеет изменчивого типа.

В обычном языке мы говорим, что тип *vptr равен volatile char &, но 5/5 говорит: "Если выражение первоначально имеет тип" ссылка на T ", тип доводится до T до любого дальнейший анализ". Предполагается, что причина, по которой *vptr имеет тип volatile char, а не volatile char & - перед анализом любого выражения, которое вы удаляете ссылку из этого типа, даже если это lvalue.

[Edit: мой ответ имел обыкновение иметь некоторый текст о cv-квалификациях, несущественный для неъектных значений целочисленного типа. Это было правдой (преобразования lvalue-to-rvalue неклассовых типов отбрасывают cv-квалификаторы, 4.1/1), но неуместно (я ошибочно полагал, что, поскольку в тексте, который вы цитировали, упоминается не ссылочный тип, он говорил о типе после этого преобразование)]

Ответ 2

Это интересный вопрос. Я думаю, что цель Стандарт заключался в том, что это должно сработать. При чтении стандарта (С++ 03, §1.9/6,7):

Наблюдаемое поведение абстрактной машины - ее последовательность чтения и записи на изменчивые данные и обращения к библиотечному вводу/выводу функции.

Доступ к объекту, обозначенному изменчивым значением lvalue, изменяющий объект, вызов функции ввода-вывода библиотеки или вызов функция, которая выполняет любую из этих операций, эффекты, которые являются изменениями в состоянии исполнения Окружающая среда. Оценка выражения может привести к последствия. В определенных определенных точках последовательности выполнения называемые точками последовательности, все побочные эффекты предыдущих оценки должны быть полными и никаких побочных эффектов последующие оценки должны иметь место.

Разница в формулировках в двух параграфах, по-видимому, Значительное: "наблюдаемое поведение" представляет собой последовательность чтений и записывает изменчивые данные. В вашем случае buffer нет волатильные данные, поэтому компилятор, по-видимому, может свободно оптимизировать доступ прочь. Я не думаю, что это намерение, но это, кажется, то, что он говорит.

В вашем случае оптимизация будет особенно проста, поскольку преобразование в летучее происходит в самой функции. Компилятор может легко определить, что vptr не указывает на данные, которые фактически нестабильны. Если вы измените параметр введите void volatile*, тогда компилятор должен будет увидеть как сайт вызова, так и функцию в то же время, чтобы безопасно выполнять оптимизацию.

И, наконец, независимо от того, что говорит стандарт, компиляторы имеют свои собственные интерпретации volatile. На практике, большинство, если не все компиляторы, предполагают, что вы используете volatile по какой-то причине, и будет генерировать машину инструкции для записи. (На практике это все, что они будут делать, что означает, что порядок, видимый за пределами потока код, в котором поток пробег остается undefined. Это не проблема для вашего использования, но это для многих других целей.)