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

В чем разница между статической глобальной и статической изменчивой переменной?

Я использовал статическую глобальную переменную и статическую изменчивую переменную в области файлов,

оба обновляются с помощью ISR, а основной цикл и основной цикл проверяет значение переменной.

здесь при оптимизации ни глобальная переменная, ни изменчивая переменная не оптимизируются. Поэтому вместо использования изменчивой переменной глобальная переменная решает проблему.

Так хорошо ли использовать глобальную переменную вместо volatile?

Любая конкретная причина использовать статическую неустойчивость??

Любая примерная программа будет заметна.

Спасибо заранее.

4b9b3361

Ответ 1

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

Global

Глобальный просто означает, что указанный идентификатор объявляется в области файлов. Существуют разные области действия, называемые функцией (где определены goto-метки), fi (где находятся глобальные группы), блок (где находятся обычные локальные переменные) и прототип функции (где находятся параметры функции). Эта концепция существует только для структурирования видимости идентификаторов. Это не имеет никакого отношения к оптимизации.

Static

static - это продолжительность хранения (мы не будем рассматривать это здесь) и способ дать имя, объявленное внутри внутренней области файла. Это может быть сделано для функций или объектов, требуемых только в одной единицы перевода. Типичным примером может быть функция help, распечатывающая принятые параметры и вызываемая только из функции main, определенной в том же файле .c.

6.2.2/2 в черновике C99:

Если объявление области действия идентификатор объекта или функции содержит спецификацию класса хранения статический, идентификатор имеет внутренний связь.

Внутренняя связь означает, что идентификатор не виден вне текущей единицы перевода (например, функция help выше).

Летучие

Летучие - это другое: (6.7.3/6)

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

Стандарт предоставляет отличный пример для примера, где volatile будет избыточным (5.1.2.3/8):

Реализация может определить взаимно однозначное соответствие между абстрактная и фактическая семантика: при каждой точке последовательности, значения фактические объекты согласятся с те, которые указаны абстрактными семантика. Ключевое слово volatileтогда будет избыточным.

Точки последовательности - это точки, где эффект побочных эффектов, относящихся к абстрактной машине, завершен (например, внешние условия, такие как значения ячейки памяти, не включены). Между правым и левым от && и ||, после ; и возврата из вызова функции являются, например, точки последовательности.

Абстрактная семантика - это то, что компилятор может вывести из наблюдения только последовательности кода внутри конкретной программы. Эффекты оптимизации здесь неактуальны. Фактическая семантика включает эффект побочных эффектов, выполняемых путем записи объектов (например, смены ячеек памяти). Квалифицируя объект как изменчивое средство, вы всегда получаете значение объекта прямо из памяти ( "как изменено неизвестными факторами" ). В стандарте нигде не упоминаются потоки, и если вы должны полагаться на порядок изменений или на атомарность операций, вы должны использовать зависящие от платформы способы, чтобы это гарантировать.

Для простого понимания обзора у Intel есть отличная статья об этом здесь.

Что мне теперь делать?

Продолжайте объявлять данные своей области (глобальные) как изменчивые. Глобальные данные сами по себе не означают, что значение переменных будет равно значению, хранящемуся в памяти. И static делает только локальные объекты для текущей единицы перевода (текущие файлы .c и все остальные файлы # include'ed им).

Ответ 2

Сначала позвольте мне упомянуть, что статическая глобальная переменная совпадает с глобальной переменной, за исключением того, что вы ограничиваете переменную областью действия файла. То есть вы не можете использовать эту глобальную переменную в других файлах с помощью ключевого слова extern.

Итак, вы можете свести свой вопрос к глобальным переменным vs volatile variables.

Теперь на volatile:

Подобно const, volatile является модификатором типа.

Ключевое слово volatile было создано для предотвращения оптимизации компилятора, которая может сделать код неправильным, особенно когда есть асинхронные события.

Объекты, объявленные как volatile, не могут использоваться при определенных оптимизации.

Система всегда считывает текущее истинное значение изменчивого объекта в той точке, в которой оно используется, даже если предыдущая команда запрашивала значение от одного и того же объекта. Кроме того, значение объекта записывается сразу при назначении. Это означает, что кэширование изменчивой переменной в регистр CPU отсутствует.

Dr. Jobb имеет отличную статью об изменчивости.

Вот пример из статьи д-ра Джобба:

class Gadget
{
public:
    void Wait()
    {
        while (!flag_)
        {
            Sleep(1000); // sleeps for 1000 milliseconds
        }
    }
    void Wakeup()
    {
        flag_ = true;
    }
    ...
private:
    bool flag_;
};

Если компилятор видит, что Sleep() является внешним вызовом, предполагается, что Sleep() не может изменить значение переменной flag_. Поэтому компилятор может хранить значение flag_ в регистре. И в этом случае он никогда не изменится. Но если другой поток вызывает пробуждение, первый поток все еще считывает из регистра CPU. Wait() никогда не будет пробуждаться.

Итак, почему бы просто не кэшировать переменные в регистры и не полностью устранить проблему? Оказывается, эта оптимизация может действительно сэкономить вам много времени. Таким образом, C/С++ позволяет явно отключить его с помощью ключевого слова volatile.

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

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

volatile с указателями:

Неустойчиво с указателями, работает как const с указателями.

Переменная типа volatile int * означает, что переменная, на которую указывает указатель, является изменчивой.

Переменная типа int * volatile означает, что сам указатель нестабилен.

Ответ 3

Ключевое слово "volatile" предлагает компилятору не выполнять определенные оптимизации кода, включающего эту переменную; если вы просто используете глобальную переменную, ничто не мешает компилятору неправильно оптимизировать ваш код.

Пример:

#define MYPORT 0xDEADB33F

volatile char *portptr = (char*)MYPORT;
*portptr = 'A';
*portptr = 'B';

Без "volatile" первая запись может быть оптимизирована.

Ответ 4

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

Изменчивая переменная в Википедии

Ответ 5

Они могут не отличаться в вашей текущей среде, но тонкие изменения могут повлиять на поведение.

  • Различные аппаратные средства (больше процессоров, различная архитектура памяти)
  • Новая версия компилятора с лучшей оптимизацией.
  • Случайное изменение времени между потоками. Проблема может произойти только один раз в 10 миллионов.
  • Различные настройки оптимизации компилятора.

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

Конечно, если ваша программа не многопоточная, это не имеет значения.

Ответ 6

I +1 friol ответ. Я хотел бы добавить некоторые исправления, поскольку в разных ответах, похоже, много путаницы: C volatile не является Java-volatile.

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

Летучие в C не имеют НИЧЕГО делать с аппаратными кэшами или многопоточными. Он не вставляет забор памяти, и у вас нет абсолютно никакой гарантии по порядку операций, если к нему обращаются два потока. Java volatile ключевое слово делает именно это: вставляя заграждения памяти там, где это необходимо.

Ответ 7

volatile переменная означает, что значение, полученное для него, не является постоянным, то есть если функция, содержащая изменчивую переменную "a = 10", и функция добавляет 1 в каждый вызов этой функции, тогда она всегда будет возвращать обновленное значение. { volatile int a=10; a++; } когда вышеуказанная функция вызывается снова и снова, переменная a не будет повторно инициализирована до 10, она всегда будет показывать обновленное значение до запуска программы. 1-й выход = 10 затем 11 затем 12 и т.д.