Почему летучий нужен в C?

Вы не хотите использовать для этого регулярные выражения. Лучшим решением было бы загрузить ваше содержимое в DOMDocument и работать с ним с использованием дерева DOM и стандартных методов DOM:

$document = new DOMDocument();
$document->loadXML('');
$document->documentElement->appendChild(
    $document->createFragment($myTextWithTags));

$MY_TAGs = $document->getElementsByTagName('MY_TAG');
foreach($MY_TAGs as $MY_TAG)
{
    $xmlContent = $document->saveXML($MY_TAG);
    /* work on $xmlContent here */

    /* as a further example: */
    $ems = $MY_TAG->getElementsByTagName('em');
    foreach($ems as $em)
    {
        $emphazisedText = $em->nodeValue;
        /* do your operations here */
    }
}

377
задан Jonathan Leffler 27 May 2015 в 17:57
поделиться

16 ответов

Volatile говорит компилятору не оптимизировать ничего, что связано с переменной volatile.

Существует только одна причина использовать его: при взаимодействии с оборудованием.

Допустим, у вас есть небольшой аппарат, который где-то отображается в ОЗУ и имеет два адреса: командный порт и порт данных:

typedef struct
{
  int command;
  int data;
  int isbusy;
} MyHardwareGadget;

Теперь вы хотите отправить некоторое команда:

void SendCommand (MyHardwareGadget * gadget, int command, int data)
{
  // wait while the gadget is busy:
  while (gadget->isbusy)
  {
    // do nothing here.
  }
  // set data first:
  gadget->data    = data;
  // writing the command starts the action:
  gadget->command = command;
}

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

Чтобы обойти это, нужно объявить гаджет указателя как изменчивый. Таким образом, компилятор вынужден делать то, что вы написали. Он не может удалить назначения памяти, он не может кэшировать переменные в регистрах и не может изменить порядок назначений:

Это правильная версия:

   void SendCommand (volatile MyHardwareGadget * gadget, int command, int data)
    {
      // wait while the gadget is busy:
      while (gadget->isbusy)
      {
        // do nothing here.
      }
      // set data first:
      gadget->data    = data;
      // writing the command starts the action:
      gadget->command = command;
    }
374
ответ дан Vikas Goel 27 May 2015 в 17:57
поделиться

Это не позволяет компилятору автоматически изменять значения переменных. переменная переменная предназначена для динамического использования.

-2
ответ дан venu 27 May 2015 в 17:57
поделиться

Volatile может быть изменено извне скомпилированного кода (например, программа может отображать энергозависимую переменную в регистр с отображением в памяти.) Компилятор не будет применять некоторые оптимизации к коду, который обрабатывает энергозависимую переменную - например, он не загрузит его в регистр, не записав его в память. Это важно при работе с аппаратными регистрами.

3
ответ дан Ori Pessach 27 May 2015 в 17:57
поделиться

По моему мнению, вы не должны ожидать слишком многого от volatile. Чтобы проиллюстрировать это, посмотрите на пример из высоко оцененного ответа Нильса Пипенбринка .

Я бы сказал, его пример не подходит для volatile. volatile используется только для: предотвращения компилятора от полезных и желательных оптимизаций . Это не относится к потокобезопасности, атомарному доступу или даже к порядку памяти.

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

    void SendCommand (volatile MyHardwareGadget * gadget, int command, int data)
    {
      // wait while the gadget is busy:
      while (gadget->isbusy)
      {
        // do nothing here.
      }
      // set data first:
      gadget->data    = data;
      // writing the command starts the action:
      gadget->command = command;
    }

только gadget->data = data перед gadget->command = command гарантируется только в скомпилированном коде компилятором. Во время выполнения процессор все еще может переупорядочивать данные и назначение команд в соответствии с архитектурой процессора. Оборудование может получить неверные данные (предположим, гаджет сопоставлен с аппаратным вводом / выводом). Необходим барьер памяти между данными и назначением команд.

4
ответ дан Community 27 May 2015 в 17:57
поделиться

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

5
ответ дан rajeshsam 27 May 2015 в 17:57
поделиться

Вики говорят все о volatile:

И документация по ядру Linux также делает превосходный запись о volatile:

5
ответ дан Mahendra Gunawardena 27 May 2015 в 17:57
поделиться

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

7
ответ дан Undo 27 May 2015 в 17:57
поделиться

Другое использование для volatile - обработчики сигналов. Если у вас есть такой код:

quit = 0;
while (!quit)
{
    /* very small loop which is completely visible to the compiler */
}

Компилятору разрешено замечать, что тело цикла не касается переменной quit, и преобразовывать цикл в цикл while (true). Даже если переменная quit установлена ​​в обработчике сигналов для SIGINT и SIGTERM; компилятор не может этого знать.

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

165
ответ дан CesarB 27 May 2015 в 17:57
поделиться

Предельное использование для энергозависимых заключается в следующем. Допустим, вы хотите вычислить числовую производную функции f:

double der_f(double x)
{
    static const double h = 1e-3;
    return (f(x + h) - f(x)) / h;
}

