Подтвердить что ты не робот

Как деинкретизировать (удалить) переменную в C?

Как и с макросами:

#undef SOMEMACRO 

Можем ли мы также декомпилировать или удалить переменные в C, чтобы мы могли сэкономить много памяти?

Я знаю о malloc() и free(), но я хочу полностью удалить переменные, чтобы, если я использую printf("%d", a);, я должен получить ошибку

test.c:4:14: error: ‘a’ undeclared (first use in this function)
4b9b3361

Ответ 1

Нет, но вы можете создать небольшие минимальные области для достижения этой цели, поскольку все локальные переменные области уничтожаются, когда область действия выходит из нее. Что-то вроде этого:

void foo() {
    // some codes
    // ...
    {    // create an extra minimum scope where a is needed
        int a;
    }
    // a doesn't exist here
}

Ответ 2

Это не прямой ответ на вопрос, но он может привести к упорядочению и пониманию того, почему этот вопрос не имеет правильного ответа и почему "удаление" переменных невозможно в C.

Точка № 1 Что такое переменные?

Переменные - это способ для программиста назначить имя памяти. Это важно, потому что это означает, что переменная не должна занимать какое-либо фактическое пространство! Пока у компилятора есть способ отслеживать память, о которой идет речь, определенная переменная может быть переведена многими способами, чтобы вообще не занимать места. Рассмотрим: const int i = 10; Компилятор может легко выбрать замену всех экземпляров i на мгновенное значение. i будет занимать 0 памяти данных в этом случае (в зависимости от архитектуры он может увеличить размер кода). В качестве альтернативы, компилятор может сохранить значение в регистре и снова, ни один стек или пустое место не будут использоваться. Нет смысла указывать метку, которая существует в основном в коде и не обязательно во время выполнения.

Точка № 2 Где хранятся переменные?

После точки №1 вы уже понимаете, что ответить на этот вопрос непросто, поскольку компилятор может делать все, что угодно, без нарушения вашей логики, но, вообще говоря, переменные хранятся в стеке. Как работает стек, очень важно для вашего вопроса. Когда функция вызывается, машина берет текущее местоположение указателя инструкции процессора и текущего указателя стека и толкает их в стек, заменяя указатель стека на следующее место в стеке. Затем он переходит в код вызываемой функции.

Эта функция знает, сколько переменных она имеет и сколько пространства они нуждаются, поэтому она перемещает указатель кадра для захвата кадра, который может занимать все функциональные переменные, а затем просто использует стек. Чтобы упростить задачу, функция фиксирует достаточное пространство для всех переменных сразу с самого начала, и каждая переменная имеет четко определенное смещение от начала кадра стека функций *. Переменные также сохраняются один за другим. В то время как вы можете манипулировать указателем на фрейм после этого действия, это будет слишком дорого и в основном бессмысленно. В текущем коде используется только последний стек стека и может занимать все оставшиеся стеки, если это необходимо (стек распределяется при запуске потока), поэтому "освобождение" переменные дают мало пользы. Освобождение переменной из середины фрейма стека потребует операции дефрагментации, которая будет очень дорогостоящей и бесполезной для восстановления нескольких байтов памяти.

Точка №3: ​​пусть компилятор выполнит свою работу

Последний вопрос здесь - тот простой факт, что компилятор мог бы намного лучше оптимизировать вашу программу, чем вы, вероятно, могли бы. Учитывая необходимость, компилятор мог обнаружить области видимости переменных и перекрытие памяти, к которым нельзя одновременно получить доступ, чтобы уменьшить потребление памяти программ (-O3 compile flag). Вам не нужно "выпускать" переменные, поскольку компилятор мог это сделать без вашего ведома.

Это дополняет все сказанное передо мной о том, что переменные слишком малы для материи и тот факт, что нет механизма для достижения того, что вы просили.


* Языки, поддерживающие массивы динамического размера, могут изменять фрейм стека для выделения пространства для этого массива только после вычисления размера массива.

Ответ 3

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

И вы не сохранили бы "много памяти". Объем памяти, который вы сохранили бы, если бы вы сделали это, был бы незначительным. Крошечный. Не стоит говорить.

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

Вызов кода, который восстанавливал бы код отдельных переменных, также занимал бы больше места, чем сами переменные.

