vector pointer to an element

Pages: 12
Could someone help me understand the return of the function. Isn't a vector like an array?

return &(*pVec)[i];

Isn't pVec[i] already the value in i, so why do you have to place the dereference operator there?
I thought it would be something like this:

return &(pVec[i]);


Or maybe something like a pointer to arrays, where you can say:

return pVec + i; (return address of pVec + i)

Or

return & (pVec + i); (return address of pVec + i)

Is there another way to get the address of the element other than what the sample program shows like you can with an array?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include <iostream>
#include <vector>
using namespace std;

string* ptrToElement (vector<string>* const pVec, int i);

int main()
{
	vector<string> supplyList;
	supplyList.push_back("pencil");
	supplyList.push_back("pen");
	supplyList.push_back("ruler");
	supplyList.push_back("notepad");

	for (auto i: supplyList)
		cout << i << endl;
	
	cout << endl << *(ptrToElement(&supplyList, 0)) << endl;
	
	return 0;
}

string* ptrToElement (vector<string>* const pVec, int i)
{
	return &(*pVec)[i];
}
Isn't a vector like an array? ... Isn't pVec[i] already the value in i...?

Nope. A vector is a dynamic C-style array contained within a class structure with other members, something like:

1
2
3
4
5
6
7
8
9
template <typename T>
class vector
{
    T*     m_data;     // pointer to allocated array
    size_t m_size;     // how many elements are in use
    size_t m_capacity; // how many elements-worth of space have been allocated
public:
    ...
};

So a pointer to a vector points to that structure, not to the data itself like a pointer to a C-style array would.

The code is rather odd and pointless, though.
Last edited on
But at the end the class is just a program and if somebody wanted to, they could have programmed the vector class with an overloaded + operator so that one could take the offset of the C-style array? So that we could have used (pVec + i) to give the memory address offset. But they chose not to program it that way and there is no other way to work it like a normal array or pointer with memory address offset?

Instead, in the class for:
return &(*pVec)[i];

They must have overloaded the subscript operator "[i]" part?
Is the "(*pVec)" part an overloaded operator ().....parenthesis too?

pVec is a pointer to a vector and the "(*pVec)" part is dereferencing the vector, which is what value exactly? Does it just translate to "(*pVec)" = inventory.....(the inventory vector)?

So that it ends up looking like &(inventory[i])?
Last edited on
That is hard to follow.
a vector is indeed just a C style pointer wrapped up to do the work for you. It makes new memory, copies the data over, and deletes the old memory when it needs to be bigger due to push_back etc. It does indeed have [] overloaded. It works just like a normal array, if you code that way:

vector<int> cstyle(1000); //acts exactly like int cstyle[1000] if you want it to.
cstyle[42] = 97; //fine.

a vector is an object, like a struct. It has several values -- its max capacity (allocated memory), its size (used memory), a pointer to its data (the array like part).

forget the pointer to vector until you understand vector itself.
once you get that, its just a normal pointer to the item. A vector, like any struct/class/object, has memory for its bits out in ram, and a pointer takes you there. Same as it does for an integer. And you can allocate a c-style block of vectors from a pointer too, but that would really be pushing insanity for almost any use case you can think up -- lots of stuff you CAN do, you should not do.
I mean to say isn't a pointer to a vector syntax like the syntax of the array and pointer to array, but apparently not completely and in particular when trying to get the address of the elements.

It is beautiful how they coded pointers & arrays to operate on both the index and the address offset (like the code below) and it would have been consistent and nice for the container classes to follow that coding/logic.

I guess it is what it is. And like you said "its just a normal pointer to the item."
So "(*pVec)" = inventory.....(the inventory vector) in the return from the function of previous code:

return &(*pVec)[i];

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
#include <iostream>
#include <vector>
using namespace std;


