What Are Near, Far and Huge Pointers

What are near, far and huge pointers?

In the old days, according to the Turbo C manual, a near pointer was merely 16 bits when your entire code and data fit in the one segment. A far pointer was composed of a segment as well as an offset but no normalisation was performed. And a huge pointer was automatically normalised. Two far pointers could conceivably point to the same location in memory but be different whereas the normalised huge pointers pointing to the same memory location would always be equal.

What is the difference between far pointers and near pointers?

On a 16-bit x86 segmented memory architecture, four registers are used to refer to the respective segments:

  • DS → data segment
  • CS → code segment
  • SS → stack segment
  • ES → extra segment

A logical address on this architecture is written segment:offset. Now to answer the question:

  • Near pointers refer (as an offset) to the current segment.

  • Far pointers use segment info and an offset to point across segments. So, to use them, DS or CS must be changed to the specified value, the memory will be dereferenced and then the original value of DS/CS restored. Note that pointer arithmetic on them doesn't modify the segment portion of the pointer, so overflowing the offset will just wrap it around.

  • And then there are huge pointers, which are normalized to have the highest possible segment for a given address (contrary to far pointers).

On 32-bit and 64-bit architectures, memory models are using segments differently, or not at all.

Near and Far pointers

The near and far keywords have their origin in the segmented memory model that Intel had before. The near pointers could only access a block of memory originally around 64Kb in size called a segment whereas the far pointers could go outside of that range consisting of a segment and offset in that segment. The near pointers were much faster than far pointers so therefore in some contexts it paid off to use them.

Nowadays with virtual memory near and far pointers have no use.

EDIT:Sorry if I am not using the correct terms, but this is how I remembered it when I was working with it back in the day :-)

what is meant by normalization in huge pointers

In the beginning 8086 was an extension of the 8 bit processor 8085. The 8085 could only address 65536 bytes with its 16 bit address bus. When Intel developed the 8086 they wanted the software to be as compatible as possible to the old 8 bit processors, so they introduced the concept of segmented memory addressing. This allowed to run 8 bit software to live in the bigger address range without noticing. The 8086 had a 20 bit address bus and could thus handle up to 1 MB of memory (2^20). Unfortunatly it could not address this memory directly, it had to use the segment registers to do that. The real address was calculated by adding the 16 bit segment value shifted by 4 to the left added to the 16 bit offset.

Example:
Segment 0x1234 Offset 0x5678 will give the real address
0x 1234
+0x 5678
---------
=0x 179B8

As you will have noticed, this operation is not bijective, meaning you can generate the real address with other combinations of segment and offset.

   0x 1264               0x 1111
+0x 5378 +0x 68A8
--------- --------- etc.
=0x 179B8 =0x 179B8

There are in fact 4096 different combinations possible, because of the 3 overlapping nibbles (3*4 = 12 bits, 2^12 = 4096) .
The normalized combination is the only one in 4096 possible values that will have the 3 high nibbles of the offset to zero. In our example it will be:

   0x 179B
+0x 0008
---------
=0x 179B8

The difference between a far and a huge pointer is not in the normalisation, you can have non normalised huge pointer, it's absolutly allowed. The difference is in the code generated when performing pointer arithmetic. With far pointers when incrementing or adding values to the pointer there will be no overflow handling and you will be only able to handle 64K of memory.

char far *p = (char far *)0x1000FFFF;
p++;
printf("p=%p\n");

will print 1000:0000
For huge pointers the compiler will generate the code necessary to handle the carry over.

char huge *p = (char huge *)0x1000FFFF;
p++;
printf("p=%p\n");

will print 2000:0000

This means you have to be careful when using far or huge pointers as the cost of the arithmetic with them is different.

One should also not forget that most 16 bit compilers had libraries that didn't handle these cases correctly giving sometimes buggy software.
Microsofts real mode compiler didn't handle huge pointers on all its string functions. Borland was even worse as even the mem functions (memcpy, memset, etc.) didn't handle offset overflows. That was the reason why it was a good idea to use normalised pointers with these library functions, the likelyhood of offset overflows was lower with them.

Use of type modifiers(near,far,huge) with normal variables

Everything that is placed on the stack can't be far modified.

You can place "non-far variables" in the stack, but not "far variables".

You can place "non-far pointers to non-far data" in the stack, but not "far pointers to non-far data**".

You can place "non-far pointers to far data" in the stack, but not "far pointers to far data".

Try this:

far int       var        = 0; /* OK */
far int far* far_var_ptr = &var; /* OK: far pointer to far data*/
int far* var_ptr = &var; /* OK: non-far pointer to far data*/ */

int main()
{
int far* var_ptr2 = &var; /* OK: Non-far ptr to far data */
far int far* far_var_ptr2 = &var; /* Fail: far ptr to far data */
far int var2 = 0; /* Fail: far data */
}

The key is that you can't define far data on the stack. Variables on the stack are:

Placed within a defined range of memory

Its exact location depends on the call stack before: it can't be known at compile time

This is not a far data:

int far* var;

It is an non-modified pointer to far data. The pointer itself is just a number not far modified that points to data in a far segment (platform specific).

This is far data:

far int* var;

And this too:

far int far* var;

The storage (far, near, huge) modifier of a variable (or function) is placed before the variable type.



Related Topics



Leave a reply



Submit