Почему GCC не выдает ошибку stackerror при длинных аргументах?

Моя цель - показать кому-нибудь, что каждый аргумент, который вы отправляете функции C, помещается в стек. Это также происходит в Ruby, Python, Lua, JavaScript и т. Д.

Итак, я написал код Ruby, который генерирует код C:

#!/usr/bin/env ruby

str = Array.new(10, &:itself)

a = <<~EOF
    #include <stdio.h>

    void x(#{str.map { |x| "int n#{x}" }.join(?,)}) {
        printf("%d\\n", n#{str.length - 1}) ;
    }

    int main() { x(#{str.join(?,)}) ; }
EOF

IO.write('p.c', a)

После запуска этого кода с интерпретатором Ruby я получаю файл pc с таким содержимым:

#include <stdio.h>

void x(int n0,int n1,int n2,int n3,int n4,int n5,int n6,int n7,int n8,int n9) {
    printf("%d\n", n9) ;
}

int main() { x(0,1,2,3,4,5,6,7,8,9) ; }

Что хорошо, компилируется и выполняется нормально.

Но если я дам программе ruby размер массива 100000, она должна сгенерировать файл C, который принимает от n0 до n999999 аргументов. Это означает 100 000 аргументов.

Быстрый поиск в Google показывает мне, что аргументы C хранятся в стеке.

Передача этих аргументов должна вызвать ошибку стека, но это не так. GCC компилирует его просто отлично, я также получаю 99999.

Но с Clang я получаю:

p.c:4:17: error: use of undeclared identifier 'n99999'
        printf("%d\n", n99999) ;
                       ^
p.c:8:195690: error: too many arguments to function call, expected 34464, have 100000
p.c:3:6: note: 'x' declared here
2 errors generated.

Как GCC справляется с таким количеством аргументов? В большинстве случаев я получаю ошибку стека на других языках программирования, когда размер стека равен 10900.

# gcc stack-overflow
Источник
  • 1
    Быстрый поиск в Google показывает мне, что аргументы C хранятся в стеке. Гугл ошибается - здесь нет упомянутого "стека" ...
  • 0
    Да, "аргументы, переданные функции C, помещаются в стек справа налево перед вызовом функции. Первое, что делает вызываемая функция, - это помещает регистр EBP, а затем копирует в него ESP. Это создает новые данные. структура, обычно называемая фреймом стека C. " - tenouk.com/Bufferoverflowc/Bufferoverflow2a.html
  • 4
    @ S.Goswami, хотя эта статья может быть верной в отношении некоторых (немногочисленных в наши дни) реализаций C, это не относится ко всем реализациям C в целом. Большинство передают некоторые аргументы в регистры ЦП, что происходит быстрее, используя стек только там, где это необходимо. Спецификации языка C в любом случае ничего не говорят об этом.
  • 3
    @ S.Goswami Я связался с копией стандарта C. А что, если у моего процессора даже нет регистра EBP?
  • 1
    Я не уверен в других языках, которые вы упомянули, но поскольку все они являются высокоуровневыми языками, обычно реализованными на C, я склонен думать, что в одном смысле они не используют ни аппаратный стек, ни регистры ЦП, а в другом они используют независимо от того, как используется реализация C, с которой они были созданы. Также можно рассмотреть и другие чувства.
  • 1
    Более того, даже в той степени , что аргументы передаются в стеке, то непонятно , почему вы предполагаете , что вызов функции с аргументами 100000 обязательно переполнение стека. Программы обычно имеют больше доступного пространства стека, чем требуется, и большинство реализаций C предоставляют способы предоставить больше. И если вы сделали более доступной стек, это unlear , почему вы думаете , что вы можете предсказать (неопределенные) результат.
  • 2
    Поскольку вы используете только последний параметр x , gcc может оптимизировать ваш код так, чтобы он не передавал 100000 параметров в вызове функции.
Codelisting
за 0 против

Лучший способ доказать это своему другу - написать бесконечную рекурсивную функцию:

#include <stdio.h>
void recurse(int x) {
  static int iterations=0;

  printf("Iteration: %d\n", ++iterations);
  recurse(x);
}

int main() {
  recurse(1);
}

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

Что касается почемуgcc компилирует,gcc не знает размер целевого стека, поэтому не может проверить переполнение стека. Теоретически возможно иметь стек, достаточно большой, чтобы вместить 100 000 аргументов. Это меньше половины мегабайта. Не знаю почемуclang ведет себя иначе; это будет зависеть от просмотра сгенерированного кода C.

Если вы можете рассказать, какую компьютерную систему / архитектуру вы используете, это будет полезно. Вы привели информацию, которая относится к 64-битным системам Intel (например, ПК / Windows).

  • 3
    Да, это может привести к переполнению стека, но это не подтвердит тезис OP о том, что «каждый аргумент, который вы отправляете функции C, помещается в стек». В самом деле, даже если он переполняет стек, это маловероятно из-за передачи туда каких-либо аргументов. Однако, поскольку она может быть подвергнута оптимизации хвостового вызова, эта конкретная функция может все-таки не переполнить стек.
  • 0
    Да, это правда, факториал или последовательность fib также могут переполнять стек, но буквально не отправляют тысячи аргументов. Я видел Ruby, Python, JS stackoverflow, если вы это сделаете, вы также не сможете сделать 1+2+3+4...100000 на интерпретируемых языках. C компилируется, и clang выдает ошибку во время компиляции, но GCC, в частности, справляется с этим хорошо, так что это может отбросить идею о том, что это хранится в стеке, потому что размер стека в моей системе не допускает> 10900 рекурсий. .
Codelisting
Популярные категории
На заметку программисту