What are near, far and huge pointers?

Can someone explain these pointers to me with a suitable example ... and when are these pointers used?

+25
c ++ c pointers x86 x86-16
Aug 26 '10 at 13:36
source share
6 answers

In the old days, according to the Turbo C manual, almost a pointer was only 16 bits when all the code and data were in the same segment. The far pointer consisted of a segment as well as an offset, but normalization was not performed. And the huge pointer automatically returned to normal. The two far pointers may possibly point to the same memory location, but be different, while the normalized huge pointers pointing to the same memory location will always be equal.

+11
Aug 26 '10 at 13:40
source share

The first example is the Intel X86 architecture.

The Intel 8086 was internally a 16-bit processor: all its registers were 16 bits wide. However, the address bus was 20 bits wide (1 MiB). This meant that you could not have spent the entire address in the register, limiting you to the first 64 kiB.

Intel's decision was to create 16-bit "segment registers", the contents of which would be shifted to the left by four bits and added to the address. For example:

DS ("Data Segment") register: 1234 h DX ("D eXtended") register: + 5678h ------ Actual address read: 179B8h 

This created the 64 kiB segment concept. Thus, the “nearest” pointer will simply be the contents of the DX register (5678h) and will be invalid if the DS register is not set correctly and the “far” pointer is 32 bits (12345678h, DS, then DX) and always works (but was slower since you had to load two registers and then restore the DS register when this is done).

(As supercat's notes below, a DX offset that is full will “flip” before adding to DS to get the final address. This allowed 16-bit offsets to access any address in the 64 kiB segment, and not just the part that was ± 32 kiB, where DX indicated how this is done in other architectures with 16-bit relative biased addressing in some instructions.)

However, note that you may have two "far" pointers, which are different values, but point to the same address. For example, the far pointer 100079B8h points to the same place as 12345678h. Thus, comparing pointers to far pointers was an unacceptable operation: pointers could be different, but still point to the same place.

It was here that I decided that Macs (with Motorola 68000 processors at the time) were not so bad, so I skipped the huge pointers. IIRC, they were just far pointers, which ensured that all overlapping bits in the register segments were 0, as in the second example.

Motorola did not have this problem with their 6800 series processors, as they were limited to 64 kB. When they created the 68000 architecture, they went straight to 32-bit registers and, therefore, never needed near, or huge pointers. (Instead, their problem was that only the bottom 24 bits of the address actually mattered, so some programmers (known to Apple) would use the high 8 bits as “pointer flags”, causing problems when expanding the address buses to 32 bits (4 gigabytes).)

Linus Torvalds only extended to 80386, which proposed "protected mode", where addresses were 32 bits and segment registers were the upper half of the address, and no addition was required, and from the very beginning he wrote Linux to use only protected mode, nothing strange segment. and why you don’t have pointer support in Linux and why no company developing a new architecture will ever come back to them if they want Linux support). And they ate Robin Minstrels, and there was a lot of joy. (Yay ...)

+28
Aug 26 2018-10-18T00:
source share

Difference between long and long pointers:

As we know by default near pointers, for example: int *p is a near pointer. The size of the near pointer is 2 bytes in the case of a 16-bit compiler. And we already know very well that the size of the compiler compiler depends on the compiler; they save only the offset address of the pointer to which it refers. An address consisting only of an offset has a range from 0 to 64 Kbytes.

Far and huge pointers:

Far and huge pointers are 4 bytes in size. They store both the segment and the offset of the address pointed to by the pointer. Then what is the difference between the two?

Far pointer limit:

We cannot change or change the segment address of a given remote address by applying any arithmetic operation to it. That is, using the arithmetic operator, we cannot move from one segment to another segment.

If you increase the far address beyond the maximum value of its offset address instead of increasing the segment address, it will repeat its offset address in a cyclic order. This is also called a wrapper, i.e. If the offset is 0xffff , and we add 1, then it is 0x0000 and similarly, if we decrease 0x0000 by 1, then it is 0xffff and remember that there is no change in the segment.

Now I'm going to compare the huge and far pointers:

1. When the far pointer increases or decreases ONLY , the pointer offset actually increases or decreases, but in the case of a huge pointer, the value of the segment and the offset changes.

Consider the following example, taken from HERE :

  int main() { char far* f=(char far*)0x0000ffff; printf("%Fp",f+0x1); return 0; } 

then output:

 0000:0000 

Changing a segment value does not change.

And in the case of huge pointers:

 int main() { char huge* h=(char huge*)0x0000000f; printf("%Fp",h+0x1); return 0; } 

Exit:

 0001:0000 

This is due to the increase operation, not only the offset value, but the segment value also changes. This means that the segment will not change in the case of Far pointers, but in the case of the huge pointer, it can move from one segment to another.

2. When relational operators are used on far pointers, only offsets are compared. In other words, relational operators will only work on far pointers if the values ​​of the segments of the pointers being compared are the same. And in the case of a huge this will not happen, in fact, a comparison of absolute addresses is performed. Let's look at an example of a Far pointer:

 int main() { char far * p=(char far*)0x12340001; char far* p1=(char far*)0x12300041; if(p==p1) printf("same"); else printf("different"); return 0; } 

