为什么保存受限指针的值不会给出未定义的行为?

时间:2018-07-20 20:27:49

标签: c undefined-behavior restrict

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

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}; // <--

1 个答案:

答案 0 :(得分:2)

仅在更高级别的优化时才考虑

Restrict关键字。仅限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