Изменение порядка доступа к нескольким изменчивым переменным в C

В этом примере:

volatile uint32_t * pOne = 0xDEADBEEF;
volatile uint32_t * pTwo = 0x0BADC0DE;

void same(void)
{
    uint32_t tmp;

    tmp = *pOne;   // A
    *pOne = 0;     // B
    *pOne = tmp;   // C
}

void different(void)
{
    uint32_t tmp;

    tmp = *pOne;
    *pOne = 0;     // E
    *pTwo = 0;     // F
    *pOne = tmp;
}

Насколько мне известно, компилятору C99 не разрешается переупорядочивать строки A, B и C в функции same(), поскольку все они относятся к одному и тому же изменчивому объекту. Но как насчет линий E и F в функции different()? Они взаимодействуют с разными летучими объектами.

  1. Разрешено ли компилятору C99 переупорядочивать строки E и F?

Мне не удалось найти ответ в самом стандарте, так как раздел 5.1.2.3 меня немного сбивает с толку. Так что если бы вы могли это объяснить, я был бы рад.

Я знаю, что это касается только переупорядочения компилятора и не влияет на переупорядочивание процессором.

  1. Так есть ли стандартная библиотека, которая (если реализована) обеспечивает барьеры памяти?

  2. На данный момент я придерживаюсь C99, но из любопытства: есть ли какие-либо изменения в C11?

C11 имеет <stdatomic.h>, который обеспечивает настоящие барьеры памяти. Для C99 стандарт действительно гарантирует, что компилятор не может переупорядочить E и F (потому что 5.1.2.3p2 говорит, что побочные эффекты все должны быть завершены в точке последовательности, и ничего не говорит о том, влияют ли они на один и тот же объект), но это буквально не требует барьера памяти, и компиляторы сильно различаются по тому, как они это реализуют. Если вы используете однопоточную реализацию, которая использует volatile для ввода-вывода с отображением в память, стандартных гарантий достаточно, но что-то мне подсказывает, что это не ваш сценарий.

Jeroen Mostert 03.09.2018 11:45

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

curiousguy 04.09.2018 19:06
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
2
268
2

Ответы 2

Реализации допускаются значительные усмотрения в отношении семантики объектов volatile. Наблюдаемое поведение программы должно согласовываться со всеми операциями со всеми изменчивыми объектами, выполняемыми в указанной последовательности, но реализации имеют полное разрешение делать почти все, что не влияет на наблюдаемое поведение программы. Реализации также имеют довольно широкие полномочия определять - в рамках «поведения, определяемого реализацией» - какие аспекты доступа с изменяемой привязкой являются «наблюдаемыми», а какие - нет.

Соответствующая реализация, которая не предназначена для встроенного программирования, может указывать, что доступ с квалификацией volatile будет вести себя странным и произвольным образом, что сделает его непригодным для таких целей. На большинстве платформ должно быть достаточно ясно, как качественная реализация, которая предназначена для встроенного программирования без использования директив, специфичных для компилятора, должна обрабатывать доступы, соответствующие требованиям volatile, и высококачественные реализации, предназначенные для такого использования, должны обрабатывать их таким образом даже хотя Стандарт не требует от них этого. К сожалению, некоторые разработчики компиляторов ограничивают свою семантику тем, что явно требует Стандарт, вместо того, чтобы расширять их семантику для соответствия базовой платформе (например, при нацеливании на платформы, на которых доступ к volatile может вести себя как вызовы подпрограмм, не меняйте порядок операций через доступ к volatile. если только его нельзя переупорядочить при вызове неизвестной функции). В то время как icc, похоже, обрабатывает записи, соответствующие требованиям volatile, таким образом, компиляторы, такие как gcc и clang, которые не предназначены для использования во встроенных системах с включенной оптимизацией, будут делать это только тогда, когда большинство оптимизаций отключено.

Из спецификации (6.7.3.6)

An object that has volatile-qualified type may be modified in ways unknown to the implementation or have other unknown side effects. Therefore any expression referring to such an object shall be evaluated strictly according to the rules of the abstract machine, as described in 5.1.2.3. Furthermore, at every sequence point the value last stored in the object shall agree with that prescribed by the abstract machine, except as modified by the unknown factors mentioned previously. What constitutes an access to an object that has volatile-qualified type is implementation-defined.

Получение / установка значения будет считаться "доступом" в большинстве, если не во всех, реализациях.

И из 5.1.2.3:

Accessing a volatile object, modifying an object, modifying a file, or calling a function that does any of those operations are all side effects, which are changes in the state of the execution environment. Evaluation of an expression may produce side effects. At certain specified points in the execution sequence called sequence points, all side effects of previous evaluations shall be complete and no side effects of subsequent evaluations shall have taken place. (A summary of the sequence points is given in annex C.)

In the abstract machine, all expressions are evaluated as specified by the semantics. An actual implementation need not evaluate part of an expression if it can deduce that its value is not used and that no needed side effects are produced (including any caused by calling a function or accessing a volatile object).

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

Конечно, многие компиляторы не на 100% соответствуют стандартам.

Здесь вы можете увидеть вывод ассемблера с различными компиляторами: https://godbolt.org/z/b0TNmT.

GCC, Clang и MSVC не изменяют порядок чтения и записи в сборке. (Хотя я не уверен, что это значит для самого исполняемого файла)

Другие вопросы по теме