Сколько памяти может быть адресовано 8-битным указателем, если минимальный размер объекта составляет 2 байта

Обновлено: 22.11.2024

Что мы подразумеваем под выравниванием данных, структурной упаковкой и заполнением?
Предсказать вывод следующей программы.

Прежде чем двигаться дальше, запишите свой ответ на бумаге и читайте дальше. Если вы настаиваете на объяснении, вы можете не понять какой-либо пробел в вашей аналогии. Выравнивание данных:
Каждый тип данных в C/C++ требует выравнивания (фактически это определяется архитектурой процессора, а не языком). Процессор будет иметь длину слова обработки, равную размеру шины данных. На 32-битной машине размер слова обработки будет 4 байта.

Исторически память имеет побайтовую адресацию и располагается последовательно. Если память организована как один банк шириной в один байт, процессору необходимо выполнить 4 цикла чтения памяти, чтобы получить целое число. Более экономично читать все 4 байта целого числа за один цикл памяти. Чтобы воспользоваться этим преимуществом, память будет организована в виде группы из 4 банков, как показано на рисунке выше.
Адресация памяти по-прежнему будет последовательной. Если банк 0 занимает адрес X, банк 1, банк 2 и банк 3 будут иметь адреса (X + 1), (X + 2) и (X + 3). Если по адресу X выделено целое число из 4 байтов (X кратно 4), процессору требуется только один цикл памяти для чтения всего целого числа.
Где как, если целое число выделено по адресу, отличному от кратного 4, он охватывает два ряда банков, как показано на рисунке ниже. Такое целое число требует двух циклов чтения памяти для выборки данных.

выравнивание данных переменной связано с тем, как данные хранятся в этих банках. Например, естественное выравнивание int на 32-битной машине составляет 4 байта. Когда тип данных выровнен естественным образом, ЦП извлекает его за минимальное количество циклов чтения.
Точно так же естественное выравнивание short int составляет 2 байта. Это означает, что short int может храниться в паре банк 0 — банк 1 или банк 2 — банк 3. double требует 8 байтов и занимает две строки в банках памяти. Любое несовпадение double приведет к более чем двум циклам чтения для выборки данных double.
Обратите внимание, что переменная double будет размещена на 8-байтовой границе на 32-битной машине. и требует двух циклов чтения памяти. На 64-битной машине, в зависимости от количества банков, двойная переменная будет размещена на границе 8 байт и требует только одного цикла чтения памяти.
Заполнение структуры:
В C/C++ в качестве данных используются структуры. пакет. Он не предоставляет каких-либо функций инкапсуляции или сокрытия данных (случай C++ является исключением из-за его семантического сходства с классами).
Из-за требований к выравниванию различных типов данных каждый член структуры должен быть выровнен естественным образом. Члены структуры располагаются в последовательно возрастающем порядке. Давайте проанализируем каждую структуру, объявленную в приведенной выше программе.
Вывод вышеуказанной программы:
Для удобства предположим, что каждая переменная типа структуры размещена на 4-байтовой границе (скажем, 0x0000), т.е. базовый адрес структуры кратно 4 (не обязательно всегда, см. объяснение structc_t).
структура A
Первый элемент structa_t — это char, выровненный по одному байту, за которым следует short int. короткий int выравнивается по 2 байта. Если элемент short int размещен сразу после элемента char, он начнется с нечетной границы адреса. Компилятор вставит байт заполнения после char, чтобы короткий int имел адрес, кратный 2 (т. е. выровненный по 2 байтам). Общий размер structa_t будет равен sizeof(char) + 1 (padding) + sizeof(short), 1 + 1 + 2 = 4 байта.
структура B
Первый элемент structb_t — это тип short int, за которым следует char. Поскольку char может находиться на любой границе байта, между short int и char не требуется заполнения, в общей сложности они занимают 3 байта. Следующий член — int. Если целое выделяется сразу, оно начнется с нечетной границы байта. Нам нужно 1 байт заполнения после члена char, чтобы адрес следующего члена int был выровнен по 4 байтам. Всего для structb_t требуется 2 + 1 + 1 (заполнение) + 4 = 8 байт.
структура C. Каждая структура также будет иметь требования к выравниванию.
Применяя тот же анализ, structc_t требует sizeof(char) + 7-байтовое заполнение + sizeof(double) + sizeof(int) = 1 + 7 + 8 + 4 = 20 байт. Однако sizeof(structc_t) будет 24 байта. Это связано с тем, что, наряду с элементами структуры, переменные типа структуры также будут иметь естественное выравнивание. Давайте разберемся на примере. Скажем, мы объявили массив structc_t, как показано ниже

