It's not necessarily simple - you have to think about it in more abstract terms. Don't look at code as a list of statements that each have to do something; look at each line as something to be evaluated, where the evaluation of functions (and sometimes other things) can change the state of the system. You may have heard the words 'substitution model' if you've taken basic CS classes; if not, don't worry about it. Consider this:
Code:
int ret = foo(0, bar(x), mything.baz);
1. Well, we're going to assign something to our new integer ret. What are we assigning? Check out the right side of the =.
2. Let's evaluate foo. To do that, we'll need to look at each of its arguments.
2-1. 0 is an int literal. No problem.
2-2. bar(x). Let's evaluate that. bar takes one argument.
2-2-1. x is a variable - somewhere else, it was defined as 62, so substitute 62 for x and continue.
2-2-1. bar(62). Dive into bar's function definition, and get a value. Let's say it comes back with 125.
2-2. Substitute 125 for bar(x). We're looking at foo(0, 125,...) right now.
2-3. mything.baz? Check out the baz field of the struct mything, and look, it's 15.
2. Great, now we've got foo(0, 125, 15). Plug all of those values into the appropriate places in foo's definition and evaluate that. Let's say that comes back as 70.
3. The right side is now a literal, so we can do the assignment. 70 is stored into ret.
A little long-winded, but do you see what I'm saying about evaluations? Whether calling foo() creates a window somewhere or not isn't important; all the system does is evaluate statements. If there were no 'int ret =', it'd be the same deal, and the return value would just be discarded. 'n;' evaluates to whatever's in n, and we move on to the next line. It doesn't do anything, but it fits the model we're working in. Call it a degenerate case.
I'm not entirely sure that will get across, so please let me know if I'm babbling.