электроника сәнгате
685 subscribers
698 photos
75 videos
56 files
344 links
сәлам дуслар! здесь схемотехника, pcb дизайн, микроконтроллеры, линукс встроенный и десктопный
Download Telegram
Forwarded from Грокаем C++
​​Volatile
#опытным

Ключевое слово, которое не embedded С++ разработчик вряд ли когда-нибудь встречал в код. Сегодня мы поговорим, для чего оно используется.

Предположим, что у нас есть переменная keyboard_press, память под которую замаплена на память устройства ввода-вывода. Когда нажимается кнопка клавиатуры, изменяется переменная keyboard_press. Оставим сам маппинг за скобками и попробуем написать какую-то детсадовскую логику с переменной keyboard_press:

int keyboard_press = 0;
size_t count_test = 0;

void some_function() {
while(keyboard_press == 0) {
count_test++;
}
// doing stuff
}


Что в ассемблере?

some_function():
mov eax, DWORD PTR keyboard_press[rip]
test eax, eax
jne .L1
.L3: // это кстати пустой бесконечный цикл, куда нельзя попасть и откуда нельзя выбраться
jmp .L3
.L1:
ret
count_test:
.zero 8
keyboard_press:
.zero 4


А где цикл? А где инкремент count_test?


На самом деле код собран с -О3 и компилятор просто выкинул цикл. Он не видит, что в данном коде где-то еще изменяется keyboard_press, поэтому разумно полагает, что мы написали бесконечный цикл без сайдэффектов, который вообще-то ub.

Но keyboard_press может изменяться, просто это никак не понятно по коду программы.

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

Однако компилятор точно видит тип переменной. И на него мы можем повлиять. Вот чтобы отучить компилятор от таких фокусов, нужно пометить keyboard_press ключевым словом volatile.

volatile int keyboard_press = 0;
size_t count_test = 0;

// same


Теперь ассемблер выглядит так:

some_function():
mov eax, DWORD PTR keyboard_press[rip]
test eax, eax
jne .L1
mov rax, QWORD PTR count_test[rip]
add rax, 1
.L3:
mov edx, DWORD PTR keyboard_press[rip]
mov rcx, rax
add rax, 1
test edx, edx
je .L3
mov QWORD PTR count_test[rip], rcx
.L1:
ret



Все, что делает volatile - все операции над переменной становятся видимыми спецэффектами и не могут быть оптимизированы компилятором. Ну и еще операции над volitile переменными не могут переупорядочиваться с другими видимыми спецэффектами в порядке кода программы.

Говорится ли здесь что-нибудь о потоках? Нет! Здесь говорится только об оптимизациях компилятора.

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

Доступ к volatile переменным не атомарный + с их помощью нельзя делать синхронизацию неатомарных переменных между потоками, так как volitile не подразумевает барьеров памяти.

Именно из-за этих ограничений volatile используется в очень узком спектре задач работы с I/O. Во всех остальных случаях в С++ используются атомики.

Don't be optimized out. Stay cool.

#cppcore #multitasking #memory
10❤‍🔥2👍2
Forwarded from Грокаем C++
​​WAT
#опытным

Спасибо, @Ivaneo, за любезно предоставленный примерчик в рамках рубрики #ЧЗХ.

"Век живи - век учись" - сказал Луций Сенека.

"Век живи - век учи С++" - реалии нашей жизни.

Просто посмотрите на следующий код:

struct Foo
{
void Bar();
};

void Foo::Foo::Foo::Foo::Foo::Foo::Foo::Foo::Foo::Foo::Foo::Foo::Bar()
{
printf("Foofoo!");
}

int main()
{
Foo f;
f.Bar();
return 0;
}


И он компилируется.

WAT?

Это называется injected class name. Имя класса доступно из скоупа этого же класса. Так сделано для того, чтобы поиск имени X внутри класса X всегда разрешался именно в этот класс.

Такое поведение может быть полезно в таком сценарии:

void X() { }
class X {
public:
static X Сreate() { return X(); }
};


injected class name гарантирует, что из метода Сreate будет возвращен именно инстанс класса Х, а не результат вызова функции Х.

Это также полезно внутри шаблонов классов, где имя класса можно использовать без списка аргументов шаблона, например, используя просто Foo вместо полного идентификатора шаблона Foo<blah, blah, blah>.

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

Так что это не у вас в глазах двоится, это плюсы такие шебутные)

Find yourself within. Stay cool.

#cppcore
4👍1🤯1