exponenta event banner

Дорогостоящая логическая операция

Логическая операция требует оценки обоих операндов из-за их порядка, что приводит к неэффективному коду

Описание

Этот дефект возникает, когда все эти условия верны:

  • Левый и правый операнды не имеют побочных эффектов.

  • Правильный операнд не содержит вызовов const функции-члены.

  • Левый операнд содержит один или несколько вызовов const функции-члены.

При оценке возможных побочных эффектов операнда:

  • Polyspace ® предполагает, чтоconst функции-члены класса не имеют побочных эффектов. Предполагается, что функции, не являющиеся членами, имеют побочные эффекты.

  • Polyspace обрабатывает операции с плавающей запятой в соответствии со стандартом C++. В C++ 03 или ранее операции с плавающей запятой не имеют побочных эффектов. В C + 11 или более поздних версиях операции с плавающей запятой могут иметь побочные эффекты, такие как изменение флагов состояния с плавающей запятой для указания ненормальных результатов или вспомогательной информации. См. раздел Среда с плавающей запятой.

  • Polyspace лечит bool оператор преобразования и логический NOT операторов struct или класс в качестве встроенных операторов. Эти операции не рассматриваются как вызовы функции-члена. Стандартная библиотека шаблонов содержит множество классов, которые определяют такой bool оператор преобразования или логический NOT оператор.

Риск

При вычислении логической операции компилятор сначала вычисляет левый аргумент, а затем вычисляет правый аргумент только при необходимости. В логической операции неэффективно ставить вызовы функций в качестве левого аргумента, а константы и переменные - в качестве правого аргумента. Рассмотрим этот код:

if(Object.attribute()|| var1){
//...
}
В логическом выражении внутри if оператор, компилятор всегда оценивает вызов функции Object.attribute(). Оценка функции не всегда необходима. Например, если var1 вычисляется как true, тогда логическое выражение всегда вычисляется как true. Поскольку var1 является правым операндом, а не левым операндом, компилятор излишне оценивает вызов функции, что неэффективно. Поскольку неэффективный код компилируется и ведет себя правильно, этот дефект может остаться незамеченным.

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

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

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

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

Примеры

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

#include <string>
bool updateNewProperty( const std::string& name );
void updateNewMetaProperty( const std::string& name );
volatile char externalFlag;

void updateProperty( const std::string& name )
{
	bool is_new_property = updateNewProperty( name );

	if( name.compare( "meta" ) == 0 && is_new_property ) //Noncompliant
	{
		updateNewMetaProperty( name );
	}
	if( name.compare( "meta" ) == 0 && externalFlag ) //Compliant
	{
		updateNewMetaProperty( name );
	}
	
}

В первом if оператор, переменная is_new_property - правильный операнд логического операнда. Функция-член std::string::compare называется левым аргументом. Компилятор оценивает вызов функции независимо от значения is_new_property, что неэффективно. Polyspace помечает логическую операцию.

Во втором if заявление, volatile переменная externalFlag является правильным операндом. Потому что переменная volatile, Polyspace предполагает, что это может вызвать побочный эффект. Потому что volatile переменная может иметь побочный эффект, Polyspace не помечает логическую операцию.

Исправление

Определите, нужно ли поддерживать порядок операндов для безопасного и правильного вычисления выражения. В этом случае два операнда is_new_property и name.compare( "meta" ) == 0 независимы и изменение их порядка не изменяет значение логического выражения. Чтобы устранить этот дефект, используйте is_new_property в качестве левого операнда.

#include <string>
bool updateNewProperty( const std::string& name );
void updateNewMetaProperty( const std::string& name );
volatile char externalFlag;

void updateProperty( const std::string& name )
{
	bool is_new_property = updateNewProperty( name );
	if(is_new_property && name.compare( "meta" ) == 0 ) //Compliant
	{
		updateNewMetaProperty( name );
	}
	if( name.compare( "meta" ) == 0 && externalFlag ) //Compliant
	{
		updateNewMetaProperty( name );
	}
}

Компилятор оценивает вызов std::string::compare в первом if оператор только тогда, когда is_new_property является true.

При использовании операций с плавающей запятой в логическом выражении Polyspace оценивает побочные эффекты операндов по-разному на основе используемой версии C++. В C++ 03 или более ранних версиях операции с плавающей запятой не имеют побочных эффектов сами по себе. В C++ 11 или выше сами операции с плавающей запятой могут иметь побочные эффекты.

class A{
	//...
	float makeFloat() const{
		//..
	}
	void testfloat(){
		if( makeFloat() == 0.1f && fp==0.2f) //Noncompliant
		{
			//...
		}
	}
	
private:
	float fp;		
};
В этом коде, если вы используете C++ 03, ни один из операндов не имеет побочных эффектов. Поскольку левый операнд вызывает вызов функции-члена, Polyspace помечает выражение.

При использовании C++ 11 или более поздней версии операции с плавающей запятой могут иметь побочные эффекты. В этом случае Polyspace не помечает логическое выражение.

Исправление

Определите, нужно ли поддерживать порядок операндов для безопасного и правильного вычисления выражения. В этом случае два операнда fp==0.2f и makeFloat() == 0.1f независимы и изменение их порядка не изменяет значение логического выражения. Чтобы устранить этот дефект, используйте fp==0.2f в качестве левого операнда.

class A{
	//...
	float makeFloat() const{
		//..
	}
	void testfloat(){
		if( fp==0.2f && makeFloat() == 0.1f) //Compliant
		{
			//...
		}
	}
	
private:
	float fp;		
};
Компилятор оценивает вызов makeFloat() только когда fp==0.2f вычисляется как true.

#include<cstdlib>
class A{
	//...
	bool isLoaded() const { return p != NULL; }
	int get() {
		if(isLoaded() && *p > 0) { // Noncompliant
			return *p;
		}
	}

	
private:
	int* p;		
};

В выражении (isLoaded() && *p > 0), дереференция *p в правом аргументе безопасен только в том случае, если левым аргументом является true. Polyspace не проверяет, когда логическое выражение требует такого конкретного порядка вычисления. Поскольку ни один из операндов не имеет побочных эффектов, а вызов функции члена является левым операндом, Polyspace помечает операцию.

Исправление

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

#include<cstdlib>
class A{
	//...
	bool isLoaded() const { return p != NULL; }
	int get() {
		if(isLoaded()== true) { // Compliant
			if(*p > 0){
				return *p;	
			}
			
		}
	}

	
private:
	int* p;		
};
Этот код проверяет два условия отдельно и не зависит от порядка анализа. Если такой рефакторинг невозможен, необходимо обосновать дефект с помощью аннотаций или просмотра информации.

Информация о результатах

Группа: Производительность
Язык: C++
По умолчанию: Откл.
Синтаксис командной строки: EXPENSIVE_LOGICAL_OPERATION
Воздействие: среднее
Представлен в R2021a