exponenta event banner

Правило AUTOSAR C++ 14 A3-8-1

Доступ к объекту вне срока его службы невозможен

Описание

Определение правила

Доступ к объекту вне срока его службы невозможен.

Объяснение

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

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

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

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

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

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

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

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

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

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

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

Внедрение Polyspace

Polyspace ® проверяет следующие сценарии, в которых доступ к объекту может осуществляться вне срока его службы:

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

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

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

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

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

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

    • Параметр функции pass-by-reference, такой как указатель, указывает на локальную переменную

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

  • Доступ к объекту с временным временем жизни: 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