Global Variables/Pass by Reference melts my brain

I'm having a lot of trouble wrapping my brain around this snippet of code from class- I think I understand it, but when I substitute different numbers in, I am reliably wrong every time. The first time the function runs, I am OK, but the second time it prints, I'm totally lost. Here is the code:

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
#include <iostream>

using namespace std;

int a=5, b=20, c=15;

void brainmelting(int x, int &y, int &z)
{
	int b=10;
	cout << "a is " << a << " and c is " << c << endl;
	x=a+c;
	cout << x << endl;
	y=z+5+b;
	cout << y << endl;
	c=x+y;
	cout << c << endl;
cout << a << " " << b << " " << c << " " << x << " " << y << " " << z << endl;
}

int main()
{
	int a=100;
	brainmelting(a,b,c);
	cout << a << " " << b << " " << c << endl;
	brainmelting(b,c,a);
	cout << a << " " << b << " " << c << endl;
	cin.get();
	return 0;
}


Any help, advice, Prozac would be greatly appreciated. Thanks for taking a look. ;)
It's okay to be confused by this. It is a horrifying mess.

You could try to rewrite this without the function to understand it better. That is, instead of brainmelting(a,b,c); put the body of brainmelting, add int x = a; and replace all y or z with b or c.
Our teacher offered it up as a way to help us learn the difference between pass by reference and pass by value- it's done nothing but confuse me, though. I can make sense of it (slowly) the first pass, but when it's passed back in... it truly melts my brain.
brainmelting(b,c,a)

What output are you expecting and why?
Just execute the program on a piece of paper step by step.
to help us learn the difference between pass by reference and pass by value

It looks to me more like an example of how not to write code.

There are much tidier ways to demonstrate pass by reference and pass by value.
Including global variables in the mix seems like a deliberate attempt to muddy the waters.
Int x in the function is a completely new and separate variable that is initialized with the value passed to it by the function call. In the first call it is initialized with the variable a, or the value 5. In the 2nd call it is initialized by b, or the value 20. Now y and z are not new, separate variables. They are references, or sometimes called aliases. A reference is simply another name for some other variable. So in the function y and z are simply names for the 2nd and 3rd arguments passed to it. In the first call, y now refers to b and z refers to c.

This is important to understand because now when you manipulate x it is ONLY modifying x, not a. However, y and z, since they are references, instead actually modify b and c (respectively.) In the 2nd call, y refers to c and z refers to a.

The reason this is important is because function can only return 1 object (which includes a variable.) So, if you need to manipulate more then 1 object you can pass them by reference to the function and manipulate them indirectly. If you passed those objects by value, you would be working with copies of the objects and not the objects themselves, so the values you obtained from manipulating them would be lost once the function exits (since variables declared in a function, even in the parameter list, are destroyed once the function exits) unless you return that value (and again, you could only return a single value that way.)

So any modifications you make to x in the functions is not made to the first variable you pass to the function (a in the first call, b in the second.) However, any changes made to y or z affect the globals because of them being references.
Last edited on
Thank you. The weird thing is, I've been using functions w/pass by reference without too much trouble. The global variables were giving me hell though. I will never look at global variables the same way again. I'm pretty sure I'm never using a global variable if I can avoid it, haha. Thank you so much!
Last edited on
Including global variables in the mix seems like a deliberate attempt to muddy the waters.

Of course, it's an academical example. They're often confusing on purpose - if the students cannot figure it out, it serves as a signal to them that the topics previously covered (such as scopes) haven't sunk in well enough yet.
This is a little tricky because you are passing some variables by reference. Which means that when you change the variable in the function, those changes will be reflected outside the function.

Quick example:

1
2
3
4
5
6
7
8
9
10
11
12
13
void byref(int& a) { a = 5; }
void byval(int  a) { a = 5; }

int main()
{
  int x = 0;

  byval(x);  // does not change x
  cout << x;  // prints 0

  byref(x);  // changes x
  cout << x;  // prints 5
}


Your code is the exact same idea... only much more confusing because of all the math you're doing.

