我有一段记忆,我是"守卫",由
定义typedef unsigned char byte;
byte * guardArea;
size_t guardSize;
byte * guardArea = getGuardArea();
size_t guardSize = getGuardSize();
为此目的,可接受的实施方案是:
size_t glGuardSize = 1024; /* protect an area of 1kb */
byte * getGuardArea()
{
return malloc( glGuardSize );
}
size_t getGuardSize()
{
return glGuardSize;
}
以下代码段是否可以为任何指针(来自不同的malloc,来自堆栈等)返回true?
if ( ptr >= guardArea && ptr < (guardArea + guardSize)) {
return true;
}
标准规定:
区域内的值将返回true。 (当ptr是成员时,所有行为都正确。)
指针将是截然不同的(只有当它们相同时才是a == b)。
所以我无法理解结果对于来自不同对象的任何指针的结果是否正确(因为它会破坏区域内其中一个指针的不同规则)。
编辑:
检测指针是否在区域内的能力非常重要,在某些时候编写代码
if ( isInMyAreaOfInterest( unknownPointer ) ) {
doMySpecialThing( unknownPointer );
} else {
doSomethingElse( unknownPointer );
}
我认为语言需要通过使这样的结构简单明了来支持开发人员,而我们对标准的解释是开发人员需要转换为int。由于&#34;未定义的行为&#34;不同对象的指针比较。
我希望能清楚地知道为什么我不能做我想做的事情(我的片段),因为我发现的所有帖子都说标准声称未定义的行为,没有任何解释或示例为什么标准比我希望的更好。
目前,我们有一条规则,我们既不理解规则存在的原因,也不了解规则是否有助于我们
示例帖子:
答案 0 :(得分:34)
尽管指针没有指向区域,但分配仍然可以生成满足条件的指针。例如,这将发生在受保护模式的80286上,Windows 3.x在标准模式和OS / 2 1.x中使用该模式。
在这个系统中,指针是32位值,分成两个16位部分,传统上写为XXXX:YYYY
。第一个16位部分(XXXX
)是“选择器”,它选择64KB的存储区。第二个16位部分(YYYY
)是“偏移”,它在该64KB存储区中选择一个字节。 (这比这更复杂,但是为了讨论的目的,我们就把它留在那里。)
大于64KB的内存块被分解为64KB块。要从一个块移动到下一个块,请将8添加到选择器。例如,0101:FFFF
之后的字节为0109:0000
。
但为什么要添加8来移动到下一个选择器?为什么不只是增加选择器?因为选择器的底部三位用于其他事情。
特别是,选择器的底部位用于选择选择器表。 (让我们忽略第1和第2位,因为它们与讨论无关。为方便起见,假设它们总是为零。)
有两个选择器表,全局选择器表(用于所有进程共享的内存)和本地选择器表(用于单个进程的私有内存)。因此,可用于进程专用内存的选择器为0001
,0009
,0011
,0019
等。同时,可用于全局内存的选择器为0008
,0010
,0018
,0020
等。(保留选择器0000
。)
好的,现在我们可以设置我们的反例。假设guardArea = 0101:0000
和guardSize = 0x00020000
。这意味着受保护的地址为0101:0000
到0101:FFFF
和0109:0000
到0109:FFFF
。此外,guardArea + guardSize = 0111:0000
。
同时,假设有一些全局内存恰好在0108:0000
分配。这是全局内存分配,因为选择器是偶数。
观察到全局内存分配不是受保护区域的一部分,但其指针值确实满足数字不等式0101:0000 <= 0108:0000 < 0111:0000
。
Bonus chatter :即使在具有平坦内存模型的CPU架构上,测试也会失败。现代编译器利用未定义的行为并相应地进行优化。如果他们看到指针之间的关系比较,则允许他们假设指针指向同一个数组(或者超过该数组的最后一个元素)。具体而言,可以合法地与guardArea
进行比较的唯一指针是guardArea
,guardArea+1
,guardArea+2
,...,guardArea + guardSize
形式的指针。对于所有这些指针,条件ptr >= guardArea
为真,因此可以优化,将测试减少到
if (ptr < (guardArea + guardSize))
现在将满足数字小于guardArea
的指针。
故事的道德:此代码不安全,即使在平面架构上也是如此。
但一切都没有丢失:指针到整数的转换是实现定义的,这意味着您的实现必须记录它的工作原理。如果您的实现将指针到整数转换定义为生成指针的数值,并且您知道自己处于扁平体系结构中,那么您可以做的是比较整数而不是指针。整数比较的约束方式与指针比较的方式不同。
if ((uintptr_t)ptr >= (uintptr_t)guardArea &&
(uintptr_t)ptr < (uintptr_t)guardArea + (uintptr_t)guardSize)
答案 1 :(得分:0)
是
void foo(void) {}
void(*a) = foo;
void *b = malloc(69);
uintptr_t ua = a, ub = b;
ua
和ub
实际上允许具有相同的值。这种情况经常发生在分段系统(如MS-DOS)上,这可能会将代码和数据放在不同的段中。