AUTOSAR C++14 Rule A12-0-2

Битовые операции и операции, которые принимают представление данных в памяти, не должны выполняться на объектах

Описание

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

Битовые операции и операции, которые принимают представление данных в памяти, не должны выполняться на объектах.

Объяснение

На C++ объектное представление в памяти может включать:

  • Элементы данных объявляются с различными правами доступа

  • Элементы данных битового поля

  • Дополнение байтов между элементами данных

  • Дополнение байтов в конце элементов данных

  • Указатели на vtable, чтобы поддерживать виртуальные функции

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

Рассмотрите этот класс, который содержит виртуальную функцию:

class notPOD{
public:
	virtual void foo();
	int value;
protected:
	double dvalue;

};
//... 
int main(){
	notPOD Obj;
	std::memset(&Obj, 57, 2); // attempts to set Obj::value to 57
}
Когда Obj хранится в блоке памяти, блок содержит указатель на виртуальную таблицу в дополнение к переменным Obj::value и Obj::dvalue. Размер этого указателя или его местоположения в памяти может зависеть от среды. В main(), std::memset() попытки установить значение Obj::value путем предположения, что:

  • Obj::value первый блок в представлении памяти Obj.

  • Obj::value представлен на 2 байта в памяти.

Поскольку эти предположения обычно не правильны, с помощью std::memset() может привести к неопределенному поведению. Например, если вы непреднамеренно изменяете указатель на виртуальную таблицу, вызывая foo() может вызвать неожиданную функцию.

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

Реализация Polyspace

Функции C, что биты памяти доступов к доступу включают std::memset(), std::memcpy(), std::memmove(), std::strcpy(), std::memcmp(), std::strcmp(). Polyspace® отмечает оператор когда:

  • Вы используете функции C, чтобы инициализировать или скопировать, инициализируют нетривиальные объекты

  • Вы используете функции C, чтобы сравнить нестандартные объекты размещения

  • Вы используете функции C на любых объектах, которые содержат дополнительные данные

Операторы, содержащие несовместимые операции, отмечаются, и подсвечены соответствующие объявления класса. Для определений тривиальных и стандартных классов макета см. Стандарт C++, [класс], абзацы 6 и 7 соответственно.

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

Поиск и устранение проблем

Если вы ожидаете нарушение правила, но не видите его, обратитесь к Кодированию Стандартных Нарушений, Не Отображенных.

Примеры

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

Рассмотрите этот код, который содержит эти классы:

  • TrivialClass тривиальный класс с дополнением данных и перегруженного operator|=.

  • NonTrivialClass нетривиальный класс с виртуальной функцией и перегруженным operator==.

Эти классы представлены по-разному в памяти. В этом примере показано, как Polyspace отмечает битовые операции, которые выполняются на таких объектах.

#include <cstdint>
#include <cstring>
class TrivialClass
{
public:
	TrivialClass() = default;
	TrivialClass(uint8_t c, uint32_t i, int8_t d) :
	c(c), i(i), d(d) {}
	TrivialClass& operator |=(const TrivialClass& other)
	{
		uint32_t buf[4] {this->c|other.c,this->i|other.i,this->d|other.d};
		memcpy(this, buf, sizeof(uint32_t) * 3); //Noncompliant
		return *this;
	}
	
private:
	uint8_t c;
	uint32_t i;
	int8_t d;
};

class NonTrivialClass 
{
public:
	NonTrivialClass() = default;
	NonTrivialClass(uint32_t a, uint32_t b, uint32_t c) : 
	a(a), b(b), c(c){}
	bool operator==(const NonTrivialClass& rhs) const noexcept
	{
		return a==rhs.a && b==rhs.b && c==rhs.c;
	}
	virtual ~NonTrivialClass() {} 
private:
	uint32_t a;
	uint32_t b;
	uint32_t c;
};

int main(void)
{
	TrivialClass A, A1{3,5,7};
	NonTrivialClass B, B1{10,11,12};
	std::memset(&A, 3, 1); //Noncompliant
	A |= A1; 
	if (!std::memcmp(&A, &A1, sizeof(TrivialClass))) {} //Noncompliant
	std::memcpy(&B, &B1, sizeof(NonTrivialClass)); //Noncompliant
	if (B == B1){} //Compliant
	return 0;
}

  • Polyspace отмечает оператор std::memset(&A, 3, 1); потому что в этом операторе, std::memset() изменяет отдельные биты в представлении памяти тривиального объекта A включая дополнение данных. Доступ к дополнительным битам данных объекта является нарушением этого правила, даже если объект является тривиальным объектом класса. По той же причине Polyspace отмечает оператор в определении TrivialClass::operator|= содержа memcopy().

  • Polyspace отмечает оператор std::memcpy(&B, &B1, sizeof(NonTrivialClass)); потому что std::memcpy() получает доступ к отдельным битам в представлении памяти нетривиального объекта B включая указатель на vtable. Этот указатель не является частью представления значения, и получающий доступ к этому указателю нарушение этого правила.

  • Polyspace не отмечает оператор if(B==B1) потому что NonTrivialClass имеет перегруженный operator== это может сравнить B и B1 не получая доступ к их отдельным битам.

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

Группа: Специальные функции членства
Категория: Необходимый, Частично автоматизированный
Введенный в R2020b