How to safely check null pointers

(Using Visual Studio 2010)

I have a simple returnLength() function for a list, defined simply as

 int returnLength() { if (!next) return 1; else return 1 + next->returnLength(); } 

next is a pointer to another node in the list. When checking an if statement to verify the correctness of the next action, Visual Studio throws a run-time error, citing an access violation. This error causes several calls deep into the recursion tree.

What is the recommended way to check for a pointer?

+4
source share
7 answers

This is the correct way. It is likely that this NULL is happening here, and therefore you get an access violation by trying to read next with a NULL pointer. You need to check for NULL on the calling site on returnLength

+4
source

The code you provided is valid. Most likely, this function is called using an invalid this pointer.

+3
source

It seems that the last node in the list has an invalid value for next - it must be NULL . Is next definitely NULL initialized in the default constructor for this class? If this is not the case, this can be a problem.

+3
source

Visual Studio does not throw a runtime error, but I know what you mean.

We need a little more code than this. Is return length a method of your class list? Where is next declared? Where is it initialized? Most likely, next is an uninitialized pointer or a pointer to freed memory. At least this is a pointer to an invalid location, so an access violation error.

Initialize next accordingly. Checking whether the pointer is null is the only suitable mechanism for the validity of the pointer.

Edit: It looks like you are not at all familiar with the reality of a pointer. You cannot check if the pointer points to a valid location. You can only check if the pointer to the universally invalid location is null or 0 . So ... if you do not have a pointer pointing to something, you should initialize or set it to null or 0 , that is, its purpose.

+2
source

Given your description, it seems that next not null, but still invalid. For instance. if your ListNode::RemoveNextNode() function had an error in which it deleted the next node but did not change this->next . In this case, you have a pointer to freed memory. You cannot verify this; you must make sure that this does not happen.

In general, of course, the solution is to use std::list .

+1
source

In C ++, variables are not guaranteed to be initialized with anything reasonable. If you create a variable: MyType * next;

The next pointer may be NULL or may be any other value. This value will be undesirable, but will not be zero and will cause a violation of your access.

To ensure that next is NULL , you must ensure that it is set to NULL in the constructor of the object.

See Initializing Variables in C ++ for more details.

+1
source
 template <typename T_TYPE, typename T_RET, typename T_DELETE_POLICY> class ToRef: public boost::tuple<T_TYPE*const&, T_RET, T_DELETE_POLICY> { public: ToRef<T_TYPE, T_RET, T_DELETE_POLICY>(T_TYPE*const& p_type, const T_DELETE_POLICY delete_policy) : boost::tuple<T_TYPE*const&, T_RET, T_DELETE_POLICY>(p_type, p_type!=NULL, delete_policy) { } }; template <typename T_TYPE> class ToRef<T_TYPE, bool, bool>: public boost::tuple<T_TYPE*const&, bool, bool> { public: explicit ToRef<T_TYPE, bool, bool>(T_TYPE*const& p_type, const bool delete_policy) :boost::tuple<T_TYPE*const&, bool, bool>(p_type, p_type!=NULL, delete_policy) { } ~ToRef() { //delete policy if (get<2>()) { if (NULL != get<0>()) delete get<0>(); const_cast<T_TYPE*&>(get<0>())= NULL; } } private: ToRef<T_TYPE, bool, bool>(ToRef<T_TYPE, bool, bool>& copy){}; ToRef<T_TYPE, bool, bool>& operator = (ToRef<T_TYPE, bool, bool>& rhs){}; public: bool is_valid(void) const { //validity of the pointer. return get<1>(); } T_TYPE& r_get(void) const { if (is_valid()) return *get<0>(); throw std::string("Invalid Pointer"); } protected: T_TYPE*const & p_get(void) const { return get<0>(); } }; //use it to safely access the pointers. //if block is an overhead here. #define safe_access_start(Ref) if (Ref.is_valid()) { #define safe_access_end } //faster mode but unsafe. exception handling takes care of preventing //unhandled exception to cascade to the top. #define fast_access_start try { #define fast_access_end }catch (std::string s_exception){ TRACE("%s, %d, %s", __FILE__, __LINE__, LPCTSTR(s_exception.c_str()));} 

In your case, this may work as follows

  int returnLength() { if (!next) return 1; sa::ToRef<Stest, bool, bool> testRef(sTest, false); safe_access_start(testRef) return 1 + testRef.r_get().returnLength(); safe_access_end return 1; } 
0
source

Source: https://habr.com/ru/post/1341729/


All Articles