The virtual address space is what an individual program sees when it executes. Depending on how the program has been configured this address space will be as large as the maximum the operating system supports.
The operating system kernel is then responsible for mapping addresses in the vas to physical memory, be that RAM, or system page files.
With this design, the programs themselves remain unaware of resources and real addresses, and can operate as if they had all system memory to themselves, or at least the maximum memory a single process can use.
In a nutshell a program works with VAS, and the operating system handles mapping VAS to real storage so that this is invisible to the running program. The running program sees only its VAS.
In older systems, the front-side bus (FSB) was synchronously tied to the northbridge and memory controller. This meant that, without the use of clock dividers (introducing complicated and expensive PLL circuitry to keep control of the different clock rates), your memory bus would operate at the FSB speed. In your case, DDR-400 was the answer, since DDR-400 memory modules have a clock rate of 200 MHz.
Now, as history progressed, systems that still used an FSB now had a clock divider between itself and the memory controller. This allowed for the use of different memory speeds independent of the FSB speed (so if we set the FSB to 400 MHz, and had a clock ratio of 1:2, the memory would run at 400 * 1 / 2 = 200 MHz).
I assume that since this isn't a computer architectures course, and since there was only one answer, it was implicitly implied that the system did not have a clock divider. If it did (and indeed, nearly all computers since the late 90's did), we could simply solve the ratio to make any of the above listed memory modules work with the computer.
To make DDR-333 work for example, we need a memory clock of 166 MHz, or a clock divider of 5:6. For DDR3-667, we need a memory I/O clock (not memory speed, DDR3 is different) of 333 MHz, or 5:3. Finally, PC100 would work with a divider of 1:2 for a memory clock of 100 MHz.
TL,DR: Without a memory clock divider, the FSB has to match the memory clock speed. With a clock divider, so long as you can create an integer ratio X:Y to match the memory:FSB speeds, then you can use that memory module (and that ratio can be satisfied for all of the memory modules listed in your question).
Best Answer
The physical address bus' bit width can be more or less then the bit width in a particular memory address, as there is all kinds of hardware hacks you can design into a system to allow weird addressing modes. For example, in some 32-bit systems, the address bus is 52-bits wide. As another example, some CPU instructions can decode a longer address by using a combination of a base address and a lookup table.
At the end of the day, it's the hardware's job to interpret a memory address from a CPU. The CPU just computes the memory address it needs, and sends it to the motherboard's memory controller (remember, we're talking hardware, not software here, see my final note at the bottom). The memory controller's job is to interpret that address, and put the appropriate data on the memory bus.
Since this is all handled on a hardware level, you can actually increase the physical address space of some lower-bit memory systems using a physical address extension. Again, how these extended addresses are handled is part of how the system/hardware was implemented.
Finally, to give some more merit to the hardware hacks I mentioned above, one good example is memory-mapped input/output (MMIO for short). This allows a CPU to access both peripherals and RAM through the address bus itself. Usually this is done though the higher-order memory addresses to avoid lower-order address conflicts. However, this gave rise to the commonly known 3 GB Memory Barrier in all consumer variants of 32-bit Windows operating systems. Again, this is just to show you what is possible on a hardware level.
From a high-level programmer's perspective, this has nothing to do with pointer variables. They always have the same data size, since these address extensions are handled for you by the operating system and/or the hardware itself. Pointer sizes, addresses, and offsets are set/computed by the compiler.