exponenta event banner

CERT C++: DCL60-CPP

Подчиняйтесь правилу одного определения

Описание

Определение правила

Подчиняйтесь правилу одного определения. [1 ]

Внедрение Polyspace

Эта проверка проверяет наличие:

  • Встроенное ограничение не соблюдается

  • Неидентичные определения функций или объектов в модулях

Примеры

развернуть все

Проблема

Встроенное ограничение не соблюдается при ссылке на изменяемую статическую переменную области действия файла или определении локальной изменяемой статической переменной в нестатической встроенной функции. Средство проверки считает переменную изменяемой, если она не является const-квалифицирован.

Например, var является изменяемым static переменная, определенная в inline функция func. g_step является изменяемой статической переменной области действия файла, на которую ссылается одна и та же встроенная функция.

static int g_step;
inline void func (void) {
   static int var = 0;
   var += g_step;
}

Риск

При изменении статической переменной в нескольких вызовах функций ожидается изменение одной и той же переменной в каждом вызове. Например, каждый раз, когда вы звоните func, тот же экземпляр var1 увеличивается, но отдельный экземпляр var2 увеличивается.

void func(void) {
   static var1 = 0;
   var2 = 0;
   var1++;
   var2++;
}

Если функция имеет встроенное и неинлинированное определение в отдельных файлах, при вызове функции стандарт C позволяет компиляторам использовать либо встроенную, либо неинлинированную форму (см. ISO ®/IEC 9899:2011, sec. 6.7.4). Если компилятор использует встроенное определение в одном вызове, а неинлинированное определение в другом, изменение одной и той же переменной в обоих вызовах больше не выполняется. Это поведение не соответствует ожиданиям от статической переменной.

Зафиксировать

Используйте одно из следующих исправлений:

  • Если изменение переменной не планируется, объявите ее как const.

    Если переменная не изменяется, то о неожиданном изменении речи не идет.

  • Сделать переменную не -static. Удалить static квалификатор из объявления.

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

  • Выполнение функции static. Добавить static квалификатор к определению функции.

    При выполнении функции staticфайл с встроенным определением использует встроенное определение при вызове функции. Другие файлы используют другое определение функции. Компилятор не решает, какое определение функции используется.

Пример - Использование статической переменной в встроенном и внешнем определении
/* file1. c  : contains inline definition of get_random()*/

inline unsigned int get_random(void) 
{

    static unsigned int m_z = 0xdeadbeef; //Noncompliant
    static unsigned int m_w = 0xbaddecaf; //Noncompliant

    /* Compute next pseudorandom value and update seeds */
    m_z = 36969 * (m_z & 65535) + (m_z >> 16); 
    m_w = 18000 * (m_w & 65535) + (m_w >> 16); 
    return (m_z << 16) + m_w;   
}


int call_get_random(void)
{
    unsigned int rand_no;
    int ii;
    for (ii = 0; ii < 100; ii++) {
         rand_no = get_random();
    }
    rand_no = get_random();
    return 0;
}
/* file2. c  : contains external definition of get_random()*/

extern unsigned int get_random(void)
{
    /* Initialize seeds */
    static unsigned int m_z = 0xdeadbeef;
    static unsigned int m_w = 0xbaddecaf;
    
    /* Compute next pseudorandom value and update seeds */
    m_z = 36969 * (m_z & 65535) + (m_z >> 16);
    m_w = 18000 * (m_w & 65535) + (m_w >> 16);
    return (m_z << 16) + m_w;
}

В этом примере: get_random() имеет встроенное определение в file1.c и внешнее определение в file2.c. Когда get_random вызывается в file1.cкомпиляторы могут выбирать, использовать ли встроенное или внешнее определение.

В зависимости от используемого определения, вы можете изменить или не изменить версию m_z и m_w в встроенной версии get_random(). Такое поведение противоречит обычным ожиданиям от статической переменной. При звонке get_random(), вы ожидаете изменить то же самое m_z и m_w.

Коррекция - Сделать встроенную функцию статической

Одна из возможных корректировок состоит в том, чтобы сделать встроенное get_random() статический. Независимо от компилятора, вызовы get_random() в file1.c затем используйте встроенное определение. Звонки в get_random() в других файлах используется внешнее определение. Это исправление устраняет неоднозначность того, какое определение используется и изменяются ли статические переменные в этом определении.

/* file1. c  : contains inline definition of get_random()*/

static inline unsigned int get_random(void) 
{

    static unsigned int m_z = 0xdeadbeef; //Compliant 
    static unsigned int m_w = 0xbaddecaf; //Compliant

    /* Compute next pseudorandom value and update seeds */
    m_z = 36969 * (m_z & 65535) + (m_z >> 16); 
    m_w = 18000 * (m_w & 65535) + (m_w >> 16); 
    return (m_z << 16) + m_w;   
}


int call_get_random(void)
{
    unsigned int rand_no;
    int ii;
    for (ii = 0; ii < 100; ii++) {
         rand_no = get_random();
    }
    rand_no = get_random();
    return 0;
}
/* file2. c  : contains external definition of get_random()*/