int main()
{
	//Array & pointer
	string strArray[3] = {"one","two","three"};
	string* ptrArray = &strArray[0];
	
	cout << "Address strArray = "    	<< strArray    	<< endl;
	cout << "Address strArray[0] = " 	<< &strArray[0] << endl;
	cout << "Address ptrArray = "		<< ptrArray 	<<endl << endl;
	
	
	cout << "Address strArray + 1 = "	<< strArray + 1	<< endl;
	cout << "Address strArray[1] = " 	<< &strArray[1] << endl;
	cout << "Address ptrArray + 1 = "	<< ptrArray + 1 <<endl << endl;
	
	cout << "Address strArray + 2 = "	<< strArray + 2	<< endl;
	cout << "Address strArray[2] = " 	<< &strArray[2] << endl;
	cout << "Address ptrArray + 2 = "	<< ptrArray + 2 <<endl << endl;
	
	
	//Vector & pointer
	vector<string> vecList;
	vecList.push_back("one");
	vecList.push_back("two");
	vecList.push_back("three");
	
	vector<string>* pVecList = &vecList;

	for (auto i: vecList)
		cout << i << endl;
	
	cout << "Address of &vecList[0] = " << &vecList[0] << endl;
	cout << "Address of &(*pVecList)[0] = " << &(*pVecList)[0] << endl;
	//cout << "Address of pVecList = " << pVecList << endl;
	
	
	cout << "Address of &vecList[1] = " << &vecList[1] << endl;
	cout << "Address of &(*pVecList)[1] = " << &(*pVecList)[1] << endl;
	//cout << "Address of pVecList + 1 = " << pVecList + 1 << endl;
	
	
	cout << "Address of &vecList[2] = " << &vecList[2] << endl;
	cout << "Address of &(*pVecList)[2] = " << &(*pVecList)[2] << endl;
	//cout << "Address of pVecList + 2 = " << pVecList + 2 << endl;
	
	
	return 0;
}
Last edited on
But they chose not to program it that way and there is no other way to work it like a normal array or pointer with memory address offset?

Use std::vector::data to get a pointer to the first element of the array.
https://en.cppreference.com/w/cpp/container/vector/data

It is beautiful how they coded pointers & arrays to operate on both the index and the address offset (like the code below) and it would have been consistent and nice for the container classes to follow that coding/logic.

It might be beautiful or convenient sometimes, but it's highly inconsistent.
Notational inconsistencies are a greater problem in C++ than C, because C++ attempts to support generic programming - which tends to expose these inconsistencies, and because C++ is larger and more complicated - which makes these inconsistencies harder to remember.

Remember that (for built-in operator[]), x[n] is equivalent to *(x + n).
Last edited on
"Remember that (for built-in operator[]), x[n] is equivalent to *(x + n)."

It makes more sense now seeing it this way. I saw code for [] with example below.
So in this case the "(*pVec)" is the left operand for the [] subscript operator.

1
2
3
4
5
6
	const char& operator [](int index) const
	{
		if (index < GetLength())
			return buffer [index];
		else
	}
I mean to say isn't a pointer to a vector syntax like the syntax of the array and pointer to array
No. I'd consider that a feature, not a bug :). std::vector is a (template) class. If you take the address of it, you get a pointer to the vector. It isn't like the weird rules for arrays.

Isn't pVec[i] already the value in i
. No, because you declared pVec as a pointer to a vector, not a vector. pVec[i] is refers to the i'th vector that pVec points to, It's similar to this:
1
2
3
int i=4, j=10;
int *jp = &j;
jp[i] = 9;  // ERROR using jp like it points to an array instead of a single item. 


Hope this helps.
pVec is a pointer to a vector.

*pVec gives you (a reference to) the vector.

(*pVec)[i] accesses the vector element at index i.

&(*pVec)[i] gives you a pointer to the vector element at index i.

---

You would have to do something similar if you had a pointer to an array.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#include <iostream>
using namespace std;

const int N = 4;

string* ptrToElement(string (*pArr)[N], int i);

int main()
{
	string supplyList[N];
	supplyList[0] = "pencil";
	supplyList[1] = "pen";
	supplyList[2] = "ruler";
	supplyList[3] = "notepad";

	for (auto s: supplyList)
		cout << s << endl;
	
	cout << endl << *(ptrToElement(&supplyList, 0)) << endl;
	
	return 0;
}

string* ptrToElement(string (*pArr)[N], int i)
{
	return &(*pArr)[i];
}

---

What you're thinking about is when you have a pointer to the fist element in the array. If you have such a pointer you can do what you say. You can have such a pointer with vectors too (because all vector elements are stored in an array). The only difference is that conversion to such a pointer does not happen automatically. You have to either do &vec[0] or vec.data().

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <iostream>
#include <vector>
using namespace std;

string* ptrToElement(string* p, int i);

