Так стоп. После того, как я скастил 0x3 к указателю, с точки зрения синтаксиса программы последующее разыменование - это обращение к объекту.
У компилятора нет никакой возможности доказать, что там этого объекта нет. Этот объект может жить в другом TU. Следовательно компилятор ничего не остаётся, кроме как поверить мне на слово.
Я не согласен. Конечно синтаксически С++ достаточно прост (хотя и Haskell не сказал бы я, что сложен). Основная убер фича С++ - это UB. Писать код без него - подобно искусству каллиграфии. Haskell может быть сложен с той точки зрения, что требует некоторого математического бекграунда для понимания. Но с точки зрения способов выстрелить в себе в ногу С++ впереди всей планеты.
Что значит речь не о том? Я компилятору вполне внятно сказал "по этому адресу лежит объект с типом volatile int, я хочу в него записать значение, и меня вообще не волнует, что ты об этом думаешь". И компилятор обязан сгенерировать код. Без вариантов. Компилятор компилирует один TU, и может тупо не видеть этот int. Но будет он там или нет - дело даже не компилятора. Компилятор не располагает объекты в бинаре. Линковщик это делает.
В данном случае компилятор может вообще выкинуть этот код, если он докажет, что там лежит не инт.
Мне лень ходить в стандарт, поэтому я схожу на cppreference.
volatile object - an object whose type is volatile-qualified... volatile accesses cannot be optimized out or reordered with another visible side effect that is sequenced-before or sequenced-after the volatile access
А я утверждаю, что UB - это прежде всего behaviour. Компилятор всегда на строчку `*((volatile int*)0x3) = 1` сгенерирует один и тот же код. Он не может не сгенерировать код записи в память, когда его об этом явно просят. С точки зрения компилятора поведение очень defined.
А вот во время исполнения мы можем получить как defined, так и undefined поведение. Если мы создали все условия, чтобы по адресу 0x3 всё было валидно, значит поведение всегда будет well-defined. В ином случае - да UB.
При таком взгляде на проблему, линкер скрипты вполне себе обеспечивают гарантированно определённое поведение.
Не какой-то, а конкретный код генерируется. UB происходит не на стадии компиляции, а на стадии исполнения программы. Если по адресу записываемому адресу на момент исполнения программы всегда живёт объект нужного типа, абсолютно валидно туда что-то писать. До момента старта программы UB нет.
Вы просто пессимист. Ввиду того, что UB включает в себя всё что угодно, включая валидную программу на С++, можно сказать, что любая программа с UB написана на С++ :)
Кроме того, есть правила, описывающие, что конкретно надо сделать, чтобы там было что-то валидное. Если вы этого не сделали, то там ничего валидного нет.
Да, но не сказано ГДЕ нужно это сделать. Я могу это сделать в любом TU в программе. Это никак не меняет того факта, что если я разместил какой-то int где угодно в программе по адресу 0x3 и этот int всё еще там, пока я пишу в адрес 0x3 - это абсолютно валидный код, ничего не нарушающий, не создающий UB, и т.д. и т.п.
В С++ нет ничего синтаксически некорректного в желании записать что-то по любому адресу. Стандарт говорит: "Если по этому адресу лежит то, что ты туда пишешь, то всё нормально". А вот если "там лежит не то, что ты туда пишешь" или "там ничего не лежит", то тут UB.
И компилятор, который ничего не знает ни про какие ld-скрипты, вполне имеет право решить, что по этому адресу ничего нет.
Окей.
*((volatile int*)0x3) = 0xDEADBEEF
Теперь компилятор не имеет никаких прав и будет делать ровно то, что я прошу. А есть там какой объект проинициализированный или нет - это уже детали. Если есть - всё будет хорошо, это не UB и поведение нормальное. А вот если нет - то UB и результат непредсказуем. Но я подчеркну - в стандарте нет ни одного правила, запрещающего мне писать по любому адресу, какому захочу. Есть правила, говорящие, что если по этому адресу нет ничего валидного - беда-беда, но это не одно и то же.
То и значит, что "где угодно". Как пример: я могу написать в любом TU int anything, потом написать ld-скрипт, который переменную с именем anything расположит ровно по адресу 0x3. И будет её всегда туда располагать.
Соответственно на старте программы и проинициализируется этот адрес. Он будет валидный и туда можно будет писать.
О, гспди. Я отвечал на комментарий, который просил привести пример неопределенного поведения. Я привёл такой пример и специально подчеркнул, что понятия не имею, какой будет результат, как бы намекая на то, что определение будет неопределённым.
Так что здесь мы наблюдаем свидетельство появление зануды, не умеющего читать между строк.
? А объект там и не должен существовать. Это может система с физической адресацией, а адрес 0x3 - это захардкоженный адрес, который мапится на совершенно другое оборудование в системе (не RAM), которое способно понять, что такое 0xDEADBEEF.
Так стоп. После того, как я скастил
0x3к указателю, с точки зрения синтаксиса программы последующее разыменование - это обращение к объекту.У компилятора нет никакой возможности доказать, что там этого объекта нет. Этот объект может жить в другом TU. Следовательно компилятор ничего не остаётся, кроме как поверить мне на слово.
Я не согласен. Конечно синтаксически С++ достаточно прост (хотя и Haskell не сказал бы я, что сложен). Основная убер фича С++ - это UB. Писать код без него - подобно искусству каллиграфии. Haskell может быть сложен с той точки зрения, что требует некоторого математического бекграунда для понимания. Но с точки зрения способов выстрелить в себе в ногу С++ впереди всей планеты.
Что значит речь не о том? Я компилятору вполне внятно сказал "по этому адресу лежит объект с типом volatile int, я хочу в него записать значение, и меня вообще не волнует, что ты об этом думаешь". И компилятор обязан сгенерировать код. Без вариантов. Компилятор компилирует один TU, и может тупо не видеть этот int. Но будет он там или нет - дело даже не компилятора. Компилятор не располагает объекты в бинаре. Линковщик это делает.
Мне лень ходить в стандарт, поэтому я схожу на cppreference.
Да. Но это ведь не означает, что внутри UB нет валидной программы на С++ :)
А я утверждаю, что UB - это прежде всего behaviour. Компилятор всегда на строчку `*((volatile int*)0x3) = 1` сгенерирует один и тот же код. Он не может не сгенерировать код записи в память, когда его об этом явно просят. С точки зрения компилятора поведение очень defined.
А вот во время исполнения мы можем получить как defined, так и undefined поведение. Если мы создали все условия, чтобы по адресу 0x3 всё было валидно, значит поведение всегда будет well-defined. В ином случае - да UB.
При таком взгляде на проблему, линкер скрипты вполне себе обеспечивают гарантированно определённое поведение.
Не какой-то, а конкретный код генерируется. UB происходит не на стадии компиляции, а на стадии исполнения программы. Если по адресу записываемому адресу на момент исполнения программы всегда живёт объект нужного типа, абсолютно валидно туда что-то писать. До момента старта программы UB нет.
Вы просто пессимист. Ввиду того, что UB включает в себя всё что угодно, включая валидную программу на С++, можно сказать, что любая программа с UB написана на С++ :)
Да, но не сказано ГДЕ нужно это сделать. Я могу это сделать в любом TU в программе. Это никак не меняет того факта, что если я разместил какой-то int где угодно в программе по адресу 0x3 и этот int всё еще там, пока я пишу в адрес 0x3 - это абсолютно валидный код, ничего не нарушающий, не создающий UB, и т.д. и т.п.
В С++ нет ничего синтаксически некорректного в желании записать что-то по любому адресу. Стандарт говорит: "Если по этому адресу лежит то, что ты туда пишешь, то всё нормально". А вот если "там лежит не то, что ты туда пишешь" или "там ничего не лежит", то тут UB.
Но я же могу подключить её в С++? Вся эта магия - она в макросах, так что существует в заголовочном файле.
В таком случае никто в Embedded в принципе не программирует на С++)
Окей.
Теперь компилятор не имеет никаких прав и будет делать ровно то, что я прошу. А есть там какой объект проинициализированный или нет - это уже детали. Если есть - всё будет хорошо, это не UB и поведение нормальное. А вот если нет - то UB и результат непредсказуем. Но я подчеркну - в стандарте нет ни одного правила, запрещающего мне писать по любому адресу, какому захочу. Есть правила, говорящие, что если по этому адресу нет ничего валидного - беда-беда, но это не одно и то же.
Да гспди, загляните в HAL-библиотеку для STM32. Там всё в таком коде, пищущим по магическим адресам.
То и значит, что "где угодно". Как пример: я могу написать в любом TU
int anything, потом написать ld-скрипт, который переменную с именем anything расположит ровно по адресу 0x3. И будет её всегда туда располагать.Соответственно на старте программы и проинициализируется этот адрес. Он будет валидный и туда можно будет писать.
Здесь - нигде. Но в С++ он мог быт начат где угодно) Писать по вот таким рандомным адресам - обычное дело в embedded.
О, гспди. Я отвечал на комментарий, который просил привести пример неопределенного поведения. Я привёл такой пример и специально подчеркнул, что понятия не имею, какой будет результат, как бы намекая на то, что определение будет неопределённым.
Так что здесь мы наблюдаем свидетельство появление зануды, не умеющего читать между строк.
.
?
А объект там и не должен существовать. Это может система с физической адресацией, а адрес 0x3 - это захардкоженный адрес, который мапится на совершенно другое оборудование в системе (не RAM), которое способно понять, что такое
0xDEADBEEF.А я думаю будет 0 :) Потому по логике операция вычитания указателей должна дать количество элементов типа T, которые поместятся между двумя адресами.
Что касательно писать в 0х3 - нет ни одного правила в стандарте, запрещающего мне это сделать.