First you need to define "the nth largest salary in the table" clearly.
What about duplicates? If two people earn 1000 and one earns 800, is 800 then the 2nd or 3rd highest salary? Can salary be NULL? If so, ignore NULL values? What if there are not enough rows?
Assuming ...
- Duplicate entries only count once - so 800 is considered 2nd in my example.
- salarycan be- NULL. Ignore- NULLvalues.
- Return NULLor nothing (no row) if there are not enough rows.
Proper solution
SELECT salary
FROM  (
   SELECT salary, dense_rank() OVER (ORDER BY salary DESC NULLS LAST) AS drnk
   FROM   company
   ) sub
WHERE  drnk = 8;
That's basically the same as Tim's answer, but NULLS LAST prevents NULL from coming first in descending order. Only needed if salary can be NULL, obviously, but never wrong. Best supported with an index using the same sort order DESC NULLS LAST. See:
FOR loop as proof of concept
If you insist on using a FOR loop for training purposes, this would be the minimal correct form:
CREATE OR REPLACE FUNCTION get_nth_max(n integer, OUT nth_max numeric)
  LANGUAGE plpgsql AS
$func$
BEGIN
   FOR nth_max IN
      SELECT DISTINCT salary
      FROM   company
      ORDER  BY salary DESC NULLS LAST
      LIMIT  n
   LOOP
      -- do nothing
   END LOOP;
END
$func$;
Working with numeric instead of real, because we don't want to use a floating-point number for monetary values.
Your version does a lot of unnecessary work. You don't need the overall max, you don't need to check anything in the loop, just run through it, the last assignment will be the result. Still inefficient, but not as much.
Notably, SELECT get_nth_max(10) returns NULL if there are only 9 rows, while the above SQL query returns no row. A subtle difference that may be relevant. (You could devise a function with RETURNS SETOF numeric to return no row for no result ...)
Using an OUT parameter to shorten the syntax. See: