Конфликтующие объявления в разных модулях перевода

Проблема

Анализ показывает ошибку или предупреждение, подобное одному из следующих сообщений об ошибке:

  • Declaration of [...] is incompatible with a 
    declaration in another translation unit ([...])

    Это сообщение появляется, когда конфликтующие объявления поступают не из одного и того же заголовочного файла.

  • Когда одно из конфликтующих объявлений находится в заголовочном файле.

    Declaration of [...] had a different meaning during compilation of [...] ([...])

    Это сообщение появляется, когда конфликтующие объявления поступают из одного и того же заголовочного файла, включенного в различные исходные файлы.

Ошибка указывает, что одна и та же переменная, функция или тип данных объявлены по-разному в разных модулях преобразования. Противоречивые заявления нарушают Правило единого определения (см. Стандарт C++, ISO/IEC 14882:2003, раздел 3.2). Когда происходят конфликтующие объявления, Polyspace® Code Prover™ не выбирает декларацию и продолжает анализ.

Общие цепочки инструментов компиляции часто не хранят информацию о типах данных в процессе связывания. Конфликтующие объявления не вызывают ошибок с вашим компилятором. Polyspace Code Prover придерживается более строгих стандартов для связывания, чтобы гарантировать отсутствие некоторых ошибок времени выполнения.

