AUTOSAR C++14 Rule A3-8-1

К объекту нельзя получить доступ за пределами его времени жизни

Описание

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

К объекту нельзя получить доступ за пределами его времени жизни.

Объяснение

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

  • Неинициализированный указатель: Вы можете непреднамеренно получить доступ к указателю прежде, чем присвоить адрес ему. Эта операция получает доступ к объекту перед своим временем жизни и приводит к доступу к непредсказуемой ячейке памяти. Лучшая практика состоит в том, чтобы инициировать указатель при помощи nullptr во время его объявления.

  • Переменная Noninitialized: Вы можете непреднамеренно считать переменную, прежде чем она будет инициализирована. Эта операция получает доступ к объекту перед своим временем жизни и приводит к чтению значения мусора, которое непредсказуемо и бесполезно. Лучшая практика состоит в том, чтобы инициировать переменную во время своего объявления.

  • Использование ранее освобожденного указателя: Вы можете получить доступ к динамически выделенной памяти об указателе после освобождения памяти. Попытка получить доступ к этому блоку памяти получает доступ к объекту после своего времени жизни и приводит к непредсказуемому поведению или даже отказу сегментации. Чтобы решить эту проблему, установите освобожденный указатель на nullptr, и затем проверять, является ли указателем nullptr прежде, чем получить доступ к нему. В качестве альтернативы используйте std::unique_ptr вместо необработанного указателя. Поскольку вы не должны освобождать выделенную память для std::unique_ptr явным образом можно постараться не непреднамеренно получать доступ к освобожденной памяти.

  • Указатель или ссылка, чтобы сложить переменный осциллограф отъезда: Вы можете присвоить нелокальный указатель на локальный объект. Например:

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

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

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

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

  • Объект Modifying с временным временем жизни: Вы можете попытаться изменить временный объект, возвращенный вызовом функции. Изменение временных объектов является неопределенным поведением, которое может привести к аварийному завершению программы в зависимости от аппаратного и программного обеспечения, которое вы используете. Лучшая практика состоит в том, чтобы присвоить временные объекты в локальных переменных и затем изменение локальных переменных.

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

Реализация Polyspace

Polyspace® проверяет на эти сценарии где объектная сила быть полученным доступ за пределами ее времени жизни:

  • Неинициализированный указатель: Polyspace отмечает указатель, если он не присвоен адрес, прежде чем к нему получат доступ.

  • Переменная Noninitialized: Polyspace отмечает переменную, если он не инициализируется, прежде чем ее значение читается.

  • Использование ранее освобожденного указателя: Polyspace отмечает операцию, где вы получаете доступ к блоку памяти после освобождения блока, например, при помощи free() функционируйте или delete оператор.

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

    • Функция возвращает указатель на локальную переменную

    • Глобальный указатель указан на локальную переменную

    • Параметр функции передачи ссылкой, такой как указатель, указан на локальную переменную

    • Элемент данных указателя класса указан на локальную переменную

  • Объект Accessing с временным временем жизни: Polyspace отмечает операцию, где вы получаете доступ к временному объекту, который возвращен вызовом функции.

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

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

Примеры

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

В этом примере показано, как флаги Polyspace, получающие доступ к указателям, которые не присвоены адресу.

#include <cstdlib>

int* Noncompliant(int* prev)
{
	int j = 42;
	int* pi;
	if (prev == nullptr){
		pi = new int;
		if (pi == nullptr) 
		return nullptr;
	}
	*pi = j; //Noncompliant                    
	return pi;
}
int* Compliant(int* prev)
{
	int j = 42;
	int* pi;
	if (prev == nullptr){
		pi = new int;
		if (pi == nullptr)
		return nullptr;
	} 
	else 
	pi = prev;              
	*pi = j;//Compliant
	return pi;
}
int* AltCompliant(int* prev)
{
	int j = 42;
	int* pi=nullptr;
	if (prev == nullptr){
		pi = new int;
		if (pi == nullptr)
		return nullptr;
	} 
	else              
	if(pi!= nullptr) 
	*pi = j;//Compliant
	return pi;
}

Polyspace отмечает указатель pi в Noncompliant() потому что pi получен доступ, прежде чем адрес присвоен ему когда prev не NULL. Можно решить эту проблему в различных способах. Например:

  • Инициируйте pi перед оператором *pi = j. Присвоение на pi в Compliant() не отмечается потому что pi инициируется prev прежде чем к этому получат доступ.

  • Инициируйте pi при помощи nullptr во время его объявления. Присвоение на pi в AltCompliant() не отмечается потому что pi инициируется nullptr во время его объявления.

В этом примере показано, как флаги Polyspace, получающие доступ к неинициализированным переменным.