Для упрощения расчетов предположим, что базовый адрес structc_array равен 0x0000. Если structc_t занимает 20 (0x14) байтов, как мы рассчитали, второй элемент массива structc_t (с индексом 1) будет иметь адрес 0x0000 + 0x0014 = 0x0014. Это начальный адрес элемента индекса 1 массива.Двойной член этой structc_t будет размещен по адресу 0x0014 + 0x1 + 0x7 = 0x001C (десятичное число 28), что не кратно 8 и противоречит требованиям выравнивания double. Как мы упоминали выше, требование выравнивания для double составляет 8 байт.
Чтобы избежать такого смещения, компилятор введет требование выравнивания для каждой структуры. Он будет как у самого крупного члена конструкции. В нашем случае выравнивание structa_t равно 2, structb_t равно 4 и structc_t равно 8. Если нам нужны вложенные структуры, размер наибольшей внутренней структуры будет выравниванием непосредственно большей структуры.
В structc_t приведенной выше программы будет добавлено 4 байта после элемента int, чтобы сделать размер структуры кратным ее выравниванию. Таким образом, sizeof (structc_t) составляет 24 байта. Это гарантирует правильное выравнивание даже в массивах. Вы можете перепроверить.
структура D – Как уменьшить отступы?
К настоящему моменту может быть ясно, что отступы неизбежны. Есть способ минимизировать отступы. Программист должен объявить элементы структуры в порядке возрастания/уменьшения их размера. Примером является structd_t, приведенный в нашем коде, размер которого составляет 16 байтов вместо 24 байтов structc_t.
Что такое упаковка структуры?
Иногда необходимо избегать заполнения байтов среди членов структуры. Например, чтение содержимого заголовка файла ELF или заголовка файла BMP или JPEG. Нам нужно определить структуру, аналогичную структуре заголовка, и отобразить ее. Однако следует проявлять осторожность при доступе к таким членам. Обычно чтение байт за байтом позволяет избежать исключений с неверным выравниванием. Это повлияет на производительность.
Большинство компиляторов предоставляют нестандартные расширения для отключения заполнения по умолчанию, такие как прагмы или переключатели командной строки. Обратитесь к документации соответствующего компилятора для получения более подробной информации.
Неполадки с указателями:
Возможна потенциальная ошибка при работе с арифметическими операциями с указателями. Например, разыменование универсального указателя (void *), как показано ниже, может вызвать исключение неправильного выравнивания,

Так что да, это немного унизительный вопрос, учитывая, что я изучаю компьютерные науки, но, тем не менее,

насколько велики адреса памяти? Я знаю, что это будет зависеть от машины, если это 64-битная или 32-битная машина и т. д. и т. д., но в С++ кажется, что каждый адрес имеет длину 8 бит или один байт, поэтому int в большинстве систем имеет длину 4 байта и указатель для int имеет длину всего один байт,

это говорит о том, что размер адреса памяти зависит от размера системы,8,16,32,64 и т. д.

поэтому я хочу уточнить, насколько велик адрес памяти

как в 32-битной, так и в 64-битной системе?

С++ кажется, что каждый адрес имеет длину 8 бит или один байт
Как вы это вычислили? Я очень надеюсь, что смогу использовать более 255 адресов!

Практически в любой потребительской системе адрес памяти (то есть тип данных указателя) является 64-разрядным для 64-разрядного компьютера и 32-разрядным для 32-разрядного компьютера. Обратите внимание, что такие операционные системы, как Windows, по-прежнему могут запускать 32-битный код в 64-битной системе, поэтому в этих программах по-прежнему будут 32-битные адреса. [Обязательное замечание, что встроенный мир может быть другим, см. ваше руководство.]

