Структура определяется по-разному для C и C ++ - это безопасно?PC-Lint предупреждает - PullRequest
4 голосов
/ 28 февраля 2011

Следующее объявление добавляет пару операторов для компиляции в файл C ++.Определение включено в файлы C и C ++.

PC-Lint сообщает Ошибка 114: Несогласованное объявление структуры для тега 'Rect' , но я уверен, что это безопасно.

Я компилирую с Visual Studio 2008.

edit - добавление объяснения, которое я отправил своему клиенту

ОтносительноПрямой вопрос;как знание того, что структура имеет одинаковый размер в C и C ++, устраняет подозрение на «неопределенное поведение».

Неопределенное поведение возникает, если фактическое расположение полей в структуре данных меняется в зависимости от компиляции.

Вы должны думать о том, что доступ ко всем переменным-членам в конечном итоге разрешается указателю, вычисляемому указателем на начало хранилища объекта плюс смещение в зависимости от того, что находится в этой структуре.

Параметры упаковки и выравнивания данных влияют на значение смещения.

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

Однако, когда вы говорите о компиляции C и C ++ данной структуры в одном и том же компиляторе с одинаковыми настройками смещения, вероятностьфактическое переупорядочение фактически равно нулю.

Таким образом, единственное, что нам нужно беспокоиться, это любая разница в смещении полей.

Для структуры, содержащей простые 4 коротких целых числа, просто подтверждая, что CВерсия и версия C ++ имеют одинаковый размер, что гарантирует, что их смещения одинаковы.Чтобы быть более внимательным, мы также можем проверить, что структура size = 4 * sizeof (short).

Я думаю, что стоит добавить эти проверки, но как только это будет сделано, нет необходимости реорганизовывать код, как это потребуетсяиспользовать отдельные типы в C и C ++ (или переместить используемые функции в свободные функции).

/**
Mac-compatible rectangle type with some operators added for C++ use.
@ingroup QuickdrawPort
*/
struct Rect {
    short                           top;
    short                           left;
    short                           bottom;
    short                           right;

#ifdef __cplusplus
    Rect(short _top=0, short _left=0, short _bottom=0, short _right=0) :
        top(_top),
        left(_left),
        bottom(_bottom),
        right(_right)
    {}
    #ifdef _WINNT_  // WinDef.h has been included
        const Rect& operator=(const tagRECT& rhs) {
            top = short(rhs.top);
            left = short(rhs.left);
            bottom = short(rhs.bottom);
            right = short(rhs.right);
            return *this;
        }

        operator tagRECT() const {
            tagRECT lhs;
            lhs.top = top;
            lhs.left = left;
            lhs.bottom = bottom;
            lhs.right = right;
            return lhs;
        }

    #endif// _WINNT_
    short height() const { return bottom - top; }
    short width() const { return right - left; }
    bool empty() const { return right==left || bottom==top; }

    bool operator==(const Rect& rhs) const {
        return top == rhs.top &&
            left == rhs.left && 
            bottom == rhs.bottom &&
            right == rhs.right;
    }
#endif  
};
#ifndef __cplusplus
typedef struct Rect Rect;
#endif

1 Ответ

7 голосов
/ 28 февраля 2011

Правильно ли предположить, что заголовочный файл, содержащий это определение, включен в несколько блоков перевода, некоторые из которых скомпилированы как C ++, а некоторые - как обычный C?

Если это правда, тоу вас есть нарушение ODR , которое, согласно стандарту C ++, вызывает неопределенное поведение.

Существует два способа борьбы с ним.

  1. Игнорируйте этот случай неопределенного поведения, при условии, что вы знаете, как именно он проявляется на платформе (ах), которую вам необходимо поддерживать.(Обратите внимание, что неопределенное поведение может проявляться как правильно работающая программа).Честно говоря, с большинством компиляторов у вас не возникнет проблем.Однако вы должны понимать, что этот кодекс работает случайно, а не по закону.Компиляторам разрешено использовать другой макет для классов с функциями-членами, чем без них.(Я готов поспорить, что этот код сломается, например, CINT .)

  2. Устранить неопределенное поведение.Я бы предложил вам пойти по этому пути.Есть несколько возможностей сделать это.Например, вы можете наследовать «C ++ Rect» от «C Rect», оставляя последний как простой struct.

...