Ошибка доступа к указателям, когда успешно выделенный массив слишком велик

У меня есть следующий очень простой код, который отлично работает:

void func(int *tab)
{
    return;
}

int main()
{
    int maxsize = 999*999;
    int tabs[maxsize][6];

    return 0;
}

Однако, когда я изменяю основной файл таким образом, чтобы получить его, происходит сбой.

int main()
{
    int maxsize = 999*999;
    int tabs[maxsize][6];

    func(tabs[0]);

    return 0;
}

У вас есть идеи, почему? Я был бы очень признателен за вашу помощь в этом, спасибо ^^


person hmicn    schedule 07.11.2013    source источник
comment
C++ не поддерживает непостоянные значения при определении размера массива, поэтому этот код даже не скомпилируется.   -  person Jonathan Potter    schedule 08.11.2013
comment
@JonathanPotter gcc и clang и, возможно, другие, хотя и не VS, поддерживают VLA как расширение   -  person Shafik Yaghmour    schedule 08.11.2013
comment
Вероятно, это переполнение стека, и в первом случае компилятор оптимизирует выделение, поскольку вы его не используете.   -  person Shafik Yaghmour    schedule 08.11.2013
comment
Вы переполняете стек этим огромным массивом. В первом случае, поскольку вы никогда не ссылаетесь на массив, компилятор вообще его не создает.   -  person Praetorian    schedule 08.11.2013
comment
Я вижу, спасибо большое!!! Вы, ребята, потрясающие   -  person hmicn    schedule 08.11.2013
comment
Вы уверены, что не делаете больше в func? Я должен фактически создать побочный эффект, чтобы вызвать ошибку seg.   -  person Shafik Yaghmour    schedule 08.11.2013


Ответы (1)


Таким образом, хотя в стандарте не говорится о стеках, большинство современных реализаций будут помещать автоматические переменные в стек, и стек обычно будет между 1M и 8M, который вы переполняете с размером вашего массива. Вы можете найти типичные размеры стека для разных систем здесь:

SunOS/Solaris   8172K bytes
Linux           8172K bytes
Windows         1024K bytes
cygwin          2048K bytes

Причина, по которой первый не выдает ошибку seg, заключается в том, что компилятору фактически не нужно ссылаться на какую-либо память, но если вам нужно вызвать какой-либо побочный эффект, компилятор генерирует доступ к памяти, который фактически вызовет переполнение стека. Поскольку вы сказали, что используете gcc, если я запускаю этот код без каких-либо побочных эффектов (живой пример) он действительно изменит указатель стека, но никогда его не использует:

subq    $23952048, %rsp

но если мы добавим побочный эффект через std::cin и std::cout (живой пример):

std::cin >> tabs[maxsize-1][5] ;
std::cout << tabs[maxsize-1][5] << std::endl ;

тогда потребуется использование указателя стека:

leaq    3(%rsp), %rbx

что обычно приводит к ошибке сегмента в Unix-подобных системах.

Обратите внимание, вы также можете заметить это предупреждение:

warning: ISO C++ forbids variable length array ‘tabs’ [-Wvla]

Это связано с тем, что массивы переменной длины не являются стандартом C++ (но действительны в C99) являются расширением gcc и при использовании -pedantic он будет предупреждать, когда вы используете расширения.

person Shafik Yaghmour    schedule 07.11.2013
comment
Просто из любопытства, кошерно ли передавать массив 2D int как int *? (Или это нормально для стека, но не для кучи?) - person ; 08.11.2013
comment
@ebyrob Это не передаваемый 2D-массив. Двумерный массив представляет собой одномерный массив одномерных массивов. Код передает первый элемент в массиве одномерных массивов, который в данном случае является int*. - person janm; 08.11.2013
comment
@ebyrob, это хорошая ссылка здесь на массив и распадается на указатели, это один из лучших наборов объяснений, которые я только что нашел. - person Shafik Yaghmour; 08.11.2013
comment
@janm спасибо за ответ, я был afk, но я нашел хорошую ссылку, хотя я бы хотел, чтобы c faq освещал это лучше. - person Shafik Yaghmour; 08.11.2013
comment
@ShafikYaghmour Спасибо! Хм, это многое объясняет, но я помню, что читал что-то о доступе к блоку памяти m*n напрямую через указатель, что в некоторых случаях может привести к сбою программы (куча, стек, глобальная память, различные ОС). Или это просто относится к незнанию m и n? - person ; 08.11.2013
comment
@ebyrob, который слишком расплывчат, чтобы ответить, потребуется код, чтобы понять, хорошо ли он себя ведет или нет, и это выходит за рамки этого вопроса. Но если вы придумаете более конкретный сценарий, который звучит так, как будто на предыдущий вопрос SO, вероятно, уже был дан ответ, а если нет, то это будет новый вопрос. - person Shafik Yaghmour; 08.11.2013