Чтобы ответить на вопрос, который вы опубликовали в нескольких комментариях (которые, я думаю, вы должны отредактировать в своем сообщении):
Что я не понимаю, так это то, как компьютер знает, когда он читает значение переменной и адрес, такой как 10001, если это int или char. Представьте, что я нажимаю на программу anyprog.exe. Сразу код начинает выполняться. Включает ли этот exe-файл информацию о том, хранятся ли переменные как в или char?
Итак, давайте добавим к нему некоторый код. Допустим, вы пишете:
int x = 4;
И давайте предположим, что он хранится в оперативной памяти:
0x00010004: 0x00000004
Первая часть - это адрес, вторая часть - значение. Когда ваша программа (которая выполняется как машинный код) запускается, все, что она видит, 0x00010004
это значение 0x000000004
. Он не «знает» тип этих данных и не знает, как они «должны» использоваться.
Итак, как ваша программа определяет, что нужно делать? Рассмотрим этот код:
int x = 4;
x = x + 5;
У нас есть чтение и запись здесь. Когда ваша программа читает x
из памяти, она находит 0x00000004
там. И ваша программа знает, чтобы добавить 0x00000005
к нему. И причина, по которой ваша программа «знает», что это допустимая операция, заключается в том, что компилятор гарантирует, что операция допустима через безопасность типов. Ваш компилятор уже проверил, что вы можете добавить 4
и 5
вместе. Поэтому, когда ваш двоичный код запускается (exe), он не должен выполнять эту проверку. Он просто выполняет каждый шаг вслепую, предполагая, что все в порядке (плохие вещи случаются, когда они на самом деле, а не в порядке).
Другой способ думать об этом, как это. Я даю вам эту информацию:
0x00000004: 0x12345678
Тот же формат, что и раньше - адрес слева, значение справа. Какой тип это значение? На данный момент вы знаете столько же информации об этом значении, сколько ваш компьютер знает при выполнении кода. Если бы я сказал вам добавить 12743 к этому значению, вы могли бы это сделать. Вы понятия не имеете, каковы будут последствия этой операции для всей системы, но вы действительно хорошо умеете добавлять два числа, чтобы вы могли это сделать. Это делает значение int
? Не обязательно - все, что вы видите, это два 32-битных значения и оператор сложения.
Возможно, некоторая путаница заключается в том, чтобы вернуть данные обратно. Если мы имеем:
char A = 'a';
Как компьютер знает, чтобы отобразить a
в консоли? Ну, есть много шагов к этому. Во-первых, нужно перейти к A
расположению в памяти и прочитать его:
0x00000004: 0x00000061
Шестнадцатеричное значение для a
ASCII равно 0x61, поэтому приведенное выше может быть чем-то, что вы увидите в памяти. Так что теперь наш машинный код знает целочисленное значение. Откуда он знает, как превратить целочисленное значение в символ для его отображения? Проще говоря, компилятор позаботился о том, чтобы выполнить все необходимые шаги для этого перехода. Но сам ваш компьютер (или программа / exe) понятия не имеет, что это за тип данных. Это 32-битное значение может быть что угодно - int
, char
, половина double
, указатель, часть массива, часть string
, часть инструкции и т.д.
Вот краткое взаимодействие вашей программы (exe) с компьютером / операционной системой.
Программа: Я хочу начать. Мне нужно 20 МБ памяти.
Операционная система: находит 20 МБ свободной памяти, которые не используются, и передает их
(Важным примечанием является то, что это может вернуть любые 20 МБ свободной памяти, они даже не должны быть смежными. На данный момент программа может работать в той памяти, которая у нее есть, не обращаясь к ОС)
Программа: Я предполагаю, что первое место в памяти - это 32-разрядная целочисленная переменная x
.
(Компилятор гарантирует, что доступ к другим переменным никогда не коснется этого места в памяти. В системе нет ничего, что говорит, что первый байт является переменной x
, или эта переменная x
является целым числом. Аналогия: у вас есть сумка. Вы говорите людям, что вы будете класть в эту сумку только шарики желтого цвета. Когда кто-то потом вытаскивает что-то из сумки, то будет шокирующим, что он вытащит что-то голубое или кубик - что-то пошло не так. То же самое касается компьютеров: ваш Теперь программа предполагает, что первая область памяти - переменная x, и что это целое число. Если что-то еще записано поверх этого байта памяти или предполагается, что это что-то другое - произошло что-то ужасное. Компилятор гарантирует, что такие вещи не не бывает)
Программа: сейчас я напишу 2
первые четыре байта, где, как я предполагаю, x
находится.
Программа: я хочу добавить 5 к x
.
Читает значение X во временный регистр
Добавляет 5 во временный регистр
Сохраняет значение временного регистра обратно в первый байт, который по-прежнему считается x
.
Программа: я предполагаю, что следующим доступным байтом является переменная char y
.
Программа: я напишу a
в переменную y
.
Библиотека используется, чтобы найти значение байта для a
Байт записывается по адресу, который предполагается программой y
.
Программа: я хочу отобразить содержимое y
Читает значение во втором месте памяти
Использует библиотеку для преобразования из байта в символ
Использование графических библиотек для изменения экрана консоли (установка пикселей с черного на белый, прокрутка одной строки и т. Д.)
(И это продолжается отсюда)
Вероятно, вы зациклились на том, что происходит, когда первое место в памяти больше не существует x
? или второго уже нет y
? Что происходит, когда кто-то читает x
как char
или y
как указатель? Короче, плохие вещи случаются. Некоторые из этих вещей имеют четко определенное поведение, а некоторые - неопределенное поведение. Неопределенное поведение - это как раз то, что может произойти все, от чего бы то ни было, до сбоя программы или операционной системы. Даже четко определенное поведение может быть вредоносным. Если я могу изменить x
указатель на свою программу и заставить вашу программу использовать его в качестве указателя, тогда я смогу заставить вашу программу начать выполнение моей программы - это именно то, что делают хакеры. Компилятор поможет убедиться, что мы не используем его int x
какstring
и тому подобное. Сам машинный код не знает типов, и он будет делать только то, что в инструкциях сказано. Во время выполнения также обнаруживается большое количество информации: какие байты памяти разрешено использовать программе? Начинается x
с первого байта или 12-го?
Но вы можете представить, как ужасно было бы на самом деле писать программы, подобные этой (и вы можете, на языке ассемблера). Вы начинаете с «объявления» своих переменных - вы говорите себе, что байт 1 x
, а байт 2 y
, и когда вы пишете каждую строку кода, загружая и сохраняя регистры, вы (как человек) должны помнить, какая из них x
и какая один y
, потому что система не имеет ни малейшего представления. И вы (как человек) должны помнить, какие типы x
и y
есть, потому что опять же - система понятия не имеет.