Square brackets mean one character out of this set. [abc] matches a single a, b or c — not the string abc, which is the first thing everyone gets wrong.
Inside the brackets, a dash between two characters is a range: [a-z], [0-9], [A-Za-z0-9]. The range runs over character codes, so [A-z] is a range too — it just also contains [, \, ], ^, _ and a backtick.
A ^ immediately after the opening bracket negates the whole set: [^aeiou] is one character that is not a vowel. Anywhere else in the class it is an ordinary caret.
A negated class still has to match a character. [^x] does not match the end of the string, and a pattern built out of negated classes will still fail on an empty input.
Most metacharacters lose their meaning inside a class. [.*+] is three literal characters. Only ], \, ^ at the start and - between two characters still mean anything.
A dash at either end of the class has nothing to range between, so it is a literal dash. [a-] and [-a] are the same set.