What does pointing an object at NULL place the member values at?

0

Sorry about the wordy title.

class Student
{
public:
// Some routines

private:
string name;
int grade;
}

In the main:

Student* newStudent = NULL;

I know that NULL is the same as zero, but what are name and grade set at now? When I do:

cout << newStudent->getName();

it does not return 0 - instead, Visual Studio gives me some kind of error.

So I'm also wondering, what is newStudent pointing at exactly? Has it just kind of eliminated all the private data, and is pointing the entire class at 0?

EDIT:

Ok chris - that's kind of what I thought. My follow up question is then: How do I check for this in the getName function.

I can't use a bool like,

if (name == 0)
// Return an error message.

so is there a work-around for this?

EDIT: The VS error is: Unhandled exception at 0x00FFAAA6 in Ex8_3.exe: 0xC0000005: Access violation reading location 0x00000014.

Be aware that this is actually part of a much larger program, I've just isolated the problem part.

EDIT: OK, here is the code that is causing problems. I'll just post the whole program since it is not too large, and I could use any constructive criticism I can get on it anyways.

#include <iostream>
#include <string>
#include <vector>
using namespace std;

class Student
{
public:
    // Accessor
    string getName();
    vector<string> getCourseList();
    Student* getLink();

    // Mutators
    void setName (string aName);        
    void setCourses(vector<string> list);       
    void setLink (Student* aLink);

private:
    string name;
    vector<string> courseList;      
    Student* link;

};

// Linked-list Non-Member Functions.
typedef Student* NodePtr;
void head_insert (NodePtr& head); 
NodePtr search (NodePtr head, string target); 
NodePtr searchPrevious (NodePtr head, string target);
void deleteNode (NodePtr& head, string target);
void show_list (NodePtr& head);
void showFirstCourses (NodePtr& head);

int main()
{
    NodePtr headPointer = NULL;
    head_insert(headPointer);
    headPointer->setName("Tim");

    head_insert(headPointer);
    headPointer->setName("Jan");

    head_insert(headPointer);
    headPointer->setName("Jim");

    deleteNode(headPointer, "Tim");

    NodePtr test1 = NULL;
    head_insert(test1);     // This is where the new Student is created.

    test1 = search (headPointer, "Jon");    // Still experiencing a problem between here and the next instruction.

    cout << test1->getName();   // Something is wrong if this == NULL.


    system("pause");
    return 0;
}

string Student::getName()
{
    return name;
}

vector<string> Student::getCourseList()
{
    return courseList;
}

Student* Student::getLink()
{
    return link;
}

void Student::setName (string aName)
{
    name = aName;
}

void Student::setCourses(vector<string> list)
{
    courseList = list;
}

void Student::setLink (Student* aLink)
{
    link = aLink;
}

void head_insert (NodePtr& head)
{
    NodePtr temp_ptr; 
    temp_ptr = new Student; 
    temp_ptr->setLink(head);    // Link this to what the head is linking to.
    head = temp_ptr;    // Use this node as the new head.
}

NodePtr search (NodePtr head, string target)        // Needs to be tested, to see if getLink is working correctly.
{
    // Point to the head node 
    NodePtr here = head; 

    // If the list is empty nothing to search 
    if (here == NULL) 
    { 
        return NULL; 
    } 
    // Search for the item 
    else 
    { 
        // while you have still items and you haven't found the target yet 
        while (here->getName() != target && here->getLink() != NULL) 
            here = here->getLink(); 
         // Found the target, return the pointer at that location 
         if (here->getName() == target) 
                return here; 
         // Search unsuccessful, return Null 
         else 
                return NULL; 
    } 
}

NodePtr searchPrevious (NodePtr head, string target)    // This function needs to be checked for the same things the above function does.
{
    // Point to the head node 
    NodePtr here = head; 

    // If the list is empty nothing to search 
    if (here == NULL) 
    { 
        return NULL; 
    } 
    // Search for the item 
    else 
    { 
        // while you still have items and you haven't found the target yet 
        while (here->getName() !=target && (here->getLink())->getName() != target && (here->getLink())->getLink() != NULL)
            here = here->getLink(); 
         // Found the target, return the pointer at that location 
        if (((here->getLink())->getName() == target) || (here->getName() == target))
        {
            return here;
        }
         // Search unsuccessful, return Null 
         else 
                return NULL; 
    } 
}

void deleteNode (NodePtr& head, string target)      
{
    NodePtr toBeDeleted = search (head, target);
    NodePtr previousPointer = searchPrevious (head, target);

    // Make sure the desired value is in the list.
    if (toBeDeleted ==  NULL)
    {
        cout << target << " is not in the list, so it can't be deleted.\n";
        return;
    }

    // Test to see if we are removing the first node.
    if (previousPointer == toBeDeleted)
    {
        head = toBeDeleted->getLink();
        delete toBeDeleted;
        return;
    }

    // Connect the node from before to after.
    previousPointer->setLink(toBeDeleted->getLink());

    delete toBeDeleted;
}
c++
class
oop
null
asked on Stack Overflow Mar 23, 2014 by (unknown user) • edited Mar 23, 2014 by (unknown user)

