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