Виды памяти в технических средствах информатизации постоянная переменная внутренняя внешняя

Обновлено: 05.07.2024

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

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

Для внешних данных вы применяете класс хранения к объекту данных с помощью обозревателя моделей или программно с помощью таких функций, как get_param и set_param .

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

Свойства класса хранилища

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

При создании класса хранения с помощью конструктора настраиваемых классов хранения вы можете указать, может ли пользователь класса хранения задавать дополнительные параметры. См. Разрешить пользователям класса хранения указывать значение свойства (Embedded Coder) .

Класс хранения по умолчанию

По умолчанию для отдельного элемента данных используется класс хранения Auto . Используя это значение по умолчанию, вы выбираете, чтобы генератор кода обрабатывал выбор класса хранения и приложение. В этом случае элемент данных подлежит оптимизации генерации кода, которая может исключить элемент из кода или изменить представление элемента. Если оптимизации не устраняют элемент данных, элемент отображается как поле стандартной структуры данных в сгенерированном коде.

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

Встроенные и предопределенные классы хранения

Чтобы оптимизация не приводила к сокращению хранилища для элемента данных, вы можете выбрать класс хранения для элемента в соответствии с вашими требованиями к генерации кода. Доступные классы хранения включают встроенные классы хранения, предопределенные классы хранения в пакете Simulink, а также могут включать другие классы хранения для конкретного проекта, определенные в словаре Embedded Coder. Если у вас есть особые требования, которым не отвечают перечисленные классы хранения, и вы создаете код для цели на основе ERT, вы можете определить и использовать новый класс хранения. См. разделы Определение классов хранения, разделов памяти и шаблонов функций для архитектуры программного обеспечения (встроенный кодер) и Создание классов хранения с помощью конструктора настраиваемых классов хранения (встроенный кодер) .

Для Simulink® Coder™ вы можете выбирать из этих встроенных и предопределенных классов хранения.

< /tr>
ТребованияКласс хранилища
Включите оптимизацию, потенциально генерирующую более эффективный код.< /td>Авто (Отдельные элементы данных)
Для элементов данных, которые нельзя оптимизировать, представьте данные как поле стандартной структуры данных. По умолчанию (сопоставление по умолчанию)
Предотвратить устранение хранилища для элемента данных при оптимизации.Модель по умолчанию (индивидуальное сопоставление)
Доступ к данным из отдельной глобальной переменной. Сгенерированный код содержит объявление и определение переменной.ExportedGlobal
Доступ к данным из автономной глобальной переменной. Сгенерированный код содержит объявление переменной. Ваш внешний код предоставляет определение.ImportedExtern, ImportedExternPointer

Если у вас есть Embedded Coder®, вы можете выбрать один из этих дополнительных предопределенных классов хранения, доступных в пакете Simulink.

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

Объявление переменной:
Типичное объявление переменной имеет форму:

Имя переменной может состоять из букв (как в верхнем, так и в нижнем регистре), цифр и символа подчеркивания «_». Однако имя не должно начинаться с цифры.

Различие ч/б объявления и определения переменной
Объявление переменной относится к той части, где переменная впервые объявляется или вводится перед ее первым использованием. Определение переменной — это часть, в которой переменной присваивается место в памяти и значение. В большинстве случаев объявление и определение переменных выполняются вместе.

См. следующую программу C для лучшего понимания:

Вывод:

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

  1. Переменная может содержать буквы, цифры и знак подчеркивания.
  2. Имя переменной может начинаться с алфавита и только с символа подчеркивания. Оно не может начинаться с цифры.
  3. В имени переменной не допускаются пробелы.
  4. Имя переменной не должно быть зарезервированным словом или ключевым словом, например. int, goto и т. д.

Типы переменных в C
1. Локальная переменная
Переменная, объявленная и используемая внутри функции или блока, называется локальной переменной.
Сфера действия ограничена функцией или блоком. Его нельзя использовать вне блока. Локальные переменные необходимо
инициализировать перед использованием.

Пример —

В приведенном выше коде x можно использовать только в области действия function() . Использование его в основной функции приведет к ошибке.