3 Answers

3

Don't think of name and grade on a pointer that points to NULL as set to something. When a Student * pointer is set to NULL, it points at nothing. It's as if you pointed to an empty space in a room and asked me what the name of the person you are pointing at is.

You can't dereference a NULL pointer, so doing this:

Student *s = NULL;
s->name;

is undefined behavior. Anything could happen; most likely, you'll get a segfault. (But this is an implementation detail, and not guaranteed.)

You can check for null, simply with:

if(s) {
    // s isn't NULL.
}

(You can also use if(s != NULL), or if(s != nullptr).)

Now that we've got your code, let's look at this case:

test1 = search (headPointer, "Jon");    // Still experiencing a problem between here and the next instruction.

cout << test1->getName();   // Something is wrong if this == NULL.

The problem here is the second line: in the case that the search fails, why to you expect to be able to output the student's name? We haven't found a student! We need to handle the lack of having a student:

test1 = search(headPointer, "Jon");
if(test1) {
    cout << "Found student; his name is " << test1->getName() << endl;
} else {
    cout << "Couldn't find that student." << endl;
}

Similarly, we have some bad logic here:

while (here->getName() != target && here->getLink() != NULL) 
    here = here->getLink(); 

Let's say that here->getLink() returns NULL in the inevitable case that we hit the end of this list. The condition in the while (here->getName()) now dereferences an invalid (NULL) pointer. Let's restructure this entire function significantly:

NodePtr search (NodePtr head, string target) {
    // Where in the list we are:
    NodePtr here = head;
    // Check that head points at a student:
    while(here != NULL) {
        // Check if this is the student we are searching for:
        if(here->getName() == target) {
            // It is. Great! Return him/her:
            return head;
        }
    }
    return NULL;
}

Note that unless you're required to implement a linked list, the STL (the standard template library) has several list containers: std::list and std::vector are two containers that contain lists of objects. They have different performance characteristics, and you'll need to choose between those, but they're going to be a lot easier to use than re-inventing a linked list.

One last thing:

OK I can take that solution, I was just thinking there might be a more elegant solution - like including this check in the getName function if possible. The reason being, is that I would have to include the check you described above every time I call getName.

Let's talk about getName. If we allow a NULL student in that function, what do we do? Since we can't get the name of the student we don't have... it's pretty hard to say. Do we output an error? Raise an exception? Kill the program?

In member functions, it is typically fair game to expect that the object you're operating on exists. If it doesn't, it's a bug in the caller for doing ->getName() on a NULL pointer. Similarly, in normal functions, we can take a reference:

void do_something_with_a_student(Student &s)

References shouldn't be NULL, and so this function is allowed to assume that it always has a valid student. The only places you need to worry about "do I have a valid student?" are places where that concern should logically arise, such as just having searched for a student. If I search my class roster for a "Billy", I might or might not have such a student: here, we need that "does not exist" value; NULL is useful for that.

Even if you take a pointer, you can always document (with a comment, perhaps) that the function only allows non-NULL pointers. This is more common in C, which lacks references; in C++, references are a better choice.

answered on Stack Overflow Mar 23, 2014 by Thanatos • edited May 17, 2014 by BenMorel
0

A pointer is not an object. It is an address of an object. Like a street address.

When you call new MyObject(), two things happen: an object is created (somewhere), then the address of this object is returned.

A pointer whose value is null is an address that reads "no such address", or is a blank address. When you use this null pointer, you tell the language "I guarantee this address is good, now use it". As your guarantee is a lie, undefined behaviour results (often a crash, but there is no guarantee).

There are no member functions or data at a blank address to have a value. What you did was the equivalent of telling a blind pilot to set their coordinates to the middle of the pacific, land their plane there, and then get out, walk into the house 30 feet away, and pick up the banana on the counter (next to the toaster oven).

There is no banana, no house, and no runway where you told the pilot to go. So things go poorly.

0

Setting a pointer to point to null is a way of "resetting" a pointer back to pointing at nothing. When you are doing newStudent->getName(), you are accessing the newStudent class and calling a function inside it. If newStudent points to nothing, then there are no functions inside it to call, since it isn't even pointing at anything. Once you give it a class object to point to, then you can call the getName() function inside it. You can think of NULL (nullptr) as the same as saying, "I am pointing at NULL, so you cannot and should not use me for anything, since there is nothing here to begin with". You can test this by doing "pointer == nullptr". Remember, you cannot assume anything about NULL, it isn't zero, it isn't anything, because it's undefined except for the fact that it is NULL.

answered on Stack Overflow Mar 23, 2014 by Salgat

User contributions licensed under CC BY-SA 3.0