What math is involved and how do I tell if two addresses are in the same 4 kilobyte page?
Well, assuming 4 KiB pages,
#include <stdint.h>
bool same_page(const void *x, const void *y)
{
uintptr_t mask = ~(uintptr_t) 4095;
return ((uintptr_t) x & mask) == ((uintptr_t) y & mask);
}
This will get ugly quickly since pages have a variable size on common architectures, and the page size of a particular region of memory can and will be changed by the operating system on the fly depending on application memory usage patters.
(Note that memory pages are virtual memory and not physical memory. Strictly speaking, it does not make sense to talk about physical pages, although we usually understand when someone says "physical page" they mean "physical memory corresponding to a page".)