exponenta event banner

Допущения относительно упорных функций

Функции заглушки проверки, не определенные в исходном коде или выбранные для заглушки. Для упорной функции:

  • Проверка делает определенные допущения относительно возвращаемого значения функции и других побочных эффектов функции.

    Можно точно настроить допущения, указав ограничения.

  • Проверка игнорирует тело функции, если оно существует. Операции в теле функции не проверяются на наличие ошибок времени выполнения.

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

Для заглушки функций можно использовать следующие опции:

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

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

Возвращаемое значение функции

Предположения

Проверка предполагает, что:

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

    Если функция возвращает enum переменная, значение переменной находится в диапазоне перечисления. Например, если enum тип принимает значения {0,5, -1,32}, а упорная функция имеет возвращаемый тип, проверка предполагает, что функция возвращает значения в диапазоне -1.. 32.

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

  • Конкретные допущения C++: Оператор new возвращает выделенную память. Операторы, такие как operator=, operator+=, operator--(версия с префиксом) или operator<< возвращает:

    • Ссылка на *this, если оператор является частью определения класса.

      Например, если оператор определен как:

      class X {
        X& operator=(const X& arg) ;
      };
      
      Возвращает ссылку на *this (объект, вызывающий оператор). Объект, вызывающий оператор или его элементы данных, имеют полный диапазон значений, допустимых их типом.

    • Первый аргумент, если оператор не является частью определения класса.

      Например, если оператор определен как:

      X& operator+=(X& arg1, const X& arg2) ;
      
      Возвращается arg1. Объект, который arg1 ссылается на или его элементы данных имеют полный диапазон значений, разрешенных их типом.

    Функции, объявленные с помощью __declspec(no_return) (Visual Studio ®) или__attribute__ ((noreturn)) (GCC) не возвращаются.

Как изменить допущения

Можно изменить предположения по умолчанию относительно возвращаемого значения функции.

  • Если функция возвращает не указывающую переменную, можно ограничить ее диапазон. Использовать опцию Constraint setup (-data-range-specifications).

    С помощью интерфейса спецификации ограничений можно задать абсолютный диапазон. [min..max]. Чтобы задать более сложные ограничения, запишите заглушку функции.

    Например, неопределенная функция имеет прототип:

    int func(int ll, int ul);
    Предположим, что возвращаемое функция значение находится между первым и вторым аргументами. Однако программное обеспечение предполагает полный диапазон возвращаемого значения, поскольку функция не определена. Чтобы смоделировать желаемое поведение и уменьшить оранжевая проверка из неточности, напишите заглушку функции следующим образом:
    int func(int ll, int ul) {
         int ret;
         assert(ret>=ll && ret <=ul);
         return ret;
    }
    Укажите заглушку функции в отдельном файле для проверки. Проверка использует заглушку в качестве определения функции.

    Если определение func существует в вашем коде, и вы хотите переопределить определение, поскольку проверка тела функции является неточной, встроить фактическое определение и заглушку в #ifdef заявление:

    #ifdef POLYSPACE
    int func(int ll, int ul) {
         int ret;
         assert(ret>=ll && ret <=ul);
         return ret;
    }
    #else
    int func(int ll, int ul) {
        /*Your function body */ 
    }
    #endif
    Определение макроса POLYSPACE с помощью опции Preprocessor definitions (-D). При проверке вместо определения фактической функции используется заглушка.

  • Если функция возвращает переменную указателя, можно указать, что указатель может иметь значение NULL.

Аргументы функции, являющиеся указателями

Предположения