int main()
{
	vector<string> supplyList1 = {"pencil", "pen"};
	string supplyList2[] = {"ruler", "notepad"};
	
	cout << *(ptrToElement(supplyList1.data(), 0)) << endl;
	cout << *(ptrToElement(supplyList2, 0)) << endl;
	
	return 0;
}

string* ptrToElement(string* p, int i)
{
	return p + i;
}

---

Note that normally when working with vectors it's more common to use references rather than pointers which makes the code look less complicated (if you ask me).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <iostream>
#include <vector>
using namespace std;

string& refToElement(vector<string>& vec, int i);

int main()
{
	vector<string> supplyList = {"pencil", "pen"};
	cout << refToElement(supplyList, 0) << endl;
	
	return 0;
}

string& refToElement(vector<string>& vec, int i)
{
	return vec[i];
}
Last edited on
1
2
3
4
string* ptrToElement (vector<string>* const pVec, int i)
{
	return pVec->data()+i;
}
Thanks, it works! I knew there just had to be a way and that the programmer of the vector would just not omit the tight relation between array & pointers.

So much easier for a newb to read:
return pVec->data() + i;

than this:
return &(*pVec)[i];


Especially on those late & tired nights, but I will have to get used to the original variant too as I understand it now. Code is from a book that shows passing of pointers to and from a function.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#include <iostream>
#include <vector>
using namespace std;

string* ptrToElement (vector<string>* const pVec, int i);

int main()
{
	vector<string> supplyList;
	supplyList.push_back("pencil");
	supplyList.push_back("pen");
	supplyList.push_back("ruler");
	supplyList.push_back("notepad");

	for (auto i: supplyList)
		cout << i << endl;
	
	cout << endl << *(ptrToElement(&supplyList, 0)) << endl;
	
	//TEST
	cout << endl;
	cout <<  *(supplyList.data() + 0) << endl;
	cout <<  *(supplyList.data() + 1) << endl;
	cout <<  *(supplyList.data() + 2) << endl;
	cout <<  *(supplyList.data() + 3) << endl;
	
	return 0;
}

string* ptrToElement (vector<string>* const pVec, int i)
{
	//return &(*pVec)[i];
	//return pVec->data() + i;
	return (*pVec).data() + i;
	
}
Last edited on
1
2
3
4
5
So much easier for a newb to read:
return pVec->data() + i;

than this:
return &(*pVec)[i];

The vector::data() were added by C++11. The vector::operator[] has been in use for decades. Introduction of "better" ways does not instantly remove legacy code and habits.

Speaking of which, why
string* ptrToElement (vector<string>* const pVec, int i);
and not
string* ptrToElement (vector<string> & pVec, int i);
protoseepp wrote:
So much easier for a newb to read:
return pVec->data() + i;

than this:
return &(*pVec)[i];

Is it?

If you understand that (*pVec)[i] gives you the element at index i it should be pretty easy to understand that using the & will give you a pointer to that element.

pVec->data() + i requires you to know about pointer arithmetic (i.e. what adding a pointer and an integer means) and you need to know what data() returns.

protoseepp wrote:
Code is from a book that shows passing of pointers to and from a function.

The book is probably overusing pointers just to show how they work. Later you'll learn about references which you will probably use instead of pointers in the vast majority of situations when passing things in and out of functions.
Last edited on
Ah, just for the hell of it ...

1
2
3
4
string* ptrToElement (vector<string>* const pVec, int i)
{
	return &pVec->operator[](i);
}



The real problems with
return &(*pVec)[i];
are the bracketing (just try removing the ()) and the precedence of operators &, * and []: is that
return &((*pVec)[i]); // yes
or
return (&(*pVec))[i]; // no


Knowing that, if p is a pointer, then p + i is a pointer to a position i objects further on is not a bad thing to be aware of.

FWIW, I have applications where knowing how to get directly to the data buffer through the data() member function are absolutely crucial.
Last edited on
I didn't mean it's bad to know or that using the data() function is bad. It was just that I would have thought that &vec[i] was easier to understand for a beginner than vec.data() + i. I could be wrong.
The book does pointers for the function in this chapter and references (&) with the exact same function in another chapter, which I understand completely because it is easy. Address offsets are easier because I practiced it and get it.

Yes, might as well.
return &pVec->operator[](i);

I get all of this now and it feels so good to get it and not just memorize it. You guys just reminded me of another one that I just memorized, but still don't quite get.

