The models came about due to the memory structure of the 16 bit x86
architecture. Addresses were in general greater than 16 bits in length,
while address registers were only 16 bits long, and were combined with a
"segment register" to convert the value to a full address. (depending on
what mode the processor was in would change how the conversion was done).
Near pointers only stored the value of the address register, and the
segment register was assumed by the type of pointer (the Data Segment
Register for "data" pointers, and the Program Segment Register for
function pointers). In addition, data pointers had a huge type, which
was like a far pointer, but could point to an object that might be
bigger than a single segment, and the compiler would need to do
additional work on address arithmetic to handle this. It mostly was used
in "Real" mode, where the Segment register was just added to the address
register after shifting up the Segment register 4 bits (giving a 20 bit
final memory address).
The program model determined what were the default sizes for each type
of pointer.
Model Data Code
Tiny near near
Small near near
Medium near far
Compact far near
Large far far
Huge huge far
The difference between Tiny and Small was that in Tiny, the code and
data were in the same segment, while in Small they were in distinct
segments.
It was by far more common to have near/far pointers in code that had a
default near size of pointers, to handle a limited number of
objects/functions that would be placed outside the default near block to
make room for them, sometimes to access things outside the current program.
Note also that the 32 bit x86 family of processors still have these
memory models (and 48 bit "far" pointers), they are just mostly ignored
and most programs are just done in the Tiny or Small model, after all
who should need more than 4GB of address space
The resurgence of
memory models for programs was headed off (for now at least) with the
introduction of 64 bit processors.