int Noncompliant(void)
{
	extern int getsensor(void);
	int command;
	int val;
	command = getsensor();
	if (command == 2){
		val = getsensor();
	}
	return val;//Noncompliant              
	
}
int Compliant(void)
{
	extern int getsensor(void);
	int command;
	int val=0;//Initialization
	command = getsensor();
	if (command == 2){
		val = getsensor();
	}
	return val;//Compliant              
}

Polyspace отмечает оператор return val в Noncompliant() потому что этот оператор доступы val прежде чем переменная инициализируется когда command не равно 2. Можно решить эту проблему несколькими способами. Например, инициализируйте переменную val обнулять во время его объявления, как показано в Compliant(). Путем инициализации переменной во время объявления это инициализируется во всех путях к выполнению, делая оператор return val совместимый с этим правилом.

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

#include <memory>
int Noncompliant(double base_val, double shift){ 
	double j;
	double* pi = new double;
	if (pi == nullptr) 
	return 0;
	*pi = base_val;
	//...
	delete pi;
	//...
	j = *pi + shift;//Noncompliant
	return j;
}
int Compliant(double base_val, double shift){
	double j;
	std::unique_ptr<double>   pi(new double(3.1416));
	if (pi == nullptr) 
	return 0;
	*pi = base_val;
	j = *pi + shift;              
	return j;
}

В функциональном Noncompliant(), указатель pi объявляется и инициализируется при помощи оператора new. Позже, динамически выделенная память освобождена при помощи оператора delete. К освобожденному указателю затем непреднамеренно получают доступ в операторе j = *pi + shift;. Polyspace отмечает этот оператор. Можно решить эту проблему в различных способах. Например, вы можете хотеть освободить выделенный ресурс после выполнения всех соответствующих операций. В качестве альтернативы можно использовать интеллектуальные указатели вместо необработанных указателей. В Compliant(), указатель pi объявляется как std::unique_ptr. Полученные ресурсы для pi автоматически освобождены в конце Compliant() путем вызова его деструктора. Поскольку память выделяется для pi не получен доступ после того, как это будет освобождено, Compliant() совместимо с этим правилом.

В этом примере показано, как Polyspace отмечает операции, где указатели на локальные переменные могут выйти к внешним осциллографам.

int* Noncompliant1(void) {
	int ret = 0; //Noncompliant
	return &ret ; 
}
auto Noncompliant2(int var) {
	int rhs = var; //Noncompliant
	auto adder = [&] (int lhs) {
		return (rhs + lhs);
	};
	return adder; 
}
int Compliant1(void) {
	int ret = 0; //Compliant
	return ret ; 
}
auto Compliant2(int var) {
	int rhs = var; //Compliant
	auto adder = [=] (int lhs) {
		return (rhs + lhs);
	};
	return adder; 
}
  • Функциональный Noncompliant1() возвращает указатель на локальную переменную ret. Локальная переменная ret удален как только Noncompliant() выполнение концов. Возвращенный указатель указывает на непредсказуемое значение. Такие операции несовместимы с правилом. Можно устранить эту проблему путем возврата локальных переменных значением, как показано в Compliant().

  • Функциональный Noncompliant2() возвращает лямбда-выражение, которое получает локальную переменную rhs ссылкой. Эта ссылка разыменовывает к непредсказуемому значению потому что rhs удален когда функциональный Noncompliant2() выполнение концов. Можно устранить эту проблему путем получения локальных переменных копией в лямбда-выражении, как показано в Compliant2().

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

#include<vector>
struct S_Array{
	int t;
	int a[5];
};
struct S_Array Factory(void);
std::vector<int> VectorFactory(int aNumber);
int Noncompliant(void) {

	return ++(Factory().a[0]); //Noncompliant
}
int Compliant(void) {
	auto tmp = Factory();
	return ++(tmp.a[0]); //Compliant
}
int Compliant2(void) {
	return ++(VectorFactory(5)[1]); //Compliant
}

В Noncompliant(), вызов Factory() создает временный объект. Изменение этого объекта несовместимо с этим правилом. Polyspace отмечает оператор return ++(Factory().a[0]). Можно решить эту проблему в различных способах. Например, можно присвоить временный объект локальной переменной прежде, чем изменить его, как показано в Compliant(). В качестве альтернативы используйте умные контейнеры, такие как std::vector как показано в Compliant2(). Контейнеры, такие как std::vector управляйте их собственным временем жизни и имейте семантику перемещения. Polyspace не отмечает оператор return ++(VectorFactory(5)[1]);.

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

Группа: Фундаментальные понятия
Категория: необходимый, неавтоматизированный
Введенный в R2020b