Absent certain exploit mitigations that intentionally randomize the process address space to defeat memory corruption attacks, a program's addresses will be highly predictable every time it runs. In other words, once you find a working exploit, it should work on every machine running the exact same version of the software (assuming there's no address space layout randomization). It does need to be an exact match - different architectures (like x86 vs. amd64 vs. ARM) won't match, nor will builds from the same source code but targeting different operating systems or compiled with different compiler flags. However, in practice, most software is distributed as a pre-built binary, so everybody on a given platform is in fact running the same version.
In some cases, the memory layout that you're targeting will be different depending on how the program was used prior to your exploit occurring. This isn't a problem in software where you control how it launches, but for many long-running servers it can be difficult to anticipate the software's current state when you attack it. One option is to just corrupt memory anyhow; the program will almost certainly crash (itself an exploitable issue, for denial of service) but often a watchdog will launch it again. Then, you attack the newly-launched process. Another option is to make your exploit robust against slightly missing the start of the shellcode/ROP chain. One way to do this is what's called a "NOP sled", where you put as many NOP (no-op) instructions as possible before your payload; when you do that, you only need to get the instruction pointer to land somewhere in the NOPs (or at the first part of the actual shellcode), and then the processor just "sleds" down the NOPs into your payload.
Defeating ASLR and other mitigations is a different topic, and potentially quite difficult.