8

Скажем, у меня есть массив символов C char buf[15] . Скажем, переменная int set_me = 0 свои данные в ячейке памяти сразу после char buf[15] . Если бы я переполнил buf строкой "aaabbbcccdddeee\xef\xbe\xad\xde" , изменился бы тип данных set_me с целого на массив символов?

2 ответа2

33

Нет.

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

Эти высокоуровневые типы данных не существуют как таковые в скомпилированном (нативном) коде. Они могут влиять на то, какие инструкции генерирует компилятор, но самим инструкциям все равно, представляют ли данные символ или число.


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

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

Так , например, байты 0x41 могут быть истолкованы как UTF-8 кодировкой символов A Это также можно интерпретировать как однобайтовое целое число 65 . Его также можно интерпретировать как один байт в многобайтовом целом числе или числе с плавающей запятой или один байт в многобайтовой кодировке символов. Это может быть 0b1000001 . Все из одного байта в одной и той же ячейке памяти. В языке C вы можете увидеть этот эффект, приведя к этим различным типам.

Когда у вас "переполнение буфера", вы делаете что-то за пределами того, что может ожидать ваш компилятор или язык. Но что касается аппаратного обеспечения 1, вы записываете байты (одиночные или множественные) в область памяти. В ячейке памяти нет "типа". Фактически, аппаратное обеспечение даже не знает, что какой-то конкретный набор байтов создает массив или буфер в вашем коде.

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


Чтобы использовать ваш пример, предположим, что ваше int является 4-байтовым (32-битным) целым числом со знаком:

+-------------+--------------------------------------------+-----------+
| Source code |                  char[15]                  |    int    |
+-------------+--------------------------------------------------------+
| Memory      |61|61|61|62|62|62|63|63|63|64|64|64|65|65|65|EF|BE|AD|DE|
+-------------+--------------------------------------------------------+

Вы можете видеть, что область памяти int теперь содержит 0xEFBEADDE , предполагая систему с прямым порядком байтов 2. Это подписанный 32-битный int -272716322 . Теперь, если вы интерпретируете ту же память, что и unsigned int (uint), вместо этого будет 4022250974 . Для точно таких же данных в памяти значение полностью зависит от того, как вы их просматриваете.


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

2 x86 на самом деле является прямым порядком байтов, что означает, что вы интерпретируете байты, составляющие большее значение в обратном направлении. Так что на x86 вы бы вместо того, чтобы иметь 0xDEADBEEF давая подписал -559038737 или без знака 3735928559

2

С точки зрения C ответом будет «Кто знает? Это неопределенное поведение ".

Типы - это концепция C, а не аппаратная часть. Но правила C не применяются, если ваша программа имеет неопределенное поведение, то есть буквальное значение Undefined Behavior в стандарте C. И переполнение буфера является одной из форм этого.

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

Всё ещё ищете ответ? Посмотрите другие вопросы с метками .