exponenta event banner

AUTOSAR C++14 Rule A3-8-1

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

Описание

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

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

Объяснение

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

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

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

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

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

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

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

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

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

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

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

Реализация Polyspace

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