int* test_prt(const vector<int>* array, const int index)
{
return array+index;
}
why the first argument can't be const ? under this circumstance, the "const" means what can not be modified?
It is common to place const after the type name, because it enables the "const to the left rule".
This is the same as what you wrote:
int* test_prt(vector<int> const* array, const int index)
{
return array+index;
}
Here, the item to the left is the vector, so this is a pointer to constant vector.
If you don't use this convention, the only special case is when const is all the way on the left (there is nothing to the left of const), which means it applies to the thing immediately to the right of the const.
array + index is not modifying either array or index, it simply computes the sum between the two operands without affecting the original, so the code is working as it's supposed to. The const here means that both the vector and index cannot change in any way. For example, if I attempt to change one of arrays elements, I get an error:
array[0] = 5; // error! assignment of read-only value
The same applies to index. By the way, I think your function signature should have been written as:
int* test_prt(const vector<int>& array, const int index)
{
return &array[index];
// or &array.at(index) which performs bounds checking
}
That is, we take the vector by reference and use an offset from the first element pointed to by its internal buffer. Taking by reference is essential as it avoids a copy and returning a pointer to a local copy would be Undefined Behavior. Your original code would have only worked if array was an actual array of vectors!
And just to be clear, here's how you would call the function:
std::vector<int> v = {1, 2, 3, 4, 5};
std::cout << test_ptr(v, 1); // prints the address of the 2nd element
I would also remove the "const" in front of the integer.
As it's an arg passed by value,
int* test_prt(vector<int> const* array, int index)
{
return array+index;
}
Related
I try this simple program:
int* getElement(int arrayy[], int index) {
return &arrayy[index];
}
int main() {
int arrayy[4]{};
*getElement(arrayy, 2)=50;
std::cout << arrayy[2] << '\n';// here it prints 50 !
return 0;
}
getElement() will return the address of an element of my array through a pointer return, dereference it in the main and then change the value.
I want to do the same thing using std::array in the place of classic array.
int* getElement(std::array<int, 4> arrayy, int index) {
return &arrayy[index];
}
int main() {
std::array<int, 4> arrayy{};
*getElement(arrayy, 2)=50;
std::cout << arrayy[2] << '\n';//here it prints 0 !
return 0;
}
in the first it prints 50 and in the second 0 !
does std::array pass by value in calls ?
In the case of passing a pointer by value, the pointed at values are the same in the function and the call site. So the first program does what you expect.
In the second program, it's different because std::array has value semantics, so passing it by value means that you are passing a copy of all the underlying memory as well.
Note that in your code in the second program, the result of 0 is not guaranteed. You are returning a pointer to a value inside the copied std::array which will die when the function returns. This invokes undefined behavior.
If you want to get the same effect as the first program, you need to pass the std::array by reference instead:
int* getElement(std::array<int, 4> &arrayy, int index) {
// ^ reference
return &arrayy[index];
}
By default everything in C++ passes by value. Even your traditional array example passes a pointer by value. Of course you can use a pointer to change what it points at.
Your code actually has undefined behaviour because you are returning a pointer to the internals of an object which has been destroyed, namely the arrayy parameter of getElement which is destroyed when you exit the function.
If you want pass by reference you have to request it
int* getElement(std::array<int, 4>& arrayy, int index) {
return &arrayy[index];
}
The & after the parameter type name makes all the difference.
does std::array pass by value in calls ?
Yes. The ability to do this (pass and return the object) is essentially the reason why std::array exists.
So, the function parameter is local to the function, and you return a pointer to this local object which becomes an invalid pointer as soon as the function returns.
No.
Your code is just wrong, because you pass your std::array as value.
You would want to pass it by reference, like so:
int* getElement(std::array<int, 4> &arrayy, int index);
I am not sure the true meaning of const vector<int *> so I compiled the code below to get an idea but am now more confused.
vector<int *> v;
int x = 1, y = 2;
v.push_back(&x);
v.push_back(&y);
const vector<int *> w = v;
w[0] = &y; //failed. Element is a constant pointer?
*(w[0]) ++; //failed. Element pointer references to constant value?
If I had stopped here, I would have assumed that const vector<int *> is a vector of const int * const, but then I tried the following which clearly contradicted that assumption.
*(w[0]) += 3; //passed. Value not constant?
*(w[0]) = 20; //passed. Why...
Now *(w[0]) for reason unknown to me obviously treats ++ and += and assignment differently. I convinced myself that const vector only declares a constant object of the vector class and that the above results might depend on the actual implementation of the operator overloading of vector class. But I can't wrap my head around this. Can anyone help explain, please?
If it is relevant, I used g++ 4.2 on a Mac.
Why is dereferenced element in const vector of int pointers mutable?
For const vector<int *>, the element would be const pointer to non-const, i.e. int * const, so you can modify the object pointed by the pointer, but not the pointer itself.
According to Operator Precedence, postfix increment operator has higher precedence than operator*, so *(w[0]) ++; is equivalent to
* ((w[0]) ++);
The increment on the pointer is performed at first, then it fails. w[0] = &y; is also trying to modify the pointer, so it fails too.
On the other hand, (*w[0]) ++; (i.e. increment on the pointee) would be fine. And the following statements are fine too, because they're both modifying the objects pointed by the pointer, not the pointers.
*(w[0]) += 3; //passed.
*(w[0]) = 20; //passed.
It's a matter of operator precedence.
When you do *(w[0]) ++ you attempt to modify the pointer.
When you do *(w[0]) += 3 you modify the data pointed to by the pointer.
w is a const vector<int *>. The const qualifier is applied to the vector. Therefore, the corresponding const member function will be used for the operator[]:
const_reference operator[]( size_type pos ) const;
Since the vector is const-qualified and contains elements of type int * ( and not const int *), the type of the expression w[0] is int * const& (instead of const int *&). That is, it is a reference to a constant pointer to an int and not a reference to a pointer to a constant int: the constness is applied to the the pointer itself, not to the data being pointed.
By doing *(w[0]) += 3 you are not modifying the value of the pointer the vector returns (which is const), but the value this pointer is pointing to. Since this pointer is of type int * const (and not const int *), you can modify what it is pointing to, so it does work. However, doing w[0] = &y is performing an assignment on a constant pointer, so it does not compile.
const vector<T> lets you access its elements as T const & (i.e. const T &). In this case, T is int *, so this is int * const &, a const reference to a pointer that points to an int. The pointer is constant, but the int is not.
The type of the vector would have needed to be vector<int const *> (i.e. vector<const int*>) in which case the elements would be accessed via int const * const &.
Bottom line, constness is transitive with templates but not with pointers. And if you put pointers in templates, you get a bit of both behaviors.
Is it possible to pass a single array element by reference (so that the argument passed in is modified)?
For example, say this is a main part:
int ar[5] = {1,2,3,4,5};
test(&ar[2]);
And now this is the function definition:
void test(int &p)
{
p = p + 10;
return;
}
The above code results in a compilation error.
&ar[2] takes the address of the element at the 3rd position in the array. So you try to pass an int* to an function expecting an int&. Typically you don't need to do anything to an expression to make it be treated as a reference, since a reference is just another name for some object that exists elsewhere. (This causes the confusion, since it sometimes seems to behave like a pointer).
Just pass ar[2], the object itself:
test(ar[2]);
Maybe it will be clearer if you put the array to one side for a moment, and just use an int.
void test (int &p);
// ...
int a = 54321;
test(a); // just pass a, not a pointer to a
Just to provide an alternative to BobTFish's answer. In case you need to pass an address, then your function definition needs to accept a pointer as an argument.
void test(int *arg);
// ...
int arg = 1234;
test(&arg);
and to use the address of an array element you do the following:
void test(int *arg);
// ...
int arg[0] = 1234;
test(&arg[0]);
Some people add parenthesis to the array element: &(arg[0]), which is fine when in doubt, But the [] operator has higher precedence than the & operator.
Does const vector<A> mean that its elements are constas well?
In the code below,
v[0].set (1234); in void g ( const vector<A> & v )
produces the compiler error
const.cpp:28:3: error: member function 'set' not viable: 'this'
argument has
type 'const value_type' (aka 'const A'), but function is not marked const
Why?
But (*v[0]).set(1234); in void h ( const vector<A *> & v )
is OK for the compiler.
What's the difference between the versions?
// ...........................................................
class A {
private:
int a;
public:
A (int a_) : a (a_) { }
int get () const { return a; }
void set (int a_) { a = a_; }
};
// ...........................................................
void g ( const vector<A> & v ) {
cout << v[0].get();
v[0].set (1234);
} // ()
// ...........................................................
void h ( const vector<A *> & v ) {
cout << (*v[0]).get();
(*v[0]).set(1234);
} // ()
Yes, a const vector provides access to its elements as if they were const, that is, it only gives you const references. In your second function, it's not the objects of type A that are const, but pointers to them. A pointer being const does not mean that the object the pointer is pointing to is const. To declare a pointer-to-const, use the type A const *.
The first version
v[0].set (1234);
does not compile because it tries to change the vector's first element returned to it by reference. The compiler thinks it's a change because set(int) is not marked const.
The second version, on the other hand, only reads from the vector
(*v[0]).set(1234);
and calls set on the result of the dereference of a constant reference to a pointer that it gets back.
When you call v[0] on a const vector, you get back a const reference to A. When element type is a pointer, calling set on it is OK. You could change the second example to
v[0]->set(1234);
and get the same result as before. This is because you get a reference to a pointer that is constant, but the item pointed to by that pointer is not constant.
So a const object can only call const methods. That is:
class V {
public:
void foo() { ... } // Can't be called
void bar() const { ... } // Can be called
};
So let's look at a vector's operator[]:
reference operator[]( size_type pos );
const_reference operator[]( size_type pos ) const;
So when the vector object is const, it will return a const_reference.
About: (*v[0]).set(1234);
Let's break this down:
A * const & ptr = v[0];
A & val = *ptr;
val.set(1234);
Note that you have a constant pointer to variable data. So you can't change what is pointed at, but you can change the value that the pointer points at.
Yes, because std::vector is a value-type rather than a reference type.
To simplify things: An std::vector considers the values in its buffer as part of itself, so that changing them means changing the vector. This may be confusing if we only think of a vector as holding a pointer to an allocated buffer and the size: We don't change these two fields when we change elements in the buffer.
It's the opposite than for pointers, which are reference-types; if you change the pointed-to value you haven't changed the pointer itself.
The fact that std::vector is a value-type is a design choice - it's not something inherent in the C++ language. Thus, for example, the std::span class is also basically a pair of a pointer and a size, but an std::span can be const while you can still change the pointed-to elements. (There are other differences between spans and vectors.)
I'm trying to understand one thing.
I know I can't change constant pointer's value, but I can change its address, if I initialize a pointer the following way:
int foo = 3;
const int *ptr = &foo;
*ptr = 6; // throws an error
int bar = 0;
ptr = &bar; // foo == 0
Now, let's say I declare (/define, I never remember which one) a function:
void change(const int arr[], int size);
int main() {
int foo[2] = {};
change(foo, 2);
std::cout << foo[0];
}
void change(const int arr[], int size) {
// arr[0] = 5 - throws an error
int bar = 5;
arr = &bar;
}
The last line in the code above doesn't throw any errors. However, when the function is over and I display the first element, it shows 0 - so nothing has changed.
Why is that so?
In both situations I have constant pointers, and I try to change its address. In the first example it works. In the second one it doesn't.
I also have another question. I've been told that if I want to pass two-pointers type to the function, const keyword won't work as expected. Is that true? And if so, then what's the reason?
You're screwing up the terminology a lot, so I'm going to start there because I think it is a major cause of your confusion. Consider:
int x;
int* p = &x;
x is an int and p is a "pointer to int". To modify the value of p means to change p itself to point somewhere else. A pointers value is the address it holds. This pointer p holds an address of an int object. To change the pointer's value doesn't mean to change the int object. For example, p = 0; would be modifying p's value.
In addition to that, the address of p is not the address it holds. The address of p would be what you get if you did &p and would be of type "pointer to pointer to int". That is, the address of p is where you would find the pointer p in memory. Since an object doesn't move around in memory, there's no such thing as "changing its address".
So now that's out of the way, let's understand what a constant pointer is. const int* is not a constant pointer. It's a pointer to a constant object. The object it points to is constant, not the pointer itself. A constant pointer type would look more like int* const. Here the const applies to the pointer, so it is of type "const pointer to int".
Okay, now I'll quickly give you an easy way to remember the difference between declaration and definition. If you bought a dictionary and all it had was a list of words in it, would you really call it a dictionary? No, a dictionary is supposed to filled with definitions of words. It should tell you what those words mean. The dictionary with no definition is only declaring that such words exist in the given language. So a declaration says that something exists, and a definition gives the meaning of it. In your case:
// Declaration
void change(const int arr[], int size);
// Definition
void change(const int arr[], int size) {
// arr[0] = 5 - throws an error
int bar = 5;
arr = &bar;
}
Now to explain the issue here. There's no such thing as an array argument type. Any array type argument is converted to a pointer. So the declaration of change is actually identical to:
void change(const int arr*, int size);
when you do arr = &bar; you are simply assigning the address of bar to the pointer arr. That has no effect on the array elements that arr is pointing to. Why should it? You are simply changing where arr points to, not the objects it points at. And in fact you can't change the objects it points at because they are const ints.
I know I can't change constant pointer's value, but I can change its address
Nah. You can't change the address of anything. Did you mean that you can't change the object it points to, but you can change the pointer itself? Because that's what is the truth - in the case of a pointer-to-const type. However, if you have a const pointer to a non-const object, then you can't change the pointer, you can only change whatever it points to.
Addendum (edit): a handy rule of thumb is that const applies to what stands on its left side, except when nothing stands on its left side, because then it applies to the type that is on its right side. Examples:
const int *ptr;
int const *ptr; // these two are equivalent: non-const pointer to const int
int *const ptr; // const pointer to non-const int
int const *const ptr; // const pointer to const int
const int *const ptr; // same as above
However, when the function is over and I display the first element, it shows 0 - so nothing has changed.
Scope. arr is a function argument - so it's local to the function. Whatever you do with it, it won't be effective outside of the function. To achieve what you want, declare it as a reference:
void change(const int *&arr, int size)
I've been told that if I want to pass two-pointers type to the function, const keyword won't work as expected. Is that true?
This depends on what your expectations are. If you read the standard attentively and have proper expectations, then it will indeed work as expected. Examples:
const int **ptr; // pointer to pointer to const int
int const **ptr; // same as above
const int *const *ptr; // pointer to const pointer to const int
etc. You can generate more of these funky declarations using CDecl
The first thing is using the proper terms, which actually helps in understanding:
const int *ptr = &foo;
That is a pointer to a constant integer, not a constant pointer to an integer. You cannot change the object pointed, but you can change the pointer to refer to a different object.
void change(const int arr[], int size);
That signature is processed by the compiler as void change( const int *arr, int size ), and I'd recommend that you type it as that, as it will reduce confusions. Where the function is called, change(foo,2), the compiler will transform the argument foo (type is int[2]) to &foo[0] which has type const int* (both transformations are commonly called decay of the array to a pointer).
Now as in the first block of code, you cannot change the pointed memory, but you can change the pointer to refer to a different object.
Additionally, in C++ the default mode is pass-by-value. The pointer arr inside change is a copy of the value &foo[0]. Inside the function you are changing that copy, but that will not affect anything outside of the function context.
const int * is doing what it's supposed to do, what's confusing you is its purpose.
Think of it as a pointer to a readonly int. You can point to any int you want, but it's going to be readonly no matter what.
You might use this to loop through an array of type const int, for example.