0

这是我设法从可以干净编译但或多或少随机方式崩溃的原始源代码中提取的代码示例:

#include <iostream>
#include <cstdio>
#include <cstring>

class base
{
    public:

        base() {}
        virtual const char f(void) = 0 ;
};

class d1 : public base
{
    static const char s = 15;

    public:

        d1()
        {
        }

        const char f()
        {
            return s;
        }
};

class d2 : public base
{
    static const char n = 25;

    public:

        d2()
        {
        }

        const char f()
        {
            return n;
        }
};

void method(char* p, size_t len)
{
    memset(p, 0, ((len * sizeof(char)) + 10));
}

int main(int argc, char **argv)
{
    base *p = NULL;


    if(argc == 2)
    {
        printf("p shall be instance of d2\n");
        p = new d2();
    }
    else
    {
        printf("p shall be instance of d1\n");
        p = new d1();
    }

    char arr[p->f()];

    printf("Size of arr is %d\n", sizeof(arr));
    method(arr, p->f());

}

在使用 GDB 和 Address Sanitizer 工具进行一些调试会话后,我们发现:

char arr[p->f()];

是堆栈损坏的罪魁祸首。

知道 p 在运行时实例化并且数组大小声明是在编译时以固定值完成的,如何编译而不抱怨?编译时 p->f() 的值是多少?

此外,为什么 memset(写入额外 10 个字节的“未确定”数组大小)运行时没有分段错误?

4

1 回答 1

3

这就是为什么您应该始终在启用警告的情况下进行编译。在这种情况下,相关的是:

main.cpp:66:20: warning: ISO C++ forbids variable length array 'arr' [-Wvla]
     char arr[p->f()];
                    ^

(请注意,还有其他警告,这只是相关警告)。gcc 允许将可变长度数组作为扩展名,这就是编译代码的原因。但是如果你看到这个警告,想必你会做出不同的决定。-Wall -Wextra是你的朋友。

此外,为什么 memset(写入额外 10 个字节的“未确定”数组大小)运行时没有分段错误?

未定义的行为是未定义的,它不一定需要分段错误。

于 2016-08-10T21:49:59.663 回答