Проблема в том, что x+h-x обычно не равно h из-за ошибок округления. Подумайте об этом: когда вы вычитаете очень близкие числа, вы теряете много значащих цифр, которые могут испортить вычисление производной (подумайте 1.00001 - 1). Возможный обходной путь может быть

double der_f2(double x)
{
    static const double h = 1e-3;
    double hh = x + h - x;
    return (f(x + hh) - f(x)) / hh;
}

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

    volatile double hh = x + h;
    hh -= x;

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

19
ответ дан Alexandre C. 27 May 2015 в 17:57
поделиться

Volatile также полезно, когда вы хотите заставить компилятор не оптимизировать конкретную кодовую последовательность (например, для написания микропрограммы).

10
ответ дан Diomidis Spinellis 27 May 2015 в 17:57
поделиться

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

171
ответ дан rphv 27 May 2015 в 17:57
поделиться

Мое простое объяснение таково:

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

Например:

bool usb_interface_flag = 0;
while(usb_interface_flag == 0)
{
    // execute logic for the scenario where the USB isn't connected 
}

Из приведенного выше кода компилятор может подумать, что usb_interface_flag определено как 0, и что в цикле while он будет равен нулю всегда. После оптимизации компилятор будет обрабатывать его как while(true) все время, что приведет к бесконечному циклу.

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

27
ответ дан PROGRAM_IX 27 May 2015 в 17:57
поделиться

Я упомяну другой сценарий, в котором летучие вещества важны.

Предположим, вы отображаете в памяти файл для более быстрого ввода-вывода, и этот файл может измениться за кулисами (например, файл не находится на вашем локальном жестком диске, а вместо этого обслуживается по сети другим компьютером).

Если вы обращаетесь к данным отображенного в памяти файла через указатели на энергонезависимые объекты (на уровне исходного кода), то код, сгенерированный компилятором, может извлекать одни и те же данные несколько раз, даже если вы об этом не знаете.

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

Если вы заботитесь о безопасности, и вам следует, это важный сценарий для рассмотрения.

10
ответ дан Alexey Frunze 27 May 2015 в 17:57
поделиться

Есть два варианта использования. Они особенно часто используются во встроенных разработках.

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

  2. Volatile используется для доступа к точным местам памяти в RAM, ROM и т. Д. ... Это чаще используется для управления отображаемыми в памяти устройствами, доступа к регистрам ЦП и определения местоположения определенных областей памяти.

См. Примеры с перечнем сборок. Re: Использование ключевого слова "volatile" на языке C во встроенных разработках

11
ответ дан Neo Cambell 27 May 2015 в 17:57
поделиться

См. Эту статью Андрея Александреску: « volatile - лучший друг многопоточного программиста »

Ключевое слово volatile было разработано для предотвращения оптимизации компилятора, которая может сделать код некорректным при наличии определенных асинхронных событий. Например, если вы объявляете примитивную переменную как volatile , компилятору не разрешается кэшировать ее в регистре - обычная оптимизация, которая будет иметь катастрофические последствия, если эта переменная будет разделена между несколькими потоками. Таким образом, общее правило: если у вас есть переменные примитивного типа, которые должны совместно использоваться несколькими потоками, объявите эти переменные volatile . Но вы можете сделать гораздо больше с этим ключевым словом: вы можете использовать его для перехвата кода, который не является потокобезопасным, и вы можете сделать это во время компиляции. Эта статья показывает, как это делается; решение включает в себя простой интеллектуальный указатель, который также упрощает сериализацию критических участков кода.

Статья относится как к C, так и к C++.

Также см. Статью « C ++ и опасности двойной проверки блокировки » Скотта Мейерса и Андрея Александреску:

Так что при работе с некоторыми областями памяти (например, памятью) сопоставленные порты или память, на которые ссылаются ISR [процедуры обработки прерываний]), некоторые оптимизации должны быть приостановлены. volatile существует для указания специального режима для таких расположений, а именно: (1) содержимое переменной volatile «нестабильно» (может изменяться неизвестным для компилятора способом), (2) все записи в volatile данные «наблюдаемы», поэтому они должны выполняться неукоснительно, и (3) все операции с изменчивыми данными выполняются в той последовательности, в которой они появляются в исходном коде. Первые два правила обеспечивают правильное чтение и письмо. Последний позволяет реализовать протоколы ввода / вывода, которые смешивают ввод и вывод. Это неофициально, что волатильные гарантии C и C ++.

30
ответ дан Robert S. Barnes 27 May 2015 в 17:57
поделиться

volatile говорит компилятору, что Ваша переменная может быть заменена другими средствами, чем код, который получает доступ к нему. например, это может быть ячейка памяти I/O-mapped. Если это не определяется в таких случаях, некоторые переменные доступы могут быть оптимизированы, например, ее содержание может быть сохранено в регистре и ячейке памяти не, чтение въезжает задним ходом снова.

56
ответ дан Chris Jester-Young 27 May 2015 в 17:57
поделиться
Другие вопросы по тегам:

Похожие вопросы: