Открыть окно память visual studio
Профилировщики этого типа выявляют операции выделения памяти в приложении и сообщают, какие методы выделяют больше всего памяти, какого типа объекты создаются и другие статистики, касающиеся памяти. Интенсивное выделение памяти в приложениях часто влечет за собой значительные накладные расходы па сборку мусора.
Выделение памяти в среде выполнения CLR является недорогой операцией, но ее освобождение может сопровождаться значительными накладными расходами. По этой причине группы небольших методов, выделяющих большие объемы памяти, могут отнимать совсем немного процессорного времени и быть практически незаметны в отчете профилировщика времени, но при этом вызывать значительные задержки на сборку мусора в случайных точках выполнения приложения. В своей практике мы встречали приложения, где память выделялась без должного внимания, и нам удавалось увеличивать их производительность - иногда в 10 раз - оптимизировав выделение памяти и управление ею.
Для профилирования выделения памяти мы будем использовать два профилировщика - вездесущий профилировщик Visual Studio, поддерживающий режим профилирования выделения памяти, и профилировщик CLR Profiler - самостоятельный и бесплатный инструмент. К сожалению, оба инструмента часто оказывают значительное влияние на производительность приложений, интенсивно использующих динамическую память, потому что для каждой операции выделения памяти профилировщик выполняет последовательность действий по сохранению информации для последующего составления отчетов. Тем не менее, результаты могут оказаться настолько ценными, что даже 100-кратное замедление при профилировании можно потерпеть.
Профилировщик выделения памяти Visual Studio
Профилировщик Visual Studio способен собирать информацию об операциях выделения памяти и жизненном цикле объектов (которые освобождаются сборщиком мусора) в обоих режимах, дискретном и инструментированном. В дискретном режиме профилировщик собирает информацию о выделении памяти в приложении в целом. В инструментированном режиме информация собирается только из инструментированных модулей.
В представлении Functions для каждого метода будет указано количество объектов и количество байтов памяти, выделенных методом (как обычно, включительные и исключительные значения). В представлении Function Details (Сведения о функции) будет представлена информация о вызывающих и вызываемых функциях, а также указаны строки кода с объемами выделенной ими памяти в поле слева:
Но самая интересная информация содержится в представлении Allocation (Выделение), показывающем, какие ветви в стеке вызовов выделили памяти больше всего:
Позже мы узнаем, насколько важно отказаться от использования временных объектов, и обсудим феномен «кризиса среднего возраста» объектов, оказывающего существенное влияние на производительность, который проявляется в способности объектов переживать несколько циклов сборки мусора. Идентифицировать наличие этого явления в приложении можно с помощью представления Object Lifetime (Жизненный цикл объектов), сообщающем, в каком поколении объекты были утилизированы. Это представление поможет увидеть, имеются ли объекты, пережившие слишком много циклов сборки мусора. На рисунке ниже можно видеть, что все объекты строк, созданные приложением (и занимающие более 1 Гбайта памяти!) были утилизированы в нулевом поколении, а это означает, что ни одному из них не удалось прожить дольше одного цикла сборки мусора:
Хотя отчеты о выделении памяти, генерируемые профилировщиком Visual Studio, отличаются богатством информации, в них все же имеются некоторые недостатки. Например, трассировка стека вызовов с целью сгруппировать операции выделения памяти по типам объектов занимает достаточно много времени, если выделение памяти производится во множестве разных мест (что всегда верно для строк и массивов байтов). Профилировщик CLR Profiler поддерживает несколько дополнительных особенностей, делающих его ценной альтернативой профилировщику Visual Studio.
CLR Profiler
Профилировщик CLR Profiler - это отдельный инструмент профилирования, не требующий установки и занимающий менее 1 Мбайта дискового пространства. Как дополнительное преимущество, он распространяется с исходными текстами, которые наверняка заинтересуют тех, кто пожелает заняться созданием собственных инструментов, использующих CLR Profiling API. Он способен подключаться к выполняющимся процессам (если используется версия CLR не ниже 4.0) или запускать выполняемые файлы, и регистрировать все операции выделения памяти и события сборки мусора.
Пользоваться профилировщиком CLR Profiler очень просто - запустите профилировщик, щелкните на кнопке Start Application (Запустить приложение), выберите приложение для профилирования и дождитесь появления отчета - богатство информации для кого-то может оказаться ошеломляющим. Мы рассмотрим здесь некоторые отчеты профилировщика, а полное руководство вы найдете в документе CLRProfiler.doc, входящем в состав загружаемого пакета. Как обычно, для экспериментов с профилировщиком вы можете использовать пример приложения JackCompiler.exe или свое приложение.
На рисунке ниже показан главный отчет, появляющийся после завершения профилируемого приложения. Он содержит основные сведения, касающиеся выделения памяти и сборки мусора. Далее из этого отчета можно пойти в нескольких направлениях. Мы сконцентрируемся на исследовании источников выделения памяти, чтобы понять, в каком месте приложения создается больше всего объектов (этот отчет напоминает представление Allocation профилировщика Visual Studio). Мы могли бы заняться исследованием сборки мусора, чтобы узнать, какие объекты утилизируются. Наконец, можно было бы исследовать содержимое динамической памяти, чтобы получить представление о ее распределении.
Щелчок на кнопке Histogram (Гистограмма) рядом с полем Allocated bytes (Выделено байтов) или Final heap bytes (Конечный объем кучи в байтах) выведет гистограмму по типам объектов, сгруппированных по размерам. Эта гистограммы можно использовать для выявления больших и малых объектов, а также объектов, создаваемых чаще других. На рисунке ниже показана гистограмма для всех объектов, создаваемых нашим примером приложения:
Каждый столбик представляет объекты определенного размера. Легенда справа содержит общее число созданных экземпляров каждого типа и объем выделенной памяти в байтах.
Щелчок на кнопке Allocation Graph (График выделения) откроет отчет о выделении памяти в дереве стека вызовов для всех объектов в приложении. Информация в этом отчете сгруппирована так, чтобы легко можно было перейти от методов, выделивших больше всего памяти, к отдельным типам объектов и посмотреть, какие методы создали больше всего экземпляров этих типов. На рисунке ниже показана малая часть графика выделения памяти, начиная от метода Parser.ParseStatement(), выделившего (включительно) 372 Мбайт памяти и до различных методов, вызываемых им. (Кроме того, в остальных отчетах профилировщика CLR Profiler присутствует пункт контекстного меню Show who's allocated (Показать, для кого выделена память), открывающий отчет с графиком выделения памяти для подмножества объектов приложения.)
Здесь показаны только методы, информация о фактических типах объектов находится на графике правее.
Щелчок на кнопке Objects by Address (Объекты по адресу) отобразит области управляемой динамической памяти в виде слоев; чем ниже слой, тем он старше. Подобно археологу вы можете погружаться в более глубокие слои и выяснять, какие объекты занимают память в вашем приложении. Этот отчет можно также использовать для диагностики фрагментации динамической памяти:
Метки слева - это адреса, метки «gen 0» и «gen 1» - это подразделы динамической памяти.
Наконец, щелчок на кнопке Time Line (График времени) в разделе Garbage Collection Statistics (Статистика сборки мусора) откроет отчет с информацией об отдельных циклах сборки мусора и их влиянии на динамическую память приложения:
Отметки на нижней оси представляют отдельные циклы сборки мусора, а в области графика изображается состояние управляемой динамической памяти. После сборки мусора объем используемой памяти резко уменьшается, а затем постепенно возрастает, до следующего цикла. В данном случае объем используемой памяти (после сборки мусора) остается постоянным, из чего следует, что в этом приложении отсутствуют утечки памяти.
Этот график можно использовать для определения типов утилизируемых объектов, и получения представления о том, как изменяется распределение динамической памяти после сборки мусора. С его помощью можно также выявлять утечки памяти, когда сборщик мусора освобождает недостаточно памяти, из-за того, что приложение продолжает удерживать все увеличивающееся количество объектов.
Графики и гистограммы выделения памяти - весьма полезные инструменты анализа, но иногда важнее бывает выявить ссылки между объектами, а не объемы выделяемой памяти в разных методах. Например, когда в приложении обнаруживаются утечки управляемой памяти, очень полезно пройтись по динамической памяти, чтобы найти категории объектов, занимающие наибольшие объемы памяти и узнать, какие объекты на них ссылаются, мешая сборщику мусора утилизировать их. Пока профилируемое приложение выполняется, щелкните на кнопке Show Heap now (Показать кучу сейчас), чтобы сгенерировать дамп динамической памяти для последующего исследования с целью классификации ссылок между объектами.
На рисунке ниже показан отчет профилировщика сразу с тремя дампами динамической памяти, расположенными друг над другом, показывающий увеличение количества объектов типа byte[], удерживаемых очередью объектов, готовых к завершению (freachable queue), из-за наличия ссылок на них в объектах Employee и Schedule:
Ha рисунке ниже показаны результаты выбора пункта Show New Objects (Показать новые объекты) контекстного меню, чтобы оставить в отчете только объекты, созданные в период времени между сохранением второго и третьего дампов:
На этом графике видно, что источником утечки памяти является цепочка ссылок из очереди объектов, готовых к завершению.
Дампы динамической памяти, созданные профилировщиком CLR Profiler, можно использовать для диагностики утечек памяти в приложениях, но средств визуализации, упрощающих это, в данном профилировщике явно недостаточно. Коммерческие инструменты, которые мы рассмотрим далее, предлагают более богатые возможности, включая автоматические средства обнаружения наиболее типичных источников утечек памяти, разнообразные фильтры и возможности более сложной группировки информации. Поскольку большинство из этих инструментов не сохраняют информацию о каждом объекте, размещенном в динамической памяти, и не сохраняют информацию о выделении памяти в разных методах, они имеют более низкие накладные расходы, что само по себе является большим преимуществом.
Коммерческие профилировщики памяти
В этом разделе мы познакомимся с двумя коммерческими профилировщиками памяти, специализирующимися на визуализации состояния динамической памяти и выявлении источников утечек памяти. Поскольку эти инструменты отличаются большой сложностью, мы исследуем лишь малое подмножество их особенностей и оставим вам возможность ознакомиться с остальными самостоятельно, прочитав соответствующие руководства.
Профилировщик памяти ANTS
Профилировщик ANTS Memory Profiler компании RedGate специализируется на анализе срезов динамической памяти. Ниже подробно описывается процесс использования ANTS Memory Profiler для диагностики утечек памяти. Если у вас есть желание самим повторить описываемые действия, загрузите пробную 14-дневную версию ANTS Memory Profiler, которую можно найти по адресу: ANTS Memory Profiler, и используйте ее для профилирования собственного приложения. Описание и скриншоты, представленные ниже, относятся к версии ANTS Memory Profiler 7.3, которая была последней на момент написания данных строк.
Следуя за экспериментом, описываемым ниже, вы можете использовать пример приложения FileExplorer.exe из папки с исходниками. Это приложение имитирует утечку памяти, выполняя обход дерева каталогов и не освобождая объекты с информацией о непустых каталогах.
Запустите приложение из профилировщика. (Подобно профилировщику CLR Profiler, ANTS поддерживает возможность подключения к выполняющимся процессам, начиная с версии CLR 4.0.)
По завершении инициализации приложения щелкните на кнопке Take Memory Snapshot (Сделать снимок памяти). Этот снимок будет служить основой для последующих исследований.
Накопив утечки памяти, сделайте еще один снимок динамической памяти.
После завершения приложения сравните снимки (базовый снимок с последним или промежуточные друг с другом) чтобы определить, какие типы объектов приводят к увеличению объема занимаемой памяти.
Выберите определенный тип и щелкните на кнопке Instance Categorizer (Классификатор экземпляров), чтобы понять, какие ссылки удерживают в памяти объекты подозреваемого в утечках типа. (На этом этапе исследуются ссылки между типами - экземпляры типа A, ссылающиеся на экземпляры типа B, будут сгруппированы по типу.)
Исследуйте отдельные экземпляры подозреваемых в утечках типов, щелкнув на кнопке Instance List (Список экземпляров). Выберите несколько наиболее представительных экземпляров и щелкните на кнопке Instance Retention Graph (График зависимостей экземпляров), чтобы посмотреть, почему они удерживаются в памяти. (На этом этапе исследуются ссылки между отдельными объектами, и здесь можно выяснить причину, мешающую сборщику мусора утилизировать конкретные объекты.)
Вернитесь в исходный код приложения и измените его так, чтобы объекты, вызывающие утечку, своевременно уничтожали ссылки на проблематичные цепочки.
К концу процесса анализа у вас должно сложиться четкое представление, почему самые тяжеловесные объекты в вашем приложении не утилизируются сборщиком мусора. Существует множество причин, вызывающих утечки памяти, и их выявление проблемных объектов из миллионов имеющихся - это целое искусство.
На рисунке ниже показан пример сравнения двух снимков динамической памяти. Основные утечки памяти (в байтах) связаны с объектами string.
Детальный осмотр типа string после щелчка на кнопке Instance Categorizer (Классификатор экземпляров) наводит на мысль, что некоторое событие создает экземпляры FileInformation в памяти, которые в свою очередь хранят ссылки па объекты byte[]:
Как видно из рисунка, строки удерживаются в памяти массивами строк, которые сами удерживаются экземплярами типа FileInformation, которые в свою очередь удерживаются событием (через делегаты System.EventHandler).
Более детальное исследование конкретных экземпляров в представлении, выводимом после щелчка на кнопке Instance Retention Graph (График зависимостей экземпляров) в результате указывает, что источником утечек является статическое событие FileInformation.FileInformationNeedsRefresh:
В пятом столбце списка указано число хранящихся в памяти экземпляров, а в седьмом - количество байтов, занимаемых ими. Основной объем памяти занимают объекты string, информация о которых скрыта здесь за всплывающей подсказкой.
Во время отладки в окне Память отображается используемая приложением область памяти.
В окнах отладчика, таких как Контрольные, Видимые, Локальные, и в диалоговом окне Быстрая проверка отображаются переменные, которые хранятся в определенных областях памяти. Окно Память показывает более полную картину. Такое представление памяти может быть полезно при анализе больших частей данных (например, буферов или больших строк), которые плохо отображаются в других окнах.
В окне Память могут отображаться не только данные. В нем может отображаться все, что находится в области памяти, включая данные, код или случайные биты мусора из свободного участка памяти.
Окно Память недоступно для отладки скриптов и SQL. Эти языки не распознают концепцию памяти.
Открытие окна "Память"
Как и другие окна отладчика, окна Память доступны только во время сеанса отладки.
Чтобы включить окна Память, необходимо выбрать параметр Включить отладку на уровне адреса в разделе Сервис > Параметры (или Отладка > Параметры) > Отладка > Общие.
Открытие окна "Память"
Убедитесь, что в разделе Сервис > Параметры (или Отладка > Параметры) > Отладка > Общие выбран параметр Включить отладку на уровне адреса.
Запустите отладку, щелкнув зеленую стрелку, нажав клавишу F5 или выбрав команду Отладка > Начать отладку.
В разделе Отладка > Windows > Память выберите Память 1, Память 2, Память 3 или Память 4. (Некоторые выпуски Visual Studio предлагают только одно окно Память.)
Перемещение по окну "Память"
Адресное пространство компьютера велико, и вы можете легко потерять нужное место при прокрутке окна Память.
Старшие адреса памяти отображаются в нижней части экрана. Для просмотра старших адресов следует прокрутить экран вниз. Для просмотра младших адресов следует прокрутить экран вверх.
Чтобы принудительно выполнить немедленное повторное вычисление выражения в поле Адрес, выберите значок Автоматический пересчет.
По умолчанию окно Память обрабатывает выражения Адрес как динамические выражения, которые вычисляются повторно по мере выполнения приложения. Динамические выражения могут быть удобны, например, для просмотра участка памяти, на который ссылается переменная-указатель.
Использование перетаскивания для перемещения к области памяти:
Выберите в любом окне отладчика адрес памяти или переменную-указатель, содержащую адрес памяти.
Перетащите адрес или указатель в окно Память. Этот адрес появляется в поле Адрес, а окно Память изменяется, чтобы отобразить этот адрес в верхней части.
Перемещение к участку памяти путем его ввода в поле "Адрес":
- Введите или вставьте адрес или выражение в поле Адрес и нажмите клавишу ВВОД или выберите значение в раскрывающемся списке в поле Адрес. Окно Память изменяется, чтобы отобразить этот адрес в верхней части.
Настройка окна "Память"
По умолчанию содержимое памяти отображается в виде шестнадцатеричных однобайтовых целых чисел, а ширина окна определяет количество отображаемых столбцов. Можно настроить способ отображения содержимого памяти в окне Память.
Изменение формата содержимого памяти:
- Щелкните правой кнопкой мыши в окне Память и выберите нужные форматы в контекстном меню.
Изменение числа столбцов в окне "Память":
- Щелкните стрелку раскрывающегося списка рядом с полем Столбцы и выберите число отображаемых столбцов или выберите Авто для автоматической подгонки по ширине окна.
Если требуется, чтобы содержимое окна Память не изменялось при выполнении приложения, можно отключить вычисление динамических выражений.
Включение или выключение вычисления в режиме реального времени:
Щелкните правой кнопкой мыши в окне Память и выберите в контекстном меню пункт Автоматический пересчет.
Вычисление динамических выражений представлено переключателем и по умолчанию включено, поэтому при выборе Автоматический пересчет оно отключается. Чтобы снова включить его, выберите Автоматический пересчет.
Можно скрывать или отображать панель инструментов в верхней части окна Память. Когда панель инструментов скрыта, поле Адрес и другие инструменты недоступны.
Переключение отображения панели инструментов:
- Щелкните правой кнопкой мыши в окне Память и выберите в контекстном меню пункт Показать панель инструментов. Панель инструментов появится или исчезнет, в зависимости от своего предыдущего состояния.
Отслеживание указателя в памяти
В приложениях машинного кода можно использовать имена регистров в качестве динамических выражений. Например, можно использовать указатель стека для отслеживания стека.
Отслеживание указателя в памяти:
В поле Адрес окна Память введите выражение указателя, которое находится в текущей области. В зависимости от языка может возникнуть необходимость в ее разыменовании.
Нажмите клавишу ВВОД.
При использовании команды отладки, такой как Шаг, адрес памяти, отображаемый в поле Адрес и в верхней части окна Память, автоматически изменяется при изменении указателя.
Долгие годы С++ программисты, пишущие под Linux язвительно пеняли разработчикам на С++ под Windows отсутствием в Visual Studio нормального профилировщика памяти. Вот в Линуксе, дескать, есть Valgrind, который решает все проблемы, а в студии что: расставляй какие-то макросы, анализируй какие-то логи — мрак. Клевета! Хотя и правда. Вернее, это было правдой до выхода Visual Studio 2015, в которой наконец-то (ура 3 раза!) присутствует нормальный профилировщик памяти, позволяющий ловить утечки памяти с закрытыми глазами, одной левой и даже не просыпаясь!
В этой статье мы посмотрим, что он умеет и как им пользоваться.
Запускаем Visual Studio 2015, создаём новый консольный проект на С++ и пишем в него следующий код:
Теперь запускаем приложение под отладчиком (F5) и видим появившуюся в Visual Studio панель Diagnostic Tool (см. скриншот выше).
Она показывает загрузку процессора и памяти, но нам интересно не это. Самое ценное в этой панели — нижняя часть, позволяющая нам создавать снимки памяти приложения в любой момент времени. По-умолчанию эта функциональной отключена (поскольку затормаживает работу приложения), чтобы её включить нужно нажать кнопку «Enable snapshots» и перезапустить приложение под отладчиком.
Теперь нам становится доступной кнопка «Take Snapshot», давайте её нажмём.
И у нас появился первый снимок памяти! Мы можем кликнуть по нему дважды и посмотреть, что там внутри:
Мы видим список всех выделений памяти, которые произошли в нашем процессе, типы созданных переменных, их количество и размер в байтах. Приложение наше простое, как двери, но всё же… Что это за массив char[] размером в 100 байт? Как узнать, где он создаётся? Просто кликаем по нему дважды — попадаем в список экземпляров объектов этого типа. У нас он всего один. Внизу окна мы видим стек вызовов, по ходу выполнения которого был аллоцирован данный блок памяти. Смотрим кто на вершине этого стека:
Итак, это функция main(), строка №9. Двойной клик переведёт нас прямо к коду.
О боже, как же так! Оказывается, я только собирался написать тот простой код, который привёл сверху, а по ходу дела создал в цикле массив на 100 байт, который нигде не удаляется и приводит к утечке памяти. Даже и не знаю как бы я её нашел, если бы не новый профилировщик Visual Studio!
«Ладно, хватит прикалываться» — скажет практично настроенный читатель — «Нашел он выделение одного массива в программе из 7 строк, где никакой другой памяти не выделяется. У меня вот в проекте 150 тыщ классов и кода как текста в „Войне и мире“, ты попробуй тут найди где там что утекает!».
А давайте попробуем. Для реализма создадим новый MFC-проект, который тянет за собой (сюрприз!) — MFC. Проект создаём стандартным визардом, ничего не меняя. И вот у нас пустой проект из 55 файлов — да здравствует «минималистичность» MFC. Хорошо хоть билдится.
Найдём метод CMFCApplication1App::OnAppAbout() и допишем в него уже знакомую нам утечку памяти:
Теперь запустим это приложение под профилировщиком памяти. Как вы догадываетесь, уже по ходу запуска MFC навыделяет себе памяти. Сразу после запуска создадим первый снимок памяти, а дальше нажмём 10 раз кнопку «About». Каждый раз будет показан модальный диалог (что приведёт к некоторому количеству операций выделения и освобождения памяти) и, как вы догадались, каждый раз будет происходить утечка 100 байт памяти. В конце создадим ещё один снимок памяти — теперь у нас их два.
Первое, что мы видим, это разницу в количестве выделенной памяти — во втором снимке на 58 выделений больше, что в сумме составляет 15.71 КB. В основном это память выделенная MFC для своих внутренних нужд (прямо как в вашем проекте со 150 тысячами классов, да?), которая потом, наверное, будет MFC освобождена. Но нас интересует не она, а утечки памяти в нашем коде. Давайте откроем второй снимок памяти:
В принципе, уже отсюда можно делать кое-какие выводы: у нас есть 10 указателей на char, по 100 байт каждый — вполне вероятно что 10 выделений памяти связаны с 10-ю кликами по кнопке, а значит можно искать в коде число «100» ну или перейти по стеку вызовов в место выделения памяти для этого массива. Но ладно, усложним себе задачу — представим, что у нас здесь не 7 строк с указателями на выделенную память, а 700 или 7000. Среди них могут быть и блоки большего размера, и другие блоки, существующие в количестве 10 экземпляров. Как же нам отследить только то, что было создано между двумя снимками памяти? Элементарно — для этого есть комбик «Compare to» в верхней части окна. Просто выбираем там снимок №1 и видим только разницу между моментом перед первым кликом по кнопке и моментом после 10-го клика. Теперь табличка выглядит значительно чище, тут уже и слепой заметит, на что следует обратить внимание.
Плюс у нас есть сортировка по столбцам и поиск по типам данных.
В общем, инструмент у Microsoft получился очень хороший, прямо редкий случай, когда и всё необходимое на месте, и ничего лишнего нет.
Я использовал режим отладки в Visual Studio раньше, но мне никогда не приходилось использовать окно памяти. Если бы у меня было простое приложение, которое вычисляет a = b + c и делает b = 8 и c = -2, как я могу найти адреса a, b и c в окне памяти и их значения без использования часов?
Когда я попытался, я увидел тонны "тарабарщины", о которых я не могу понять. Вот скриншот:
Если бы я хотел сделать то же самое, но в среде Linux, как я мог добиться этого?
Одним из способов поиска адреса переменной в Visual Studio является использование окна QuickWatch (в меню отладки, если вы не знаете горячую клавишу, Ctrl + Alt + Q ). Если вы наберете &a , он отобразит адрес переменной a . Затем вы можете ввести этот адрес в окне памяти. Или вы можете просто ввести &a в окне памяти.
Но чтобы увидеть все переменные с окном памяти, они должны быть в нескольких байтах друг от друга, так как он показывает непрерывную память. Для локальных переменных в стеке это обычно не будет проблемой. Для целочисленных переменных вы можете более легко просмотреть их в читаемом формате, щелкнув правой кнопкой мыши по окну памяти и изменив макет (например, выберите 4-байтные целые числа с подписанным дисплеем).
Сказав все это, похоже, было бы гораздо проще использовать окно просмотра, так как все уже хорошо маркировано и легко определить, какое значение связано с какой переменной.
Я видел тонны "тарабарщины": небольшой пример может помочь (особенно для следующих читателей:)
Скопируйте/вставьте следующий код и отлаживайте его:
Запустите отладчик Visual Studio, добавьте часы для всех переменных (щелкните правой кнопкой мыши каждую переменную и нажмите "Добавить Watch" ). Теперь, если он не открыт, откройте окно просмотра (меню Debug → Window → * Watch) и перетащите переменную adresse_int_variable из окна просмотра в окно памяти. Вы получите следующее:
Вы увидите, что значение 41 появляется по этому адресу. В шестнадцатеричном формате 0x41 равно 65. Таким образом, вы видите, что адрес переменной int_variable содержит в действительности 65. (Заметим, что на самом деле память содержит биты: 01000001, но она представлена шестнадцатеричной для удобства чтения.)
Введите &int_variable2 в окне памяти, вы получите:
int_variable2 содержит значение 10000 , а в шестнадцатеричном - 0x2710 . Теперь найдите значения, хранящиеся для переменных char_variable_1 и char_variable_2 : вы видите 0x41 и 0x42 . Вот как A и B закодированы в ASCII Table. Обратите внимание, что в памяти int_variable и char_variable_1 совпадают.
Наконец, введите &mystruct в окне памяти, и вы увидите:
Это соответствует памяти переменной mystruct , которая содержит четыре переменные (a int и три char s). Вы видите переменную age ( 10000 = 0x2710 ) и три следующих символа: A , B и 65 , которые сохраняются как 0x41 , 0x42 , 0x41 (справа налево), Обратите внимание, что в правой части окна вы можете видеть ABA как строковое представление памяти (если не щелкните правой кнопкой мыши по окну и щелкните ANSI).
Читайте также: