Проблемы использования
Керниган и Ричи указали две причины использования typedef. Во-первых, он предоставляет средства, позволяющие сделать программу более переносимой или более простой в обслуживании. Вместо того, чтобы изменять тип при каждом появлении в исходных файлах программы, нужно изменить только один оператор typedef. size_t и ptrdiff_t в <stdlib.h> являются такими именами typedef. Во-вторых, определение типа может облегчить понимание сложного определения или объявления.
Некоторые программисты выступают против широкого использования определений типов. Большинство аргументов основано на идее, что typedef просто скрывает фактический тип данных переменной. Например, Грег Кроа-Хартман , хакер ядра Linux и разработчик документации, не рекомендует использовать их для чего-либо, кроме объявления прототипов функций. Он утверждает, что такая практика не только излишне запутывает код, но также может привести к тому, что программисты будут случайно неправильно использовать большие структуры, считая их простыми типами.
Методы
Добавляет смещение к значению указателя без знака. |
|
Сравнивает текущий экземпляр с другим объектом того же типа и возвращает целое число, которое показывает, расположен ли текущий экземпляр перед, после или на той же позиции в порядке сортировки, что и другой объект. |
|
Сравнивает текущий экземпляр с другим объектом того же типа и возвращает целое число, которое показывает, расположен ли текущий экземпляр перед, после или на той же позиции в порядке сортировки, что и другой объект. |
|
Возвращает значение, показывающее, равен ли данный экземпляр заданному объекту. |
|
Указывает, равен ли текущий объект другому объекту того же типа. |
|
Возвращает хэш-код данного экземпляра. |
|
Преобразует доступное только для чтения количество символов в представлении числа по дополнительному указанному стилю и дополнительно указанному формату для определенного языка и региональных параметров в собственный целочисленный эквивалент без знака. |
|
Преобразует строковое представление числа в эквивалентное ему собственное целое число без знака. |
|
Преобразует строковое представление числа в формате, соответствующем языку и региональным параметрам, в эквивалентное ему собственное целое число без знака. |
|
Преобразует строковое представление числа в указанном формате в эквивалентное ему собственное целое число без знака. |
|
Преобразует строковое представление числа в формате, соответствующем стилю, языку и региональным параметрам, в эквивалентное ему собственное целое число без знака. |
|
Вычитает смещение из значения указателя без знака. |
|
Преобразует значение этого экземпляра в указатель незаданного типа. |
|
Преобразует числовое значение данного экземпляра в эквивалентное ему строковое представление. |
|
Преобразует числовое значение данного экземпляра в эквивалентное ему строковое представление с использованием указанного формата и сведений об особенностях форматирования для данного языка и региональных параметров. |
|
Преобразует числовое значение данного экземпляра в эквивалентное строковое представление с использованием указанного формата. |
|
Форматирует значение текущего экземпляра, используя указанный формат. |
|
Преобразует значение данного экземпляра в 32-битовое целое число без знака. |
|
Преобразует значение данного экземпляра в 64-битовое целое число без знака. |
|
Пытается отформатировать значение текущего экземпляра в указанный диапазон символов. |
|
Преобразует доступное только для чтения представление символов числа в указанном стиле и формате, соответствующем языку и региональным параметрам, в собственный целочисленный эквивалент без знака. Возвращает значение, указывающее, успешно ли выполнено преобразование. |
|
Преобразует доступное только для чтения представление символов числа в собственный целочисленный эквивалент без знака. Возвращает значение, указывающее, успешно ли выполнено преобразование. |
|
Преобразует строковое представление числа в формате, соответствующем стилю, языку и региональным параметрам, в эквивалентное ему собственное целое число без знака. Возвращает значение, указывающее, успешно ли выполнено преобразование. |
|
Преобразует строковое представление числа в эквивалентное ему собственное целое число без знака. Возвращает значение, указывающее, успешно ли выполнено преобразование. |
Прямая интерпретация
An IntPtr является целое число который того же размера, что и указатель.
Вы можете использовать IntPtr для хранения значения указателя в типе без указателя. Эта функция важна в .NET, поскольку использование указателей очень подвержено ошибкам и, следовательно, незаконно в большинстве контекстов. Позволяя сохранять значение указателя в «безопасном» типе данных, переходя между небезопасно Сегменты кода могут быть реализованы в более безопасном высокоуровневом коде или даже на языке .NET, который напрямую не поддерживает указатели.
Размер IntPtr зависит от платформы, но эту деталь редко нужно учитывать, поскольку система автоматически использует правильный размер.
Название «IntPtr» сбивает с толку — что-то вроде могло бы быть более подходящим. Я изначально предполагал, что «IntPtr» был указателем. к целое число. Документация MSDN для IntPtr содержит несколько загадочных деталей, но при этом не дает подробного понимания значения имени.
Комментарии
IntPtrТип разрабатывается как целое число, размер которого зависит от платформы. То есть экземпляр этого типа должен иметь 32 бит на 32-разрядном оборудовании и операционных системах, а 64-бит на 64-разрядном оборудовании и операционных системах.
IntPtrТип может использоваться языками, поддерживающими указатели, и как распространенный способ обращения к данным между языками, которые не поддерживают указатели.
IntPtr объекты также можно использовать для хранения дескрипторов. Например, экземпляры IntPtr широко используются в System.IO.FileStream классе для хранения дескрипторов файлов.
IntPtrТип является CLS-совместимым, а UIntPtr тип — нет. IntPtrВ среде CLR используется только тип. UIntPtrТип предоставляется преимущественно для поддержки архитектурной симметрии с IntPtr типом.
Этот тип реализует ISerializable , и, в .net 5,0 и более поздних версиях, IFormattable интерфейсы.
Definition
- Namespace:
- System
- Assembly:
- System.Runtime.dll
- Assembly:
- mscorlib.dll
- Assembly:
- netstandard.dll
Important
Some information relates to prerelease product that may be substantially modified before it’s released. Microsoft makes no warranties, express or implied, with respect to the information provided here.
A platform-specific type that is used to represent a pointer or a handle.
In this article
- Inheritance
-
Object
ValueType
IntPtr
- Attributes
-
SerializableAttribute
ComVisibleAttribute
- Implements
-
IComparable
IComparable<IntPtr>
IEquatable<IntPtr>
IFormattable
ISerializable
ISpanFormattable
Definition
- Namespace:
- System
- Assembly:
- System.Runtime.dll
- Assembly:
- mscorlib.dll
- Assembly:
- netstandard.dll
Important
Some information relates to prerelease product that may be substantially modified before it’s released. Microsoft makes no warranties, express or implied, with respect to the information provided here.
A platform-specific type that is used to represent a pointer or a handle.
In this article
- Inheritance
-
Object
ValueType
IntPtr
- Attributes
-
SerializableAttribute
ComVisibleAttribute
- Implements
-
IComparable
IComparable<IntPtr>
IEquatable<IntPtr>
IFormattable
ISerializable
ISpanFormattable
Примеры
В следующем примере управляемые указатели используются для изменения порядка символов в массиве. После того как инициализирует String объект и получает его длину, он выполняет следующие действия:
-
Вызывает Marshal.StringToHGlobalAnsi метод, чтобы скопировать строку Юникода в неуправляемую память как символ ANSI (однобайтовый). Метод возвращает IntPtr объект, указывающий на начало неуправляемой строки. в Visual Basic примере этот указатель используется напрямую; в примерах C++ и C# он приводится к указателю на байт.
-
Вызывает Marshal.AllocHGlobal метод для выделения того же числа байтов, занимаемого неуправляемой строкой. Метод возвращает IntPtr объект, указывающий на начало неуправляемого блока памяти. в Visual Basic примере этот указатель используется напрямую; в примерах C++ и C# он приводится к указателю на байт.
-
в Visual Basic примере определяется переменная с именем , равная длине строки ANSI. Он используется для определения смещения в неуправляемую память, в которую копируется следующий символ в строке ANSI. Поскольку его начальное значение равно длине строки, операция копирования скопирует символ из начала строки в конец блока памяти.
В примерах C# и C++ вызывается ToPointer метод для получения неуправляемого указателя на начальный адрес строки и неуправляемый блок памяти и добавляется одна меньше длины строки в начальный адрес строки ANSI. Так как указатель неуправляемой строки теперь указывает на конец строки, операция копирования скопирует символ из конца строки в начало блока памяти.
-
Использует цикл для копирования каждого символа из строки в неуправляемый блок памяти.
в Visual Basic примере вызывается метод для считывания байта (или однобайтового символа) с указанным смещением от управляемого указателя к строке ANSI. Смещение увеличивается при каждой итерации цикла. Затем он вызывает метод для записи байта в адрес памяти, определенный начальным адресом неуправляемого блока памяти плюс . Затем он уменьшается .
Примеры C# и C++ выполняют операцию копирования, а затем уменьшают указатель до адреса следующего расположения в неуправляемой строке ANSI и увеличивают указатель до следующего адреса в неуправляемом блоке.
-
Все примеры вызывают метод Marshal.PtrToStringAnsi для преобразования блока неуправляемой памяти, содержащего скопированную строку ANSI, в управляемый String объект Юникода.
-
После отображения исходных и обратных строк все примеры вызывают FreeHGlobal метод для высвобождения памяти, выделенной для неуправляемой строки ANSI, и неуправляемого блока памяти.
Типы чисел с плавающей точкой (по IEEE 754)[править]
Число половинной точности (Binary16, Half precision)править
Число́ полови́нной то́чности — компьютерный формат представления чисел, занимающий в памяти половину машинного слова (в случае 32-битного компьютера — бит или байта). В силу невысокой точности этот формат представления чисел с плавающей запятой обычно используется в видеокартах, где небольшой размер и высокая скорость работы важнее точности вычислений.
Знак | ||||
---|---|---|---|---|
Порядок | Мантисса | |||
1, | ||||
14 | 10 | 9 |
Порядок записан со сдвигом . То есть чтобы получить актуально значение порядка нужно вычесть из него сдвиг. Сдвиг можно получить по формуле , где — число бит, отведенное на хранение порядка (в случае числа половинной точности ).
Ограничения точности
- Целые от нуля до передаются как есть.
- Целые от до округляются к ближайшему чётному целому.
- Целые от до округляются до ближайшего целого, делящегося нацело на четыре.
- Целые от до округляются до ближайшего целого, делящегося на восемь.
- Целые от до округляются до ближайшего целого, делящегося на шестнадцать.
- Целые от до округляются до ближайшего целого, делящегося на тридцать два.
Число одинарной точности (Binary32, Single precision, float)править
Число́ одина́рной то́чности — компьютерный формат представления чисел, занимающий в памяти одно машинное слово (в случае 32-битного компьютера — бита или байта). Используется для работы с вещественными числами везде, где не нужна очень высокая точность.
Знак | ||||
---|---|---|---|---|
Порядок (8 бит) | Мантисса (23+1 бита) | |||
1, | ||||
30 | 23 | 22 |
Порядок записан со сдвигом .
Число двойной точности (Binary64, Double precision, double)править
Число́ двойно́й то́чности —
компьютерный формат представления чисел, занимающий в памяти два машинных слова (в случае 32-битного компьютера — бита или байт). Часто используется благодаря своей неплохой точности, даже несмотря на двойной расход памяти и сетевого трафика относительно чисел одинарной точности.
Знак | ||||
---|---|---|---|---|
Порядок(11 бит) | Мантисса(52+1 бит) | |||
1, | ||||
62 | 52 | 51 |
Порядок записан со сдвигом .
Число четверной точности (Binary128, Quadruple precision)править
Число́ четверно́й то́чности —
компьютерный формат представления чисел, занимающий в памяти четыре машинных слова (в случае 32-битного компьютера — бит или байт). Используется в случае необходимости крайне высокой точности.
Знак | ||||
---|---|---|---|---|
Порядок(15 бит) | Мантисса(112+1 бит) | |||
1, | ||||
126 | 112 | 111 |
Мантисса(112+1 бит) |
---|
Порядок записан со сдвигом .
Обычно этот формат реализуется программно, случаи аппаратной реализации крайне редки. Также не гарантируется поддержка этого типа в языках программирования, хотя кое-где она и реализована (например, компилятор gcc для архитектуры x86 позволяет использовать тип __float128, являющийся программной реализацией числа с четверной точностью).
В совокупности эти факторы делают Quadruple весьма экзотичным и редко встречающимся форматом чисел с плавающей запятой.
Диапазон значений чисел с плавающей запятойправить
Диапазон чисел, которые можно записать данным способом, зависит от количества бит, отведённых для представления мантиссы и показателя. Пара значений показателя (когда все разряды нули и когда все разряды единицы) зарезервирована для обеспечения возможности представления специальных чисел. К ним относятся ноль, значения NaN (Not a Number, «не число», получается как результат операций типа деления нуля на ноль) и .
Данная таблица только лишь примерно указывает границы допустимых значений, без учета возрастающей погрешности с ростом абсолютного значения и существования .
Название в IEEE 754 | Название типа переменной в Си | Диапазон значений | Бит в мантиссе | Бит на переменную |
---|---|---|---|---|
Half precision | — | 6,10×10-5..65504 | 11 | 16 |
Single presicion | float | -3,4×1038..3,4×1038 | 23 | 32 |
Double precision | double | -1,7×10308..1,7×10308 | 53 | 64 |
Extended precision | На некоторых архитектурах (например в сопроцессоре Intel) long double | -3,4×104932..3,4×104932 | 65 | 80 |
Примеры
В следующем примере управляемые указатели используются для изменения порядка символов в массиве. После того как инициализирует String объект и получает его длину, он выполняет следующие действия:
-
Вызывает Marshal.StringToHGlobalAnsi метод, чтобы скопировать строку Юникода в неуправляемую память как символ ANSI (однобайтовый). Метод возвращает IntPtr объект, указывающий на начало неуправляемой строки. в Visual Basic примере этот указатель используется напрямую; в примерах C++ и C# он приводится к указателю на байт.
-
Вызывает Marshal.AllocHGlobal метод для выделения того же числа байтов, занимаемого неуправляемой строкой. Метод возвращает IntPtr объект, указывающий на начало неуправляемого блока памяти. в Visual Basic примере этот указатель используется напрямую; в примерах C++ и C# он приводится к указателю на байт.
-
в Visual Basic примере определяется переменная с именем , равная длине строки ANSI. Он используется для определения смещения в неуправляемую память, в которую копируется следующий символ в строке ANSI. Поскольку его начальное значение равно длине строки, операция копирования скопирует символ из начала строки в конец блока памяти.
В примерах C# и C++ вызывается ToPointer метод для получения неуправляемого указателя на начальный адрес строки и неуправляемый блок памяти и добавляется одна меньше длины строки в начальный адрес строки ANSI. Так как указатель неуправляемой строки теперь указывает на конец строки, операция копирования скопирует символ из конца строки в начало блока памяти.
-
Использует цикл для копирования каждого символа из строки в неуправляемый блок памяти.
в Visual Basic примере вызывается метод для считывания байта (или однобайтового символа) с указанным смещением от управляемого указателя к строке ANSI. Смещение увеличивается при каждой итерации цикла. Затем он вызывает метод для записи байта в адрес памяти, определенный начальным адресом неуправляемого блока памяти плюс . Затем он уменьшается .
Примеры C# и C++ выполняют операцию копирования, а затем уменьшают указатель до адреса следующего расположения в неуправляемой строке ANSI и увеличивают указатель до следующего адреса в неуправляемом блоке.
-
Все примеры вызывают метод Marshal.PtrToStringAnsi для преобразования блока неуправляемой памяти, содержащего скопированную строку ANSI, в управляемый String объект Юникода.
-
После отображения исходных и обратных строк все примеры вызывают FreeHGlobal метод для высвобождения памяти, выделенной для неуправляемой строки ANSI, и неуправляемого блока памяти.
Соглашение вызова
Соглашение вызова calling convention — правила, по которым осуществляется вызов функции на конкретной программно-аппаратной платформе (задаётся связкой конкретной архитектуры команд центрального процессора и операционной системы, например, на x86-32 и Windows NT одни правила, а на ARMv7 и GNU/Linux другие правила). Соглашение вызова — часть определяемого системой двоичного интерфейса приложения application binary interface, ABI, низкоуровневых особенностей функционирования программ на данной системе. Знание ABI может потребоваться в том числе для “склеивания” программ, написанных на разных языках программирования или откомпилированных разными компиляторами.
Соглашение вызова определяет (некоторые из этих элементов могут быть не стандартизованы, что делает разные компиляторы или даже разные версии одного компилятора не полностью совместимыми даже на одной платформе — впрочем, эта проблема не касается компиляторов C, если не предполагается использовать отладчик):
- какие регистры процессора сохраняются в стеке вызывающей функцией, а какие — вызываемой;
- сохраняется ли адрес возврата непосредственно при вызове или используется специальный регистр для его сохранения;
- используется ли какой-либо регистр в качестве указателя на начало кадра, добавляются ли вспомогательные данные в кадр, упрощающие работу отладчика, либо реализующие определённые возможности языка (например, исключения C++), и если да, то какой формат они имеют;
- какие регистры и в каком порядке и для каких размеров данных можно применять для передачи параметров функции и возвращения результатов;
- в какую сторону растёт стек (в сторону увеличения адресов как в примере выше или в сторону уменьшения адресов);
- в каком порядке параметры складываются на стек и какая функция их удаляет (вызывающая или вызываемая);
- какое выравнивание используется для кадров стека (про выравнивание см. ниже), оставляется ли в кадре дополнительное неиспользуемое пространство для нужд системы или, наоборот, гарантирует ли система сохранность части свободного места над кадром стека (т.н. “красная зона” red zone);
- прочие детали, специфичные для конкретной платформы.
Конкретные соглашения вызова здесь рассматриваться не будут, так как это довольно узкая низкоуровневая специфика. Но представление об их существовании иметь следует. В качестве некоторых примеров соглашений вызова на существующих актуальных платформах можно привести ссылки на Википедию (для серьёзного изучения требуется изучать официальные спецификации, выпущенные для каждой системы, т.к. сведения Википедии могут быть недостаточно точны и полны): x86, краткий общий обзор.
Прямая интерпретация
An IntPtr является целое число который того же размера, что и указатель.
Вы можете использовать IntPtr для хранения значения указателя в типе без указателя. Эта функция важна в .NET, поскольку использование указателей очень подвержено ошибкам и, следовательно, незаконно в большинстве контекстов. Позволяя сохранять значение указателя в «безопасном» типе данных, переходя между небезопасно Сегменты кода могут быть реализованы в более безопасном высокоуровневом коде или даже на языке .NET, который напрямую не поддерживает указатели.
Размер IntPtr зависит от платформы, но эту деталь редко нужно учитывать, поскольку система автоматически использует правильный размер.
Название «IntPtr» сбивает с толку — что-то вроде могло бы быть более подходящим. Мое первоначальное предположение заключалось в том, что «IntPtr» был указателем к целое число. Документация MSDN для IntPtr содержит несколько загадочных деталей, но при этом не дает подробного понимания значения имени.
Операторы
Добавляет смещение к значению указателя без знака. |
|
Определяет, равны ли два заданных экземпляра класса UIntPtr. |
|
Преобразует значение 32-битового целого числа без знака в значение типа UIntPtr. |
|
Преобразует значение 64-битового целого числа без знака в значение типа UIntPtr. |
|
Преобразует значение заданного указателя UIntPtr в 32-битовое целое число без знака. |
|
Преобразует значение заданного указателя UIntPtr в 64-битовое целое число без знака. |
|
Преобразует значение заданной структуры UIntPtr в указатель на незаданный тип. Этот интерфейс API CLS-несовместим. |
|
Преобразует заданный указатель на незаданный тип в UIntPtr. Этот интерфейс API CLS-несовместим. |
|
Определяет, являются ли два заданных экземпляра класса UIntPtr неравными. |
|
Вычитает смещение из значения указателя без знака. |
Рефакторинг кода с целью перехода на типы ptrdiff_t и size_t
Как читатель уже убедился, использование типов ptrdiff_t и size_t имеет ряд преимуществ для 64-битных программ. Но и заменить, скажем все unsigned на size_t — не является выходом. Во-первых, это не гарантирует корректность программы на 64-битной системы. Во-вторых, скорее всего из-за такой замены возникнут новые ошибки, нарушится совместимость форматов данных и так далее. Не стоит забывать, что после такой замены существенно возрастет и объем потребляемой программой памяти. Причем увеличение объема требуемой памяти может замедлить работу приложения, так как в кэш будет помещаться меньше объектов, с которыми идет работа.
Следовательно, внедрение в старый код типов ptrdiff_t и size_t является задачей постепенного рефакторинга, требующего большого количества времени. Фактически необходимо просмотреть весь код и внести необходимые исправления. Такой подход практически является слишком дорогостоящим и неэффективным. Можно предложить 2 варианта:
- Использовать специализированные инструменты, такие как Viva64, входящий в состав PVS-Studio. Viva64 это статический анализатор кода, обнаруживающий места, где рационально изменить типы данных, чтобы программа была корректна и эффективно работала на 64-битных системах.
- Если 32-битную программу не планируется адаптировать для 64-битных систем, то и нет смысла заниматься рефакторингом типов данных. 32-битная программа не получит никаких преимуществ от использования типов ptrdiff_t и size_t.
Operators
Adds an offset to the value of a pointer. |
|
Determines whether two specified instances of IntPtr are equal. |
|
Converts the value of a 32-bit signed integer to an IntPtr. |
|
Converts the value of a 64-bit signed integer to an IntPtr. |
|
Converts the value of the specified IntPtr to a 32-bit signed integer. |
|
Converts the value of the specified IntPtr to a 64-bit signed integer. |
|
Converts the value of the specified IntPtr to a pointer to an unspecified type. This API is not CLS-compliant. |
|
Converts the specified pointer to an unspecified type to an IntPtr. This API is not CLS-compliant. |
|
Determines whether two specified instances of IntPtr are not equal. |
|
Subtracts an offset from the value of a pointer. |
Операторы
Добавляет смещение к значению указателя. |
|
Определяет, равны ли два заданных экземпляра класса IntPtr. |
|
Преобразует 32-разрядное целочисленное значение со знаком в IntPtr. |
|
Преобразует 64-разрядное целочисленное значение со знаком в IntPtr. |
|
Преобразует значение заданной структуры IntPtr в формат 32-разрядного целого числа со знаком. |
|
Преобразует значение заданной структуры IntPtr в формат 64-разрядного целого числа со знаком. |
|
Преобразует значение заданной структуры IntPtr в указатель на незаданный тип. Этот интерфейс API CLS-несовместим. |
|
Преобразует заданный указатель на незаданный тип в IntPtr. Этот интерфейс API CLS-несовместим. |
|
Определяет, являются ли два заданных экземпляра класса IntPtr неравными. |
|
Вычитает смещение из значения указателя. |