Output:

 different 

In huge pointer:

 int main() { char huge * p=(char huge*)0x12340001; char huge* p1=(char huge*)0x12300041; if(p==p1) printf("same"); else printf("different"); return 0; } 

Output:

 same 

Explanation: As we can see, the absolute address for p and p1 is 12341 ( 1234*10+1 or 1230*10+41 ), but they are not considered equal in the first case, since in the case of Far pointers only offsets are compared, i.e. it checks if 0001==0041 . This is not true.

And in the case of huge pointers, the comparison operation is performed at absolute addresses equal to.

  • The far pointer is never normalized, but the huge pointer is normalized. A normalized pointer is one that has as many addresses in the segment as possible, which means that the offset will never be greater than 15.

    Suppose that if 0x1234:1234 , then its normalized form is 0x1357:0004 (absolute address 13574 ). The huge pointer is normalized only when some arithmetic operation is performed on it and is not standardized at the destination.

      int main() { char huge* h=(char huge*)0x12341234; char huge* h1=(char huge*)0x12341234; printf("h=%Fp\nh1=%Fp",h,h1+0x1); return 0; } 

    Output:

     h=1234:1234 h1=1357:0005 

    Explanation: huge pointer is not normalized in case of assignment. But if an arithmetic operation is performed on it, it will be normalized. So h is 1234:1234 and h1 is 1357:0005 , which is normalized.

    4. The offset of a huge pointer is less than 16 due to normalization, and not in the case of distant pointers.

    lets take an example to understand what I want to say:

      int main() { char far* f=(char far*)0x0000000f; printf("%Fp",f+0x1); return 0; } 

Output:

  0000:0010 

In the case of a huge pointer:

  int main() { char huge* h=(char huge*)0x0000000f; printf("%Fp",h+0x1); return 0; } Output: 0001:0000 

Explanation: as we increase the pointer by 1, it will be 0000:0010 . And as we increase the huge pointer by 1, then it will be 0001:0000 , because its offset cannot exceed 15, in other words, it will be normalized.

+18
Nov 05 '13 at 9:33
source share

All of the materials in this answer apply only to the old segmented memory model 8086 and 80286.

next: a 16-bit pointer that can address any byte in a 64k segment

far: 32-bit pointer containing segment and offset. Note that since segments can overlap, two different pointers can point to the same address.

huge: a 32-bit pointer in which the segment is "normalized", so no two pointers point to the same address unless they have the same value.

tee: a drink with jam and bread.

This will bring us back to doh oh oh oh oh

and when are these pointers used?

in 1980 and 90, until 32-bit Windows became ubiquitous,

+3
Aug 26 2018-10-14T00:
source share

This terminology was used in 16-bit architectures.

On 16-bit systems, data was divided into 64 kb segments. Each loadable module (program file, dynamically loaded library, etc.) had an associated data segment that could store only up to 64 Kbytes of data.

The NEAR pointer was a pointer with 16-bit storage and referenced data (only) in the current module data segment.

16-bit programs that required more than 64 KB of data could access special allocators that would return a FAR pointer - which was the identifier of the data segment in the upper 16 bits and a pointer to this data segment in the lower 16 bits.

However, larger programs would like to deal with more than 64 Kb of continuous data. The HUGE pointer looks exactly like a far pointer - it has 32-bit storage, but the distributor took care of arranging the range of data segments with consecutive identifiers, so that by simply increasing the data segment selector, the next 64-bit data fragment can be reached.

Appropriate C and C ++ language standards have never officially recognized these concepts in their memory models - all pointers in a C or C ++ program must be the same size. Thus, the NEAR, FAR, and HUGE attributes were extensions provided by various compiler providers.

+2
Aug 26 '10 at 13:52
source share

In some architectures, a pointer that can point to every object in the system will work longer and slower than one that can point to a useful subset of things. Many people gave answers related to the 16-bit x86 architecture. Different types of pointers were common on 16-bit systems, although differences in proximity / fear might appear on 64-bit systems depending on how they are implemented (I won’t be surprised if many development systems switch to 64-bit pointers for everything, despite the fact that in many cases it will be very wasteful).

In many programs, it is quite easy to subdivide memory usage into two categories: small things that together make up a fairly small amount of material (64 KB or 4 GB), but will be available often, and large things that can be added up to a much larger amount, but to them often do not need to contact. When an application needs to work with a part of an object in the "big things" area, it copies this part to the "little things" area, works with it and, if necessary, writes it back.

Some programmers are faced with the need to distinguish between “near” and “distant” memory, but in many cases, such differences may allow compilers to produce much better code.

(note: even in many 32-bit systems access to some areas of memory is possible directly without additional instructions, while other areas cannot. If, for example, on 68000 or ARM, one stores a register pointing to a global memory variable, you can will directly load any variable within the first 32K (68000) or 2K (ARM) of this register.To obtain a variable stored elsewhere, you will need additional instructions to calculate the address. Its regions and knowledge by the compiler will improve the efficiency of code generation.

+2
Aug 26 '10 at 16:18
source share



All Articles