When I bind a socket to 0.0.0.0 it means to all interfaces on the host machine, of which 127.0.0.1 is one of those interfaces. Not sure if that answers your question.
If you ping 0.0.0.0 on Linux it pings 127.0.0.1. That's the behavior I'm talking about. Binding to 0.0.0.0 is something completely different and not Linux-specific.
I don't see the reason in the documentation of either syscall.
I was thinking it might be due to its subnet being the biggest one one commonly has (/8), but I just set up a (/1), and it still used 127.0.0.1. Even if I set the lo interface down, it still tries 127.0.0.1 and just hangs until I bring it back up. I removed the 127.0.0.1/24 address from lo, and gave lo the address 128.0.0.1/1 with the same options 127.0.0.1/8 had, and that caused `ping 0` to return the error, "connect: Invalid argument". So, I don't know. At least I learned that the behavior seems to really be tied to the address and not the loopback interface, which I though was supposed to abstract the address.
Looking at the RFC it seems to say that { 0, 0 } means "this host on the network" and must not be sent except as a source address during initialization in order for the host to learn its own IP address. My interpretation is not that this means trying to ping 0.0.0.0 is supposed to be equivalent to 127.0.0.1, but just that you can't actually set your destination address to 0.0.0.0 when sending IP packets. Linux ping's interpretation of this as meaning to fall back to 127.0.0.1 when trying to ping 0.0.0.0 doesn't seem unreasonable, but nor does it seem to be mandated by the RFC.