2016-07-13 3 views
0

У меня есть следующий фрагментС ++: странное поведение с reinterpret_cast

#include <iostream> 

using namespace std; 

class foobar 
{ 
    public: 
    unsigned int a = 97; 
    unsigned int b = 98; 
    unsigned int c = 99; 
}; 


class barfoo 
{ 
    public: 
    char a:32,b:32,c; 

}; 


int main(){ 
    foobar * f = new foobar(); 
    barfoo * b = (reinterpret_cast<barfoo *>(f)); 
    cout << b->a << "-" << b->b << "-" << b->c; 

} 

Выход:

a-b-c 

Однако, если указать ширину с в barfoo, он не работает (все значения равны 0). Наблюдайте

#include <iostream> 

using namespace std; 

class foobar 
{ 
    public: 
    unsigned int a = 97; 
    unsigned int b = 98; 
    unsigned int c = 99; 
}; 


class barfoo 
{ 
    public: 
    char a:32,b:32,c:32; 

}; 


int main(){ 
    foobar * f = new foobar(); 
    barfoo * b = (reinterpret_cast<barfoo *>(f)); 
    cout << b->a << "-" << b->b << "-" << b->c; 

} 

Выход:

-- 

В символы все 0

Любая идея, почему это происходит?

Ссылки на ideone first snippet - работа

Линка к ideone second snippet - не работает

Компилятор GCC-5.1 Спасибо !!

+1

Вы уверены, что 'char a: 32' может содержать 32 бита? Также я бы не ожидал слишком много согласованности при использовании UB. –

+5

Что вы делаете, называется Undefined Behavior. Поэтому поведение 'reinterpret_cast' является правильным в этом случае. Это сбой вашей программы также будет правильным поведением. Форматирование жесткого диска - правильное поведение. Правильное поведение демонов вылетает из вашего носа. Ничего не гарантировано. –

+0

@ πάνταῥεῖ Ну, он скомпилирован, и вывод выглядит правильно. Поэтому я склонен сказать «да». Кроме того, когда синтаксический анализ обрабатывается, он все еще остается только 8 бит, это всего лишь выравнивание полей. – Kariem

ответ

2

Сначала проверьте, имеют ли 2 класса одинаковый размер. Разумеется, отступы и выравнивание могут сделать позиции битовых полей a, b и c отличными от положений обычных членов a, b и c и т. Д. Почему вы используете char для 32-битных битовых полей? Мой компилятор предупреждает о битовом поле, превышающем размер типа.

В противном случае (если вы измените char на unsigned int для класса barfoo), независимо от любого стандартного-UB, брошенного на вас, reinterpret_cast и доступ имеют хорошие шансы на работу. Может быть, даже более того, если вы используете хорошо поддержанную и устоявшуюся идиому типа Punning через союз.

Тип-punning через объединение - это способ сообщить вашему компилятору, что «эти 2 указателя разных типов могут быть псевдонимом, не делайте радикальных оптимизаций, предполагая, что они не могут быть псевдонимом».

4

Это reinterpret_cast не отвечает требованиям, предъявляемым к определенным поведением, поэтому в то время как назначение это результат приемлем любой последующий доступ b не определено поведение:

barfoo * b = (reinterpret_cast<barfoo *>(f)); 

reinterpret_cast должны соответствовать одному из этих условий, в которых foobar является DynamicType и barfoo является AliasedType:

  • AliasedType является (возможно, CV-qualifie г) DynamicType
  • AliasedType и DynamicType оба (возможно, многоуровневые, возможно, резюме квалифицированного на каждом уровне) указатели на тот же тип Т
  • AliasedType является (возможно, резюме квалифицированного) знаком или без знака варианта DynamicType
  • AliasedType - это совокупный тип или тип объединения, который содержит один из вышеупомянутых типов как элемент или нестатический член (включая рекурсивно, элементы субагрегатов и нестатические члены данных объединенных объединений): это делает его безопасным для получения полезного указателя на структуру или объединение с указанием указателя на его нестатический элемент или элемент.
  • AliasedType является (возможно, резюме квалифицированных) базовый класс DynamicType
  • AliasedType является char или unsigned char: это позволяет экспертизу объекта представления любого объекта как массив беззнаковых символов.

Поскольку AliasedType не попадает ни в одну из этих условий за доступом в результате reinterpret_cast «s не определено поведение.

+2

Я не согласен. '' Reinterpret_cast' сам по себе не является неопределенным поведением - на самом деле страница, с которой вы связаны, указывает, что вы должны иметь возможность «reinterpret_cast» 'b' вернуться, чтобы получить тот же указатель, что и' f'. Он говорит только, что разыменование «b» является неопределенным поведением. – skyking

+0

@skyking Вы на 100% правильны. Прямая страница, на которую я ссылаюсь, говорит: «Приведение всегда выполняется успешно, но полученный указатель или ссылка могут использоваться только для доступа к объекту, если одно из следующих значений истинно». Я редактировал. Спасибо, что нашли время, чтобы читать и комментировать, а не голосовать и уходить. Вы являетесь благом для моего ответа конкретно и http://stackoverflow.com в целом! –

+0

Первая строка по-прежнему неверна (или, по крайней мере, запутывает) ... – skyking

Смежные вопросы