Хороший исходный код не заботится о том, насколько велик указатель (адрес памяти).
Это всегда будет sizeof(

Размер указателя также зависит от типа данных. sizeof(int *) и sizeof(float *) не обязательно должны быть равны. Пустота * должна содержать любой заданный указатель данных (не обязательно указатели на функции), поэтому sizeof(void *) — это размер самых больших указателей данных на платформе в байтах.

Практически в любой потребительской системе адрес памяти (т. е. тип данных указателя) является 64-битным для 64-битного компьютера и 32-битным для 32-битного компьютера. . Обратите внимание, что такие операционные системы, как Windows, по-прежнему могут запускать 32-битный код в 64-битной системе, поэтому в этих программах по-прежнему будут 32-битные адреса. [Обязательно обратите внимание, что встроенный мир может быть другим, см. ваше руководство.]

Значит, адрес памяти не 8-битный? почему char всего 8 бит, а int 4 байта, я был почти уверен, что размер адреса памяти равен 8 битам (256 возможных значений)

В большинстве систем адрес памяти, вероятно, не будет 8-битным. Определенно не что-то, работающее в современной среде рабочего стола.

почему char имеет размер всего 8 байт
sizeof(char) определен равным 1. В большинстве операционных систем это означает 8 биты.

Чтобы вычислить количество битов, тип:
sizeof (type) * CHAR_BIT (CHAR_BIT определен в , и обычно равен 8)

кажется, что каждый адрес памяти имеет длину 8 бит или один байт

но int имеет длину 4 байта? как это повлияет на доступ к arr[256] и далее?

arr[512] представляет собой массив целых чисел, каждое из которых составляет 4 байта? так что arr[1] будет 4 байта + arr[0],arr[2] будет 8 байт плюс arr[0], как это повлияет на то, что мы превысим 256?

Две разные вещи. Размер адреса и размер самих данных.

Если у вас есть указатель типа T,
T* ptr;
а вы делаете
ptr++;
адрес увеличивается на sizeof(T).

Я думаю, вы неправильно читаете сообщение о переполнении стека. Одним из свойств компьютера является его «адресуемость», которая относится к наименьшей единице данных, которая может быть представлена ​​адресом. Большинство современных компьютеров имеют побайтовую адресацию, что означает, что аппаратная инструкция может обращаться к одному байту данных и работать с ним.

Поэтому было бы правильно сказать, что *целью* адреса (на что указывает адрес) является байт в этом контексте. Но фактические данные, составляющие адрес, обычно намного больше на современных машинах, обычно 32 или 64 бита. Чем больше адрес (или то, что мы называем «указателем» в C и C++), тем больший диапазон он может представлять.

Надеюсь, это немного поможет.

Эта ссылка говорит об арифметике указателей.

но допустим, у нас есть char,

char состоит всего из 8 бит или одного байта. так что было бы пустой тратой памяти хранить один символ в одном 32-битном адресе памяти, верно? значит ли это, что мы можем хранить несколько символов в одном 32-битном адресе??

Адреса памяти — это то, что хранится в указателях. Они относятся к расположению байта в памяти. Адрес переменной int — это место в памяти, где хранится первый байт переменной int.

char состоит только из 8 бит или одного байта. так что было бы пустой тратой памяти хранить один символ в одном 32-битном адресе памяти, верно? значит ли это, что мы можем хранить несколько символов в одном 32-битном адресе??

Мне нравится то, что сказал mzimmers, возможно, это прольет свет на это.
р>

Если у нас есть массив целых чисел и допустим, что sizeof(int) == 4, то это будет выглядеть так:
int arr[200];
&arr[0] == 1000
&arr[1] == 1004
&arr[2] == 1008
и т.д.


Сам по себе адрес не определяет размер объекта. Независимо от того, равен ли sizeof вашего объекта 4 или 4000, он все равно может иметь один и тот же адрес памяти.

Если у нас есть массив целых чисел и допустим, что sizeof(int) == 4, то это будет выглядеть так:
int arr[200];
&arr[0] == 1000
&arr[1] == 1004
&arr[2] == 1008
и т.д.

Должно быть, я что-то здесь упускаю, поэтому скажем, в первом примере, который вы привели, у нас есть массив символов размером в один байт, как вы сказали, адрес первого символа &arr[0] будет 1000 и [1] будет 1001, поэтому это означает, что адрес памяти 1000 равен 8 битам или одному байту, а не 32 битам (при условии, что мы работаем в 32-битной системе)

в примере с массивом int у нас есть массив целых чисел, каждое целое число равно 4 байтам (конечно, это может варьироваться, но по большей части это верно), поэтому это занимает адрес памяти от 1000 до 1004, первые 8 бит целого числа (конечно, в зависимости от того, является ли система большой или маленькой, но давайте проигнорируем это здесь) хранится в 1000, а следующие 8 бит хранятся в адресе 1002 и так далее, так что снова это означает, что адрес памяти равен 8 битам или байтам. ???


спасибо, что остаетесь со мной

Похоже, это проблема общения.

То, что вы называете адресом памяти, на самом деле называется позицией в памяти. Позиция памяти содержит значение, а адрес памяти используется для поиска позиции в памяти. По аналогии, если память — это отель, то позиция в памяти — это номер в отеле, а адрес — это номер комнаты. Если бы в вашем отеле был миллион одноместных номеров, номера ваших комнат имели бы размер 7 цифр, а номера имели бы размер «одна кровать».
Если бы у вас была память с побитовой адресацией (к отдельным битам можно получить доступ независимо) с 65 536 битами, ваши адреса были бы 16-битными, а позиции были бы однобитными.

Как сказал гелиос, возможно, нам следует вместо этого говорить позиция в памяти.
Не имеет значения, насколько велик сам тип данных int — он все равно может начинаться с позиции памяти 1000.

Чтобы получить доступ ко всем 1 миллиону деревьев, нам потребуется адрес памяти длиной не менее 20 бит, потому что 2 20 больше, чем один миллион.

Имеет ли смысл эта часть хотя бы сама по себе? Если нет, не читайте этот пост до конца.

Теперь, допустим, у нас есть миллион деревьев, а также имя игрока. Мы уже использовали первый миллион ячеек памяти для хранения данных нашего дерева. Итак, мы можем сохранить имя игрока, начиная с позиции 1 000 001.

Теперь, независимо от того, зовут меня «Билл» или «Навуходоносор», я все равно могу сказать, что имя игрока хранится, начиная с позиции 1 000 001. Но «Билл» будет занимать 4 единицы, а «Навуходоносор» — 14 единиц.

адрес 1 000 001 : B
адрес 1 000 002 : i
адрес 1 000 003 : l
адрес 1 000 004 : l

Возвращаясь к фактическим типам данных, допустим, я хочу сохранить 2-байтовый тип.
2-байтовое целое может выглядеть так: 1100 0011 1010 0111.
Мы собираемся сохранить это в ячейке памяти 2 миллиона, потому что мы уже используем младшие адреса для других объектов. Поскольку нам нужно адресовать не менее 2 миллионов позиций, нам нужно хранить этот адрес, используя не менее 21 бита (поскольку 2·20 > 2 миллиона) -- независимо от размера целого числа.

Теперь, поскольку sizeof(my 2-byte int) == 2, это означает, что 2 позиции в памяти будут "принадлежать" int.
Ячейка памяти 2 000 000 будет содержать "1100 0011".
Ячейка памяти 2 000 001 будет содержать «1010 0111».

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

но почему нельзя хранить более 256 деревьев? Я понимаю, что 8 бит могут хранить только значения от 0 до 256, но что нам мешает создать массив больше 256? Я предполагаю, что для этого примера каждое дерево является однобитным, поэтому адрес памяти (если бы он был 8-битным) мог бы хранить 256 деревьев, так почему бы нам просто не иметь массив этих адресов памяти, скажем,деревья[500]< /p>


Итак, если я правильно понимаю, скажем, адрес памяти имеет длину 32 бита, что во многих случаях мы можем только 2 ^ 32 значения в нашем массиве? мы не можем превысить это число? Итак, скажем, нам нужен большой массив целых чисел arr[1000000000], который на 2 цифры длиннее предела, это было бы невозможно? так что у нас может быть не массив адресов памяти, а просто позиции памяти?


также, если int имеет длину 4 байта, это означает, что байт 1000 не является фактическим адресом (ну, это всего лишь позиция в адресе), а позиция, в которой данные начинаются в этом адресе, или мы могли бы использовать термин смещение?


надеюсь, я на правильном пути

то, что вы вызываете адрес памяти, на самом деле называется позицией памяти. Позиция памяти содержит значение, а адрес памяти используется для поиска позиции в памяти. По аналогии, если память — это отель, то позиция в памяти — это номер в отеле, а адрес — это номер комнаты. Если бы в вашем отеле был миллион одноместных номеров, номера ваших комнат имели бы размер 7 цифр, а номера имели бы размер «одна кровать».
Если бы у вас была память с побитовой адресацией (к отдельным битам можно получить доступ независимо) с 65 536 битами, ваши адреса были бы 16-битными, а позиции имели бы один бит.
если я правильно понимаю, скажем, адрес памяти имеет длину 32 бита, что во многих случаях мы можем только 2 ^ 32 значения в нашем массиве? мы не можем превысить это число?

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

скажем, нам нужен большой массив целых чисел arr[1000000000], который на 2 цифры длиннее ограничения, это невозможно?

Вы правы, за исключением того, что 4000000000 на самом деле не превышает предела 2 32 (при условии, что sizeof(int) == 4).

Эта проблема является причиной того, что большинство новых компьютеров используют 64-разрядную версию, потому что мы хотим иметь возможность адресовать более 4 ГБ памяти.

Адреса памяти — это то, что хранится в указателях. Они относятся к расположению байта в памяти. Адрес переменной int — это место в памяти, где хранится первый байт переменной int.

Возвращаясь к предыдущему,

Давайте, например, воспользуемся примером программы

хорошо, скажем, первое будет помещено в память с адресом 1000 и занимает 4 байта, поэтому этот int сначала получит свой собственный адрес памяти?

теперь второй будет хранить символ или байт, получит ли второй свой собственный адрес памяти или он будет делиться адресом памяти с первым и вместо этого будет указывать на позицию 1004, начиная с адреса памяти, начинающегося с 1000?

третьим будет один адрес памяти размером 4*200 байт? или он также будет совместно использовать адрес памяти одного, двух или обоих?

то же самое касается четвертого и пятого

если int имеет длину 4 байта, это означает, что байт 1000 не является фактическим адресом (ну, это всего лишь позиция в адресе), а позиция, с которой данные начинаются в этом адресе или мы могли бы использовать термин смещение?

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

Ваша программа имеет доступ к пространству памяти. Если это 32-битное приложение, это пространство памяти будет состоять из 2 32 адресов памяти. Не все адреса памяти будут сопоставляться с чем-либо, поэтому, если вы попытаетесь прочитать или записать такой адрес, вы, вероятно, получите ошибку сегментации, что приведет к сбою вашей программы.

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

также, если int имеет длину 4 байта, это означает, что байт 1000 не является фактическим адресом
Нет, 1000 в предыдущем примере будет адрес.

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

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

Представьте себе 16-разрядную систему, память которой состоит из четырех микросхем ОЗУ по 32 КБ, помеченных от 0 до 3. Адреса от 0x0000 до 0x7FFF всегда сопоставляются с микросхемой 0, но ЦП может указать материнской плате включить или отключить любую другую микросхему. три чипа (только один из них включен в любой момент времени). Такая система может использовать 16-битные адреса, которые обычно позволяют использовать только 64 КБ ОЗУ, для использования 128 КБ ОЗУ.
Например, программа для очистки всей оперативной памяти может выглядеть так:

Вы используете слово "в", но вам следует использовать слово "в":
Данные размещаются в, а не в конкретный адрес памяти. Адрес — это просто метка, а не хранилище.

Если четырехбайтовое целое помещается по адресу памяти 1000, то оно занимает область в памяти от начала ячейки 1000 до конца ячейки 1003 — четыре байта.

Размер самого адреса (1000) не имеет значения — это может быть четыре, восемь или n байтов, но единственное, что имеет значение, — это размер объекта по адресу 1000. В данном случае это четырехбайтный int, но адрес не записывает это, он только говорит, где находится объект в пространстве памяти.

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

[править] std::nullptr_t

std::nullptr_t — это тип литерала нулевого указателя, nullptr. Это отдельный тип, который сам по себе не является типом указателя или указателем на тип члена. Его значения являются константами нулевого указателя (см. NULL ) и могут быть неявно преобразованы в любой указатель и указатель на тип члена.

sizeof ( std:: nullptr_t ) равен sizeof ( void * ) .

[править] Модели данных

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

  • LP32 или 2/4/4 (int 16-битный, long и указатель 32-битный)
  • API Win16
  • ILP32 или 4/4/4 (int, long и указатель являются 32-разрядными);
  • API Win32
  • Unix и Unix-подобные системы (Linux, macOS)
  • LLP64 или 4/4/8 (целые и длинные 32-битные, указатель 64-битные)
  • API Win64
  • LP64 или 4/8/8 (целое 32-битное, длинное и указатель 64-битное)
  • Unix и Unix-подобные системы (Linux, macOS)

Другие модели очень редки. Например, ILP64 (8/8/8: int, long и указатель являются 64-разрядными) появился только в некоторых ранних 64-разрядных системах Unix (например, UNICOS на Cray).

[edit] Целочисленные типы со знаком и без знака

int - основной целочисленный тип. Ключевое слово int может быть опущено, если используется какой-либо из модификаторов, перечисленных ниже. Если модификаторы длины отсутствуют, гарантируется, что он будет иметь ширину не менее 16 бит. Однако в 32/64-битных системах почти всегда гарантирована ширина не менее 32 бит (см. ниже).

[править] Модификаторы

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

signed — целевой тип будет иметь знаковое представление (это значение по умолчанию, если оно опущено) unsigned — целевой тип будет иметь беззнаковое представление

short – целевой тип будет оптимизирован для занимаемого места и будет иметь ширину не менее 16 бит. long - целевой тип будет иметь ширину не менее 32 бит.

Примечание: как и для всех спецификаторов типов, допускается любой порядок: unsigned long long int и long int unsigned long name одного и того же типа.

[править] Свойства

В следующей таблице приведены все доступные целочисленные типы и их свойства в различных распространенных моделях данных:

Примечание. Целочисленная арифметика определяется по-разному для типов целых чисел со знаком и без знака. См. арифметические операторы, в частности целочисленные переполнения.

std::size_t – это беззнаковый целочисленный тип результата оператора sizeof, а также sizeof. оператор и оператор alignof (начиная с C++11).

[править] Логический тип

bool - тип, способный хранить одно из двух значений: true или false. Значение sizeof ( bool ) определяется реализацией и может отличаться от 1.

[править] Типы символов

char16_t — тип для представления символа UTF-16, который должен быть достаточно большим для представления любой единицы кода UTF-16 (16 бит). Он имеет тот же размер, подписание и выравнивание, что и std::uint_least16_t , но относится к другому типу.

Помимо минимального количества битов, стандарт C++ гарантирует, что

1 == sizeof (char) sizeof (short) sizeof (int) sizeof (long) sizeof (long long).

Примечание: это допускает крайний случай, когда байты имеют размер 64 бита, все типы (включая char ) имеют ширину 64 бита, а sizeof возвращает 1 для каждого типа.

[править] Типы с плавающей запятой

Следующие три типа и их версии с указанием cv вместе называются типами с плавающей запятой.

  • Формат Binary128 используется некоторыми реализациями HP-UX, SPARC, MIPS, ARM64 и z/OS.
  • Наиболее известным форматом IEEE-754 с расширением binary64 является 80-битный формат расширенной точности x87. Он используется во многих реализациях x86 и x86-64 (заметным исключением является MSVC, который реализует long double в том же формате, что и double , т. е. binary64).

[править] Свойства

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

  • бесконечность (положительная и отрицательная), см. БЕСКОНЕЧНОСТЬ
  • отрицательный ноль, - 0.0 . Он сравнивается с положительным нулем, но имеет смысл в некоторых арифметических операциях, например. 1.0 / 0.0 == INFINITY , но 1.0 /- 0.0 == - INFINITY ), и для некоторых математических функций, например. sqrt (std::complex)
  • не-число (NaN), которое ни с чем не сравнивается равным (включая самого себя). Несколько битовых шаблонов представляют NaN, см. std::nan, NAN. Обратите внимание, что C++ не уделяет особого внимания сигнализации NaN, кроме обнаружения их поддержки с помощью std::numeric_limits::has_signaling_NaN и рассматривает все NaN как тихие.

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

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

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

