Does the following program contain a dangling reference? - c++

I have the following program:
#include <iostream>
#include <string>
using namespace std;
using int_arr = int[3];
int& f(int_arr& arr, int index)
{
return arr[index];
}
int main() {
int arr[3] = {1, 2, 3};
int& g = f(arr, 0);
g = 5;
std::cout << arr[0] << std::endl;
}
Is arr[index] returned by f considered a dangling reference?
I don't think this is a dangling reference since the arr object continues to exist even after f returns (so the reference is valid), but I wanted to confirm my understanding. I compiled this with -fsanitize=undefined and it compiled fine and produced the expected output.

No, arr and g have the same life time, so there's no dangling reference.
Note however that you can easily create a dangling reference with your function:
int empty;
int& ref = empty;
int &f(int arr[], int idx) { return arr[idx]; }
void g()
{
int arr[] = { 1, 2, 3 };
ref = f(arr, 0);
}
int main()
{
g();
// ref is accesable here and complete garbage
}

Related

Pointer address assignment in c++ [duplicate]

This question already has answers here:
What are the differences between a pointer variable and a reference variable?
(44 answers)
Closed 1 year ago.
i'm code beginner
#include <bits/stdc++.h>
using namespace std;
void init(int* arr){
arr = new int[10];
cout << arr << endl;
}
int main(int argc, char* argv[]){
int *arr;
init(arr);
cout << arr << endl;
}
this is a simple code.
My question is that why arr's address in init function and arr's address in main function is different?
My think is that i gave arr's address to init function and in init function, arr is assigned a new address through 'new int[10]'
So, arr's address in init function and in main will be same.
but this code doesn't work as i think.....
Can you tell me why?
Step back a little and think about how arguments are passed into functions.
void foo(int x) {
}
int main() {
int x;
foo(x);
}
Here the x in main() is actually passed by value to foo() which means a copy of x is created when passing to foo().
With the same logic, if you think of int * as another variable type
using intPtr = int*; //a sort of typedef
void foo(intPtr x) {
}
int main() {
intPtr x;
foo(x);
}
a copy of x is again created. This is what is happening in your program. For what you expect, you need to pass in the variable by reference
using intPtr = int*;
void foo(intPtr& x) {
}
int main() {
intPtr x;
foo(x);
}
Adapting the same to your program:
#include <iostream>
void init(int*& arr){
arr = new int[10];
std::cout << arr << '\n';
}
int main(int argc, char* argv[]){
int *arr;
init(arr);
std::cout << arr << '\n';
delete(arr);
}
There's another way with pointers that is using a pointer to a pointer (**)
#include <iostream>
void init(int** parr){
*parr = new int[10];
std::cout << *parr << '\n';
}
int main(int argc, char* argv[]){
int *arr;
init(&arr);
std::cout << arr << '\n';
delete(arr);
}
where the address of arr (&arr) is passed into the function. Inside the function, the contents of parr is modified (which is arr in this case).

Rvalues, Lvalues and references confusion

