Search code examples
c++iteratorrandom-access

For C++ Random Access Iterator (vector iterator), how is the difference between iterators calculated?


I have following code to randomize the elements in a Random Access Iterator (vector<int>::iterator) -

#include <vector>
#include <list>
#include <iterator>
#include <algorithm>
#include <stdlib.h>
#include <iostream>
using namespace std;

template<class RandomAccesIterator>
void randomize(RandomAccesIterator iterBegin, RandomAccesIterator iterEnd)
{
    while (iterBegin != iterEnd)
    {
        int rand1 = rand();
        auto iterdiff = iterEnd - iterBegin;
        auto secondarg = iterBegin + rand1 % (iterdiff);
        iter_swap(iterBegin, secondarg);
        ++iterBegin;
    }
}

And following is the main() function:

int main()
{
    //container used as to apply algorithm to.
    list<int> List = {34,77,16,2,35,76,18,2,56};

    //randomize example.
    cout << "calling randomize on sorted vector: " << endl;
    List.sort();
    vector<int> temp(List.begin(), List.end());
    cout << "before randomize: " << endl;
    for (vector<int>::iterator it = temp.begin(); it != temp.end(); it++)
    {
        cout << *it << " ";
    }
    cout << endl;

    randomize(temp.begin(),temp.end());
    cout << "after randomize: " << endl;
    for (vector<int>::iterator it = temp.begin(); it != temp.end(); it++)
    {
        cout << *it << " ";
    }
    cout << endl<<endl;
    return 0;
}

In the randomize template function, how is the difference between iterators calculated (iterEnd - iterBegin)?

I tried a couple of things in the immediate window, and it looks like iterEnd - iterBegin is calculated like so (the vector has 9 elements in it, and the below calculation gives 9). I tried with various number of elements in the vector<int>, and the answer was correct each time. The computation is for the first time we encounter iterEnd - iterBegin in the while loop (that is, for 9 elements in the vector):

In the immediate window -

1.

iterEnd
{-33686019}
    [ptr]: 0x0080f9dc {-33686019}
    [Raw View]: {...}

2.

iterBegin
{2}
    [ptr]: 0x0080f9b8 {2}
    [Raw View]: {...}

3.

0x0080f9dc-0x0080f9b8 //iterEnd - iterBegin gives 36.
36

4.

36/4 //Dividing 36 by 4, since an integer is 4 bytes (we are iterating over a vector of integers).
9

I also tried with 8 elements in the vector<int>, and the same type of calculation resulted in 8 elements in step 4. above.

I have a couple of questions here:

  1. Are the steps that I am performing to get the number of elements in the vector correct (steps 1. to 4. above)?
  2. In step 4. above, I am dividing 36, which is in decimal - by 4 bytes. How is this giving me the correct result? It would make sense if I were dividing 36 bytes by 4 bytes, and then that would give me 9 elements. Why is dividing decimal 36 by 4 bytes giving me the correct answer?

Please see: I am using the following compiler: Microsoft Visual Studio Enterprise 2019 (Version 16.2.1). The operating system platform is 64 bit, x64-based processor. I am building on Debug x86 environment. The Windows edition is Windows 10 Pro


Solution

  • Your steps are correct, but only because:

    • int happens to be 4 bytes long on your system
    • std::vector<int>::iterator happens to trivally wrap a raw pointer (int*) on your system

    Instead of hard-coding the value 4, you can use sizeof(int) to evaluate the correct number of bytes on any system you compile your code on.

    std::size_t numElements = (0x0080f9dc - 0x0080f9b8) / sizeof(int);  // Better
    

    As for your second question, the 36 you are calculating is not a unitless decimal value. The raw integer value of a raw pointer (remember: std::vector<int>::iterator trivially wraps an int* so it has the same size) uses bytes as their implicit unit, so you are actually dividing bytes by bytes in your steps.

    Finally, I suggest avoiding this sort of pointer arithmetic (rationale). The standard already provides a function to calculate exactly this, std::distance, and it will work correctly across all standard iterators and on any system you compile your code on.

    std::size_t numElements = std::distance(iterBegin, iterEnd);  // Best