Дополнительные сведения, ограничения и свойства типов с плавающей запятой см. в разделе Ограничения типов с плавающей запятой и std::numeric_limits.

[править] Диапазон значений

В следующей таблице приведены справочные данные об ограничениях распространенных числовых представлений.

  • мин ниже нормы:
    ± 1,401 298,4 · 10 -45
  • мин. норма:
    ± 1.175 494,3 · 10 -38
  • макс.:
    ± 3,402 823,4 · 10 38
  • мин. ниже нормы:
    ±0x1p-149
  • мин. нормальный:
    ±0x1p-126
  • макс.:
    ±0x1.fffffep+127
  • мин ниже нормы:
    ± 4,940 656 458 412 · 10 – 324
  • мин. норма:
    ± 2,225,073,858,507,201,4 · 10 -308
  • макс.:
    ± 1 797 693 134 862 315,7 · 10 308
  • мин. ниже нормы:
    ±0x1p-1074
  • мин. нормальный:
    ±0x1p-1022
  • макс.:
    ±0x1.ffffffffffffffp+1023
  • мин ниже нормы:
    ± 3,645 199 531 882 474 602 528
    · 10 -4951
  • минимальное нормальное значение:
    ± 3,362 103 143 112 093 506 263
    · 10 -4932
  • макс.:
    ± 1 189 731 495 357 231 765 021
    · 10 4932
  • минимум ниже нормы:
    ±0x1p-16446
  • мин. нормальный:
    ±0x1p-16382
  • макс.:
    ±0x1.fffffffffffffffep+16383
  • мин ниже нормы:
    ± 6,475,175,119,438,025,110,924,
    438,958,227,646,552,5 · 10 -4966
  • минимальное нормальное:
    ± 3,362 103 143 112 093 506 262,
    677 817 321 752 602,6 · 10 -4932
  • макс.:
    ± 1 189 731 495 357 231 765 085,
    759 326 628 007 016,2 · 10 4932
  • минимум ниже нормы:
    ±0x1p-16494
  • мин. нормальный:
    ±0x1p-16382
  • макс.:
    ±0x1.ffffffffffffffffffffffffffffffffffffffffffff
    p+16383
  1. ↑ Представление объекта обычно занимает 96/128 бит на 32/64-битных платформах соответственно.

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