<р>2. Глобальная переменная
Переменная, объявленная вне функции или блока, называется глобальной переменной.
Объявляется при запуске программы. Он доступен для всех функций.

Пример —

В приведенном выше коде обе функции могут использовать глобальную переменную x, поскольку глобальные переменные уже доступны для всех функций.

3.Статическая переменная
Переменная, которая сохраняет свое значение между вызовами нескольких функций, называется статической переменной.
Он объявляется с ключевым словом static.

Пример-

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

4.Автоматическая переменная
Все переменные в C, объявленные внутри блока, по умолчанию являются автоматическими переменными. Мы
можем явно объявить автоматическую переменную, используя ключевое слово auto. Автоматические переменные аналогичны
локальным переменным.

Пример —

В приведенном выше примере и x, и y являются автоматическими переменными. Единственное отличие состоит в том, что переменная y явно объявлена ​​с ключевым словом auto.

5.Внешняя переменная
Внешняя переменная может совместно использоваться несколькими файлами C. Мы можем объявить внешнюю переменную с помощью ключевого слова extern.

Пример:

В приведенном выше примере x — это внешняя переменная, которая используется в нескольких файлах.

Ключевые слова — это определенные зарезервированные слова в C, каждое из которых имеет определенную функцию, связанную с ним. Почти все слова, которые помогают нам использовать функциональность языка C, включены в список ключевых слов. Так что можете себе представить, что список ключевых слов будет немаленьким!

Всего в C 44 ключевых слова (C89 – 32, C99 – 5, C11 – 7):

Большинство этих ключевых слов уже обсуждались в различных подразделах языка C, таких как типы данных, классы хранения, операторы управления, функции и т. д.

Давайте обсудим некоторые другие ключевые слова, которые позволяют нам использовать основные функции C:

const: const может использоваться для объявления постоянных переменных. Постоянные переменные — это переменные, которые при инициализации не могут изменить свое значение. Или, другими словами, присвоенное им значение не может быть изменено дальше в программе.
Синтаксис:

Примечание. Постоянные переменные должны быть инициализированы во время их объявления. Ключевое слово const также используется с указателями. Пожалуйста, обратитесь к квалификатору const в C, чтобы понять то же самое.

extern: extern просто сообщает нам, что переменная определена в другом месте, а не в том же блоке, где она используется. По сути, значение присваивается ему в другом блоке, и оно также может быть перезаписано/изменено в другом блоке. Таким образом, внешняя переменная — это не что иное, как глобальная переменная, инициализированная допустимым значением, где она объявлена ​​для использования в другом месте. Доступ к нему можно получить из любой функции/блока. Кроме того, обычную глобальную переменную также можно сделать внешней, поместив ключевое слово «extern» перед ее объявлением/определением в любой функции/блоке. В основном это означает, что мы не инициализируем новую переменную, а вместо этого используем/имеем доступ только к глобальной переменной.Основная цель использования внешних переменных заключается в том, что к ним можно получить доступ между двумя разными файлами, которые являются частью большой программы.
Синтаксис:

static: ключевое слово static используется для объявления статических переменных, которые обычно используются при написании программ на языке C. Статические переменные имеют свойство сохранять свое значение даже после того, как они выходят за пределы своей области видимости! Следовательно, статические переменные сохраняют значение своего последнего использования в своей области. Таким образом, можно сказать, что они инициализируются только один раз и существуют до завершения работы программы. Таким образом, новая память не выделяется, потому что они не объявляются повторно. Их область действия является локальной для функции, для которой они были определены. Доступ к глобальным статическим переменным можно получить в любом месте этого файла, поскольку их область действия является локальной для файла. По умолчанию компилятор присваивает им значение 0.
Синтаксис:

void: void — это особый тип данных. Но что делает его таким особенным? void, как это буквально означает, является пустым типом данных. Это означает, что он ничего не имеет или не имеет ценности. Например, когда он используется в качестве возвращаемого типа данных для функции, он просто означает, что функция не возвращает никакого значения. Точно так же, когда он добавляется к заголовку функции, он означает, что функция не принимает аргументов.
Примечание: void также часто используется с указателями. Пожалуйста, обратитесь к указателю void в C, чтобы понять то же самое.

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

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

