有人可以给我解释一下这些指针是什么,能否用一个合适的例子来说明这些指针是如何使用的?
有人可以给我解释一下这些指针是什么,能否用一个合适的例子来说明这些指针是如何使用的?
主要的例子是英特尔X86架构。
英特尔8086内部是一个16位处理器:所有寄存器都是16位宽。然而,地址总线宽度为20位(1 MiB)。这意味着你不能把整个地址保存在一个寄存器中,限制了你只能访问前64 kiB。
英特尔的解决方案是创建16位“段寄存器”,其内容将左移四位并加上地址。例如:
DS ("Data Segment") register: 1234 h
DX ("D eXtended") register: + 5678h
------
Actual address read: 179B8h
这创造了64 kiB段的概念。 因此,“near”指针只是DX寄存器的内容(5678h),除非DS寄存器已经正确设置,否则它将无效,而“far”指针为32位(12345678h,后跟DS的DX),始终有效(但较慢,因为您需要加载两个寄存器,然后在完成后恢复DS寄存器)。integer
是2个字节。(另一方面,Pascal 字符串提供了 O(1) 字符串长度计算、在字符串中具有 null 的能力以及几乎完全消除缓冲区溢出错误。)x86 系列允许在段中“环绕”偏移量,但代价是你必须使用段寄存器和近距离和远距离指针。 - Mike DeSimonefar指针和huge指针的区别:
默认情况下,指针是near类型的,例如:int *p
是一个near指针。在16位编译器中,near指针的大小为2个字节。我们已经知道,不同编译器的大小不同,它们只存储指针所引用地址的偏移量。仅由偏移量构成的地址范围为0-64K字节。
far
和huge
指针:
far
和huge
指针的大小为4个字节。它们同时存储指针所引用地址的段和偏移量。那么它们之间的区别是什么呢?
far指针的限制:
我们不能通过对其应用任何算术运算来更改或修改给定far地址的段地址。也就是说,我们不能使用算术运算符从一个段跳转到另一个段。
如果你将far地址增加到超过其偏移地址的最大值,那么它将以循环顺序重复其偏移地址,而不是增加段地址。这也被称为“wrapping”。例如,如果偏移量是0xffff
,我们加1,则为0x0000
;类似地,如果我们将0x0000
减1,则为0xffff
,但请注意,段地址不会改变。
现在我要比较huge指针和far指针:
1.当增加或减少far指针时,仅实际增加或减少指针的偏移量,但是对于huge指针,段和偏移值都会改变。
考虑以下示例,摘自这里:
int main()
{
char far* f=(char far*)0x0000ffff;
printf("%Fp",f+0x1);
return 0;
}
那么输出结果为:
0000:0000
段的值没有改变。
对于大型指针:
int main()
{
char huge* h=(char huge*)0x0000000f;
printf("%Fp",h+0x1);
return 0;
}
输出结果为:
0001:0000
这是因为增量操作不仅改变了偏移值,也改变了段值。这意味着在使用 far
指针时,段不会改变,但在使用 huge
指针时,它可以从一个段移到另一个段。
2.当在远指针上使用关系运算符时,只有偏移量会被比较。换句话说,在比较的指针的段值相同的情况下,关系运算符将仅对远指针起作用。而在巨型指针中,实际上进行绝对地址的比较。让我们以一个 far
指针的示例来理解:
int main()
{
char far * p=(char far*)0x12340001;
char far* p1=(char far*)0x12300041;
if(p==p1)
printf("same");
else
printf("different");
return 0;
}
输出:
different
在 huge
指针中:
int main()
{
char huge * p=(char huge*)0x12340001;
char huge* p1=(char huge*)0x12300041;
if(p==p1)
printf("same");
else
printf("different");
return 0;
}
输出:
same
解释:我们可以看到,p
和p1
的绝对地址都是12341
(1234*10+1
或1230*10+41
),但在第一种情况下它们不被认为是相等的,因为对于far
指针只比较偏移量,即它将检查是否0001 == 0041
,这是错误的。
而在巨型指针的情况下,比较操作是在相等的绝对地址上执行的。
远指针从未被规范化,但是huge
指针会被规范化。 规范化的指针是尽可能多地放置在段中的指针,这意味着偏移量永远不大于15。
例如,如果我们有0x1234:1234
,则其规范形式为0x1357:0004
(绝对地址为13574
)。只有在对其执行某些算术操作时,巨型指针才会被规范化,而在赋值期间不会被规范化。
int main()
{
char huge* h=(char huge*)0x12341234;
char huge* h1=(char huge*)0x12341234;
printf("h=%Fp\nh1=%Fp",h,h1+0x1);
return 0;
}
输出:
h=1234:1234
h1=1357:0005
说明:huge
指针在赋值时未得到规范化,但如果对其进行算术操作,则会被规范化。因此,h
为1234:1234
,而h1
为1357:0005
,已经过规范化。
4.由于规范化,巨型指针的偏移量小于16,而远指针则不是这样。
让我们举一个例子来理解我想说的:
int main()
{
char far* f=(char far*)0x0000000f;
printf("%Fp",f+0x1);
return 0;
}
输出:
0000:0010
对于巨型
指针的情况:
int main()
{
char huge* h=(char huge*)0x0000000f;
printf("%Fp",h+0x1);
return 0;
}
Output:
0001:0000
解释:当我们将far指针增加1时,它将变为0000:0010
。而当我们将huge指针增加1时,它将变为0001:0000
,因为它的偏移量不能大于15,换句话说,它会被规范化。
在旧时代,根据Turbo C手册的描述,当您的整个代码和数据适合一个段时,一个near指针仅仅是16位。一个far指针由一个段和一个偏移组成,但不执行规范化。而huge指针则会自动进行规范化。两个far指针可以可能指向内存中相同的位置,但却是不同的,然而指向同一内存位置的规范化huge指针总是相等的。
这个回答中的所有内容都只与旧的8086和80286分段内存模型相关。
near:16位指针,可以寻址64k段内的任何字节。
far:32位指针,包含一个段和偏移量。请注意,由于段可以重叠,因此两个不同的far指针可以指向相同的地址。
huge:32位指针,其中段被“规范化”,以便除非它们具有相同的值,否则没有两个远指针指向同一地址。
tee:一种带果酱和面包的饮料。
那会让我们回到 doh oh oh oh。
这些指针什么时候使用?
在1980年代和90年代直到32位Windows变得无处不在之前。
这个术语被用于16位架构。
在16位系统中,数据被分为64Kb的段。每个可加载的模块(程序文件、动态加载库等)都有一个关联的数据段,只能存储最多64Kb的数据。
NEAR指针是一个具有16位存储器的指针,仅引用当前模块数据段中的数据。
需要超过64Kb数据的16位程序可以访问特殊的分配器,返回FAR指针-它是上16位的数据段id和下16位的数据段内指针。
然而,更大的程序将处理超过64Kb的连续数据。HUGE指针看起来与远指针完全相同-它具有32位存储器-但分配器已经安排了一系列数据段,具有连续的ID,因此通过简单地增加数据段选择器,可以访问下一个64Kb数据块。
C和C++语言标准从未在其内存模型中正式承认这些概念- C或C++程序中的所有指针应该具有相同的大小。因此,NEAR、FAR和HUGE属性是各种编译器供应商提供的扩展。