Проверка предполагает, что:

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

    Например, в этом примере проверка предполагает, что упорная функция stubbedFunc записывает любое возможное значение в val. Поэтому утверждение оранжевое.

    void stubbedFunc(int*);
    
    void main() {
        int val=0, *ptr=&val;
        stubbedFunc(ptr);
        assert(val==0);
    }

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

    В коде C++ только элементы данных первого уровня структуры могут быть записаны через указатель на структуру. Например, в этом примере анализ обладает знаниями о том, что pb->j указывает на, но не на то, что pb->pa->i указывает на. Итак, после звонка Foo, b.j отображается как инициализированный, но a.i не инициализирован.

    struct A {
       int i;
    };
     
    struct B {
       A* pa;
       int j;
    };
     
    void Foo(B*);
     
    void main() {
        A a;
        B b;
        b.pa = &a;
        Foo(&b); 
        int var1 = b.j;
        int var2 = a.i;
    }

  • Если аргумент является указателем на другой указатель, функция может записать в объект любое значение, на которое указывает второй указатель (только код C). Это предположение продолжается до произвольных глубин иерархии указателей.

    Например, предположим, что указатель **pp указывает на другой указатель *p, который указывает на int переменная var. Если используется упорная функция **p в качестве аргумента проверка предполагает, что после вызова функции var имеет любое int значение. *p может указывать на любое место в выделенной памяти или может указывать на var но не указывает на другую переменную в коде.

  • Если аргумент является указателем функции, функция, на которую он указывает, вызывается (только код C).

    Например, в этом примере функция упора stubbedFunc принимает указатель функции funcPtr в качестве аргумента. funcPtr указывает на func, который вызывается при звонке stubbedFunc.

    typedef int (*typeFuncPtr) (int);
    
    int func(int x){
      	return x;
    }
    
    int stubbedFunc(typeFuncPtr);
    
    void main() {
        typeFuncPtr funcPtr = (typeFuncPtr)(&func);
        int result = stubbedFunc(funcPtr);
    }
    Если указатель функции принимает другой указатель функции в качестве аргумента, функция, на которую указывает второй указатель функции, упирается.

Как изменить допущения

Можно ограничить диапазон аргумента, передаваемого по ссылке. Использовать опцию Constraint setup (-data-range-specifications).

С помощью интерфейса спецификации ограничений можно задать абсолютный диапазон. [min..max]. Чтобы задать более сложные ограничения, запишите заглушку функции.

Например, неопределенная функция имеет прототип:

void func(int *x, int ll, int ul);
Предположим, вы знаете, что значение записано в x лежит между вторым и третьим аргументами. Однако программное обеспечение предполагает полный диапазон значений *x поскольку функция не определена. Чтобы смоделировать желаемое поведение и уменьшить оранжевая проверка из неточности, напишите заглушку функции следующим образом:
void func(int *x, int ll, int ul) {
     assert(*x>=ll && *x <=ul);
}
Укажите заглушку функции в отдельном файле для проверки. Проверка использует заглушку в качестве определения функции.

Если определение func существует в вашем коде, и вы хотите переопределить определение, поскольку проверка тела функции является неточной, встроить фактическое определение и заглушку в #ifdef заявление:

#ifdef POLYSPACE
void func(int *x, int ll, int ul) {
     assert(*x>=ll && *x <=ul);
}
#else
void func(int *x, int ll, int ul) {
     /* Your function body */
}
#endif
Определение макроса POLYSPACE с помощью опции Preprocessor definitions (-D). При проверке вместо определения фактической функции используется заглушка.

Глобальные переменные

Предположения

Проверка предполагает, что заглушка функции не изменяет глобальные переменные.

Как изменить допущения

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

Например, неопределенная функция имеет прототип:

void func(void);
Предположим, что функция записывает значение 0 или 1 в глобальную переменную glob. Чтобы смоделировать нужное поведение, напишите заглушку функции следующим образом:
void func(void) {
     volatile int randomVal;
     if(randomVal)
          glob = 0;
     else
          glob = 1;
}
Укажите заглушку функции в отдельном файле для проверки. Проверка использует заглушку в качестве определения функции.

Если определение func существует в вашем коде, и вы хотите переопределить определение, поскольку проверка тела функции является неточной, встроить фактическое определение и заглушку в #ifdef утверждение следующим образом:

#ifdef POLYSPACE
void func(void) {
     volatile int randomVal;
     if(randomVal)
          glob = 0;
     else
          glob = 1;
}
#else
void func(void) {
     /* Your function body */
}
#endif
Определение макроса POLYSPACE использование опции Preprocessor definitions (-D). При проверке вместо определения фактической функции используется заглушка.