[править] Ключевые слова

[править] Отчеты о дефектах

Следующие отчеты о дефектах, изменяющих поведение, были задним числом применены к ранее опубликованным стандартам C++.

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

x/ nfu адрес x адрес x

Используйте команду x для проверки памяти.

n , f и u — необязательные параметры, которые указывают, сколько памяти отображать и как ее форматировать; addr — это выражение, задающее адрес, с которого вы хотите начать отображение памяти. Если вы используете значения по умолчанию для nfu , вам не нужно вводить косую черту «/». Несколько команд устанавливают удобные значения по умолчанию для адреса.

n , количество повторов

Счетчик повторов – это десятичное целое число; значение по умолчанию равно 1. Он указывает, сколько памяти (считая в единицах u ) отображать. Если указано отрицательное число, память проверяется в обратном направлении от адреса.

f , формат отображения

Формат отображения – это один из форматов, используемых функцией печати ('x', 'd', 'u', 'o', 't', 'a', 'c', 'f', 's' ), 'i' (для машинных инструкций) и 'm' (для отображения тегов памяти). По умолчанию изначально используется «x» (шестнадцатеричный). Значение по умолчанию меняется каждый раз, когда вы используете x или print .

u , размер блока

Размер блока – любой из

Полуслова (два байта).

Слова (четыре байта). Это исходное значение по умолчанию.

