Скажем , у меня есть массив C символов char buf[15]
. Скажем, переменная int set_me = 0
хранит свои данные непосредственно в ячейке памяти char buf[15]
. Если бы я переполнился buf
строкой "aaabbbcccdddeee\xef\xbe\xad\xde"
, set_me
изменился бы тип данных с целого на массив символов?
8
Ответы:
Нет.
«Тип данных» переменной имеет значение только в исходном коде (и даже тогда только в некоторых языках). Он говорит компилятору, как обращаться с переменной.
Эти высокоуровневые типы данных не существуют как таковые в скомпилированном (нативном) коде. Они могут влиять на то, какие инструкции генерирует компилятор, но самим инструкциям все равно, представляют ли данные символ или число.
Переменные не существуют в аппаратном обеспечении. В оборудовании у вас есть области памяти и инструкции, которые работают с ними.
Переменная может рассматриваться как представление данных в ячейке памяти - если вы косите и смотрите на одну и ту же память немного по-разному (другая переменная с другим типом ссылается на одну и ту же ячейку), то одно и то же двоичное значение может иметь другое значение ,
Например, байт 0x41 можно интерпретировать как символ в кодировке UTF-8
A
. Это также можно интерпретировать как однобайтовое целое число65
. Его также можно интерпретировать как один байт в многобайтовом целом числе или числе с плавающей запятой или один байт в многобайтовой кодировке символов. Это может быть битсет0b1000001
. Все из одного байта в одной и той же ячейке памяти. В языке C вы можете увидеть этот эффект, приведя к этим различным типам.Когда у вас «переполнение буфера», вы делаете что-то за пределами того, что может ожидать ваш компилятор или язык. Но, что касается оборудования 1 , вы записываете байты (одиночные или множественные) в область памяти. В ячейке памяти нет «типа». Фактически, оборудование даже не знает, что какой-то конкретный набор байтов создает массив или буфер в вашем коде.
Где бы вы в следующий раз не обращались к этой ячейке памяти в вашем коде, инструкции будут выполняться в соответствии с первоначальным определением. например, если они ожидали число там, они будут действовать на любые байты данных, как если бы они были числом.
Чтобы использовать ваш пример, предположим, что у вас
int
4-байтовое (32-битное) целое число со знаком:Вы можете видеть, что место в
int
памяти теперь содержит0xEFBEADDE
, предполагая систему с прямым порядком байтов 2 . Это подписанный 32-битный int-272716322
. Теперь, если вы интерпретируете ту же память, что и unsigned int (uint
), она будет4022250974
вместо этого. Для точно таких же данных в памяти значение полностью зависит от того, как вы их просматриваете.1 Существуют некоторые механизмы, которые препятствуют записи в защищенные области памяти и могут привести к сбою программы, если вы попытаетесь это сделать.
2 x86 на самом деле является прямым порядком байтов, что означает, что вы интерпретируете байты, составляющие большее значение в обратном направлении. Так что на x86 вы бы вместо этого
0xDEADBEEF
, давали подписанные-559038737
или неподписанные3735928559
.источник
0xdeadbeef
, на архитектуре x86, будет занимать меньше места в памяти, чем ее десятичный аналог3735928559
,?0xDEADBEEF
хранится в памяти как0x30 0x78 0x44 0x45 0x41 0x44 0x42 0x45 0x45 0x46
.0xEFBEADDE
. Возможно, немного перефразирую. В противном случае это превосходный ответ - мне особенно нравится аналогия «взгляда» и идея «щурки» :)С точки зрения C ответом будет «Кто знает? Это неопределенное поведение».
Типы - это концепция C, а не аппаратная часть. Но правила C не применяются, если ваша программа имеет неопределенное поведение, то есть буквальное значение Undefined Behavior в стандарте C. И переполнение буфера является одной из форм этого.
Первоначально я написал «правила C больше не применяются», но на самом деле неопределенное поведение имеет обратную силу. Правила C не применяются к программе, которая будет иметь неопределенное поведение в будущем.
источник