shell: read: differentiate between EOF and newline

Reading a single character, how can I tell the difference between the null <EOF> and n?

Eg:

f() { read -rn 1 -p "Enter a character: " char &&
      printf "nYou entered '%s'n" "$char"; }

With a printable character:

$ f
Enter a character: x
You entered 'x'

When pressing Enter:

$ f
Enter a character: 

You entered ''

When pressing Ctrl + D:

$ f
Enter a character: ^D
You entered ''
$ 

Why is the output the same in the last two cases? How can I distinguish between them?

Is there a different way to do this in POSIX shell vs bash?