lots_of_questions
lots_of_questions

Reputation: 1149

C++ pointer scoping issue

I'm running into some C++ scoping problems, and I can't figure out why. I've simplified this a lot from the original problem by making everything public. Can someone help me understand why vec is null at the end of main? How do I get Get1stVec(...) to actually set vec to something which doesn't get immediately destroyed?

class Vec2
{
public:
    float x, y;
    Vec2(float x_, float y_) : x(x_), y(y_) {}
    Vec2() : x(0.0f), y(0.0f) {}
};

class Polygon
{
public:
    void AddVertex(Vec2 vert) { verts.push_back(vert); }
    std::vector<Vec2> verts;
};

void Get1stVec(Polygon* poly, Vec2* vec)
{
    Vec2* tmp = &poly->verts.at(0); // tmp gets a valid pointer here.
    vec = tmp;
}

int _tmain(int argc, _TCHAR* argv[])
{
    Polygon poly;
    poly.AddVertex(Vec2(1.0f, 1.0f));
    Vec2* vec = nullptr;
    Get1stVec(&poly, vec);
    vec->x = 2.0f; // vec is nullptr here. Why?
    return 0;
}

Upvotes: 1

Views: 81

Answers (5)

Chris Drew
Chris Drew

Reputation: 15334

You are passing the Vec2 pointer to Get1stVec by value. In other words, you are taking a copy of the pointer inside the function. If you modify that local pointer to point at something else it will not change the pointer in the main function.

You could pass the pointer by reference Vec2*& but it would be more idiomatic to return the pointer by value from Get1stVec. In this case the pointer cannot be null so it would be even better to return a reference instead of a pointer:

Vec2& Get1stVec(Polygon& poly) 
{
    return poly.verts.at(0);
}

int main() 
{
    Polygon poly;
    poly.AddVertex(Vec2(1.0f, 1.0f));
    Vec2& vec = Get1stVec(poly);
    vec.x = 2.0f; // vec is nullptr here. Why?
}

I've also changed Get1stVec to take Polygon by reference instead of pointer.

Live demo.

Upvotes: 0

Viktor Liehr
Viktor Liehr

Reputation: 538

I see two issues in the code. vec in the _tmain() function will not change the value in this example. It is a nullptr because you initialized it as a nullptr.

To understand why. we have to examine Get1stVec:

void Get1stVec(Polygon* poly, Vec2* vec)
{
    Vec2* tmp = &poly->verts.at(0); // tmp gets a valid pointer here.
    vec = tmp;
}

Note: In Get1stVec it is possible to access the vec (if it is valid) pointer and dereference it. Here it is a nullptr, to accessing or dereferencing would lead to an access violation.

poly->verts.at(0); will return a copy of Vec2 from the verts vector. As we are in the Get1stVec function, that copy is created on the stack.

Note: Stack variables and object life only as long as the function is executed. So do the passed parameter values to the function. The same applies to locally defined variables and objects.

So

Vec2* tmp = &poly->verts.at(0);

tmp is a locally defined pointer and lives on the stack. The result of poly->verts.at(0); lives also on the stack.

Now

poly->verts.at(0);

vec = tmp; is assigned. vec is a passed parameter, it lives also on the stack.

When the code execution leaves Get1stVec, the stack will be unwind and all information is lost.

If you want to save the information of the stack unwind, then using pass a reference to your pointer to Get1stVec like this:

Get1stVec(Polygon* poly, Vec2&* vec)

parameter vec is now referencing a pointer to vec in the _tmain function and not the stack.

Upvotes: 0

jaykumarark
jaykumarark

Reputation: 2449

I have modified your Get1stVec signature. You are trying to modify the local pointer vec. So its address has be passed to function to get a valid modification. The earlier declaration would have worked if the vec pointer in main already had some address to begin with.

#include <iostream>
#include <vector>

class Vec2
{
public:
    float x, y;
    Vec2(float x_, float y_) : x(x_), y(y_) {}
    Vec2() : x(0.0f), y(0.0f) {}
};

class Polygon
{
public:
    void AddVertex(Vec2 vert) { verts.push_back(vert); }
    std::vector<Vec2> verts;
};

void Get1stVec(Polygon* poly, Vec2** vec)
{
    Vec2* tmp = &poly->verts.at(0); // tmp gets a valid pointer here.
    *vec = tmp;
}

int main()
{
    Polygon poly;
    poly.AddVertex(Vec2(1.0f, 1.0f));
    Vec2* vec = nullptr;
    Get1stVec(&poly, &vec);   //passing address of the vec pointer
    vec->x = 2.0f; // vec is nullptr here. Why? - **Look at previous line**
    return 0;
}

Upvotes: 1

Vlad from Moscow
Vlad from Moscow

Reputation: 310990

Function parameters are its local variables. They are initialized by copies of the supplied arguments.

You can imagine this function

void Get1stVec(Polygon* poly, Vec2* vec)
{
    Vec2* tmp = &poly->verts.at(0); // tmp gets a valid pointer here.
    vec = tmp;
}

and its call

Get1stVec(&poly, vec);

the following way (I renamed parameters that they would differ from arguments)

void Get1stVec(/*Polygon* poly1, Vec2* vec1*/)
{
    Polygon* poly1 = &poly;
    Vec2* vec1 = vec;

    Vec2* tmp = &poly1->verts.at(0); // tmp gets a valid pointer here.
    vec1 = tmp;
    ^^^^
}

As you cann see it is the parameter that was changed not the argument.

You should pass an argument by reference if you want that it would be changed in a function.

For example

void Get1stVec(Polygon* poly, Vec2* &vec )
                                    ^^^^
{
    Vec2* tmp = &poly->verts.at(0); // tmp gets a valid pointer here.
    vec = tmp;
}

Upvotes: 0

Baum mit Augen
Baum mit Augen

Reputation: 50063

void Get1stVec(Polygon* poly, Vec2* vec)

modifies the local copy of vec, not the variable vec you have in your main. They're different variables.

You may pass a reference instead or alternatively redesign and get rid of all those useless pointers.

Upvotes: 2

Related Questions