CERT C: Rule MSC40-C

Не нарушайте ограничения

Описание

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

Не нарушайте ограничения.[1]

Реализация Polyspace

Эта проверка проверяет, не соблюдается ли ограничение Inline.

Примеры

расширить все

Проблема

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

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

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++;
}

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

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

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

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

    Если вы не изменяете переменную, о неожиданных изменениях речи не идет.

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

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

  • Сделайте функцию static. Добавление static квалификатор определения функции.

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

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

inline unsigned int get_random(void) 
{

    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;   
}


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 затем используйте inlined definition. Вызовы в get_random() в других файлах используйте внешнее определение. Это исправление удаляет неоднозначность в отношении того, какое определение используется и изменяются ли статические переменные в этом определении.

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

static inline unsigned int get_random(void) 
{

    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;   
}


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;
}

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

Группа: Правило 48. Разное (MSC)
Введенный в R2019a

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

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

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