Неустойчивый доступ к члену структуры

Если доступ к члену структуры, отмеченному как volatile, из прерывания, нужно ли помечать всю цепочку доступа как volatile?

Например

struct bar
{
  volatile uint16_t a;
  volatile uint16_t b;
};

struct foo
{
  struct bar bar1;
  struct bar bar2;
};

struct foo foo_arr[10];

void interrupt_handler(void)
{
  struct bar *bar = &foo_arr[0].bar1;
  bar->a = 2;
  bar->b = 3;
}

Достаточно ли этого, чтобы участники a и b были отмечены как volatile?

Меня больше интересует волатильность foo_arr[0].bar1 = foo_arr[0].bar2;.

Ian Abbott 04.07.2024 11:54

Не дубликат, а наоборот.

Eric Postpischil 04.07.2024 13:59
Стоит ли изучать 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 называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
4
2
191
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Обычно бывает проблематично указать квалификаторы для отдельных членов структуры, а не для объекта структуры. В этом случае, поскольку каждый отдельный член является volatile, вы могли бы также сделать volatile struct bar bar1, разрешив существование экземпляров структуры, не являющихся volatile.

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


Что касается использования этих переменных, единственное, что имеет значение, — это тип, используемый для «доступа к значению lvalue» объекта-члена, который должен осуществляться через правильно определенный тип. Например, * (int*)&bar->a вызовет неопределенное поведение — то же самое, если вы использовали const в объявлении.

Компилятор также не может ничего предполагать об этой структуре, он должен воспринимать ее как volatile во всех отношениях. Однако это было бы гораздо яснее и самодокументируемым для программиста, если бы это было явно объявлено volatile на каждом шагу. Не объявлять что-либо volatile, когда намерение состоит в том, чтобы относиться к этому как к volatile, — это просто плохой стиль программирования. Но что касается языка C, в этом нет необходимости.

Вы можете легко это проверить: https://godbolt.org/z/rxqPe8abx Закомментируйте volatile, и компилятор (порт x86_64) оптимизирует записи в одну 32-битную запись, которая не будет соответствовать двум изменчивым переменным. С помощью volatile мы получаем две отдельные 16-битные записи, что требуется правилами выполнения программы C («абстрактная машина»).

Значит, компилятор будет рассматривать структуры и даже массив как изменчивые из-за двух членов?

Fredrik 04.07.2024 11:57

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

Ext3h 04.07.2024 12:09

@Fredrik Да, это то, что означает «компилятор не может ничего предполагать об этой структуре», поскольку он не может использовать какие-либо ярлыки или оптимизировать ее.

Lundin 04.07.2024 12:35

Поставьте себя на место компилятора.

Сначала давайте разложим выражение &foo_arr[0].bar1 синтаксически, в порядке старшинства:

  • foo_arr — имя вашего массива
  • foo_arr[0] получает доступ к смещению (0) и извлекает значение
  • foo_arr[0].bar1 получает доступ к фиксированному смещению внутри элемента массива и извлекает значение
  • &foo_arr[0].bar1 возвращает адрес последнего доступного значения

В этот момент у вас есть локальный указатель bar. На этом пути ничего не отмечено volatile и оно вам там не нужно. Сам массив выделяется статически и его расположение не может внезапно измениться. Это статический макет размера 10 * sizeof(struct foo).

Выражения bar->a и bar->b используются, когда вы действительно получаете доступ к данным в этой области. Компилятор сгенерирует код для разыменования указателя, а затем получит доступ к фиксированному смещению оттуда. Поскольку члены a и b отмечены volatile, с помощью выражения должна быть сгенерирована инструкция доступа к памяти.

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