5 Replies - 2220 Views - Last Post: 20 June 2012 - 02:31 PM

#1 pokiaka  Icon User is offline

  • D.I.C Head
  • member icon

Reputation: 10
  • View blog
  • Posts: 76
  • Joined: 05-August 11

Easy iteration using a pointer

Posted 20 June 2012 - 11:58 AM

Let's say I have an array/vector of integers.
And I want to pass through them all, set a value, and print them in console.

Take this for example:
vector<int> my_ints(100);
for (size_t i = 0; i < my_ints.size(); ++i)
{
	my_ints[i] = i;
	cout << my_ints[i] << endl;
}


I find it pretty unpleasant to write my_ints[ i ] over and over again so I thought of just pointing to it like so:

for (size_t i = 0; i < my_ints.size(); ++i)
{
	int & current = my_ints[i]; //Use a pointer to point to it

	current = i; //Using 'current' instead of 'my_ints[i]'
	cout << current << endl;
}

Obviously, at least for me, this is much more readable. in longer code it's very beneficial.

But the question comes to mind, does this approach use unnecessary memory/CPU resources? etc.
and in general - would this count as a good programming habit?

Thank you.

Is This A Good Question/Topic? 0
  • +

Replies To: Easy iteration using a pointer

#2 ishkabible  Icon User is offline

  • spelling expret
  • member icon




Reputation: 1622
  • View blog
  • Posts: 5,709
  • Joined: 03-August 09

Re: Easy iteration using a pointer

Posted 20 June 2012 - 12:44 PM

those 2 snippets we likely generate the same code.

if you find the array access notation you might consider iterators

for(auto i = my_ints.begin(); i != my_ints.end(); ++i) {
   *i = i - my_ints.begin();
   cout<<*i<<endl;
}



note: that requires that 'auto' keyword is allowed by your compiler, if not see about turning C++11 features on.

or you can use std::for_each with a lambda function(another C++11 feature)

std::for_each(my_ints.begin(), my_ints.end(), [&](int& i) {
   //...what ever in here, 'i' is like 'current' here
});


Was This Post Helpful? 0
  • +
  • -

#3 NickDMax  Icon User is offline

  • Can grep dead trees!
  • member icon

Reputation: 2250
  • View blog
  • Posts: 9,245
  • Joined: 18-February 07

Re: Easy iteration using a pointer

Posted 20 June 2012 - 01:21 PM

Actually your method is bound to cause you troubles! the standard library has a nasty habit of moving memory around on you. For example take this little test code:
#include <iostream>
#include <vector>

using namespace std;

int main() {
    vector<int> myVect;
    
    myVect.push_back(1);
    cout << &myVect[1] << endl;
    myVect.push_back(1);
    cout << &myVect[1] << endl;    
    myVect.push_back(1);
    cout << &myVect[1] << endl;
    myVect.push_back(1);
    cout << &myVect[1] << endl;
    myVect.push_back(1);
    cout << &myVect[1] << endl;
    myVect.push_back(1);
    cout << &myVect[1] << endl;

    return 0;
}


the output for me is:
> "C:\CProjects\Forum Help\stdvectorMoves.exe " 
0x5446cc
0x541e84
0x541e94
0x541e94
0x541eac
0x541eac


Notice how the address of myVect[1] changes! This is because when the vector was resized a new buffer was created, and the vector copied over to the new buffer.

Long story short -- it is not generally safe to access a member of a vector via a pointer!

Your idea however is actually sound! In general you can avoid lookups into a data set which *might* offer some efficiency over doing the lookup again and again.

So with vectors rather than a pointer, you can use an iterator.
#include <iostream>
#include <vector>

using namespace std;

int main() {
    vector<int> myVect;
    
    myVect.push_back(1);
    cout << &myVect[1] << endl;
    myVect.push_back(1);
    cout << &myVect[1] << endl;    
    myVect.push_back(1);
    cout << &myVect[1] << endl;
    myVect.push_back(1);
    cout << &myVect[1] << endl;
    myVect.push_back(1);
    cout << &myVect[1] << endl;
    myVect.push_back(1);
    cout << &myVect[1] << endl;
    int counter = 0;
    for(vector<int>::iterator i = myVect.begin(); i != myVect.end(); i++, counter++) {
        *i = counter;
    }
    
     for(vector<int>::iterator i = myVect.begin(); i != myVect.end(); i++) {
        cout << *i << endl;
    }   

    return 0;
}


Of course now you have to write all these long std::vector<std::string>::interator kind of variable declarations and they get a little irritating.

In C++11 you can use auto to make things easier:
     for(auto i = myVect.begin(); i != myVect.end(); i++) {
        cout << *i << endl;
    } 


Of course you could always have used the std::for_each:
#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;

class Counter {
    int value;
    public:
    Counter() : value(0) { }
    void operator()(int& in) { in = value++; }
};

void printVectIntItem(int& in) { cout << in << endl; }