Огромные слова (восемь байт).

addr , начальный отображаемый адрес

addr — это адрес, с которого вы хотите, чтобы GDB начал отображать память. Выражение не обязательно должно иметь значение указателя (хотя и может); он всегда интерпретируется как целочисленный адрес байта памяти. Дополнительные сведения о выражениях см. в разделе Выражения. Адрес по умолчанию для addr обычно находится сразу после последнего проверенного адреса, но несколько других команд также устанавливают адрес по умолчанию: info breakpoints (на адрес последней указанной точки останова), info line (на начальный адрес строки) и print (если вы используете его для отображения значения из памяти).

Например, ‘ x/3uh 0x54320 ’ – это запрос на отображение трех полуслов ( h ) памяти в формате десятичных целых чисел без знака ( ‘ u ’), начиная с адреса 0x54320 . ‘ x/4xw $sp ’ печатает четыре слова (‘ w ’) памяти над указателем стека (здесь ‘ $sp ’; см. Регистры) в шестнадцатеричном формате (‘ x ’).

Вы также можете указать отрицательное количество повторений, чтобы проверить память в обратном направлении от заданного адреса. Например, ‘x/-3uh 0x54320’ выводит три полуслова (h) по адресам 0x54314, 0x54328 и 0x5431c.

Поскольку все буквы, обозначающие размеры единиц измерения, отличаются от букв, обозначающих выходные форматы, вам не нужно запоминать, что идет первым: размер или формат единиц; любой порядок работает. Выходные характеристики «4xw» и «4wx» означают одно и то же. (Однако число n должно идти первым; ‘ wx4 ’ не работает.)

