Building with Makefiles

Always compile with warnings on!
gcc -Wall foo.c
The -Wall means Warn all. Without it you will have subtle flaws in your program that will be impossible to debug. Behavior that just does not make sense because of some trivial syntax or cast precision loss. It does not take a lot of effort to produce warning free code, but it does take a lot of effort to find a bug that the compile could have told you about. You will also learn quite a lot about the subtleties C/C++ in a controlled environment, instead of the lost in the wilderness with wild animals way.

Build debug symbols (-g), you wont regret it when you get a phone call at 2am in the morning.

Use compiler optimisation (-O1 -O2 or -O3), it really does improve performance dramatically.

When in doubt, make clean.
Often you are working with other people's make files. Maybe edited by multiple people afterward. They or you might not have set the dependencies up in a fool proof manner, resulting in a broken build. If you have library dependencies, make clean;make on those too! Spend some time reading yosefk if you are depressed about this and need cheering up. But quite often if you are having some weird problem it is a broken build. Make clean will either fix it or at least give a compile error as to what is wrong. (eg: a function might have been redefined with different arguments, but if the .o is not rebuilt you will link to it incorrectly and crash!)

Makefiles can be complicated things, but really we want them to do something very simple: If anything changes, recompile it! Dependencies can be generated for you to do this, so you don't need to sit there figuring them out.
gcc -MMD
creates a .d file for all your
-include $(OBJS:.o=.d) will include these dependency files (if they exist, and the .o can't exist if they don't) so this will always keep you up to date and avoid make clean paralysis.

You can make rules to automatically compile your stuff for you. Check this out, it even puts the .o files and .d files into an obj directory so it wont clutter up your filesystem:
$(OBJDIR)/%.o: %.cpp Makefile
@$(RM) -f $@
@mkdir -p $(DIRS)
$(CPP) -MMD $(CFLAGS) $(INCLUDE) -c $< -o $@ 2>&1 | more

If you are lazy it can even find all your source files to compile:
SRCS = $(shell find -name 'tests' -prune -o -name '*.cpp' -printf "%P ")
DIRS = $(shell find \( -name 'CVS' -o -name '$(OBJDIR)' -o -name 'tests' \) -prune -o -type d -printf "$(OBJDIR)/%P ")
OBJS = $(patsubst %.cpp,$(OBJDIR)/%.o,$(SRCS))

However sometimes it is preferable to be able to specify the source manually if you have multiple executables to build.