What’s the difference between “get_posts” and “wp_get_recent_posts” when used with “setup_postdata”?

If you look at the source of setup_postdata() you’ll find that it requires an object ($post), to be passed, not an array.

wp_get_recent_posts() (source), by default (for pre 3.1 backwards compatibilty) returns each post as an array. The second, optional argument, that can be passed to wp_get_recent_posts() can prevent this:

$posts = wp_get_recent_posts( $args, OBJECT_K )

(though any value other than ARRAY_A in the second argument will do).