Несмотря на то, что размер единицы измерения u игнорируется для форматов ‘s’ и ‘i’, вы все равно можете использовать число n; например, «3i» указывает, что вы хотите увидеть три машинных инструкции, включая любые операнды.Для удобства, особенно при использовании с командой display, формат ‘i’ также печатает инструкции слота задержки ветвления, если таковые имеются, за пределами указанного количества, которые сразу следуют за последней инструкцией, которая находится в пределах счетчика. Команда дизассемблировать дает альтернативный способ проверки машинных инструкций; см. Исходный и машинный код.

Если для форматов ‘ s ’ или ‘ i ’ указано отрицательное количество повторений, команда отображает строки или инструкции, заканчивающиеся нулем, перед заданным адресом столько, сколько абсолютное значение данного числа. Для формата «i» мы используем информацию о номере строки в отладочной информации, чтобы точно определить границы инструкций при дизассемблировании в обратном направлении. Если информация о строке недоступна, команда прекращает проверку памяти с сообщением об ошибке.

Все значения по умолчанию для аргументов x предназначены для упрощения продолжения сканирования памяти с минимальными характеристиками каждый раз, когда вы используете x . Например, после того как вы проверили три машинных инструкции с помощью «x/3i addr», вы можете проверить следующие семь с помощью только «x/7». Если вы используете RET для повторения команды x, число повторений n используется снова; другие аргументы по умолчанию как для последовательного использования x .

