The other answers were generally good, but didn't necessarilly spell everything out clearly IMHO.
To load a compiled program into a microcontroller, the PC that holds the compiled program needs to be connected directly to the microcontroller in some way that provides a method of bidirectional communication. That's one of the jobs of a programing device: to provide that communication path.
Some microcontrollers - but not all - require high voltages to program. By high voltage, I mean higher than Vcc. Providing that voltage is another important function of the programing device.
Every microcontroller that I have experience with allows the possibility of programming it with a so-called bootloader. On power up (or reset) the bootloader looks on one or more specific communication channels for someone trying to replace the existing firmware with new. If it finds that to be the case, and everything checks out security wise, then the bootloader accepts the new firmware and puts it in the place of the old program. This is very often done with commercial products that allow firmware updates by the user. Note that this requires the bootloader to already have been loaded into flash memory by 'conventional' methods. So we still haven't solved the problem of programming a fresh chip.
In any case, the bootloader takes up some space in flash memory which may or may not be precious.
Having said all of that, some microcontrollers can be programmed without special voltages. The AVR series is a good example. These still need some sort of hardware to connect the software on your computer to the digital pins on your microcontroller (and software on the PC to make it work, obviously.) You can get everything off the Internet to turn an unloved Arduino into a programmer for AVR chips. This takes care of requirement (1) above, and since AVR chips don't have requirement (2), Bob's your uncle.
As a point of interest, I once built an instrument that was controlled by a Raspberry Pi. It turned out the RPI couldn't provide certain signals that I needed with adequate timing requirements. I looked for an existing IC that could do what I needed, but couldn't find anything. So, I used an ATtiny85. I wrote some simple software to make it into my dream chip, and programed it directly through the RPI digital GPIO. No programmer required. It turned out to be both easy and fun. But, circuit design and microcontroller programming are always fun for me