Going by wp_get_current_user() information in the Codex, the function utilizes the global
$current_user object and if necessary initializes it before use. As others have stated,
get_current_user_id() uses this function on the backend.
Consider /wp-includes/user.php, lines 323-327 (the function definition for this code). At the tail end, the return value is
return ( isset( $user->ID ) ? (int) $user->ID : 0 ); — this code will return
0 if the logged-in user is somehow unavailable.
Use the init or any subsequent action to call this function. Calling
it outside of an action can lead to troubles. See #14024 for details.
This comes from the documentation for
wp_get_current_user. If you were using this code within a template, you could be assured that
init had already been called. However, if you just randomly grab for that user information before
init action is called, you will not get a current user. This explains why you got the User's ID when adding those actions into functions.php (as those actions take place after
init), whereas with your original code it's not clear when you invoke that.
Please refer to the Plugin API page for a general idea of the order in which these various actions are invoked.