I ran across this discussion on the subreddit Explain Like I’m Five.
The answer given by Redditor mredding was excellent and worth saving for posterity:
Transistors were first invented for signal amplification; a little current proportionately controls the flow of a large standing current. Coincidentally, it can also thus be used as a switch, where the little current turns on or off the large current.
Digital computers don’t care about matters of degree, only whether the circuit is open or closed, thus, we abstract the reality with 0 or 1. Computers don’t care about numbers, they’re electrical machines, these abstractions are for the humans.
Transistors can be arranged into Logic Gates, of which there is the AND, OR, NOT, and XOR. Look them up. For their inputs, whether they’re on or not determines the output. Don’t worry about the physical implementation – that requires transistors, resistors, diodes, standby power, and so forth; at this point, we only care about the gate as a unit of computation and 0 and 1 as inputs and outputs.
Gates can be arranged to make flip-flop and half adder circuits. Look those up, they’re pretty straight forward. Adders can be stacked to make full adders, by which they can add two integers of some arbitrary number of bits. You’re going to have to learn binary addition and conversion from binary to decimal to really get what’s going on, but it’s very simple and should take you less than 5 minutes. Go look.
And notice that if you want to add two 8 bit integers, you need two sets of inputs, 8 inputs for each integer, one input for each significant bit for each. Welcome to the concept of a “bus”, a bunch of wires that move data from one component to another. They exist within your CPU, and they exist across your computer. There’s a memory bus, an IDE bus, which is what all your expansion cards plug into, there’s an SATA bus, which is what your hard drive plugs into, there’s USB or Universal Serial Bus…
So if you want your computer to be able to add, it needs the physical circuitry to actually add. If you want your computer to subtract, multiply, divide… You need circuit paths for each one (sort of). Truth be told, you can do compute anything with very little (called Turing Complete), but computers will have dedicated circuits for common tasks that it could otherwise do with the use of it’s simpler circuits. This is called hardware acceleration.
So you have all these circuit paths to do a bunch of simple computations, now you need to tell the computer what paths your data should go down, so the right computation can happen. A sort of traffic controller. If you give each circuit path a unique number to identify it, you can call those numbers opcodes. A series of opcodes is the same thing as a program.
Languages like C++ are text files that a program, called a compiler, can turn into a symbolic representation, and them mathematically reason about it’s structure. It all breaks down to a series of simple computations, and the output is a stream of opcodes.