При просмотре машинных инструкций инструкция на текущем программном счетчике отображается с маркером =>. Например:

Если архитектура поддерживает теги памяти, теги можно отобразить с помощью ‘ m ’. См. Теги памяти.

Информация будет отображаться один раз для каждого размера гранулы (количество байтов, которое покрывает конкретный тег памяти). Например, размер гранул AArch64 составляет 16 байт, поэтому тег будет отображаться через каждые 16 байт.

Из-за того, что GDB выводит информацию с помощью команды x (не выровненной по определенной границе), информация тега будет относиться к начальному адресу, отображаемому в определенной строке. Если граница тега памяти пересекается в середине строки, отображаемой командой x, она будет отображаться на следующей строке.

Формат ‘ m ’ не влияет на другие указанные форматы, которые были переданы команде x.

Адреса и содержимое, напечатанное командой x, не сохраняются в истории значений, потому что их часто бывает слишком много, и они будут мешать. Вместо этого GDB делает эти значения доступными для последующего использования в выражениях в качестве значений вспомогательных переменных $_ и $__. После команды x последний проверенный адрес доступен для использования в выражениях вспомогательной переменной $_. Содержимое этого адреса после проверки доступно во вспомогательной переменной $__ .

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

Большинство целей имеют адресный блок памяти размером 8 бит. Это означает, что с каждым адресом памяти связаны 8 бит данных. Однако некоторые цели имеют другие размеры адресуемых блоков памяти. В GDB и в этом документе термин адресуемая единица памяти (или для краткости единица памяти) используется при явной ссылке на фрагмент данных этот размер. Слово byte используется для обозначения порции данных из 8 бит, независимо от размера адресуемой единицы памяти цели. Для большинства систем адресуемая единица памяти является синонимом байта.

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

сравнить разделы [имя раздела | -р ]

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

Примечание: для удаленных целей эту команду можно ускорить, если цель поддерживает вычисление контрольной суммы CRC блока памяти (см. пакет qCRC).

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

Эта проблема является причиной того, что большинство новых компьютеров используют 64-битную систему, потому что мы хотим иметь возможность адресовать более 4 ГБ памяти.