Comments 38
каждая строка занимает 20+(n/2)×4
А можно поподробней, почему n/2?
В статье Джон Скит не расписывает механизм хранения строк детально, да и я не могу утверждать, что являюсь экспертом во внутренностях .NET, однако полагаю, формула 20+(n/2)×4 связана с тем, что каждый символ хранится в UTF-16 и занимает 2 байта. 20 — это вспомогательная информация. А вот почему
(n/2)×4
, а не n×2
— к сожалению, не знаю.Возможно, символы как раз хранятся по 2 в 32битных ячейках. Соответственно, суррогатные пары хранятся каждая в целой ячейке, а формула их просто не учитывает.
Возможно, память выделяется дискретами по 32 бита?
А вот почему (n/2)×4, а не n×2 — к сожалению, не знаю.
Есть мнение, что это оптимизация доступа к памяти.
Ну по виду обычное выравнивание в 4 байта.
В оригинальной статье написано:
> strings take up 20+(n/2)*4 bytes (rounding the value of n/2 down)
(n / 2) — целочисленное деление (13 / 2 == 6; 12 / 2 == 6)
> strings take up 20+(n/2)*4 bytes (rounding the value of n/2 down)
(n / 2) — целочисленное деление (13 / 2 == 6; 12 / 2 == 6)
В таком случае, строка, длинной в 1 символ, будет занимать 20 + (1/2) * 4 = 20 байт? Значит один из символов входит во вспомогательную информацию?
Ну это вопрос не ко мне, а к Джону Скиту или разработчикам CLR :)
20 + (1/2) * 4 = 20 + 0.5 * 4 = 22
Округлить 0.5 в меньшую сторону забыли.
Ну почему же забыли!
Возможно, это 16 + (n/2+1)*4 = 20 + (n/2)*4
Возможно, это 16 + (n/2+1)*4 = 20 + (n/2)*4
ничего не забыл, так что все верно, вообще в таких случаях нужно всегда 1-м делать умножение и потом уже делить, тогда результат будет намного точнее
Вы видели rounding the value of n/2 down?
И как можно в «таких случаях» делать умножение первым, когда скобки явно указывают на порядок вычисления?
И как можно в «таких случаях» делать умножение первым, когда скобки явно указывают на порядок вычисления?
В данном случае либо недопонимание или автор не дорассказал. Если n = 1, то получается 0, что неверно, не может один символ храниться в 0 байтах, а если n = 3, то получится 4 байта, как вы себе представляете 3 символа в 4-х байтах? Ну и т. д.
Формулу (n/2)*4 нужно читать как два символа в 4-х байтах, причем конечный результат всегда выровнен по границе 4, а для этого нужно было применять такую формулу ((n+1)/2) * 4
Формулу (n/2)*4 нужно читать как два символа в 4-х байтах, причем конечный результат всегда выровнен по границе 4, а для этого нужно было применять такую формулу ((n+1)/2) * 4
Легко, если на самом деле данные об объекте занимают 18 байт (но в другой статье Скит говорит о 14 байтах), а выровнено в 4 байта, тогда два байта из 20 можно использовать под один символ. И формулу нужно читать как Math.Floor(n/2)*4.
Оказывается все проще: пустая строка тоже занимает 2 байта (т.е. значение null). А его формула просто «убирает» из 20 эти 2 байта с помощью округления, если строка не null.
Сам Скит скинул ссылку на более новую статью, где размер уже высчитывается как 14 + length * 2 для x86. Какая статья точнее он не уверен.
Сам Скит скинул ссылку на более новую статью, где размер уже высчитывается как 14 + length * 2 для x86. Какая статья точнее он не уверен.
Нашёл такую информацию:
Насколько правдива и актуальна — неизвестно.
A string is composed of:
• An 8-byte object header (4-byte SyncBlock and a 4-byte type descriptor)
• An int32 field for the length of the string (this is returned by String.Length).
• An int32 field for the number of chars in the character buffer.
• The first character of the string in a System.Char.
• The rest of the string in a character buffer, terminating with a null terminator char.
Насколько правдива и актуальна — неизвестно.
По поводу интернирования можно было бы добавить, что его можно отлючить специальным атрибутом для проекта.
Интересно, в чём главные отличия между JVM и .NET intern pool'ом?
Если внимательно вчитаться, то можно увидеть, что имелось ввиду, что интернирование является фичей не конкретного языка на платформе .NET, а платформы в целом. О его наличии или отсутствии в других языках или платформах не сказано ровным счётом ничего.
>>Метод IndexOf будет учитывать эсцет как «ss» (двойное «s»), но вот если вы используете одну из перегрузок CompareInfo.IndexOf, где укажете CompareOptions.Ordinal, то эсцет будет обработан правильно.
Странно в данном случае говорить о «правильности». Это особенности, которые надо знать (не все из них я знаю), но то что Zurich и Zürich — одно и тоже вполне нормально. Про такое поведение, насколько я помню, написано в CLR via C#, и плох разработчик, который ни разу его не читал.
Странно в данном случае говорить о «правильности». Это особенности, которые надо знать (не все из них я знаю), но то что Zurich и Zürich — одно и тоже вполне нормально. Про такое поведение, насколько я помню, написано в CLR via C#, и плох разработчик, который ни разу его не читал.
В оригинале это предложение звучит как «IndexOf will treat the eszett as the same as „ss“, unless you use a CompareInfo.IndexOf and specify CompareOptions.Ordinal as the options to use.», слово «правильно» подобрал я при переводе. Вместе с тем, насколько я понял Джона Скита, его «смущает» (если можно так выразиться) не столько то, как обрабатываются подобные символы, а то, что разными функциями они обрабатываются по-разному.
В данном случае все логично. Есть поведение по-умолчанию, но иногда же надо работать с точностью до символа, для таких случаев есть опции.
В случае Equals и Compare тоже все логично. Согласно МСДН: первый сравнивает значения, второй определяет алфавитный порядок. Е и Ё, к примеру, тоже приравниваются в словарях (бумажных).
Это все — логичное поведение, таким образом спроектированное. Другой вопрос, что при переходе с того же c++ (или другого языка постарше) это непривычное поведение.
В случае Equals и Compare тоже все логично. Согласно МСДН: первый сравнивает значения, второй определяет алфавитный порядок. Е и Ё, к примеру, тоже приравниваются в словарях (бумажных).
Это все — логичное поведение, таким образом спроектированное. Другой вопрос, что при переходе с того же c++ (или другого языка постарше) это непривычное поведение.
плох разработчик, который ни разу его не читалЧем плох?
UFO just landed and posted this here
Как жаль Как жаль, я тоже недавно хотел перевести эту статью…
Помоему это наследние этой статьи habrahabr.ru/post/164193/
Только в более академической форме!!!
Помоему это наследние этой статьи habrahabr.ru/post/164193/
Только в более академической форме!!!
… символом Юникода в диапазоне от U+0000 до U+FFFF ...
В оригинальной статье написано «character» (может для упрощения понимая?), хотя по стандарту Unicode правильно это называется «code point», т.к. там кроме «characters» есть куча управляемых непечатаемых символов. Всегда интересно было как корректно перевести «code point» на русский…
(англ. zero-width non-joiner character), что бы это не значило, чёрт возьми!
В некоторых языках, например в арабском, буквы одного слова могут менять свое начертание в зависимости от того, в какой части слова они находятся, т.н. «вязь». Вставленный между двумя буквами, non-joiner отключает этот механизм и буквы будут отображены так, как если бы они были написаны сами по себе, вне слова. Zero-with означает, что сам non-joiner не отображается. Обработка joiner'ов, mark-ов и прочих спецсредств юникода как символов — это ошибка реализации библиотек.
Sign up to leave a comment.
Строки в C# и .NET