I've been trying to understand rvalues, lvalues and references and their usage as a returning values of functions (methods) so i created few small examples for practice purpose.
So firstly, i came up with this code (after reading somewhere, possibly here, that whenever i have a "regular" (without reference) return value for some method, it is considered to be rvalue, exception is when i add reference operator sign in the return value, like in this example:
#include <iostream>
int x = 5;
int& References()
{
return x;
}
int main()
{
References() = 3;
std::cout << x;
getchar();
getchar();
}
So here, function References returns lvalue when called, and this code works out just fine, however, since this works, i thought that i can do something similar other way around, and this is what i tried:
#include <iostream>
int x = 5;
int References()
{
return x;
}
int main()
{
int a = References();
std::cout << a;
getchar();
getchar();
}
This code works just fine, the output is 5, which means that i successfully assigned value to the variable a, which is something i expected since this function returns "ordinary" integer so it a rvalue.
HOWEVER,
when i once again add reference operator sign to the return value of function References, again, it works fine:
#include <iostream>
int x = 5;
int& References()
{
return x;
}
int main()
{
int a = References();
std::cout << a;
getchar();
getchar();
}
So, even though my function now returns int& which is returned as lvalue, this code still works and the output is still 5, which means that i managed to assign the value to my variable a successfully. What is going on here? Any help appreciated!
When you return by reference you have an lvalue, when you return by value you have a prvalue. In your case the difference you can read from both of them, but cannot assign to prvalue, not clear where is your confusion coming from:
int i1 = lvalue; // fine
int i2 = prvalue; // fine
but:
lvalue = 123; // fine
prvalue = 123; // error
closer to your case:
int &func1();
int func2();
int i1 = func1(); // fine
int i2 = func2(); // fine
func1() = 123; // fine
func2() = 123; // error
more info: Value Category
When you use a reference in an expression or an assignment, it evaluates out to what the reference refers to, not the memory address, which I suppose is what you expect to see.
Compare the output of the following functions:
int x = 5;
int& Reference()
{
return x;
}
int *Pointer()
{
return &x;
}
int main()
{
std::cout << Reference() << std::endl;
std::cout << Pointer() << std::endl;
return 0;
}
According to https://learn.microsoft.com/en-us/previous-versions/f90831hc(v=vs.140) :
You can think of an lvalue as an object that has a name. All variables, including nonmodifiable (const) variables, are lvalues. An rvalue is a temporary value that does not persist beyond the expression that uses it.
The third example is exactly the same as the second one, it's copying the value. You can copy a value from an lvalue just how you can copy a value from an rvalue. If your a variable was of type int& instead, you wouldn't be copying the actual value, you'd just get the same reference. This might help you understand:
#include <iostream>
int x = 5;
int& References()
{
return x;
}
int main()
{
int a = References();
int& b = References();
std::cout << a; // 5
std::cout << b; // 5
std::cout << x; // 5
a = 6;
b = 7;
std::cout << a; // 6
std::cout << b; // 7
std::cout << b; // 7
getchar();
getchar();
}
For comparison:
int n = 10;
int& r = n;
int* p = &n;
int x = n; // copy by value
int y = r; // copy by value, too, use the variable referenced as source
int z = *p; // copy by value third time, using variable pointed to
int& r0 = r; // copy the reference, i. e. r0 now references n as well
int* p0 = p; // copy the pointer...
n = 12;
// now x, y, z all STILL have value 10!
// r, r0, *p and *p0, in contrast, all yield 12
Not different with functions:
int& ref() { return n; }
int val() { return n; }
int& r1 = ref(); // copy the reference, again r1 references n!
int& r2 = val(); // INVALID!!!
// the latter is comparable to:
int& r3 = 7; // invalid...

Why can't I use std::begin/std::end with int(*p)[3] while I can with int(&p)[3]?

This works:
void foo(int (&a)[3]) {
auto ibegin = begin(a);
auto ebegin = end(a);
}
While this doesn't:
void foo(int (*a)[3]) {
auto ibegin = begin(a);
auto ebegin = end(a);
}
I consider int (&a)[3] and int (*a)[3] have the same meaning!
Your code is analogous to:
void foo(vector<int>& a) {
auto ibegin = begin(a);
auto ebegin = end(a);
}
void foo(vector<int>* a) {
auto ibegin = begin(a);
auto ebegin = end(a);
}
The first one works and the second one doesn't for the same reason as it works on int (&a)[3] and doesn't on int (*a)[3]. When you're using pointers to collections instead of references, you need to dereference them when you pass them to the standard library's begin/end.
void foo(vector<int>* a) {
auto ibegin = begin(*a);
auto ebegin = end(*a);
}
void foo(int (*a)[3]) {
auto ibegin = begin(*a);
auto ebegin = end(*a);
}
I consider int (&a)[3] and int (*a)[3] have the same meaning!
Absolutely not. int (&a)[3] declares a reference to array, and int (*a)[3] declares a pointer to array. These are different in most of the same ways a reference to int and a pointer to int are different. (Though when C-style arrays are involved, the automatic array-to-pointer conversion sometimes complicates things.)
I consider int (&a)[3] and int (*a)[3] have the same meaning!
No! The first one is a reference to an array, the second is a pointer to an array.
In C++14 std::begin and std::end are defined as:
template<class T, std::size_t N>
constexpr T* begin(T (&array)[N]) noexcept;
template<class T, std::size_t N>
constexpr T* end(T (&array)[N]) noexcept;
Clearly, the functions take a reference to an array, not a pointer.
You may understand the difference in function overloading where the three are respectively reference to array, pointer to array and array of pointers. So they don't have the same meaning.
#include <iostream>
void foo(int (&a)[3]) {
std::cout << "(&a)[3]" << std::endl;
}
void foo(int (*a)[3]) {
std::cout << "(*a)[3]" << std::endl;
}
void foo(int *a[3]) {
std::cout << "*a[3]" << std::endl;
}
int main() {
int m[3];
int *n[3] = {m + 0, m + 1, m + 2};
foo(m); // (&a)[3]
foo(&m); // (*a)[3]
foo(n); // *a[3]
}
The & symbol is getting the location of the pointer in (&a) you are getting the address of a at location 0 in the array.
While *a is getting the value stored at a[0] this is called dereferencing a pointer. For example:
int * a = {0,1,2};
&a[0] //gets value of the memory pointer of a[0], maybe 0xff123
*a[0] //dereferences the pointer at [0] in this gets case '0'.
For a more complete look at pointers, I'd check out: What does "dereferencing" a pointer mean?

Why is it possible to modify a returned value when the return type is const?

I was under the impression that by declaring a return type const, you could prevent the data structure from being modified. However, I tested this and I can modify the data structures. Why is that?
For example, the following code prints 1 2 3 4 5 6 when compiled with --std=c++11:
#include <iostream>
#include <set>
using namespace std;
const set<int> f(void) {
set<int> s = {1, 2, 3, 4, 5};
return s;
}
int main(void) {
set<int> s = f();
s.insert(6);
for (auto elem: s) {
cout << elem << " ";
}
return 0;
}
Because s is a different object which constructed from the returned value. It has nothing to do with the returned object, and it's a non-const object itself.
The const qualifier for the return value means you can't modify on it directly, like
f().insert(6); // fail

Moving a pointer through array - passing by reference or incrementation?

I have absolutely no clue, what the difference is between the two following examples:
void function(int *p) {
p++;
}
int main() {
int values[] = {1,2,3};
int *p = values;
function(p);
cout << *p;
return 0;
}
This one returns "1".
Whereas a slight modification yields "2" (which is the wanted result):
int main() {
int values[] = {1,2,3};
int *p = values;
p++;
cout << *p;
return 0;
}
Where lies the problem? Is it due to passing by reference or incrementing?
The issue here is
void function(int *p) {
p++;
}
Is using pass by value - not pass by reference. Since the pointer is passed by value any change you make to the pointer itself is not reflected in the call site. If you need to modify where the pointer points then you need to pass it by reference like
void function(int*& p) {
p++;
}
Now when you increment it will point to the second element like it does in your second example.
In this example
void function(int *p) {
p++;
}
int main() {
int values[] = {1,2,3};
int *p = values;
function(p);
cout << *p;
return 0;
}
You are passing a pointer by value, which means you simply pass a copy of the address the pointer is pointing at. You then proceed to increment the function's local copy of that pointer and then exit the function. This has no effect on the original pointer as you incremented a local copy.
In this example, however
int main() {
int values[] = {1,2,3};
int *p = values;
p++;
cout << *p;
return 0;
}
You directly increment your pointer, which means it is now pointing at the next element in the array.
In the first case the value of address is passed by value.
function(p) ==> void function(int *p){}
The 'p' on right side a local variable. So any modification to the pointer will be visible only inside function.