Спецификаторы класса хранения являются частью decl-specifier-seq синтаксиса объявления имени. Вместе с областью действия имени они управляют двумя независимыми свойствами имени: его длительностью хранения и его связью.

  • auto или (до C++11) без спецификатора - automatic продолжительность хранения.
  • register - автоматическая продолжительность хранения. Также подсказывает компилятору поместить объект в регистр процессора. (устарело)
  • static — продолжительность хранения static или thread и внутренняя связь (или внешняя связь для статических членов класса, не в анонимном пространстве имен).
  • extern — статическая или потоковая продолжительность хранения и внешняя связь.
  • thread_local — длительность хранения потока.
  • изменяемый — не влияет на продолжительность хранения или привязку. Объяснение см. в const/volatile.


В объявлении может присутствовать только один спецификатор класса хранения, за исключением того, что thread_local может сочетаться со static или extern (начиная с C++11).

Содержание

[править] Объяснение

1) Спецификатор auto был разрешен только для объектов, объявленных в области блока или в списках параметров функций. В нем указана продолжительность автоматического хранения, которая является значением по умолчанию для таких деклараций. Значение этого ключевого слова было изменено в C++11.

2) Спецификатор регистра разрешен только для объектов, объявленных в области блока и в списках параметров функций. Он указывает продолжительность автоматического хранения, которая является значением по умолчанию для таких объявлений. Кроме того, наличие этого ключевого слова может быть использовано оптимизатором как подсказка для сохранения значения этой переменной в регистре ЦП. Это ключевое слово устарело в C++11.

3) Статический спецификатор разрешен только в объявлениях объектов (кроме списков параметров функций), объявлениях функций (кроме области блока) и объявлениях анонимных объединений. При использовании в объявлении члена класса он объявляет статический член. При использовании в объявлении объекта он указывает продолжительность статического хранения (кроме случаев, когда он сопровождается thread_local ). При использовании в объявлении в области пространства имен он указывает внутреннюю связь.

4) Спецификатор extern разрешен только в объявлениях переменных и функций (кроме членов класса или параметров функции). Он указывает внешнюю связь и технически не влияет на продолжительность хранения, но его нельзя использовать в определении объекта автоматической продолжительности хранения, поэтому все внешние объекты имеют статическую продолжительность или продолжительность потока. Кроме того, объявление переменной, использующее extern и не имеющее инициализатора, не является определением.

5) Ключевое слово thread_local разрешено только для объектов, объявленных в области пространства имен, объектов, объявленных в области блока, и статических элементов данных. Это указывает на то, что объект имеет продолжительность хранения потока.Его можно комбинировать со статическим или внешним для указания внутренней или внешней связи (за исключением статических элементов данных, которые всегда имеют внешнюю связь) соответственно, но этот дополнительный статический параметр не влияет на продолжительность хранения.

[править] Срок хранения

Все объекты в программе имеют одну из следующих продолжительностей хранения:

  • автоматическая продолжительность хранения. Память для объекта выделяется в начале окружающего блока кода и освобождается в конце. Все локальные объекты имеют этот срок хранения, кроме объявленных static , extern или thread_local .
    Продолжительность
  • статического хранения. Память для объекта выделяется при запуске программы и освобождается при завершении программы. Существует только один экземпляр объекта. Все объекты, объявленные в области пространства имен (включая глобальное пространство имен), имеют эту продолжительность хранения, а также объекты, объявленные с помощью static или extern . См. Нелокальные переменные и Статические локальные переменные для получения подробной информации об инициализации объектов с этим сроком хранения.
    Длительность хранения
  • потока. Память для объекта выделяется, когда начинается поток, и освобождается, когда поток заканчивается. Каждый поток имеет свой собственный экземпляр объекта. Только объекты, объявленные thread_local, имеют этот срок хранения. thread_local может отображаться вместе со static или extern для настройки связи. См. Нелокальные переменные и Статические локальные переменные для получения подробной информации об инициализации объектов с этим сроком хранения.
  • динамическая продолжительность хранения. Хранилище для объекта выделяется и освобождается по запросу с помощью функций динамического выделения памяти. Подробнее об инициализации объектов с такой продолжительностью хранения см. в разделе new-expression.

