首页 > 解决方案 > 为什么保存受限指针的值不会给出未定义的行为?

问题描述

我正在对虚拟机进行编程,并且我已将受限指针联合起来以迭代脚本的指令流:

union Pointer {
    uint8_t *restrict UInt8Ptr;
    uint16_t *restrict UInt16Ptr;
    uint32_t *restrict UInt32Ptr;
    uint64_t *restrict UInt64Ptr;

    int8_t *restrict Int8Ptr;
    int16_t *restrict Int16Ptr;
    int32_t *restrict Int32Ptr;
    int64_t *restrict Int64Ptr;

    float *restrict FloatPtr;
    double *restrict DoublePtr;
    const char *restrict CStrPtr;
    void *restrict Ptr;
};

对于 CALL 操作码,我保存了指令指针的值(间接地),如果我理解“restrict”关键字的用法,会导致未定义的行为。

    (--regs[regStk].SelfPtr)->Ptr = ip.Ptr; /* push rip */
    *--regs[regStk].SelfPtr = regs[regBase];    /* push rbp */
    regs[regBase] = regs[regStk];   /* mov rbp, rsp */

我还应该说,在 RET 操作码中,指令指针的值被恢复了。

    regs[regStk] = regs[regBase]; /* mov rsp, rbp */
    regs[regBase] = *regs[regStk].SelfPtr++; /* pop rbp */
    ip.Ptr = (*regs[regStk].SelfPtr++).Ptr; /* pop rip */

我已经做了很多测试,甚至使用了不同的编译器(GCC 和 clang v3.5 和 clang v6.0),这似乎并没有产生未定义的行为,这是为什么呢?

编辑更新:

这些变量都被声明为一个局部块范围:

int32_t VM_Exec(struct VM *const restrict vm)
{
    if( !vm or !vm->CurrScript.Ptr ) {
        return ErrInstrBounds;
    }

    union Value *const restrict regs = vm->Regs; // <--
    union Pointer pc = (union Pointer){.UInt8Ptr = regs[regInstr].UCharPtr}; // <--

标签: cundefined-behaviorrestrict

解决方案


只有在更高级别的优化时才考虑限制关键字。仅限 gcc -O2 和 -O3。

在您的示例中,我没有看到任何可能导致问题的东西,因为我们不知道这些数组是如何声明的以及如何使用的。

在这里你有一个例子 - 我打破了与编译器的合同。

unsigned p = 100;

void foo1(void)
{
    p++;
}

void foo(unsigned *restrict x)
{
    printf("p = %u\n", *x);
    foo1();
    printf("p = %u\n", *x);
}

int main()
{
    foo(&p);
}

结果是(-O3)

 100
 100

用 -O1 编译

100
101

另一个例子:-O3

unsigned p = 100;

void foo1(void)
{
    p++;
}

void foo(unsigned *restrict x)
{
    unsigned *restrict p1;

    p1 = x;
    printf("p = %u\n", *x);
    foo1();
    printf("p = %u\n", *x);
    *p1++;
    printf("p = %u\n", *x);
}

int main()
{
    foo(&p);
}

结果:

p = 100
p = 100
p = 101

推荐阅读