Итак, если существует волшебный метод purge(), который очищает переменные, то не только реализация purge() будет больше, чем любой объем памяти, который вы когда-либо надеялись бы восстановить, очистив переменные в вашей программе, но также в int a; purge(a); вызов purge() будет занимать больше места, чем a.

Это потому, что переменные, о которых вы говорите, очень малы. Пример printf("%d", a);, который вы указали, показывает, что вы думаете о том, чтобы каким-то образом восстановить память, занятую отдельными переменными int. Даже если бы был способ сделать это, вы бы сохранили что-то порядка 4 байтов. Общий объем памяти, занимаемой такими переменными, чрезвычайно мал, поскольку он является прямой функцией того, сколько переменных вы, как программист, объявляете, вручную набирая свои объявления. На клавиатуре потребуется много лет, ничего не делая, кроме бессмысленного объявления переменных, прежде чем вы объявите число переменных int, занимающих объем памяти, о котором стоит говорить.

Ответ 4

Ну, вы можете использовать блоки ({ }) и определять переменную как можно позже, чтобы ограничить область, в которой она существует.

Но если адрес переменной не принят, то это не влияет на сгенерированный код вообще, так как определение компилятором области, в которой он должен сохранять значение переменной, существенно не влияет.

Если адрес переменной принят, отказ от анализа эвакуации, в основном из-за встроенных барьеров, таких как отдельная компиляция или разрешение семантического интерполяции, может заставить компилятор предположить, что он должен поддерживать его до тех пор, пока блок не будет строго необходим. Это редко бывает значительным (не беспокойтесь о горстке int s, и чаще всего несколько строк кода, более долго сохраняющих его жизнь, несущественны), но лучше всего учитывать это в редком случае, когда это может иметь значение.

Ответ 5

Если вы обеспокоены крошечным объемом памяти, находящейся в стеке, то вам, вероятно, будет интересно понять специфику вашего компилятора. Вам нужно будет узнать, что он делает, когда компилирует. Фактическая форма фрейма стека не определяется языком C. Компилятору это нужно выяснить. Чтобы взять пример из принятого в настоящее время ответа:

void foo() {
    // some codes
    // ...
    {    // create an extra minimum scope where a is needed
        int a;
    }
    // a doesn't exist here
}

Этот может или не может влиять на использование памяти функцией. Если бы вы сделали это в основном компиляторе, таком как gcc или Visual Studio, вы обнаружите, что они оптимизируются для скорости, а не размера стека, поэтому они заранее выделяют все пространство стека, которое им нужно в начале функции. Они будут проводить анализ, чтобы выяснить минимальное необходимое предварительное распределение, используя анализ области использования и анализа переменных, но эти алгоритмы буквально не будут подвержены дополнительным ограничениям. Они уже умнее, чем это.

Другие компиляторы, особенно для встроенных платформ, могут распределять структуру стека по-разному. На этих платформах такое определение может быть трюком, в котором вы нуждались. Как вы понимаете разницу? Возможны только следующие варианты:

  • Прочитайте документацию
  • Попробуйте и посмотрите, что работает

Кроме того, убедитесь, что вы понимаете точный характер своей проблемы. Я работал над конкретным встроенным проектом, который избегал стека для всего, кроме возвращаемых значений и нескольких int s. Когда я нажал на старших разработчиков об этой глупости, они объяснили, что в этом конкретном приложении пространство стека было более ценным, чем пространство для глобальных переменных. У них был процесс, который им пришлось пройти, чтобы доказать, что система будет действовать по назначению, и этот процесс был намного легче для них, если бы они распределяли все по фронту и избегали рекурсии. Я гарантирую, что вы никогда не достигнете такого сложного решения, если только вы не узнаете точный характер того, что вы решаете.

В качестве другого решения, на которое вы могли бы обратить внимание, вы всегда можете создавать свои собственные фреймы стека. Создайте объединение структур, где каждая структура содержит переменные для одного кадра стека. Затем следите за ними самостоятельно. Вы также можете посмотреть такие функции, как alloca, что позволяет увеличивать фрейм стека во время вызова функции, если ваш компилятор поддерживает его.

Будет ли профсоюз структур работать? Попробуй. Ответ зависит от компилятора. Если все переменные хранятся в памяти на вашем конкретном устройстве, этот подход, вероятно, минимизирует использование стека. Однако это также может существенно смутить алгоритмы раскраски регистров и привести к увеличению использования стека! Попробуйте и посмотрите, как это происходит для вас!