[править] Связь

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

Распознаются следующие связи:

[edit] нет связи

На имя можно ссылаться только из той области, в которой оно находится.

Любое из следующих имен, объявленных в области блока, не имеет связи:

  • переменные, явно не объявленные extern (независимо от модификатора static);
  • локальные классы и их функции-члены;
  • другие имена, объявленные в области блока, такие как typedefs, enumerations и enumerators.

Имена, не указанные с помощью external , module (начиная с C++20) или внутренней связи, также не имеют связи, независимо от того, в какой области они объявлены.

[править] внутренняя связь

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

Любое из следующих имен, объявленных в области пространства имен, имеет внутреннюю связь:

  • переменные, шаблоны переменных (начиная с C++14), функции или шаблоны функций, объявленные статическими;
  • энергонезависимые нешаблонные (начиная с C++14) неинлайновые (начиная с C++17) неэкспортируемые (начиная с C++20) константные переменные (включая constexpr), не объявленные extern и ранее не были заявлены как имеющие внешнюю ссылку;
  • члены данных анонимных союзов.

Кроме того, все имена, объявленные в безымянном пространстве имен или пространстве имен внутри безымянного пространства имен, даже явно объявленные extern , имеют внутреннюю связь.

[править] внешняя ссылка

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

Любое из следующих имен, объявленных в области пространства имен, имеет внешнюю связь, если только они не объявлены в безымянном пространстве имен или их объявления не присоединены к именованному модулю и не экспортируются (начиная с C++20):

  • переменные и функции, не перечисленные выше (то есть функции, не объявленные static , неконстантные переменные, не объявленные static , и любые переменные, объявленные extern );
  • перечисления;
  • названия классов, их функции-члены, статические данные-члены (константные или нет), вложенные классы и перечисления, а также функции, впервые представленные в объявлениях друзей внутри тела класса;
  • имена всех шаблонов, не перечисленных выше (то есть не шаблоны функций, объявленные статическими).

Любое из следующих имен, впервые объявленных в области блока, имеет внешнюю ссылку:

  • имена переменных, объявленных extern ;
  • названия функций.
связь модулей

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

Имена, объявленные в области пространства имен, связаны с модулем, если их объявления прикреплены к именованному модулю, не экспортируются и не имеют внутренней связи.

[править] Статические локальные переменные

Переменные, объявленные в области блока со спецификатором static или thread_local (начиная с C++11), имеют статическое или потоковое (начиная с C++11) время хранения, но инициализируются при первом прохождении управления через их объявление (если только их инициализация не нулевую или константную инициализацию, которую можно выполнить перед первым входом в блок). При всех дальнейших вызовах объявление пропускается.

Если инициализация вызывает исключение, переменная не считается инициализированной, и попытка инициализации будет предпринята снова при следующем прохождении управления через объявление.

Если инициализация рекурсивно входит в блок, в котором инициализируется переменная, поведение не определено.

Если несколько потоков пытаются одновременно инициализировать одну и ту же статическую локальную переменную, инициализация происходит ровно один раз (аналогичное поведение можно получить для произвольных функций с помощью std::call_once ).

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

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

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

[edit] Местные сущности блока перевода

Концепция локальных единиц перевода стандартизирована в C++20, дополнительные сведения см. на этой странице.

Объект является локальным для единицы перевода (или локальным для TU для краткости), если

  • у него есть имя с внутренней связью, или
  • у него нет связанного имени, и он вводится в определение локального объекта TU, или
  • это шаблон или специализация шаблона, аргумент шаблона или объявление шаблона которого использует локальный объект TU.

Плохие вещи (как правило, нарушение ODR) могут произойти, если тип нелокального объекта TU зависит от локального объекта TU, или если объявление или руководство по выводу для (начиная с C++17 ) нелокальный объект TU называет локальный объект TU вне его

  • тело функции для невстроенной функции или шаблона функции
  • инициализатор для переменной или шаблона переменной
  • объявления друзей в определении класса
  • использование значения переменной, если переменная может использоваться в константных выражениях

Такое использование запрещено в интерфейсном блоке модуля (вне его частного-модуля-фрагмента, если таковой имеется) или в разделе модуля и не рекомендуется в любом другом контексте.