Чтобы идентифицировать первопричину ошибки:

  1. Из сообщения об ошибке идентифицируйте два исходных файла с конфликтующими объявлениями.

    Например, сообщение об ошибке выглядит следующим образом:

    C:\field.h, line 1: declaration of class "a_struct" had
          a  different meaning during compilation of "file1.cpp"
    | struct a_struct {
    |
    | Detected during compilation of secondary translation unit "file2.cpp"
    Сообщение показывает, что структура a_struct имеет конфликтующее объявление в file1.cpp и file2.cpp, оба из которых включают заголовочный файл field.h.

    Альтернативное сообщение об ошибке может выглядеть следующим образом:

    C:\field2.h, line 1: declaration of class "a_struct" had
          is incompatible with a declaration in another translation unit
    | the other declaration is at line 1 of field1.h"
    | struct a_struct {
    |
    | Detected during compilation of secondary translation unit "file2.cpp"
    Сообщение показывает, что структура a_struct имеет конфликтующее объявление в field2.h и field.h. Заголовочный файл field2.h включен в исходный файл file2.cpp.

  2. Попытайтесь идентифицировать конфликтующие объявления в исходных файлах.

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

    1. Перезапустите анализ с флагом -keep-relaunch-files так, чтобы все модули перевода были сохранены. В пользовательском интерфейсе введите флаг опции Other.

      Анализ останавливается после компиляции. Модули перевода или предварительно обработанные файлы хранятся в zipped файле ci.zip в подпапке .relaunch папки результатов.

    2. Разархивируйте содержимое ci.zip.

      Предварительно обработанные файлы имеют то же имя что и исходные файлы. Для образца предварительно обработанный файл с file1.cpp называется file1.ci.

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

Возможная причина: Переменная декларация и несоответствие определения

Объявление переменной не соответствует его определению. Для образца:

  • В объявлении и определении используются различные типы данных.

  • Переменная объявляется как подписанная, но определяется как беззнаковая.

  • В объявлении и определении используются различные типы проверки.

  • Переменная объявлена как массив, но задана как переменная, отличная от массива.

  • Для переменной массива в объявлении и определении используются различные размеры массива.

В этом примере код показывает ошибку связывания из-за несоответствия в классификаторах типов. Декларация в file1.c не использует классификаторы типов, но определяет в file2.c использует volatile квалификатор.

file1.cfile2.c
extern int x;           

void main(void)
{/* Variable x used */}
 volatile int x;

В этих случаях обычно можно определить различие, посмотрев на исходные файлы. Вам не нужно видеть предварительно обработанные файлы.

Решение

Убедитесь, что объявление переменной соответствует его определению.

Возможная причина: объявление функции и несоответствие определения

Объявление функции не соответствует его определению. Для образца:

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

  • В объявлении и определении используется разное количество аргументов.

  • Функция переменного аргумента или varargs объявлена в одной функции, но она вызывается в другой функции без предыдущего объявления.

    В этом случае в сообщении об ошибке указывается, что необходимый прототип для функции отсутствует.

В этом примере код показывает ошибку связывания из-за несоответствия типа возврата. Декларация в file1.c имеет возвращаемый тип int, но определение в file2.c имеет возвращаемый тип float.

file1.cfile2.c
int input(void);

void main() {
  int val = input();
}
float input(void) {
  float x = 1.0;
  return x;
}

В этих случаях вы обычно можете найти различие, посмотрев на исходные файлы. Вам не нужно видеть предварительно обработанные файлы.

Решение

Убедитесь, что объявление функции соответствует его определению.

Даже если процесс сборки допускает эти ошибки, у вас могут быть неожиданные результаты во время выполнения. Если в коде существует объявление функции и определение с конфликтующими прототипами, при вызове функции результат может оказаться неожиданным.

Для функции переменного аргумента или varargs объявите функцию перед вызовом. Если вы не хотите изменять свой исходный код, можно обойти эту ошибку привязки.

  1. Добавьте объявление функции в отдельный файл.

  2. Только для целей верификации, #include этот файл в каждом исходном файле при помощи опции Include (-include).

Возможная причина: конфликты из несвязанных деклараций

Вы используете то же имя идентификатора для двух несвязанных объектов. Это некоторые распространенные причины несвязанных объектов в том же проекте Polyspace:

  • Вы намеревались объявить объекты static чтобы они не имели внешних редактирований, но опускали static спецификатор.

  • Вы объявили один и тот же объект в нескольких исходных файлах вместо помещения объявления в заголовочный файл и включения в исходные файлы.

  • Вы создали проект Polyspace из команды build с помощью polyspace-configure команда. Команда build создала несколько независимых двоичных файлов, но файлы, задействованные во всех двоичных файлах, были собраны в один проект Polyspace.

Решение

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

Если ваш проект Polyspace был создан из команды сборки, и исходные файлы для независимых двоичных файлов были объединены, разделите проект на модули при трассировке вашей команды сборки. См. «Модуляризация анализа Polyspace при помощи команды Build».

Возможная причина: Макрозависимые определения

Определение переменной зависит от макроса, заданного ранее. Один исходный файл задает макрос, другой - нет, вызывая конфликты в определениях переменных.

В этом примере file1.cpp и file2.cpp включить заголовочный файл field.h. Заголовочный файл определяет структуру a_struct это зависит от определения макроса. Только один из двух файлов, file2.cpp, задает макрос DEBUG. Определение a_struct в модуле перевода с file1.cpp отличается от определения в модуле измерения file2.cpp.

file1.cppfile2.cpp
#include "field.h"

int main()
{
    a_struct s;
    init_a_struct(&s);
    return 0;
}
#define DEBUG

#include <string.h>
#include "field.h"

void init_a_struct(a_struct* s)
{
    memset(s, 0, sizeof(*s));
}

field.h:

struct a_struct {
    int n;
#ifdef DEBUG
    int debug;
#endif
};

Когда вы открываете предварительно обработанные файлы file1.ci и file2.ci, вы видите конфликтующие объявления.

file1.cifile2.ci
struct a_struct {
    int n;



};
struct a_struct {
    int n;

    int debug;

};

Решение

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

Возможная причина: ключевое слово переопределено как макрос

Ключевое слово переопределяется как макрос, но не во всех файлах.

В этом примере bool является ключевым словом в file1.cpp, но он переопределяется как макрос в file2.cpp.

file1.cppfile2.cpp
#include "bool.h"

int main()
{
    return 0;
}
#define false 0
#define true (!false)

#include "bool.h"

bool.h:

template <class T>
struct a_struct {
    bool flag;
    T t;
    a_struct() {
        flag = true;
    }
};

Решение

Будьте согласны с использованием ключевого слова на протяжении всей программы. Используйте ключевое слово, заданное в стандартном заголовке библиотеки, или переопределенную версию.

Возможная причина: Различия в упаковке структуры

A #pragma pack(n) оператор изменяет выравнивание упаковки структуры, но не во всех файлах. См. также Допущения о директивах # pragma.

В этом примере выравнивание упаковки по умолчанию используется в file1.cpp, но #pragma pack(1) оператор обеспечивает выравнивание упаковки 1 байт в file2.cpp.

file1.cppfile2.cpp
int main()
{
    return 0;
}
#pragma pack(1)

#include "pack.h"

pack.h:

struct a_struct {
    char ch;
    short sh;
};

Решение

Введите #pragma pack(n) оператор в заголовочном файле так, чтобы он применялся ко всем исходным файлам, которые включают заголовок.