Что делает это сложное выражение с использованием volatile, указателей и выделения памяти под капотом? - PullRequest
0 голосов
/ 05 сентября 2018

Я знаю, что люди уже ответили, что такое волатильность, и что (int *) просто вызывает, но я не могу понять, что на самом деле скрывается под этим выражением: volatile int *p = (int *)0x0

Итак, у нас есть указатель p на int, который, очевидно, может неожиданно изменить его значение. Мы назначаем ему другой указатель, который указывает на адрес памяти 0? Так это указатель на указатель или я делаю его более сложным, чем должно быть? Я был бы очень признателен, если бы вы могли предоставить такой простой набросок, который поможет мне понять.

Sketch

Ответы [ 2 ]

0 голосов
/ 05 сентября 2018

Давайте проанализируем эту строку.

volatile int * p; объявляет указатель на volatile int. A volatile int из хранилища и семантики нормальный int. Но volatile указывает компилятору, что его значение может измениться в любое время или иметь другие побочные эффекты.

На правой стороне задания у вас есть (int *) 0x0. Здесь вы сообщаете компилятору, что он должен принимать указатель на int, который указывает на адрес 0x0.

Полное назначение volatile int * p = (int *) 0x0; присваивает p значение 0x0. Итак, в общей сложности вы сказали компилятору, что по адресу 0x0 это целочисленное значение, которое имеет побочные эффекты. Это может быть доступ по *p.

летучим * * 1 022 Вам, кажется, неясно, что означает изменчивый. Посмотрите на этот код: int * ptr = (int *) 0x0BADC0DE; *ptr = 0; *ptr = 1; Оптимизирующий компилятор кратко рассмотрит этот код и скажет: Установка *ptr в 0 не имеет никакого эффекта. Поэтому мы пропустим эту строку и сразу назначим 1, чтобы сохранить некоторое время выполнения и одновременно сжать двоичный файл. Таким образом, компилятор будет эффективно компилировать только следующий код: int * ptr = (int *) 0x0BADC0DE; *ptr = 1; Обычно это нормально, но когда мы говорим об операциях ввода-вывода с отображением в памяти, все становится иначе. IO с отображением в памяти - это некое оборудование, которым можно манипулировать, используя специальные адреса ОЗУ. Очень простым примером будет вывод - простой провод, выходящий из вашего процессора. Итак, давайте предположим, что по адресу 0x0BADC0DE вывод памяти. И когда мы записываем 1 в него, этот выход устанавливается на высокий уровень, а когда мы пишем 0, выход устанавливается на низкий уровень. Когда компилятор пропускает первое присваивание, вывод не изменяется. Но мы хотим сигнализировать о другом компоненте что-то с нарастающим фронтом. В этом случае мы хотим избавиться от оптимизации. И один из распространенных способов сделать это - использовать ключевое слово volatile. Он указывает компилятору, что он не может проверить каждый аспект доступа для чтения или записи в этот регистр. volatile int * ptr = (int *) 0x0BADC0DE; *ptr = 0; *ptr = 1; Теперь компилятор не оптимизирует доступ к *ptr, и мы можем сигнализировать о нарастающем фронте для внешнего компонента. Операции ввода-вывода с отображением в памяти важны при программировании встроенных систем, операционных систем и драйверов. Другим вариантом использования для ключевого слова volatile могут быть общие переменные в параллельных вычислениях (что в дополнение к ключевому слову volatile потребуется некоторый мьютекс семафора для ограничения одновременного доступа к этим переменным).

0 голосов
/ 05 сентября 2018

volatile Указатель NULL не имеет особого смысла, поскольку никто не собирается его разыменовывать.

Но если я сделаю это выражение более понятным (это пример микроконтроллера STM32)

 volatile uint32_t *GPIOA_CRL = (volatile uint32_t *)0x40010800UL;

Я объявляю volatile указатель GPIO_CLR на объект uint32_t. Присвоение преобразует адрес этого аппаратного регистра, заданного длинной без знака, в тип указателя.

0x40010800UL - адрес аппаратного регистра

(volatile uint32_t *) - преобразует длинный беззнаковый адрес в указатель

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

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

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