Yes -- two main pieces of advice when looking at codebase which is brand new to you, and trying to figure out "how stuff works" (as opposed to looking to solve a specific problem):
It's perfectly reasonable to start with
main() and work your way down. Ideally, you'll find that the program is a sequence of relatively few operations, such as (hypothetically):
- set up graphics contexts
- set up IO
- set up datastructures representing the world
- loop on events
For now, act as if these high-level calls are part of the language -- assume each works as needed, and treat each as a black box, until you have an idea how the whole program (relatively simple at this level) fits together.
Once this is clear, pick one high-level operation you're interested in, find the function that implements it... and go back to step one, figuring out how that function works in terms of the simple operations it uses. Lather, rinse, and recur until you have a better grasp on the program as a whole; for any given branch of this exploration, you can stop at the point where you feel you understand enough "for now" (such as when the operation a function calls is provided by another library), and move on.
Tools, tools, tools -- one thing which will make the above process a lot easier is a set of tools to help you answer questions like
- where is function
foo() declared? defined?
- what are all the places that function
bar() is called?
On Unix, tools like gnu idtools, global, or the tags support in emacs or vim can help you here. Most IDEs, such as XCode, Eclipse, or Visual Studio, also have tools to answer these kinds of questions.
But most of all, remember that in many ways a big program is like a small program, just... bigger (and with tricks and techniques to manage this size). The language works the way you already know, and in a well-designed program, the code to do any one thing will be relatively small (even if it depends on a operations defined elsewhere to stay manageable in size).