extern unsigned int get_random(void)
{
    /* Initialize seeds */
    static unsigned int m_z = 0xdeadbeef;
    static unsigned int m_w = 0xbaddecaf;
    
    /* Compute next pseudorandom value and update seeds */
    m_z = 36969 * (m_z & 65535) + (m_z >> 16);
    m_w = 18000 * (m_w & 65535) + (m_w >> 16);
    return (m_z << 16) + m_w;
}
Проблема

Неидентичные определения функции или объекта между модулями возникают, когда функция или объект определяется в нескольких модулях, но с различиями в маркерах, таких как идентификаторы, ключевые слова, литералы, операторы, пунктуаторы и другие разделители. Средство проверки не создается для неиспользуемого кода, например

  • Неподтвержденные шаблоны

  • Неуместный static или extern функции

  • Неустановленные и неопределенные локальные функции

  • Неиспользуемые типы и переменные

Проверка не помечает эту проблему в анализе Polyspace ® по умолчанию в качестве кода. См. раздел Шашки, деактивированные в Polyspace как анализ кода по умолчанию (Polyspace Bug Finder Access).

Риск

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

Зафиксировать

Определение объектов и неинлинговых функций без каких-либо различий в маркерах. Используйте одну и ту же последовательность и типы маркеров в определениях объектов и нелинейных функций в модулях.

Пример: Определение объекта с разницей маркеров

В этом примере используются два файла:

  • file1.cpp:

    typedef struct S 
    {
       int x;
       int y;
    }S; 
    void foo(S& s){
    //...
    }
  • file2.cpp:

    typedef struct S //Noncompliant
    {
       int y;
       int x;
    }S ; 
    void bar(S& s){
    //...
    }

В этом примере оба file1.cpp и file2.cpp определить структуру, S. Определения изменяют порядок полей структуры.

Исправление: использовать идентичное определение для всех модулей

Одной из возможных корректировок является определение структуры. S в файле заголовка и включить заголовок в два модуля.

  • S.h:

    struct S //Compliant
    {
       int x;
       int y;
    }; 
  • file1.cpp:

    #include"S.h" 
    void foo(S& s){
    //...
    }
  • file2.cpp:

    #include"S.h" 
    void bar(S& s){
    //...
    }

Проверить информацию

Группа: 01. Объявления и инициализация (DCL)
Представлен в R2019a

[1] Данное программное обеспечение было создано компанией MathWorks и включает в себя следующие компоненты: «Веб-сайт SEI CERT-C», © 2017 Университет Карнеги-Меллон, веб-сайт SEI CERT-C + + © 2017 Университет Карнеги-Меллон, "Стандарт кодирования SEI CERT C - Правила разработки безопасных, Надежные и безопасные системы - 2016 Edition ", © 2016 Университет Карнеги-Меллон, и "Стандарт кодирования SEI CERT C++ - Правила разработки безопасных, Надежные и безопасные системы в C++ - 2016 Edition "© 2016 Университет Карнеги-Меллон, со специальным разрешением от его Института программного обеспечения.

ЛЮБОЙ МАТЕРИАЛ УНИВЕРСИТЕТА КАРНЕГИ МЕЛЛОНА И/ИЛИ ЕГО ПРОГРАММНОГО ИНЖЕНЕРНОГО ИНСТИТУТА, СОДЕРЖАЩИЙСЯ В НАСТОЯЩЕМ ДОКУМЕНТЕ, ПОСТАВЛЯЕТСЯ КАК ЕСТЬ. УНИВЕРСИТЕТ КАРНЕГИ МЕЛЛОН НЕ ДАЕТ НИКАКИХ ГАРАНТИЙ, ВЫРАЖЕННЫХ ИЛИ ПОДРАЗУМЕВАЕМЫХ, В ОТНОШЕНИИ ЛЮБЫХ ВОПРОСОВ, ВКЛЮЧАЯ, НО НЕ ОГРАНИЧИВАЯСЬ, ГАРАНТИИ ПРИГОДНОСТИ ДЛЯ ЦЕЛЕЙ ИЛИ ТОВАРНОЙ ПРИГОДНОСТИ, ИСКЛЮЧИТЕЛЬНОСТИ ИЛИ РЕЗУЛЬТАТОВ, ПОЛУЧЕННЫХ ОТ ИСПОЛЬЗОВАНИЯ УНИВЕРСИТЕТ КАРНЕГИ МЕЛЛОН НЕ ДАЕТ НИКАКИХ ГАРАНТИЙ В ОТНОШЕНИИ СВОБОДЫ ОТ ПАТЕНТА, ТОВАРНОГО ЗНАКА ИЛИ НАРУШЕНИЯ АВТОРСКИХ ПРАВ.

Данное программное обеспечение и связанная с ним документация не были рассмотрены и не одобрены Университетом Карнеги-Меллона или его Институтом разработки программного обеспечения.