int main() {
    vector<int> myVect;
    
    myVect.push_back(1);
    cout << &myVect[1] << endl;
    myVect.push_back(1);
    cout << &myVect[1] << endl;    
    myVect.push_back(1);
    cout << &myVect[1] << endl;
    myVect.push_back(1);
    cout << &myVect[1] << endl;
    myVect.push_back(1);
    cout << &myVect[1] << endl;
    myVect.push_back(1);
    cout << &myVect[1] << endl;
    
    Counter counter;
    for_each(myVect.begin(), myVect.end(), counter);
    for_each(myVect.begin(), myVect.end(), printVectIntItem);
    
    return 0;
}


While this avoids having to write out a lot of vector<>::iterator declarations, and avoids the dereferencing (*i), you NOW have to write fuctors and special purpose functions.

Again C++11 to the rescue!
#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;

int main() {
    vector<int> myVect;
    
    myVect.push_back(1);
    cout << &myVect[1] << endl;
    myVect.push_back(1);
    cout << &myVect[1] << endl;    
    myVect.push_back(1);
    cout << &myVect[1] << endl;
    myVect.push_back(1);
    cout << &myVect[1] << endl;
    myVect.push_back(1);
    cout << &myVect[1] << endl;
    myVect.push_back(1);
    cout << &myVect[1] << endl;

    int counter = 0;
    for_each(myVect.begin(), myVect.end(), [&](int& i) { i = counter; counter++;});
    for_each(myVect.begin(), myVect.end(), [](const int& i) { cout << i << endl; });

    return 0;
}


Now as neat as this is, we actually came full circle - this program suffers from the same dangers as your initial idea! If the vector myVect is resized inside of the lambda then the address of the current element i may change... so did we really solve anything? Not really - the safe thing (if the vector may change size inside your loop) is still to use the iterators directly rather than try to reference them away.
Was This Post Helpful? 0
  • +
  • -

#4 sepp2k  Icon User is online

  • D.I.C Lover
  • member icon

Reputation: 2087
  • View blog
  • Posts: 3,175
  • Joined: 21-June 11

Re: Easy iteration using a pointer

Posted 20 June 2012 - 01:29 PM

View Postishkabible, on 20 June 2012 - 09:44 PM, said:

or you can use std::for_each with a lambda function(another C++11 feature)

std::for_each(my_ints.begin(), my_ints.end(), [&](int& i) {
   //...what ever in here, 'i' is like 'current' here
});



There's no need to use for_each in C++11 when the code to be executed is known at write time. You can just use the new syntax for for-loops:

for(int& i : my_ints) {
    // ...
}



View PostNickDMax, on 20 June 2012 - 10:21 PM, said:

So with vectors rather than a pointer, you can use an iterator.[code]#include <iostream>


Using an iterator instead of a pointer does not help you at all when inserting elements into vector. Changing the size of a vector invalidates all iterator, just like it does with pointers. So the only safe way to iterate over a vector while adding to it is via indices.

That said, you usually don't add to a vector while iterating over it.

This post has been edited by sepp2k: 20 June 2012 - 02:30 PM

Was This Post Helpful? 3
  • +
  • -

#5 NickDMax  Icon User is offline

  • Can grep dead trees!
  • member icon

Reputation: 2250
  • View blog
  • Posts: 9,245
  • Joined: 18-February 07

Re: Easy iteration using a pointer

Posted 20 June 2012 - 01:46 PM

View Postsepp2k, on 20 June 2012 - 04:29 PM, said:

View PostNickDMax, on 20 June 2012 - 10:21 PM, said:

So with vectors rather than a pointer, you can use an iterator.[code]#include <iostream>


Using an iterator instead of a pointer does not help you at all when inserting elements into vector. Changing the size of a vector invalidates all iterator, just like it does with pointers. So the only safe way to iterate over a vector while adding to it is via indices.

That said, you usually don't add to a vector while iterating over it.



You know, now that you mention it I am pretty sure your right. I should have looked into that, I think it was tickling the back of my mind as I wrote. oh well, I will get back into the swing of things eventually.
Was This Post Helpful? 0
  • +
  • -

#6 ishkabible  Icon User is offline

  • spelling expret
  • member icon




Reputation: 1622
  • View blog
  • Posts: 5,709
  • Joined: 03-August 09

Re: Easy iteration using a pointer

Posted 20 June 2012 - 02:31 PM

seep2k: Yes, a range based for loop is even better, thanks for reminding us!

there is a minor caveat if you're using however. VC++10 doesn't support range based for loops, VC++12 beta does and VC++12 will but currently it's VC++ doesn't support it.

also even though I didn't do it, you should us begin(my_ints) instead of my_ints.begin(). It makes no difference if you know you will always be using a vector but if you're writing generic code then it's preferable becuase it will work on containers and things that are not containers.

This post has been edited by ishkabible: 20 June 2012 - 02:32 PM

Was This Post Helpful? 0
  • +
  • -

Page 1 of 1