A pointer (ptr) to an int or string will print out:
ptr; //Address pointer points to
*ptr; //prints out the value that pointer points to.
&ptr; //prints out the address of where pointer is stored at.

But nooo, for an array to char all of sudden the rules change.
ptrArray; //prints "Hello World"
&ptrArray; //prints address of where pointer is stored at (I think).
(unsigned int*)ptrArray; // prints out address of myCharArray, but HOW?

ptrArray is a char* and casting it to an (unsigned int*) gets the address...how in the world?


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <iostream>
using namespace std;

int main()
{
	char myCharArray[] = {'H', 'e', 'l', 'l', 'o', ' ', 'W', 'o', 'r', 'l', 'd', '\0'};
	char* ptrArray = myCharArray;
	
	cout << "&myCharArray[0] = " << &myCharArray << endl;
	cout << "(unsigned int*)ptrArray = " << (unsigned int*)ptrArray << endl;
	
	
	cout << "ptrArray = " << ptrArray << endl;	//"Hello World"

	return 0;
}
Your book might cover this in later chapters....

Using C style casts are not type safe, a key feature of C++. You should use the C++ casting operators if at all possible in C++ code.
https://www.cplusplus.com/doc/tutorial/typecasting/

1
2
3
4
5
6
7
8
9
10
11
12
13
#include <iostream>

int main()
{
	char  myCharArray[] { 'H', 'e', 'l', 'l', 'o', ' ', 'W', 'o', 'r', 'l', 'd', '\0' };
	char* ptrArray		  { myCharArray };

	std::cout << "&myCharArray[0] = " << &myCharArray << '\n';
	std::cout << "reinterpret_cast<unsigned int*>(ptrArray) = " 
				 << reinterpret_cast<unsigned int*>(ptrArray) << '\n';

	std::cout << "ptrArray = " << ptrArray << '\n';
}

Yes, the syntax is ugly, compared to C casts. So ugly doing a C++ cast really stands out.

Especially a reinterpret_cast.
https://www.learncpp.com/cpp-tutorial/explicit-type-conversion-casting-and-static-cast/

Doing a cast can be a clue that the C++ code design might have problems/flaws.
@protoseepp,
I think the fundamental answer to your question is that << has a special overload for c-style (null-terminated) strings, const char *
http://www.cplusplus.com/reference/ostream/ostream/operator-free/

If you cast that away - however you choose to do that cast - then the special case will no longer apply.
Last edited on
And while we're at it, whenever possible you should prefer static_cast over reinterpret_cast because it doesn't allow as many "dangerous" casts. You need reinterpret_cast to convert a char* to an unsigned int* but I would say that is not what you want in this case. All you need is a void* (pointer to unspecified type) and for that you can use static_cast.

 
std::cout << static_cast<void*>(ptrArray) << '\n';

The reason why std::cout's << operator treat char* different from other pointers is because char* is so often used for strings that it would have been inconvenient if it didn't treat it as a string. It's that << overload that gets called when you print a string literal.

 
std::cout << "hello\n"; // You wouldn't want this to print a memory address, would you? 

I know its inconsistent, and potentially dangerous, but it is how it is for historical reasons.
Last edited on
So both the older c-style cast and the newer c++ cast require (unsigned int*). So I now get that I just need to use it because of legacy C.

Just to see if I might be missing anything else let me take one step back and review it all:
char = is a built-in type and char array[] are arrays of built-in types, but they are not classes or vectors.

string = is a class.

A pointer to a char array, points to what exactly? Like the other pointers, to the address of the first index? Because it knows the first address, but returns the whole c-style string because of the need to support legacy C and convenience. The return of the pointer almost behaves like a (*this), a pointer to the thing...the whole c-style string.

1
2
	char myCharArray[] = {'H', 'e', 'l', 'l', 'o', ' ', 'W', 'o', 'r', 'l', 'd', '\0'};
	char* ptrArray = myCharArray;


Now, it might be true that cout << treats char* differently and that there is some overloading, BUT even outside the cout the way the pointer to the char array behaves holds true and there is NO OVERLOADING here!....so the below line sends the whole string and not the address:

string strHello = ptrArray; // prints out "Hello World" and doesn't give address, because legacy needs this format for whole c-style string...it is just the way it is!

And I cannot do this:
void* intAddress = ptrArray; //doesn't give address, because legacy needs this format for whole c-style string...it is just the way it is!

