![]() |
Модераторы: Daevaorn |
![]() ![]() ![]() |
|
Thunderbolt |
|
||||||||||
![]() DevRel ![]() Профиль Группа: Участник Сообщений: 122 Регистрация: 7.11.2007 Где: Тула Репутация: 10 Всего: 16 |
Что такое size_t и ptrdiff_t
Андрей Карпов ООО "Системы программной верификации" Сентябрь 2009 Аннотация Введение Тип size_t Тип ptrdiff_t Переносимость size_t и ptrdiff_t Безопасность типов ptrdiff_t и size_t в адресной арифметике Быстродействие кода, использующего типы ptrdiff_t и size_t Рефакторинг кода с целью перехода на типы ptrdiff_t и size_t Библиографический список Аннотация Статья поможет читателю разобраться, что представляют собой типы size_t и ptrdiff_t, для чего они нужны и когда целесообразно их использование. Статья будет интересна разработчикам, начинающим создание 64-битных приложений, где использование типов size_t и ptrdiff_t обеспечивает быстродействие, возможность работы с большими объемами данных и переносимость между разными платформами. Введение Типы size_t и ptrdiff_t были созданы для того, чтобы осуществлять корректную адресную арифметику. Долгое время было принято считать, что размер int совпадает с размером машинного слова (разрядностью микропроцессора) и его можно использовать в качестве индексов, для хранения размеров объектов или указателей. Соответственно адресная арифметика также строилась с использованием типов int и unsigned. Тип int используется в большинстве обучающих материалов по программированию на Си и Си++ в телах циклов и в качестве индексов. Практически каноническим выглядит пример следующего вида:
Как видно, не так просто выбрать тип переменной для хранения указателя или размера объекта. Чтобы наиболее красиво решить эту проблему и появились типы size_t и ptrdiff_t. Они гарантированно могут использоваться для адресной арифметики. Теперь каноническим должен стать следующий код:
Тип size_t Тип size_t - базовый беззнаковый целочисленный тип языка Си/Си++. Является результатом выполнения оператора sizeof. Размер типа выбирается таким образом, чтобы в него можно было записать максимальный размер теоретически возможного массива. Например, на 32-битной системе size_t будет занимать 32-бита, на 64-битной - 64-бита. Другими словами в переменную типа size_t может быть безопасно помещен указатель. Исключение составляют указатели на функции классов, но это особый случай. Тип size_t обычно применяется для счетчиков циклов, индексации массивов, хранения размеров, адресной арифметики. Максимально допустимым значением типа size_t является константа SIZE_MAX. Тип ptrdiff_t Тип ptrdiff_t - базовый знаковый целочисленный тип языка Си/Си++. Размер типа выбирается таким образом, чтобы в него можно было записать максимальный размер теоретически возможного массива. На 32-битной системе ptrdiff_t будет занимать 32-бита, на 64-битной - 64-бита. Как и в size_t в переменную типа ptrdiff_t может быть безопасно помещен указатель, за исключением указателя на функцию класса. Также ptrdiff_t является результатом выражения, где один указатель вычитается из другого (ptr1-ptr2). Тип ptrdiff_t обычно применяется для счетчиков циклов, индексации массивов, хранения размеров, адресной арифметики. Переносимость size_t и ptrdiff_t Код созданный с использованием типов size_t и ptrdiff_t является хорошо переносимым. Размер size_t и ptrdiff_t всегда совпадают с размером указателя. По этой причине именно эти типы следует использовать в качестве индексов больших массивов, для хранения указателей и арифметики с указателями. Разработчики Linux приложений часто используют для этих целей тип long. В рамках 32-битных и 64-битных моделей данных, принятых в Linux это действительно работает. Размер типа long совпадает с размером указателя. Но такой код несовместим с моделью данных Windows и соответственно его нельзя считать хорошо переносимым. Более правильным решением будет использование типов size_t и ptrdiff_t. Разработчики Windows в качестве альтернативы size_t и ptrdiff_t могут использовать типы DWORD_PTR, SIZE_T, SSIZE_T и так далее. Но желательно также ограничиваться типами size_t и ptrdiff_t. Безопасность типов ptrdiff_t и size_t в адресной арифметике Проблемы адресной арифметики стали активно проявлять себя с началом освоения 64-битных систем. Наибольшее число проблем при переносе 32-битных приложений на 64-битные системы связанно с использованием неподходящих для работы с указателями и массивами типов, таких как int и long. Этим проблемы переноса приложений на 64-битные системы не ограничивается, но большинство ошибок связанны именно с адресной арифметикой и работе с индексами. Возьмем самый простой пример:
Пример еще одной дремлющей ошибки, которая проявит себя при определенном сочетании входных данных (значении переменных A и B):
Приведенные ошибки можно легко избежать, используя тип size_t или ptrdiff_t. В первом случае, если тип переменной "i" будет size_t, то не возникнет зацикливания. Во втором, если мы используем типы size_t или ptrdiff_t для переменных "A" и "B", то корректно распечатаем число "3". Сформулируем совет: везде, где присутствует работа с указателями или массивами следуeт использовать типы size_t и ptrdiff_t. Более подробно с тем, каких ошибок можно избежать, используя типы size_t и ptrdiff_t можно познакомиться в следующих статьях:
Быстродействие кода, использующего типы ptrdiff_t и size_t Использование типов ptrdiff_t и size_t в адресной арифметике помимо повышения надежности кода может дать дополнительный выигрыш в производительности. Например, использование в качестве индекса типа int, размерность которого отличается от размерности указателя приводит к тому, что в двоичном коде будут присутствовать дополнительные команды преобразования данных. Речь идет о 64-битном коде, в котором размер указателей стал равен 64-битам, а размер типа int остался 32-битным. Показать короткий пример преимущества size_t над unsigned не простая задача. Чтобы быть объективным необходимо использовать оптимизирующие возможности компилятора. А два варианта оптимизированного кода часто становятся слишком непохожим, чтобы легко было продемонстрировать отличие. Попытка создать нечто близкое к простому примеру увенчалась успехом только с шестой попытки. И все равно пример не идеален, так как показывает не лишние преобразование типов данных, о которых сказано выше, а то, что компилятор смог построить более эффективный код при использовании типа size_t. Рассмотрим код программы, переставляющий элементы массива в обратном порядке:
![]() Рисунок N1. Сравнение 64-битного ассемблерного кода при использовании типов unsigned и size_t Компилятор смог построить более лаконичный код, когда использовал 64-битные регистры. Автор не берется утверждать, что код, созданный при использовании типа unsigned (текст слева), будет работать медленнее, чем код с использованием size_t (текст слева). Сравнить скорость выполнения кода на современных процессорах крайне сложная задача. Но из примера видно, что когда компилятор работает с массивами, используя 64-битные типы, он может строить более короткий и быстрый код. По личному опыту автора, грамотная замена типов int и unsigned на ptrdiff_t и size_t может дать на 64-битной системе дополнительный прирост производительности до 10%. С одним из примеров увеличения скорости от использования типов ptrduff_t и size_t можно познакомиться в четвертой главе статьи "Разработка ресурсоемких приложений в среде Visual C++" [5]. Рефакторинг кода с целью перехода на типы ptrdiff_t и size_t Как читатель уже убедился, использование типов ptrdiff_t и size_t имеет ряд преимуществ для 64-битных программ. Но и заменить, скажем все unsigned на size_t - не является выходом. Во-первых, это не гарантирует корректность программы на 64-битной системы. Во-вторых, скорее всего из-за такой замены возникнут новые ошибки, нарушится совместимость форматов данных и так далее. Не стоит забывать, что после такой замены существенно возрастет и объем потребляемой программой памяти. Причем увеличение объема требуемой памяти может замедлить работу приложения, так как в кэш будет помещаться меньше объектов, с которыми идет работа. Следовательно, внедрение в старый код типов ptrdiff_t и size_t является задачей постепенного рефакторинга, требующего большого количества времени. Фактически необходимо просмотреть весь код и внести необходимые исправления. Такой подход практически является слишком дорогостоящим и неэффективным. Можно предложить 2 варианта:
Библиографический список
--------------------
Карпов Андрей, DevRel в PVS-Studio. |
||||||||||
|
|||||||||||
niXman |
|
|||
![]() Бывалый ![]() Профиль Группа: Участник Сообщений: 211 Регистрация: 4.7.2009 Репутация: 0 Всего: 0 |
Чел, ты реально нужным делом занимаешься. Прочитал все твой статьи на этом форуме, нашел ответы на интересующие меня вопросы.
В общем, респект и уважуха! п.с. жаль не могу плюсик поставить, не дорос еще ![]() Это сообщение отредактировал(а) niXman - 6.10.2009, 22:36 --------------------
if ( !c++ ) { *power--; this->wussiness += 10;} |
|||
|
||||
![]() ![]() ![]() |
Правила форума "С++:Общие вопросы" | |
|
Добро пожаловать!
Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, Earnest Daevaorn |
0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей) | |
0 Пользователей: | |
« Предыдущая тема | C/C++: Общие вопросы | Следующая тема » |
|
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности Powered by Invision Power Board(R) 1.3 © 2003 IPS, Inc. |