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);
Related
I know that, if we declare variables inside a function without allocating memory for them, they will be lost after the function finishes its job.
The following code prints:
(null)
5
char* getString()
{
char arr[] = "SomeText";
return arr;
}
int getInt()
{
int b = 5;
return b;
}
int main()
{
printf("%s", getString());
printf("\n");
printf("%d", getInt());
return 0;
}
Both arr and b variables are created on the stack, so they both should be destroyed when the functions end. My question is, how come variable b doesn't get lost while variable arr is lost?
A unique and often confusing feature of C (and inherited by C++) is that an array when used in an expression is not treated as a collection of values, but (most of the time) as a pointer to its first element.† So, when you return an array from a function, you are returning the address of its first element.
Dereferencing the address of an object with automatic storage duration that is no longer in scope results in undefined behavior.
When you return a value from a function, a copy of the value is returned to the caller.
Thus, when you return an integer, the caller receives a copy of that integer value.
If the value is a pointer, the copied value is a pointer. If the pointer is pointing to an invalid object, then if the receiver of the pointer tried to dereference the pointer value, it would result in undefined behavior.
† There are 3 exceptions: (1) As an operand to &; (2) As an operand to sizeof; and (3) A string literal used to initialize an array. In C++, there are other exceptions: (4) As an operand of decltype; (5) As the function argument to a reference parameter; (6) An object to initialize a reference variable; ... probably something else I am forgetting...
Both getInt and getString return a value.
getInt returns an int value of 5. It remains 5 in the caller.
getString returns a char * value that points to arr. While the caller receives a pointer, the thing it points to, arr, no longer exists (in the C standard’s model of computation) when the function returns.
Thus, it is not the value being returned by the function that is the problem so much as its meaning. The number 5 retains its meaning. A pointer to a thing that ceases to exist does not retain its meaning.
Why these definitions are all ok:
int func(int p=255) {
return p;
}
int func1(const int &p=255) {
return p;
}
but this definition:
int func2(int &p=255) {
return p;
}
leads to compile error ?
What is the logic behind it ?
Taking arguments by reference means, you dont work with your local copy of the variable, but with a variable already defined in the scope of the calling function.
While your first example makes sense (you have a local variable p that you can fill with a default value) the second example is a bit more tricky: Usually when using references you expect the variable to have an address, since you want to modify it. For const-refernces, the compiler will still allow you to pass a literal, even if something like "reference to a literal" makes no sense at all.
In the third case the compiler expects you to modify p. But what part of the memory should this modification affect? "255" has no address - therefore it cant be used as a reference.
If you want to have a more detailed explanation, you should probably look for keywords like "rvalue" and "lvalue".
The attempted function definition
auto func2( int& p = 255 )
-> int
{ return p; }
… fails because you can't bind an rvalue to a reference to non-const. Basically that rule is because a simple value like 255 isn't modifiable. While the reference can be used to modify.
One simple solution is to express the default as a separate overload:
auto func2( int& p )
-> int
{ return p; }
auto func2()
-> int
{
int scratchpad = 255;
return func2( scratchpad );
}
A non-const reference must be bound to lvalue (i.e. its address could be got). 255 (i.e. an int literal) is not a lvalue, so int &p=255 fails.
A const reference could be bound to rvalue, and for this case, a temporary int will be created and initialized from 255. The temporary int's lifetime will be the same as the const reference.
int func(int p=255) {
return p;
}
p here is copied by value, and it is defined to exist in the scope of func.
int func2(int &p) {
return p;
}
// e.g. use:
int value = 10;
func2(value); // func2 *could* modify value because it is passed by non-const reference
In this case the compiler here expects p to have a name somewhere in memory (i.e. lvalue), so it can possibly write to it within func2. Passing by non-const reference allows you to modify the variable used in the function call. Since p must belong to someone else somewhere since it can be modified, you can't assign a default value to it.
But what about the const-reference case? Here, the compiler is smart enough to know that p can never be written to since it is const, so it doesn't need to have a name in memory to write to. In cases of a literal being passed (e.g. 255), it (behind the scenes) essentially creates a temporary and passes that temporary variable to the function.
int func1(const int &p=255) {
return p;
}
func1(10);
// Behind the scenes, the compiler creates something along these lines
// since it can never be modified.
const int some_temporary = 10;
func1(some_temporary);
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.
I came across something I don't understand well. Let's suppose I want to pass a character pointer to a function that takes a reference to a void pointer.
void doStuff(void*& buffer)
{
// do something
}
I would usually do something like this :
int main()
{
unsigned char* buffer = 0;
void* b = reinterpret_cast<void *>(buffer);
doStuff(b);
return 0;
}
Why it is not possible to directly pass the reinterpret_cast to the function?
int main()
{
unsigned char* buffer = 0
// This generate a compilation error.
doStuff(reinterpret_cast<void *>(buffer));
// This would be fine.
doStuff(reinterpret_cast<void *&>(buffer));
return 0;
}
There must be a good reason behind this behavior but I don't see it.
In the first example, you're actually passing the pointer variable b. So it works.
In the second example, the first reinterpret_cast returns a pointer (by value), which doesn't match the reference the function should get, while the second returns said reference.
As an example to show you how references work, look at these two functions,
void doSomething( unsigned char *ptr );
void doSomethingRef( unsigned char *&ptr );
Say we have this pointer,
unsigned char *a;
Both functions are called the same way,
doSomething( a ); // Passing pointer a by value
doSomethingRef( a );// Passing pointer a by reference
Though it may look like you're passing it by value, but the function takes a reference so it will be passed as a reference.
A reference is similar to a pointer but it has to be initialized with a left value and can't be null.
Having said that, there are much better alternatives to using void* and especially void*&. void* makes code harder to read and easier to shoot yourself in the foot (if anything by making yourself use these strange casts).
As I said in the comments, you could use a template and not bother with void casting.
template< class T > void doStuff( T *&buffer ) {
...
}
Or,
template< class T > T* doStuff( T* buffer ) {
...
}
EDIT: On a side note, your second example is missing a semicolon,
unsigned char* buffer = 0; // Right here
int main()
{
unsigned char* buffer = 0;
void* b = reinterpret_cast<void *>(buffer);
doStuff(b);
return 0;
}
b is a pointer and doStuff(b) is receiving the address of a pointer. The types match, b is of type void*& (*b is of type void*) and doStuff receives a parameter of type void*&.
int main()
{
unsigned char* buffer = 0
// This generate a compilation error.
doStuff(reinterpret_cast<void *>(buffer));
// This would be fine.
doStuff(reinterpret_cast<void *&>(buffer));
return 0;
}
The second call is like the the call from the above function with b as parameter.
The first call is passing simply a void pointer. The types are different, look closer void* is not the same as void*&
This is how you would specify a reinterpret_cast as the function argument directly, without using an intermediate variable. As others have told you, it's bad practice, but I want to answer your original question. This is for educational purposes only, of course!
#include <iostream>
void doStuff(void*& buffer) {
static const int count = 4;
buffer = static_cast<void*>(static_cast<char*>(buffer) + count);
}
int main() {
char str[] = "0123456789";
char* ptr = str;
std::cout << "Before: '" << ptr << "'\n";
doStuff(*reinterpret_cast<void**>(&ptr)); // <== Here's the Magic!
std::cout << "After: '" << ptr << "'\n";
}
Here we have a pointer to char named ptr and we want to wrangle its type to void*& (a reference to a void pointer), suitable for passing as an argument to function doStuff.
Although references are implemented like pointers, they are semantically more like transparent aliases for another value, so the language doesn't provide the kind of flexibility you get for manipulating pointers.
The trick is: a dereferenced pointer converts directly into a correspondingly typed reference.
So to get a reference to a pointer, we start with a pointer to a pointer:
&ptr (char** - a pointer to a pointer to char)
Now the magic of reinterpret_cast brings us closer to our goal:
reinterpret_cast<void**>(&ptr) (now void** - a pointer to a void pointer)
Finally add the dereferencing operator and our masquerade is complete:
*reinterpret_cast<void**>(&ptr) (void*& - a reference to a void pointer)
This compiles fine in Visual Studio 2013. Here is what the program spits out:
Before: '0123456789'
After: '456789'
The doStuff function successfully advanced ptr by 4 characters, where ptr is a char*, passed by reference as a reinterpret_cast void*.
Obviously, one reason this demonstration works is because doStuff casts the pointer back to a char* to get the updated value. In real-world implementations, all pointers have the same size, so you can probably still get away with this kind of manipulation while switching between types.
But, if you start manipulating pointed-to values using reinterpreted pointers, all kinds of badness can happen. You will also probably be in violation of the "strict aliasing" rule then, so you might as well just change your name to Mister Undefined Behavior and join the circus. Freak.
I'm not sure if this is right, but...
I believe it's as simple matching the argument type:
void doStuff(void* buffer) {
std::cout << reinterpret_cast<char*>(buffer) << std::endl;
return;
}
You could do the above and the int main() would compile correctly.
A reference is different from a copy of a value--the difference is that the copied value doesn't necessarily need to live in a variable or in a place in memory--a copied value could be just a stack variable while a reference shouldn't be able to point to an expiring value. This becomes important once you start playing around with reference and value semantics.
tl;dr: Don't mix references and values when casting. Doing operations on a reference is different than doing operations on a value; even if argument substitution is implicitly casted.
Does dereferencing a pointer and passing that to a function which takes its argument by reference create a copy of the object?
In this case the value at the pointer is copied (though this is not necessarily the case as the optimiser may optimise it out).
int val = *pPtr;
In this case however no copy will take place:
int& rVal = *pPtr;
The reason no copy takes place is because a reference is not a machine code level construct. It is a higher level construct and thus is something the compiler uses internally rather than generating specific code for it.
The same, obviously, goes for function parameters.
In the simple case, no. There are more complicated cases, though:
void foo(float const& arg);
int * p = new int(7);
foo(*p);
Here, a temporary object is created, because the type of the dereferenced pointer (int) does not match the base type of the function parameter (float). A conversion sequence exists, and the converted temporary can be bound to arg since that's a const reference.
Hopefully it does not : it would if the called function takes its argument by value.
Furthermore, that's the expected behavior of a reference :
void inc(int &i) { ++i; }
int main()
{
int i = 0;
int *j = &i;
inc(*j);
std::cout << i << std::endl;
}
This code is expected to print 1 because inc takes its argument by reference. Had a copy been made upon inc call, the code would print 0.
No. A reference is more or less just like a pointer with different notation and the restriction that there is no null reference. But like a pointer it contains just the address of an object.