В идеале я бы предположил, что компилятор/компоновщик пометит это как ошибку, но это не так.
Вы предоставляете компилятору разные определения класса для одного и того же класса для разных единиц перевода. Это неопределенное поведение, поэтому ни один компилятор не обязан его диагностировать. Как упоминалось в комментариях, у разработчиков компиляторов есть другие заботы, чем пользователь компилятора, искажающий свои определения таким образом.
На более техническом уровне каждая единица перевода создает объектный файл. Объектные файлы связываются вместе компоновщиком. Но объектные файлы ничего не знают о классах, только о функциях. Таким образом, он не имеет явных сведений о размерах объектов или смещениях элементов.
Можно ли выдавать «комментарии компилятора» в объектных файлах, чтобы обнаружить это? Или это может быть частью символов отладки? Да, но это, вероятно, привело бы к значительному раздуванию и увеличению времени компиляции. Кроме того, не требуется, чтобы в бинарном файле вашей библиотеки было что-либо из этого, поэтому в этом случае это не поможет. Так что это будет ненадежная помощь в редком случае, когда пользователь запутается, со значительными недостатками.
Почему адрес sanitizer/valgrind не может обнаружить это, так как это похоже на запись в память, которая ему не принадлежит.
Я недостаточно знаю внутреннюю работу valgrind, чтобы дать здесь хороший ответ, но, по-видимому, str
доступ, который get
предполагает, что i
на самом деле находится внутри a
в main
, не кажется сразу подозрительным для valgrind, потому что начало str
все еще в пределах памяти, выделенной для A
. Если вы get
используете только один символ, оптимизация небольших строк также может привести к тому, что main
никогда не будет обращаться к A
за пределами тех первых нескольких байтов, зарезервированных для int
.
Кроме того, что нельзя использовать такие макросы в заголовках, как это обнаружить?
Это ужасная идея использовать макросы таким образом - именно потому, что эти проблемы почти невозможно обнаружить. Вы сами упомянули некоторые инструменты, которые могут быстро обнаружить «вредоносное» неопределенное поведение (например, std::vector, пытающийся управлять памятью после того, как его структура управления была перезаписана), и вы можете настроить свою операционную систему и компилятор так, чтобы ваша программа более строго соответствовала получать уведомления (например, -fstack-protector-all
в gcc, /Gs
и /RTCs
в MSVC, эти функции безопасности в более новых версиях Windows и т. д.), когда он делает что-то сомнительное.
Тем не менее, это по-прежнему неопределенное поведение, о котором мы говорим, поэтому нет гарантированного способа найти проблему, главным образом потому, что "все работает, как ожидалось, когда вы пытаетесь выявить проблемы" все еще находится в сфере «все могло случиться». Или, другими словами, может просто не быть симптомов, которые обнаруживаются этими инструментами, даже если ваша программа по-прежнему делает некоторые неправильные вещи.
person
Max Langhof
schedule
07.06.2018
int I
перезаписывается наstring str
немного непонятно. Каков ваш фактический и ожидаемый результат? - person Sean Monroe   schedule 07.06.2018I would expect the Compiler to at-least put up a warning here.
Как так? Он не знает, что вы определили свой класс по-разному в двух разных единицах компиляции. Какова цель определенияclass A
двумя разными способами? Используется ли он в двух разных программах? (Это было бы нормально). - person Paul Sanders   schedule 07.06.2018