16 bit compilers like Turbo C++ 3.0 or Borland C++ 3.0 have different types of pointer variables depending on the memory model of the compilation/linking.
These compiled applications run in real mode of x86 processor. In this mode x86 processor can access up to 20bit of memory lines which is only 1MB of system memory.
MSDOS based compilers typically offer a choice of 5 memory models known as small/tiny, medium, compact, large and huge. Their characterisitics are summarised below.
Small/Tiny - This means that near addresses are used for both data objects and code objects (functions). The data and code spaces are thus restricted to 64 KByte each.
Medium - Near addresses are used for data but far addresses are used for code. This restricts the data space to 64 KByte and the code space can grow beyond 64KBytes. This suggests a large complex program manipulating a small amount of data.
Compact - Far addresses are used for data but near addresses are used for code. This restricts the code space to 64 KByte but the data space can grow beyond 64KBytes. This suggests a small simple program manipulating a massive amount of data.
Large - Far addresses are used for both data and code. The data and code spaces can grow beyond 64KBytes. This suggests large programs manipulating large amounts of data.
Huge - The ability to handle large amounts of data in the large and compact memory models suggests that the data may be stored in large aggregates or arrays which can grow beyond 64KBytes. Unfortunately the large and compact models calculate addresses within data objects by adding a near offset to the base address of the object. This implies a restriction of 64 KByte on the size of a data object. This restriction is relaxed in the huge memory model which is otherwise similar to the large model.
x86 processor points a memory location by a combining segment registers along with offset registers. Both registers are 16 bits wide. Now effective address is calculated by
Near pointer is 16 bits wide and holds only offset address. Segment register remains in data/extra segment. Thus this can only point upto 64kb or 0xFFFF offset in a given segment.
int i = 0;
int * i_pointer = &i;
Far pointers are 32 bits wide and hold both 16bit segment and 16bit offset addresses. Thus a far pointer can point to any segment and to any offset inside that segment. MK_FP is a compiler macro which take segment and offset values and constructs a far pointer.
intfar * i_pointer = (intfar * )MK_FP(0xA000, 0);
i_pointer can have offset values from 0 to 0xFFFF.
Thus, it can access address inside range 0xA0000 to 0xAFFFF. We have some more examples to understand this different combinations of segment and offset registers and their effective addresses.
Huge pointers are also far pointers i.e. 32 bit pointers. The difference is that compiler rounds off the offset of a far pointer to zero when the offset reaches 0xFFFF but for a huge pointer, it increments the segment value on reaching 0xFFFF. Thus huge pointers can be increased or decreased uniformly between any segments and can have any value from 0 to 1MB.