CERT C++: DCL60-CPP

Соблюдите правило с одним определением

Описание

Управляйте определением

Выполните одно определение rule.[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, секунда. 6.7.4). Если ваш компилятор использует встроенное определение в одном вызове и невстроенное определение в другом, вы больше не изменяете ту же переменную в обоих вызовах. Это поведение игнорирует ожидания от статической переменной.

Фиксация

Используйте одни из этих мер:

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

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

  • Сделайте переменную non-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;
}
Проблема

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

  • Неинстанцированные шаблоны

  • Невостребованный 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 Carnegie Mellon University, веб-сайт SEI CERT-C © 2017 Carnegie Mellon University”, CERT SEI C Кодирование Стандарта – Правил для Разработки безопасных, Надежных и Защищенных систем – 2 016 Выпусков”, © 2016 Carnegie Mellon University, and “CERT SEI Стандарт Кодирования C++ – Правил для Разработки безопасных, Надежных и Защищенных систем на C++ – 2 016 Выпусков” © 2016 Carnegie Mellon University, со специальным разрешением от его Института программной инженерии.

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

Это программное обеспечение и сопоставленная документация не были рассмотрены, ни являются подтвержденным Университетом Карнеги-Меллон или его Институтом программной инженерии.