Объявление, которое появляется в одной единице перевода, не может называть локальный объект TU, объявленный в другой единице перевода, которая не является единицей заголовка. Объявление, созданное для шаблона, появляется в момент создания экземпляра специализации.

[править] Примечания

Имена в области пространства имен верхнего уровня (области файла в C), которые являются константными, а не внешними, имеют внешнюю связь в C, но внутреннюю связь в C++.

Начиная с C++11, auto больше не является спецификатором класса хранения; он используется для обозначения вывода типа.

В C адрес регистровой переменной нельзя взять, но в C++ переменная, объявленная в регистре, семантически неотличима от переменной, объявленной без каких-либо спецификаторов класса хранения.

В C++, в отличие от C, переменные нельзя объявлять в регистре.

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

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

Ключевое слово mutable — это спецификатор класса хранения в грамматике языка C++, хотя оно не влияет на продолжительность хранения или связывание.

< /таблица>

Спецификаторы класса хранилища, за исключением thread_local , не допускаются для явных специализаций и явных экземпляров:

Учитывая, что память разделена на четыре сегмента: данные, куча, стек и код, где находятся глобальные переменные, статические переменные, постоянные типы данных, локальные переменные (определенные и объявленные в функциях), переменные (в основной функции ), указатели и динамически выделяемое пространство (с использованием malloc и calloc) сохраняются в памяти?

Я думаю, что они будут распределены следующим образом:

  • Глобальные переменные -------> данные
  • Статические переменные -------> данные
  • Постоянные типы данных -----> код
  • Локальные переменные (объявленные и определенные в функциях) --------> стек
  • Переменные, объявленные и определенные в основной функции -----> куча
  • Указатели (например, char *arr , int *arr ) -------> куча
  • Динамически выделяемое пространство (с использованием malloc и calloc) --------> стек

Я имею в виду эти переменные только с точки зрения C.

Пожалуйста, поправьте меня, если я ошибаюсь, так как я новичок в C.

Большинство мелких деталей, вероятно, зависят от реализации. (То есть ничто не запрещает компилятору C/среде выполнения, которая выделяет в куче все, включая «фреймы стека».)

9 ответов 9

Вы правильно ответили на некоторые вопросы, но тот, кто написал вопросы, обманул вас по крайней мере в одном вопросе:

  • глобальные переменные -------> данные (правильно)
  • статические переменные -------> данные (правильно)
  • постоянные типы данных -----> код и/или данные. Рассмотрим строковые литералы для ситуации, когда сама константа будет храниться в сегменте данных, а ссылки на нее будут встроены в код
  • локальные переменные (объявлены и определены в функциях) --------> стек (правильно)
  • переменные, объявленные и определенные в основной функции ----->heap, также складываются (учитель пытался вас обмануть)
  • указатели (пример: char *arr , int *arr ) ------->куча данные или стек, в зависимости от контекста. C позволяет объявить глобальный или статический указатель, и в этом случае сам указатель окажется в сегменте данных.
  • динамически выделяемое пространство (с использованием malloc , calloc , realloc ) -------->стек куча

Стоит отметить, что "стек" официально называется "классом автоматического хранения".

Также стоит отметить, что куча официально никак не называется. Выделенная память откуда-то берется, в стандарте нет имени для этого "где-то".

В некоторых системах (а именно в Linux и *BSD) есть также alloca, который работает аналогично malloc, но выделяет стек.

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

@myradio В объявлении int a[10] или int a[b] нет указателя, они объявляют массивы. Эти массивы помещаются в автоматическую область памяти.

Для тех будущих посетителей, которым может быть интересно узнать об этих сегментах памяти, я пишу важные моменты о 5 сегментах памяти в C:

Некоторые советы:

  1. Каждый раз, когда выполняется программа C, для выполнения программы в ОЗУ выделяется некоторая память. Эта память используется для хранения часто исполняемого кода (бинарных данных), программных переменных и т. д. Об этом говорят следующие сегменты памяти:
  2. Обычно существует три типа переменных:
    • Локальные переменные (также называемые автоматическими переменными в C)
    • Глобальные переменные
    • Статические переменные
    • У вас могут быть глобальные статические или локальные статические переменные, но три вышеуказанных типа являются родительскими.