But I can still do it this way:
void* intAddress = (unsigned int*)ptrArray; //NO OVERLOADING!

I did some playing around in the code below and here are some notes:
1) Interesting that I can cast to many types and it still works, probably because all these pointers are all 8 bytes and all can hold an address.

2) To get address of a particular index I used "(unsigned int*)(ptrArray+i)".

3) At the bottom of the code you cannot even get the address of a single char "&charK" and I tried (unsigned int*)charK that Gives [Warning] cast to pointer from integer of different size [-Wint-to-pointer-cast]. So it needs cast to a wide int, never did that type yet.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
#include <iostream>
using namespace std;

int main()
{
	char myCharArray[] = {'H', 'e', 'l', 'l', 'o', ' ', 'W', 'o', 'r', 'l', 'd', '\0'};
	char* ptrArray = myCharArray;
	
	cout << "&myCharArray = " << &myCharArray << endl;
	cout << "&myCharArray[0] = " << &myCharArray[0] << endl;	//<<<<<<<<<<<<<<<<<<<<<< prints "Hello World"
	cout << "&myCharArray[1] = " << &myCharArray[1] << endl;	//<<<<<<<<<<<<<<<<<<<<<< prints "ello World"
	cout << "&myCharArray[2] = " << &myCharArray[2] << endl;	//<<<<<<<<<<<<<<<<<<<<<< prints "llo World"
	cout << "(unsigned int*)ptrArray = " << (unsigned int*)ptrArray << endl;
	cout << "(unsigned int*)ptrArray = " << (unsigned int*)(ptrArray+ (sizeof(char))) << endl;
	cout << "(unsigned int*)ptrArray = " << (unsigned int*)(ptrArray+1) << endl;
	cout << "(unsigned int*)ptrArray = " << (unsigned int*)(ptrArray+2) << endl;
	cout << "__________________________________________\n";
	
	//Address for every index
	for (int i = 0; i < sizeof(myCharArray) - 1; ++i )
		cout << "(unsigned int*)ptrArray" << "[" << i << "] =" << (unsigned int*)(ptrArray+i) << endl;
	
	cout << "ptrArray = " << ptrArray << endl;	//"Hello World"
	
	//sizeof()
	cout << sizeof(char) << endl;			// = 1
	cout << sizeof(myCharArray[0]) << endl;	// = 1
	cout << sizeof(myCharArray[1]) << endl;	// = 1
	cout << sizeof(myCharArray[2]) << endl;	// = 1
	
	cout << sizeof(ptrArray) << endl;		// = 8
	cout << sizeof(unsigned int*) << endl;	// = 8
	cout << sizeof(char*) << endl;			// = 8
	cout << "__________________________________________\n";
	
	//Interesting, can cast into any type
	cout << (int*)ptrArray << endl;
	cout << (long long*)ptrArray << endl;
	cout << (short int*)ptrArray << endl;
	cout << (double*)ptrArray << endl;
	cout << (long double*)ptrArray << endl;
	cout << (float*)ptrArray << endl;
	cout << (string*)ptrArray << endl;
	
	cout << sizeof((int*)ptrArray) << endl;			//8
	cout << sizeof((long long*)ptrArray) << endl;	//8
	cout << sizeof((short int*)ptrArray) << endl;	//8
	cout << sizeof((double*)ptrArray) << endl;		//8
	cout << sizeof((long double*)ptrArray) << endl;	//8
	cout << sizeof((float*)ptrArray) << endl;		//8
	cout << sizeof((string*)ptrArray) << endl;		//8
	
	cout << "__________________________________________\n";
	
	string strHello = ptrArray;
	cout << "strHello = " << strHello << endl;		//"Hello World"
	
	//void* intAddress = ptrArray;		//Still does not give address, even without a cout <<
	//unsigned int* intAddress = (unsigned int*)ptrArray;
	void* intAddress = (unsigned int*)ptrArray;
	cout << intAddress << endl;
	cout << (unsigned int*)ptrArray	<< endl;
	
	char charK = 'k';
	cout << charK << endl << endl;
	//cout << &charK << endl << endl;     //does not work
	cout << (unsigned int*)charK << endl; //Gives [Warning] cast to pointer from integer of different size [-Wint-to-pointer-cast]
	
	return 0;
}

Last edited on
Pages: 12