Unlike the IATA codes, the ICAO codes have a regional structure, i.e. the first letter is allocated by continent, the second is a country within the continent, the remaining two are used to identify each airport.
Continental letters:
A = Antartica & South Pacific
B = Greenland & Iceland
C = Canada
D = Northwest Africa
E = Northern Europe
F = Southern & Central Africa
G = West Africa, Canary Islands
H = East Africa
K = United States
L = Southern Europe
M = Central America & Caribbean
N = South Pacific
O = Middle East
P = Hawaii, Alaska & North Pacific
R = East Asia
S = South America
T = Caribbean
U = Russia and former USSR countries
V = South Asia
W = Southeast Asia
Y = Australia
Z = People's Republic of China
Some country/airport examples:
EBBR: Belgium - Brussels International Airport, Brussels (IATA code BRU)
FAJS: South Africa - Johannesburg International Airport, Johannesburg (IATA code JNB)
KBOS: United States - Logan International Airport, Boston (IATA code BOS)
LIRF: Italy - Leonardo Da Vinci International Airport, Fiumicino, near Rome (IATA code FCO)
OMDB: United Arab Emirates - Dubai International Airport, Dubai
Others are less logical; very few UK airports are obvious, for example:
EGLL - London Heathrow, EGKK - London Gatwick (see also List of UK airfields)
In the United States and Canada, most airports which have been assigned three-letter codes by their respective regulatory agencies use the same code with leading "K" or "C" as their ICAO code; e.g., YYC (Calgary International Airport, Calgary, Alberta) and CYYC, IAD (Dulles International Airport, Chantilly, Virginia) and KIAD. These codes are not to be confused with radio callsigns, even though both countries use four-letter callsigns starting with those letters.
A list of airports, sorted by IATA code, is available.