5 сегментов памяти в C:

  • Сегмент кода, также называемый текстовым сегментом, представляет собой область памяти, содержащую часто выполняемый код.
  • Часто сегмент кода доступен только для чтения, чтобы избежать риска переопределения программными ошибками, такими как переполнение буфера и т. д.
  • Сегмент кода не содержит программных переменных, таких как локальные переменные (также называемые автоматическими переменными в C), глобальные переменные и т. д.
  • На основе реализации C сегмент кода может также содержать строковые литералы, доступные только для чтения. Например, когда вы делаете printf("Hello, world"), тогда строка "Hello, world" создается в сегменте кода/текста. Вы можете проверить это с помощью команды size в ОС Linux.

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

2. Неинициализированный сегмент данных

  • Этот сегмент также известен как bss.
  • Это часть памяти, которая содержит:
    1. Неинициализированные глобальные переменные(включая переменные-указатели)
    2. Неинициализированные постоянные глобальные переменные.
    3. Неинициализированные локальные статические переменные.
  • Любая неинициализированная глобальная или статическая локальная переменная будет храниться в неинициализированном сегменте данных.
  • Например: глобальная переменная int globalVar; или статическая локальная переменная static int localStatic; будет храниться в неинициализированном сегменте данных.
  • Если вы объявите глобальную переменную и инициализируете ее как 0 или NULL, тогда она все равно перейдет в неинициализированный сегмент данных или bss.

3. Сегмент инициализированных данных

  • В этом сегменте хранится:
    1. Инициализированные глобальные переменные(включая переменные-указатели)
    2. Инициализированные постоянные глобальные переменные.
    3. Инициализированы локальные статические переменные.
  • Например: глобальная переменная int globalVar = 1; или статическая локальная переменная static int localStatic = 1; будет храниться в инициализированном сегменте данных.
  • Этот сегмент можно разделить на инициализированную область только для чтения и инициализированную область чтения-записи. Инициализированные постоянные глобальные переменные помещаются в инициализированную область только для чтения, а переменные, значения которых могут быть изменены во время выполнения, помещаются в инициализированную область чтения-записи.
  • Размер этого сегмента определяется размером значений в исходном коде программы и не изменяется во время выполнения.
  • Сегмент стека используется для хранения переменных, созданных внутри функций (функция может быть основной или определяемой пользователем функцией), таких переменных, как
    1. Локальные переменные функции (включая переменные-указатели)
    2. Аргументы, переданные функции
    3. Обратный адрес
  • Переменные, хранящиеся в стеке, будут удалены, как только завершится выполнение функции.
  • Этот сегмент предназначен для поддержки динамического выделения памяти. Если программист хочет выделить часть памяти динамически, то в C это делается с помощью методов malloc, calloc или realloc.
  • Например, если int* prt = malloc(sizeof(int) * 2), то восемь байтов будут выделены в куче, а адрес памяти этого места будет возвращен и сохранен в переменной ptr. Переменная ptr будет находиться либо в стеке, либо в сегменте данных, в зависимости от того, как она объявлена/используется.



Относительно "хранится в неинициализированном сегменте данных" (несколько экземпляров): Вы имеете в виду "хранится в неинициализированном сегменте данных"?

@PeterMortensen Я имею в виду и то, и другое. "Любая неинициализированная глобальная или статическая локальная переменная будет храниться в неинициализированном сегменте данных"

Современный GNU binutils ld отделяет .rodata , помещая его в собственный доступный только для чтения сегмент non-exec, отдельный от кода (я тестировал на GNU/Linux). Это означает, что статические константы, такие как строковые литералы, больше не являются возможными кандидатами для гаджетов Spectre/ROP, поскольку они находятся на неисполняемых страницах.

Исправил ваши неправильные предложения

локальные постоянные переменные -----> стек

инициализированная глобальная константная переменная -----> сегмент данных

неинициализированная глобальная константная переменная -----> bss

переменные объявлены и определены в основной функции -----> стек

pointers(ex:char *arr,int *arr) -------> размер этой переменной указателя будет в стеке.