Also, you are mixing local and global variables because you have multiple variables named 'a' and 'b'. So that's some cause for confusion. When you refer to 'a', it will chose the local variable if there is one... or the global variable if there's no local variable.


So let's break this down starting with main:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
int a=5, b=20, c=15;

//...
int main()
{
	int a=100;
	// at this point, you have 4 variables:
	// a == 100  (the 'a' local to main)
	//  ::a == 5   (the :: indicates it's the global 'a')
	//  ::b == 20
	//  ::c == 15

	// here we call brainmelting with 3 params:
	//  a (local, 100) is being passed by value
	//  ::b (global, 20) is being passed by reference
	//  ::c (global, 15) is being passed by reference
	brainmelting(a,b,c);


Now let's step into brainmelting and see what it will do with that.

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

// for this first call, we have:
//  x = 100
//  y = ::b (20)
//  z = ::c (15)
//
//  remember that since y and z are references, changes to y and z will
//  also change ::b and ::c.

void brainmelting(int x, int &y, int &z)
{
	int b=10;   // creating a local 'b' that is also 10.  Note this is
		// a different var from the global  ::b.

	cout << "a is " << a << " and c is " << c << endl;  // prints 5 & 15 (global ::a and global ::c)
	x=a+c;   // assigns x to 20
	cout << x << endl;  // prints 20
	y=z+5+b;   // assigns y to 15+5+10, which is 30
		// note that this also changes ::b, so now ::b is also 30
	cout << y << endl;  // prints 30
	c=x+y;   // assigns global ::c to 20+30  = 50
		// note that since z is a reference to ::c, this also changes z
	cout << c << endl;  // prints 50

// at this point, we have:
//  ::a = 5
//  ::b = 30  (but this is not being printed on the below line)
//  b = 10
//  ::c = 50
//  x = 20
//  y = 30
//  z = 50 (same as ::c)

cout << a << " " << b << " " << c << " " << x << " " << y << " " << z << endl;
// prints 5, 10, 50, 20, 30, 50
}




I think I got that right. I didn't actually compile this to try it out.

Anyway I don't have time to walk through the second call to brainmelting but if you step through it in the same manner as illustrated above you might be able to sort it out.



EDIT: bah... I'm ninja'd by like 6 people.
Last edited on
ROFL, that's fine. I really appreciate everyone taking the time to look at my brain-melting code. I can't wait until I learn enough to help other people. Thank you! :)
Maybe this might make it easier to understand (maybe):

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
#include <iostream>

using namespace std;

int a=5, b=20, c=15;
//globals a,b,c have scope here, and anywhere else in this program.

void brainmelting(int x, int &y, int &z)
{
	//globals a,b,c have scope here
	//copy of x is made, which starts its scope here and ends at the end of this function
	//reference of y and z is used here.  It will be viewable by main() since we passed the address (reference)

	int b=10;	//This local b will now take over.  Global b is now "ignored"!

	x=a+c;

	y=z+5+b;

	c=x+y;

	//The copy of x is now destroyed, since it was passed-by-value
	//The values of y and z are preserved and "stored" into whatever names they were in main().
}

int main()
{
	//Global variables a, b, c have scope here
	int a=100;	//This variable is now used as a.  Global "a" is now ignored.
	brainmelting(a,b,c);	//pass these variables; once we get inside the function, we don't care what they were called.  They could've been donkey, giraffe, and platypus for all we care.  We're just passing the integers, not the "name"

	brainmelting(b,c,a);

	return 0;
}
I was writing an example... good thing I refreshed the page

Of course, it's an academical example. They're often confusing on purpose - if the students cannot figure it out, it serves as a signal to them that the topics previously covered (such as scopes) haven't sunk in well enough yet.
That's a pretty stupid thing to do though. Examples are meant to clarify something, not to make them confused even more because of unrelated matters.

I will never look at global variables the same way again. I'm pretty sure I'm never using a global variable if I can avoid it, haha.
Then your teacher did indeed teach you a valuable lesson. That's exactly one of the reasons why it's better avoiding global variables
Last edited on
I just wanted to thank you guys again- I took my final today and a variant of this question was on it. I felt a lot better going in since I'd practiced it a bit. =)
Topic archived. No new replies allowed.