Какая ошибка программирования обычно приводит к переполнению стека во время выполнения программы
Я искал везде и не могу найти твердый ответ. Согласно документации, Java бросает java.ленг.StackOverflowError ошибка при следующих обстоятельствах:
возникает, когда стек переполнение происходит, потому что приложение повторяется слишком глубоко.
но это вызывает два вопроса:
- а разве нет других способов для переполнения стека происходит не только через рекурсию?
- StackOverflowError происходит до того, как JVM фактически переполняет стек или после?
чтобы уточнить второй вопрос:
когда Java бросает StackOverflowError, вы можете с уверенностью предположить, что стек не сделал написать в кучу? Если вы уменьшите размер стека или кучи в try / catch на функцию, которая вызывает переполнение стека, вы можете продолжить работу? Это где-нибудь задокументировано?
ответы, которые я не ищу:
- StackOverflow происходит из-за плохой рекурсии.
- StackOverflow происходит, когда куча встречается со стеком.
кажется, вы думаете, что a ошибка stackoverflow похоже на исключение переполнения буфера в собственных программах, когда существует риск записи в память, которая не была выделена для буфера, и, таким образом, повредить некоторые другие места памяти. Это не так.
JVM имеет заданную память, выделенную для каждого стека каждого потока, и если попытка вызвать метод происходит, чтобы заполнить эту память, JVM выдает ошибку. Как это сделать, если вы были попытка записи по индексу N массива длины N. никакого повреждения памяти произойти не может. Стек не может писать в кучу.
StackOverflowError для стека-это то же, что OutOfMemoryError для кучи: он просто сигнализирует, что больше нет доступной памяти.
описание ошибок виртуальной машины (§6.3)
StackOverflowError: реализация виртуальной машины Java исчерпала пространство стека для a поток, как правило, потому, что поток выполняет неограниченное количество рекурсивных вызовов в результате ошибки в выполняющейся программе.
а разве нет других способов для переполнения стека происходит не только через рекурсию?
конечно. Просто продолжайте вызывать методы, никогда не возвращаясь. Вам понадобится много методов, если вы не позволите рекурсии. На самом деле, это не имеет значения: кадр стека-это кадр стека, независимо от того, является ли он одним из рекурсивных методов или нет.
ответ на ваш второй вопрос: stackoverflow обнаруживается, когда JVM пытается выделите кадр стека для следующего вызова, и находит это невозможно. Таким образом, ничего не будет перезаписано.
нет ли других способов для переполнения стека, не только через рекурсию?
Вызов принят :) StackOverflowError без рекурсии (вызов не удалось, см. комментарии):
компиляция со стандартом javac Test.java и запускать с java -Xss104k Test 2> out . После этого, more out скажу вам:
вторая попытка.
теперь идея еще проще. Примитивы в Java могут храниться на стек. Итак, давайте объявим много двойников, как double a1,a2,a3. . Этот скрипт может писать, компилировать и запускать код о нас:
и. Я получил что-то неожиданное:
это 100% повторяется. Это связано с вашим вторым вопросом:
StackOverflowError происходит до того, как JVM фактически переполняется стек или после?
Итак, в случае OpenJDK 20.0-b12 мы можем видеть, что JVM во-первых взорванный. Но это похоже на ошибку, может быть, кто-то может подтвердить это в комментариях, пожалуйста, потому что я не уверен. Должен ли я сообщить об этом? Может быть, это уже исправлено в какой-то новой версии. Согласно ссылка на спецификацию JVM (дан JB Nizet в комментарии) JVM должен бросить StackOverflowError , не умирают:
если вычисление в потоке требует большей виртуальной машины Java стек, чем разрешено, виртуальная машина Java бросает StackOverflowError.
третья попытка.
мы хотим создать новый
наиболее распространенной причиной StackOverFlowError является чрезмерно глубокая или бесконечная рекурсия.
например:
В Java:
здесь two области в памяти куча и стек. Элемент stack memory используется для хранения локальных переменных и вызов функции, в то время как heap memory используется для хранения объектов в Java.
если в стеке не осталось памяти для хранения вызова функции или локальной переменной, JVM бросит java.lang.StackOverFlowError
в то время как если там нет больше места кучи для создания объекта, JVM будет бросать java.lang.OutOfMemoryError
- нет, это единственный способ получить эту ошибку: получение ваш стек полный. Но не только через рекурсию, но и вызывая методы, которые бесконечно вызывают другие методы. Это очень специфическая ошибка, так что нет.
- он выбрасывается до того, как стек заполнен, именно тогда, когда вы его проверяете. Куда бы вы поместили данные, если нет свободного места ? Перекрывая других ? Неа.
есть два основных места, что вещи могут храниться в Java. Первый-это куча, которая используется для динамически выделяемых объектов. new .
кроме того, каждый поток получает свой собственный стек, и он получает объем памяти, выделенной для этого стека.
при вызове метода данные помещаются в стек для записи вызова метода, передаваемых параметров и выделяемых локальных переменных. Метод с пятью локальными переменными и тремя параметры будут использовать больше пространства стека, чем a void doStuff() метод без локальных переменных.
основные преимущества стека заключаются в том, что нет фрагментации памяти, все для одного вызова метода выделяется в верхней части стека, и что возврат из методов прост. Чтобы вернуться из метода, вы просто разматываете стек обратно к предыдущему методу, устанавливаете любое значение, необходимое для возвращаемого значения, и все готово.
потому что стек имеет фиксированный размер на поток, (обратите внимание, что спецификация Java не требует фиксированного размера, но большинство реализаций JVM на момент написания используют фиксированный размер) и поскольку пространство в стеке необходимо всякий раз, когда вы делаете вызов метода, надеюсь, теперь должно быть ясно, почему он может закончиться и что может привести к его завершению. Существует не фиксированное количество вызовов метода, нет ничего конкретного о рекурсии, вы получаете исключение, которое вы пытаетесь вызвать метод и не хватает памяти.
конечно размер стеки установлены достаточно высоко, что вряд ли произойдет в обычном коде. В рекурсивном коде, хотя это может быть довольно легко рекурсировать на огромные глубины, и в этот момент Вы начинаете работать с этой ошибкой.
StackOverflow происходит, когда выполняется вызов функции и стек заполнен.
Так же, как ArrayOutOfBoundException. Он не может ничего испортить, на самом деле его очень легко поймать и восстановить.
обычно это происходит в результате неконтролируемой рекурсии, но это также может быть вызвано просто очень глубоким вызовом стека функций.
- а разве нет других способов для переполнения стека происходит не только через рекурсию?
- StackOverflowError происходит до того, как JVM фактически переполняет стек или после?
Это также может произойти, когда мы выделяем размер больше, чем предел стека (например. int x[10000000]; ).
ответ на второй
каждый поток имеет свой собственный стек, который содержит фрейм для каждого метода, выполняющегося в этом потоке. Таким образом, текущий выполняемый метод находится в верхней части стека. Новый фрейм создается и добавляется (выталкивается) в верхнюю часть стека для каждого вызова метода. Кадр удаляется (выскакивает), когда метод возвращает нормально или если во время вызова метода возникает неперехваченное исключение. Стек не управляется напрямую, за исключением объектов push и pop frame, а также поэтому объекты фрейма могут быть выделены в куче, и память не должна быть непрерывной.
поэтому, рассматривая стек в потоке, мы можем сделать вывод.
стек может быть динамического или фиксированного размера. Если поток требует большего стека, чем разрешено StackOverflowError бросается. Если поток требует нового кадра и недостаточно памяти для его выделения, то OutOfMemoryError бросается.
Как происходит переполнение стека и как лучше всего этого избежать или как предотвратить его, особенно на веб-серверах, но были бы интересны и другие примеры?
Стек в этом контексте является буфером «последним пришел - первым вышел», в который вы помещаете данные во время выполнения программы. Последний вошел - первым ушел (LIFO) означает, что последнее, что вы вставляете, всегда первое, что вы получаете обратно - если вы вставляете в стек 2 элемента, 'A', а затем 'B', то первое, что вы вставляете вне стека будет «B», а следующим будет «A».
Когда вы вызываете функцию в своем коде, следующая инструкция после вызова функции сохраняется в стеке и любое пространство памяти, которое может быть перезаписано вызовом функции. Вызываемая функция может использовать больше стека для собственных локальных переменных. Когда это будет сделано, он освобождает используемое пространство стека локальной переменной, а затем возвращается к предыдущей функции.
Переполнение стека
Переполнение стека - это когда вы использовали больше памяти для стека, чем предполагалось использовать вашей программе. Во встроенных системах у вас может быть только 256 байтов для стека, и если каждая функция занимает 32 байта, тогда у вас может быть только вызовы функций глубиной 8 - функция 1 вызывает функцию 2, которая вызывает функцию 3, кто вызывает функцию 4 . кто вызывает функция 8, которая вызывает функцию 9, но функция 9 перезаписывает память вне стека. Это может привести к перезаписи памяти, кода и т. Д.
Многие программисты делают эту ошибку, вызывая функцию A, которая затем вызывает функцию B, которая затем вызывает функцию C, которая затем вызывает функцию A. Она может работать большую часть времени, но только один раз неправильный ввод заставит ее навсегда войти в этот круг. пока компьютер не обнаружит, что стек слишком раздут.
Рекурсивные функции также являются причиной этого, но если вы пишете рекурсивно (т. Е. Ваша функция вызывает сама себя), вам необходимо знать об этом и использовать статические / глобальные переменные для предотвращения бесконечной рекурсии.
Обычно операционная система и язык программирования, который вы используете, управляют стеком, и это вне ваших рук. Вы должны посмотреть на свой граф вызовов (древовидную структуру, которая показывает из вашего main, что вызывает каждая функция), чтобы увидеть, насколько глубоко заходят ваши вызовы функций, и чтобы обнаружить циклы и рекурсию, которые не предназначены. Преднамеренные циклы и рекурсия необходимо искусственно проверять, чтобы исключить ошибки, если они вызывают друг друга слишком много раз.
Помимо хороших практик программирования, статического и динамического тестирования, на этих высокоуровневых системах мало что можно сделать.
Встроенные системы
Во встраиваемом мире, особенно в коде высокой надежности (автомобильный, авиационный, космический), вы выполняете обширный анализ и проверку кода, но вы также делаете следующее:
- Запретить рекурсию и циклы - обеспечивается политикой и тестированием
- Храните код и стек далеко друг от друга (код во флеш-памяти, стек в ОЗУ, и никогда не совпадать)
- Поместите защитные полосы вокруг стека - пустую область памяти, которую вы заполняете магическим числом (обычно это команда программного прерывания, но здесь есть много вариантов), и сотни или тысячи раз в секунду вы смотрите на защитные полосы, чтобы убедиться, что они не были перезаписаны.
- Использовать защиту памяти (т.е. не выполнять в стеке, не читать и не писать только вне стека)
- Прерывания не вызывают вторичные функции - они устанавливают флаги, копируют данные и позволяют приложению позаботиться об их обработке (в противном случае вы можете получить 8 глубоко в дереве вызовов функций, иметь прерывание, а затем выйти из других функций внутри прерывание, вызывающее выброс). У вас есть несколько деревьев вызовов - одно для основных процессов и одно для каждого прерывания. Если ваши прерывания могут мешать друг другу . ну, драконы есть .
Языки и системы высокого уровня
Но на языках высокого уровня, работающих в операционных системах:
- Уменьшите объем хранилища локальных переменных (локальные переменные хранятся в стеке - хотя компиляторы довольно умны в этом отношении и иногда помещают большие локальные переменные в кучу, если ваше дерево вызовов неглубокое)
- Избегайте или строго ограничивайте рекурсию
- Не разбивайте свои программы слишком далеко на все меньшие и меньшие функции - даже без подсчета локальных переменных каждый вызов функции потребляет до 64 байтов в стеке (32-битный процессор, экономия половины регистров ЦП, флаги и т. Д.)
- Держите дерево вызовов неглубоким (аналогично приведенному выше утверждению)
Веб-серверы
От вашей «песочницы» зависит, можете ли вы контролировать стек или даже видеть его. Скорее всего, вы можете обращаться с веб-серверами, как с любым другим языком высокого уровня и операционной системой - это в значительной степени не в ваших руках, но проверьте язык и стек серверов, которые вы используете. Это является возможным взорвать стек на вашем SQL сервере, например.
Переполнение стека - программная ошибка времени выполнения, при которой программа захватывает всю память, выделенную ей под стек, что обычно приводит к аварийному завершению её работы.
Стек программы
Стек программы - это специальная области памяти, организованная по принципу очереди LIFO (Last in, first out - последним пришел, первым ушел). Название "стек" произошло из-за аналогии принципа его построения со стопкой (англ. stack) тарелок - можно класть тарелки друг на друга (метод добавления в стек, "заталкивание", "push"), а затем забирать их, начиная с верхней (метод получения значения из стека, "выталкивание", "pop"). Стек программы также называют стек вызовов, стек выполнения, машинным стеком (чтобы не путать его со "стеком" - абстрактной структурой данных).
Для чего нужен стек? Он позволяет удобно организовать вызов подпрограмм. При вызове функция получает некоторые аргументы; также она должна где-то хранить свои локальные переменные. Кроме того, надо учесть, что одна функция может вызвать другую функцию, которой тоже надо передавать параметры и хранить свои переменные. Используя стек, при передаче параметров нужно просто положить их в стек, тогда вызываемая функция сможет их оттуда "вытолкнуть" и использовать. Локальные переменные тоже можно хранить там же - в начале своего кода функция выделяет часть памяти стека, при возврате управления - очищает и освобождает. Программисты на высокоуровневых языках обычно не задумываются о таких вещах - весь необходимый рутинный код за них генерирует компилятор.
Последствия ошибки
Теперь мы подошли почти вплотную к проблеме. В абстрактном виде стек представляет собой бесконечное хранилище, в которое можно бесконечно добавлять новые элементы. К сожалению, в нашем мире все конечно - и память под стек не исключение. Что будет, если она закончится, когда в стек заталкиваются аргументы функции? Или функция выделяет память под свои переменные?
Произойдет ошибка, называемая переполнением стека. Поскольку стек необходим для организации вызова пользовательских функций (а практически все программы на современных языках, в том числе объектно-ориентированных, так или иначе строятся на основе функций), больше они вызываться не смогут. Поэтому операционная система забирает управление, очищает стек и завершает программу. Здесь можно подчеркнуть различие между переполнением буфера и переполнением стека - в первом случае ошибка происходит при обращении к неверной области памяти, и если защита на этом этапе отсутствует, в этот момент не проявляет себя - при удачном стечении обстоятельств программа может отработать нормально. Если только память, к которой шло обращение, была защищена, происходит ошибка сегментации. В случае со стеком программа непременно завершается.
Чтобы быть совсем точным, следует отметить, что подобное описание событий верно лишь для компиляторов, компилирующих в "родной" (native) код. В управляемых языках у виртуальной машины есть свой стек для управляемых программ, за состоянием которого гораздо проще следить, и можно даже позволить себе при возникновении переполнения передать программе исключение. В языках Си и Си++ на подобную "роскошь" рассчитывать не приходится.
Причины ошибки
Что же может привести к такой неприятной ситуации? Исходя из описанного выше механизма, один из вариантов - слишком большое число вложенных вызовов функций. Особенно вероятен такой вариант развития событий при использовании рекурсии. Бесконечная рекурсия (при отсутствии механизма "ленивых" вычислений) прерывается именно таким образом, в отличие от бесконечного цикла, который иногда имеет полезное применение. Впрочем, при небольшом объеме памяти, отведенной под стек (что, например, характерно для микроконтроллеров), достаточно может быть и простой последовательности вызовов.
Другой вариант - локальные переменные, требующие большого количества памяти. Заводить локальный массив из миллиона элементов, или миллион локальных переменных (мало ли что бывает) - не самая лучшая идея. Даже один вызов такой "жадной" функции легко может вызвать переполнение стека. Для получения больших объемов данных лучше воспользоваться механизмами динамической памяти, которая позволит обработать ошибку её нехватки.
Однако динамическая память является довольно медленной в плане выделения и освобождения (поскольку этим занимается операционная система), кроме того, при прямом доступе приходится вручную выделять её и освобождать. Память же в стеке выделяется очень быстро (по сути, надо лишь изменить значение одного регистра), кроме того, у объектов, выделенных в стеке, автоматически вызываются деструкторы при возврате управления функцией и очистке стека. Разумеется, тут же возникает желание получить память из стека. Поэтому третий путь к переполнению - самостоятельное выделение в стеке памяти программистом. Специально для этой цели библиотека языка Си предоставляет функцию alloca. Интересно заметить, что если у функции для выделения динамической памяти malloc есть свой "близнец" для её освобождения free, то у функции alloca его нет - память освобождается автоматически после возврата управления функцией. Возможно, это только осложняет ситуацию - ведь до выхода из функции освободить память не получится. Даже несмотря на то, что согласно man-странице "функция alloca зависит от машины и компилятора; во многих системах ее реализация проблематична и содержит много ошибок; ее использование очень несерьезно и не одобряется" - она все равно используется.
Примеры
В качестве примера рассмотрим код для рекурсивного поиска файлов, расположенный на MSDN:
Эта функция получает список файлов указанной директории, а затем вызывает себя же для тех элементов списка, которые оказались директориями. Соответственно, при достаточно глубоком дереве файловой системы, мы получим закономерный результат.
Как видно, в функции main выделяется память в стеке под массивы типов int и float по миллиону элементов каждый, что в сумме дает чуть менее 8 мегабайт. Если учесть, что по умолчанию Visual C++ резервирует под стек лишь 1 мегабайт, то ответ становится очевидным.
А вот пример, взятый из GitHub-репозитория проекта Flash-плеера Lightspark:
Можно надеятся, что h.getLength()-7 не будет слишком большим числом, чтобы на следующей строчке не произошло переполнения. Но стоит ли сэкономленное на выделении памяти время "потенциального" вылета программы?
Переполнение стека - фатальная ошибка, которой наиболее часто страдают программы, содержащие рекурсивные функции. Однако даже если программа не содержит таких функций, переполнение все равно возможно из-за большого объема локальных переменных или ошибки в ручном выделении памяти в стеке. Все классические правила остаются в силе: если есть возможность выбора, вместо рекурсии лучше предпочесть итерацию, а также не заниматься ручной работой вместо компилятора.
Для веб-сайта см. Переполнение стека . Чтобы узнать о других значениях, см. Переполнение стека (значения) .
В программном обеспечении переполнение стека происходит, если указатель стека вызовов превышает границу стека . Стек вызовов может состоять из ограниченного адресного пространства , часто определяемого в начале программы. Размер стека вызовов зависит от многих факторов, включая язык программирования, архитектуру машины, многопоточность и объем доступной памяти. Когда программа пытается использовать больше места, чем доступно в стеке вызовов (то есть, когда она пытается получить доступ к памяти за пределами стека вызовов, что по сути является переполнением буфера ), стек считается переполненным , что обычно приводит к сбой программы. [1]
СОДЕРЖАНИЕ
Наиболее частой причиной переполнения стека является чрезмерно глубокая или бесконечная рекурсия, при которой функция вызывает себя столько раз, что места, необходимого для хранения переменных и информации, связанной с каждым вызовом, больше, чем может поместиться в стеке. [2]
Пример бесконечной рекурсии в C .
Функция foo , когда она вызывается, продолжает вызывать себя, каждый раз выделяя дополнительное пространство в стеке, пока стек не переполнится, что приведет к ошибке сегментации . [2] Однако некоторые компиляторы реализуют оптимизацию хвостового вызова , позволяя выполнять бесконечную рекурсию определенного вида - хвостовую рекурсию без переполнения стека. Это работает, потому что вызовы хвостовой рекурсии не занимают дополнительного места в стеке. [3]
Некоторые параметры компилятора C позволяют эффективно выполнять оптимизацию хвостового вызова ; например, компиляция указанной выше простой программы с использованием gcc с -O1 приведет к ошибке сегментации, но не при использовании -O2 или -O3 , поскольку эти уровни оптимизации подразумевают параметр -foptimize-sibling-calls компилятора. [4] Другие языки, такие как Scheme , требуют, чтобы все реализации включали хвостовую рекурсию как часть языкового стандарта. [5]
Рекурсивная функция, которая завершается теоретически, но вызывает переполнение буфера стека вызовов на практике, может быть исправлена путем преобразования рекурсии в цикл и сохранения аргументов функции в явном стеке (вместо неявного использования стека вызовов). Это всегда возможно, потому что класс примитивных рекурсивных функций эквивалентен классу вычислимых функций LOOP. Рассмотрим этот пример в псевдокоде, подобном C ++ :
Примитивная рекурсивная функция, подобная той, что находится слева, всегда может быть преобразована в цикл, как на правой стороне.
Функция, подобная приведенному выше примеру слева, не будет проблемой в среде, поддерживающей оптимизацию хвостового вызова ; однако все еще можно создать рекурсивную функцию, которая может привести к переполнению стека на этих языках. Рассмотрим приведенный ниже пример двух простых целочисленных функций возведения в степень.
Обе pow(base, exp) функции, указанные выше, вычисляют эквивалентный результат, однако функция слева может вызвать переполнение стека, поскольку оптимизация хвостового вызова для этой функции невозможна. Во время выполнения стек для этих функций будет выглядеть так:
Обратите внимание, что функция слева должна хранить в своем стеке exp количество целых чисел, которые будут умножены, когда рекурсия завершится и функция вернет 1. Напротив, функция справа должна хранить только 3 целых числа в любой момент и вычисляет промежуточный результат, который передается его следующему вызову. Поскольку никакая другая информация, кроме текущего вызова функции, не должна храниться, оптимизатор хвостовой рекурсии может «отбрасывать» предыдущие кадры стека, исключая возможность переполнения стека.
Другая основная причина переполнения стека возникает из-за попытки выделить в стеке больше памяти, чем может поместиться, например, путем создания слишком больших переменных локального массива. По этой причине некоторые авторы рекомендуют выделять массивы размером более нескольких килобайт динамически, а не в качестве локальной переменной. [6]
Пример очень большой переменной стека в C :
Переполнение стека усугубляется всем, что уменьшает эффективный размер стека данной программы. Например, одна и та же программа, запущенная без нескольких потоков, может работать нормально, но как только будет включена многопоточность, программа выйдет из строя. Это связано с тем, что большинство программ с потоками имеют меньше места в стеке на поток, чем программа без поддержки потоков. Поскольку ядра обычно многопоточны, новичкам в разработке ядра обычно не рекомендуется использовать рекурсивные алгоритмы или большие стековые буферы. [7]
Это предупреждение указывает, что использование стека, превышающее пороговое значение предустановки (constant_2), обнаружено в функции. Размер кадра стека по умолчанию для этого предупреждения составляет 16 КБ для пользовательского режима, 1 КБ для режима ядра. Стек ( даже в пользовательском режиме) ограничен, и сбой фиксации страницы стека приводит к исключению переполнения стека. Режим ядра имеет ограничение в 12 КБ стека, которое невозможно увеличить. Попробуйте агрессивно ограничить использование стека в коде в режиме ядра.
Чтобы устранить проблему, связанную с этим предупреждением, можно переместить некоторые данные в кучу или в другую динамическую память. В пользовательском режиме одна большая рамка стека может не быть проблемой , и это предупреждение может быть подавлено, но большой кадр стека увеличивает риск переполнения стека. (Большой кадр стека может возникать, если функция использует стек сильно или рекурсивно.) Общий размер стека в пользовательском режиме можно увеличить, если на самом деле происходит переполнение стека, но только до ограничения системы.
Для кода в режиме ядра (например, в проектах драйверов) значение constant_2 равно 1 КБ. Хорошо написанные драйверы должны иметь несколько функций, которые подходят к этому значению, и изменение предела вниз может быть желательно. Те же общие методы, которые используются для пользовательского кода для уменьшения размера стека, можно адаптировать к коду в режиме ядра.
Настройка размера стека для подавления предупреждения
Параметр командной /analyze:stacksize строки можно использовать для изменения значения для constant_2, но при этом увеличивается риск того, что ошибка может не отображаться.
Отключение предупреждения в командной строке
- /analyze:stacksize Добавьте параметр в командную строку компилятора. Используйте значение для этого значения, превышающее constant_1. Например, если constant_1 имеет значение 27180, можно ввести /analyze:stacksize 32768 .
Отключение предупреждения в интегрированной среде разработки
В интегрированной среде разработки Visual Studio выберите проект в окне Обозреватель решений.
В строке меню щелкните Проект>Свойства.
В дополнительных параметрах добавьте /analyze:stacksize , где больше constant_1. Например, если constant_1 имеет значение 27180, можно ввести /analyze:stacksize 32768 . Выберите ОК для сохранения внесенных изменений.
Например, .
Следующий код создает это предупреждение, так как char buffer в стеке требуется 16 382 байта, а для локальной целочисленной переменной i требуется еще 4 байта, что вместе превышает ограничение размера стека по умолчанию в 16 КБ.
Следующий код исправляет это предупреждение путем перемещения некоторых данных в кучу.
Использование malloc и free имеет много ошибок, таких как утечки памяти и исключения. Чтобы избежать таких утечек и проблем исключений, используйте механизмы, предоставляемые стандартной библиотекой C++ (STL). Это операторы shared_ptr , unique_ptr , а также vector . Дополнительные сведения см. в разделе "Интеллектуальные указатели " и "Стандартная библиотека C++".
Читайте также: