First of all here is a snippet of some code that made me feel not quite sure about how reference identifiers work:
#include <iostream>
using namespace std;
void theUgly (int *z, int *q)
{
if(q == z)
*z=3;
/*else*/
*z=6;
};
void theNice (int &y, int *q)
{
theUgly(&y, q);
};
int main()
{
int x = 5;
theNice(x, &x);
cout << x << endl;
}
I wasn't sure to expect output be 3 or 5 since I wasn't sure about reference running with 2 identifiers having the 2 address, what seemed odd to me, or just leaving this handling to the user.
But I actually got 6 as output, what now lets me assume there is compiled in some kind of atomic operations.
I tried to find the exact documentation about this behavior in my "n3690" copy of the c++11 standard. I found the most part I was able to look up was dealing with capture reference declarations or other stuff named capture or lambda expressions. But just a handful of times I was able to strg+f "by reference" in it. And nothing really seemed to me like explaining the mechanic that describes the behaving of my snippet.
So my question simply is: Where exactly in the c++11 standard it is described, how a program has to handle the parameters and scopes, as happened for my test snippet?
EDIT:
After noticing and adding the missing else the snippet puts out what I would expect. But since I wasn't able to find any information about the behaving of passes by refference in the standard docs, The question remains as it is, independed of the snippet.
Your *z=6; is not in an else clause. This assignment is executed in any case.
Actually, g++-5.3 -O2 -std=c++14 transforms theNice into:
void theNice (int &y, int *q)
{
y = 6;
};
As for the behaviour of references as functions arguments:
[dcl.fct]: function parameter declaration
[dcl.init.ref]: initialization of references
[expr.call]: initialization of function parameters with argument expressions
In short: They behave like local references and reference (alias) the (l)value they are bound to. Calling a function which expects a (lvalue) reference with a (l)value binds that reference to the provided value in the scope of the callee.
Related
I did a bit of an experiment to try to understand references in C++:
#include <iostream>
#include <vector>
#include <set>
struct Description {
int a = 765;
};
class Resource {
public:
Resource(const Description &description) : mDescription(description) {}
const Description &mDescription;
};
void print_set(const std::set<Resource *> &resources) {
for (auto *resource: resources) {
std::cout << resource->mDescription.a << "\n";
}
}
int main() {
std::vector<Description> descriptions;
std::set<Resource *> resources;
descriptions.push_back({ 10 });
resources.insert(new Resource(descriptions.at(0)));
// Same as description (prints 10)
print_set(resources);
// Same as description (prints 20)
descriptions.at(0).a = 20;
print_set(resources);
// Why? (prints 20)
descriptions.clear();
print_set(resources);
// Object is written to the same address (prints 50)
descriptions.push_back({ 50 });
print_set(resources);
// Create new array
descriptions.reserve(100);
// Invalid address
print_set(resources);
for (auto *res : resources) {
delete res;
}
return 0;
}
https://godbolt.org/z/TYqaY6Tz8
I don't understand what is going on here. I have found this excerpt from C++ FAQ:
Important note: Even though a reference is often implemented using an address in the underlying assembly language, please do not think of a reference as a funny looking pointer to an object. A reference is the object, just with another name. It is neither a pointer to the object, nor a copy of the object. It is the object. There is no C++ syntax that lets you operate on the reference itself separate from the object to which it refers.
This creates some questions for me. So, if reference is the object itself and I create a new object in the same memory address, does this mean that the reference "becomes" the new object? In the example above, vectors are linear arrays; so, as long as the array points to the same memory range, the object will be valid. However, this becomes a lot trickier when other data sets are being used (e.g sets, maps, linked lists) because each "node" typically points to different parts of memory.
Should I treat references as undefined if the original object is destroyed? If yes, is there a way to identify that the reference is destroyed other than a custom mechanism that tracks the references?
Note: Tested this with GCC, LLVM, and MSVC
The note is misleading, treating references as syntax sugar for pointers is fine as a mental model. In all the ways a pointer might dangle, a reference will also dangle. Accessing dangling pointers/references is undefined behaviour (UB).
int* p = new int{42};
int& i = *p;
delete p;
void f(int);
f(*p); // UB
f(i); // UB, with the exact same reason
This also extends to the standard containers and their rules about pointer/reference invalidation. The reason any surprising behaviour happens in your example is simply UB.
The way I explain this to myself is:
Pointer is like a finger on your hands. It can point to memory blocks, think of them as a keyboard. So pointer literally points to a keypad that holds something or does something.
Reference is a nickname for something. Your name may be for example Michael Johnson, but people may call you Mike, MJ, Mikeson etc. Anytime you hear your nickname, person who called REFERED to the same thing - you. If you do something to yourself, reference will show the change too. If you point at something else, it won't affect what you previously pointed on (unless you're doing something weird), but rather point on something new. That being said, as in the accepted answer above, if you do something weird with your fingers and your nicknames, you'll see weird things happening.
References are likely the most important feature that C++ has that is critical in coding for beginners. Many schools today start with MATLAB which is insanely slow when you wish to do things seriously. One of the reasons is the lack of controlling references in MATLAB (yes it has them, make a class and derive from the handle - google it out) as you would in C++.
Look these two functions:
double fun1(std::valarray<double> &array)
{
return array.max();
}
double fun2(std::valarray<double> array)
{
return array.max();
}
These simple two functions are very different. When you have some STL array and use fun1, function will expect nickname for that array, and will process it directly without making a copy. fun2 on the other hand will take the input array, create its copy, and process the copy.
Naturally, it is much more efficient to use references when making functions to process inputs in C++. That being said, you must be certain not to change your input in any way, because that will affect original input array in another piece of code where you generated it - you are processing the same thing, just called differently.
This makes references useful for a bit controversial coding, called side-effects.
In C++ you can't make a function with multiple outputs directly without making a custom data type. One workaround is a side effect in example like this:
#include <stdio.h>
#include <valarray>
#include <iostream>
double fun3(std::valarray<double> &array, double &min)
{
min = array.min();
return array.max();
}
int main()
{
std::valarray<double> a={1, 2, 3, 4, 5};
double sideEffectMin;
double max = fun3(a,sideEffectMin);
std::cout << "max of array is " << max << " min of array is " <<
sideEffectMin<<std::endl;
return 0;
}
So fun3 is expecting a reference to a double data type. In other words, it wants the second input to be a nickname for another double variable. This function then goes to alter the reference, and this will also alter the input. Both name and nickname get altered by the function, because it's the same "thing".
In main function, variable sideEffectMin is initialized to 0, but it will get a value when fun3 function is called. Therefore, you got 2 outputs from fun3.
The example shows you the trick with side effect, but also to be ware not to alter your inputs, specially when they are references to something else, unless you know what you are doing.
I expect to crash program with this code:
void f(int& ref)
{
for (auto i{0}; i < 0xfffff; ++i)
{
std::clog << i << ":" << ref++ << std::endl;
}
}
void run(void)
{
int n = 10;
std::thread(f, std::ref(n)).detach();
}
int main(void)
{
run();
std::this_thread::sleep_for(3s);
}
I have GCC 9.3 and compile above program with default parameters. When i run the program i expect to crash which in void f(int&); function we no longer have local int n; variable decelered in void run(void); function, but it's clearly run the program and increase ref variable each time and printed until the 3 second sleep in main function get over. Where i do wrong ?
Your code has undefined behavior because you are using a reference to access an object whose lifetime already ended. There are some special rules regarding extension of lifetime when binding to a const reference, but that does not apply here.
The C++ standard never guarantees that your program will crash if you make a mistake. The C++ standard only specifies what you get when you compile and run valid code. There would be no gain from specifiying things like "If you dereference a null pointer you will get a segmentation fault". Instead the standard specifies what is valid code and mostly stays silent about what happens when you write invalid C++ code. In a nutshell, this is what undefined behavior is.
Repeating a comment of mine:
If you cross a streed when the lights are red, you will not necessarily get hit by car. It just means you should not cross the streets when there are red lights (and when you do you might be hit by a car).
As any analogy this isn't a perfect one. Even if you don't see a car coming and you know that you will not get hit, you should not "cross the street" when "the lights are red". That is because code that relies on undefined behavior like yours may appear to work today, but tomorrow with a different compiler, different version of same compiler, or even same compiler targeting a different platform, bad things may happen.
What I'm confused about is whether the question is asking about passing arguments to functions as references (as opposed to "Call by Value") or about how we can set a pointer to point to a function.
The exact question is:
"Explain the function call by reference giving a suitable example in support of your answer."
The way it's termed makes it feel like it's asking about the latter. In my head, it seems as if it's asking how a function is called by reference, i.e, using a pointer.
C++ for what I'm talking about: int (*ptr)(int, int);
Which can later be assigned to a function like int max(int a, int b);
I know this is a bit of an odd post for Stack since it's more of an English question, but it's the only place I could think to ask.
EDIT: A lot of people are asking what I mean by the second thing. Since I'm new to the concept, I'll just copy - paste what my textbook gives as an example: (Sorry for the terrible formatting, sorry!)
main() {
int max(int a, int b){...}; //Your typical maximum function
int (*ptr)(int, int);
ptr = max;
//Then if you want to run the program
m = (*ptr)(2, 5);
cout << m; //Outputs 5
}
I've been coding for a while in other languages and am pretty proficient, but now I am diving more deeply into C++ and have come across some weird problems that I never had in other languages. The most frustrating one, which a google search hasn't been able to answer, is with two different code orders.
The background is that I have an array of integers, and a pointer an element in the array. When I go to print the pointer one method prints correctly, and the other prints nonsense.
An example of the first code order is:
#include <iostream>
using namespace std;
void main(){
int *pAry;
int Ary[5]={2,5,2,6,8};
pAry=&Ary[3];
cout<<*pAry<<endl;
system("pause");
}
and it works as expected. However this simple order wont work for the full project as I want other modules to access pAry, so I thought a global define should work, since it works in other languages. Here is the example:
#include <iostream>
using namespace std;
int *pAry;
void evaluate();
void main(){
evaluate();
cout<<*pAry<<endl;
system("pause");
}
void evaluate(){
int Ary[5]={2,5,2,6,8};
pAry=&Ary[3];
}
When I use this second method the output is nonsense. Specifically 1241908....when the answer should be 6.
First I would like to know why my global method isn't working, and secondly I would like to know how to make it work. Thanks
In the second example, Ary is local to the function evaluate. When evaluate returns, Ary goes out of scope, and accessing it's memory region results in undefined behaviour.
To avoid this, declare Ary in a scope where it will still be valid at the time you try to access it.
It's not working because your pAry is pointing into a local array, which is destroyed when you return from evaluate(). That's undefined behavior.
One possible fix is to make your local array static:
static int Ary[5]={2,5,2,6,8};
In the evaluate() function, you're aiming pAry at a variable (actually array element) local to that function. A pointer can only be dereferenced as long as the object to which it points still exists. Local objects cease to exist when they go out of scope; in this case, this means all elements of Ary cease to exist when evalute() ends, and thus pAry becomes a dangling pointer (it doesn't point anywhere valid).
Dereferncing a dangling pointer gives undefined behaviour; in your particular case, it outputs a garbage value, but it might just as well crash or (worst of all) appear to work fine until the program changes later.
To solve this issue, you can either make Ary global as well, or make it static:
void evaluate(){
static int Ary[5]={2,5,2,6,8};
pAry=&Ary[3];
}
A static local variable persists across function calls (it exists from first initialisation until program termination), so the pointer will remaing valid.
The problem is that you need to declare Ary with file scope.
int Ary[] = {1,2,3,4,5};
void print_ary(void); // The void parameter is a style thingy. :-)
int main(void)
{
print_ary();
return EXIT_SUCCESS;
}
void print_ary(void)
{
for (unsigned int i = 0; i < (sizeof(Ary) / sizeof(Ary[0]); ++i)
{
std::cout << Ary[i] << std::endl;
}
}
A better solution would be to pass a std::vector around to your functions rather than using a global variable.
#include <iostream>
int main()
{
const int i=10;
int *p =(int *) &i;
*p = 5;
cout<<&i<<" "<<p<<"\n";
cout<<i<<" "<<*p;
return 0;
}
Output:
0x22ff44 0x22ff44
10 5
Please Explain.
Well, your code obviously contains undefined behaviour, so anything can happen.
In this case, I believe what happens is this:
In C++, const ints are considered to be compile-time constants. In your example, the compiler basically replaces your "i" with number 10.
You've attempted to modify a const object, so the behavior is
undefined. The compiler has the right to suppose that the const
object's value doesn't change, which probably explains the
symptoms you see. The compiler also has the right to put the
const object in read only memory. It generally won't do so for
a variable with auto lifetime, but a lot will if the const has
static lifetime; in that case, the program will crash (on most
systems).
I'll take a shot at it: since there's no logical reason for that output, the compiler must have optimised that wretched cout<<i<<" " to a simple "cout<<"10 ". But it's just a hunch.