以下代码可以用于指向不同事物的指针

时间:2016-08-26 07:23:46

标签: c pointers language-lawyer

我有一段记忆,我是"守卫",由

定义
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)。

  • 可以通过递增基数来访问字节数组中的所有地址。
  • 任何指针都可以转换为char *,而不会损坏。

所以我无法理解结果对于来自不同对象的任何指针的结果是否正确(因为它会破坏区域内其中一个指针的不同规则)。

编辑:

用例是什么?

检测指针是否在区域内的能力非常重要,在某些时候编写代码

if (  isInMyAreaOfInterest( unknownPointer ) ) {
    doMySpecialThing( unknownPointer );
} else {
    doSomethingElse( unknownPointer );
}

我认为语言需要通过使这样的结构简单明了来支持开发人员,而我们对标准的解释是开发人员需要转换为int。由于&#34;未定义的行为&#34;不同对象的指针比较。

我希望能清楚地知道为什么我不能做我想做的事情(我的片段),因为我发现的所有帖子都说标准声称未定义的行为,没有任何解释或示例为什么标准比我希望的更好。

目前,我们有一条规则,我们既不理解规则存在的原因,也不了解规则是否有助于我们

示例帖子:

SO: checking if a pointer is in a malloced area

SO: C compare pointers

2 个答案:

答案 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位,因为它们与讨论无关。为方便起见,假设它们总是为零。)

有两个选择器表,全局选择器表(用于所有进程共享的内存)和本地选择器表(用于单个进程的私有内存)。因此,可用于进程专用内存的选择器为0001000900110019等。同时,可用于全局内存的选择器为0008001000180020等。(保留选择器0000。)

好的,现在我们可以设置我们的反例。假设guardArea = 0101:0000guardSize = 0x00020000。这意味着受保护的地址为0101:00000101:FFFF0109:00000109:FFFF。此外,guardArea + guardSize = 0111:0000

同时,假设有一些全局内存恰好在0108:0000分配。这是全局内存分配,因为选择器是偶数。

观察到全局内存分配不是受保护区域的一部分,但其指针值确实满足数字不等式0101:0000 <= 0108:0000 < 0111:0000

Bonus chatter :即使在具有平坦内存模型的CPU架构上,测试也会失败。现代编译器利用未定义的行为并相应地进行优化。如果他们看到指针之间的关系比较,则允许他们假设指针指向同一个数组(或者超过该数组的最后一个元素)。具体而言,可以合法地与guardArea进行比较的唯一指针是guardAreaguardArea+1guardArea+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;

uaub实际上允许具有相同的值。这种情况经常发生在分段系统(如MS-DOS)上,这可能会将代码和数据放在不同的段中。