Учтите, что вы выделяете память из n байтов (используя malloc или calloc ) динамически, а затем создаете переменную указателя, чтобы указать на нее. Теперь, когда n байтов памяти находятся в куче, а переменная указателя требует 4 байта (для 64-битной машины 8 байтов), которые будут в стеке для хранения начального указателя n байтов фрагмента памяти.

Примечание. Переменные-указатели могут указывать на память любого сегмента.

динамически выделяемое пространство (с использованием malloc, calloc) --------> куча

Не могли бы вы указать на следующей карте памяти, где находятся стек и куча? Я не уверен, что это правильный вопрос, поскольку стек и память могут быть применимы только во время выполнения. КАРТА ПАМЯТИ: "текстовые данные bss dec шестнадцатеричное имя файла 7280 1688 1040 10008 2718 a.exe"

инициализированная глобальная константная переменная -----> сегмент данных Нет, этот ответ неверен, вопрос был правильным для более старых компоновщиков. Если раздел .rodata не связан с текстовым сегментом (Read + eXec) вместе с кодом, как это делали старые компоновщики, современный GNU ld по умолчанию связывает его с собственным сегментом, который доступен только для чтения и не исполняемый. Если не оптимизировать полностью, ненулевые глобальные константные переменные, конечно, не попадают в R+W раздел .data или не связываются с сегментом данных R+W. Вы правы в том, что в .bss помещаются единицы с нулевым значением.

В популярной архитектуре рабочего стола виртуальная память процесса делится на несколько сегментов:

Текстовый сегмент: содержит исполняемый код. Указатель инструкции принимает значения в этом диапазоне.

Сегмент данных: содержит глобальные переменные (т.е.объекты со статической связью). Подразделяются на данные только для чтения (например, строковые константы) и неинициализированные данные ("BSS").

Сегмент стека: содержит динамическую память для программы, т. е. свободное хранилище («кучу») и локальные кадры стека для всех потоков. Традиционно стек C и куча C росли в сегмент стека с противоположных концов, но я считаю, что от этой практики отказались, поскольку она слишком небезопасна.

Программа на C обычно помещает объекты со статической длительностью хранения в сегмент данных, динамически размещаемые объекты в свободное хранилище и автоматические объекты в стек вызовов потока, в котором они находятся.

На других платформах, таких как старый реальный режим x86 или встроенные устройства, все может быть совершенно иначе.

"Я считаю, что от этой практики отказались, потому что она слишком небезопасна" - и делает невозможным реализацию потоков, так как тогда нужно более одного стека на программу и они не могут быть все в конце :-)< /p>

@SteveJessop: Да, я тоже об этом подумал. Но потоки существуют уже давно — я не знаю, росли ли все стеки потоков также в обратном направлении или они росли бы как куча. так или иначе, в настоящее время все идет в том же направлении, и есть страницы защиты.

Я имею в виду эти переменные только с точки зрения C.

С точки зрения языка C все, что имеет значение, — это протяженность, объем, связь и доступ; именно то, как элементы отображаются в разные сегменты памяти, зависит от конкретной реализации, и это будет варьироваться. Стандарт языка не говорит о сегментах памяти вообще. Большинство современных архитектур действуют в основном одинаково; переменные блочной области и аргументы функций будут выделены из стека, файловая область и статические переменные будут выделены из сегмента данных или кода, динамическая память будет выделена из кучи, некоторые постоянные данные будут храниться в сегментах только для чтения и т. д.


