query_posts clarification needed

Can someone please explain how “$posts” is used within the loop?

It isn’t.

It returns the array of queried posts , but it is insignificant and not used at all: if you remove it than the code works the same:

global $query_string; 
query_posts($query_string.'&cat=-9'); 

if (have_posts()) : while (have_posts()) : the_post();
...
endwhile; endif;

This is because query_posts() override global $wp_query object, that is the same query object that loop functions like have_posts() and the_post() use to display posts.

and this is the reason why that function should not be used:

  • overriding a widely used global variable it easily has side effects
  • it is very slow, because you override a already ran query with another, running query to database 2 times.

If you need a separate query from main (global) query, than make no sense override main query, just perform a secondary query using WP_Query object or get_posts.

If you need to modify main query, than modify it before it runs, in this way you’ll run only 1 query instead of 2 and your code will be faster. To edit main query before it runs, use 'pre_get_posts' action hook.