2016-08-10 2 views
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 и Адрес Sanitizer инструменты мы в что

char arr[p->f()]; 

является виновником повреждения стека.

Зная, что p создается во время выполнения, и объявление размера массива выполняется во время компиляции с фиксированным значением, как это компилируется без жалобы? Каково значение p-> f() во время компиляции?

Кроме того, почему memset (запись дополнительных 10 байтов «неопределенного» размера массива) выполняется без ошибки сегментации?

+0

Вы используете gcc/clang? Это расширение, которое по умолчанию используется для них. – NathanOliver

+0

[Не компилируется чисто.] (Http://coliru.stacked-crooked.com/a/62ef3e46c5ee6a37) Вы говорите о диалекте GnuC++? Если да, сделайте это явным. –

+0

Ваш memset() в funciton метода() перехватывает ваш массив на 10 байтов. Это может привести к повреждению стека. Вы можете получить случайное поведение, вы можете получить сбой. Вероятно, вы также используете компилятор, который поддерживает VLA в C++ (это означает, что ваш массив имеет размер во время выполнения, а не во время компиляции). – nos

ответ

3

Именно поэтому вы всегда должны компилировать с включенными предупреждениями. В этом случае соответствующее одно:

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

(Обратите внимание, что есть и другие предупреждения, это только соответствующая одна). Variable length arrays разрешены gcc как расширение, поэтому ваш код скомпилирован. Но если бы вы увидели это предупреждение, возможно, вы приняли бы другое решение. -Wall -Wextra - твой друг.

Кроме того, почему memset (запись дополнительных 10 байтов «неопределенного» размера массива) выполняется без ошибки сегментации?

Неопределенное поведение не определено, оно не обязательно требует ошибки сегментации.

+0

Комментарии, которые я использую: «-ggdb -std = C++ 14 -Wall -Wextra -Wshadow -Wnon-virtual-dtor -pedantic -Wcast-align -Wcast-qual -Wconversion -Wpointer-arith -Wunused -Woverloaded-virtual -O0 "с g ++ на ubuntu –