Одна вещь, которую нужно помнить о хранилище, – правило как если бы. Компилятору не требуется размещать переменную в определенном месте — вместо этого он может разместить ее там, где ему заблагорассудится, пока скомпилированная программа ведет себя как если бы она выполнялась на абстрактной машине C в соответствии с правила абстрактной машины C. Это относится ко всем срокам хранения. Например:

  • переменная, к которой нет доступа all, может быть полностью устранена - у нее нет места для хранения. в любом месте. Пример. Посмотрите, как в сгенерированном ассемблерном коде есть 42, но нет признаков 404.
  • переменная с автоматической длительностью хранения, адрес которой не занят, вообще не нуждается в сохранении в памяти. Примером может служить переменная цикла.
  • переменная, которая является константной или фактически константной, не обязательно должна находиться в памяти. Пример — компилятор может доказать, что foo фактически является константой, и встраивает ее использование в код. bar имеет внешнюю связь, и компилятор не может доказать, что он не будет изменен за пределами текущего модуля, поэтому он не встроен.
  • объект, выделенный с помощью malloc, не обязательно должен находиться в памяти, выделенной из кучи! Пример — обратите внимание, что в коде нет вызова malloc, и значение 42 никогда не сохраняется в памяти, оно хранится в регистре!
  • таким образом, объект, который был выделен с помощью malloc, и ссылка потеряна без освобождения объекта со свободной нет необходимости утечки памяти.
  • объект, выделенный malloc, не обязательно должен находиться в куче ниже разрыва программы ( sbrk(0) ) в Unixen.

Нет, они могут быть в стеке или в сегменте данных. Они могут указывать куда угодно.


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

  • Переменные/автоматические переменные ---> раздел стека
  • Динамически выделяемые переменные ---> раздел кучи
  • Инициализированные глобальные переменные -> раздел данных
  • Неинициализированные глобальные переменные -> раздел данных (bss)
  • Статические переменные -> раздел данных
  • Строковые константы -> раздел текста/раздел кода
  • Функции -> раздел текста/раздел кода
  • Код текста -> раздел текста/раздел кода
  • Регистры -> Регистры процессора
  • Ввод командной строки -> раздел среды/командной строки
  • Переменные среды -> раздел среды/командной строки



Минимальные запускаемые примеры Linux с анализом дизассемблирования

Поскольку это детали реализации, не указанные в стандартах, давайте просто посмотрим, что делает компилятор в конкретной реализации.

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

Все они есть в различных версиях Ubuntu/GCC, и результаты, вероятно, довольно стабильны в разных версиях, но если мы обнаружим какие-либо различия, давайте укажем более точные версии.

Локальная переменная внутри функции

Будь то главная или любая другая функция:

  • -O0 : стек
  • -O3 : регистрирует, если они не разливаются, в противном случае складываются

Глобальные переменные и переменные статических функций

  • если инициализировано значением 0 или не инициализировано (и, следовательно, неявно инициализировано значением 0 ): раздел .bss, см. также: Почему требуется сегмент .bss?
  • иначе: раздел .data

char * и char c[]

TODO будут ли также помещаться в стек очень большие строковые литералы? Или .данные? Или компиляция не работает?

Аргументы функции

Затем, как показано в разделе Что означает в gdb?, -O0 сбрасывает все в стек, а -O3 пытается максимально использовать регистры.

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

константа

Я считаю, что это не имеет значения, потому что вы можете привести его к типу.

И наоборот, если компилятор может определить, что некоторые данные никогда не записываются, он теоретически может поместить их в .rodata, даже если они не являются константами.

Указатели

Это переменные (содержащие адреса, то есть числа), как и все остальное :-)

маллок

Вопрос не имеет особого смысла для malloc, так как malloc — это функция, и в:

*i — это переменная, содержащая адрес, поэтому она соответствует описанному выше случаю.

Что касается того, как malloc работает внутри, когда вы вызываете его, ядро ​​Linux помечает определенные адреса как доступные для записи в своих внутренних структурах данных, и когда программа изначально касается их, происходит ошибка, и ядро ​​​​включает таблицы страниц, которые позволяет осуществлять доступ без segfaul: Как работает пейджинг x86?

Обратите внимание, однако, что системный вызов exec делает то же самое, когда вы пытаетесь запустить исполняемый файл: он помечает страницы, которые он хочет загрузить, и записывает туда программу. См. также: Как ядро ​​получает исполняемый файл бинарный файл работает под linux? За исключением того, что у exec есть некоторые дополнительные ограничения на то, куда загружать (например, код нельзя перемещать).

Точный системный вызов, используемый для malloc, — это mmap в современных реализациях 2020, а в прошлом использовался brk: использует ли malloc() brk() или mmap()?

Читайте также:

Этот раздел неполный
Причина: правила повторного объявления имен в одной и той же ТУ