exponenta event banner

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

Проблема

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

  • 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.

      Анализ останавливается после компиляции. Единицы перевода или предварительно обработанные файлы хранятся в архивированном файле 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 создан из команды построения с помощью команды polyspace-configure команда. Команда build создала несколько независимых двоичных файлов, но файлы, задействованные во всех двоичных файлах, были собраны в одном проекте Polyspace.

Решение

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

Если проект Polyspace был создан из команды build, а исходные файлы для независимых двоичных файлов были объединены, разбейте проект на модули при трассировке команды 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) оператор в файле заголовка, чтобы он применялся ко всем исходным файлам, включающим заголовок.