LINUX.ORG.RU
ФорумTalks

почему строки в C такие, какие есть?

 ,


0

3

Чем мотивировано задание строки с помощью завершающего нуля? Ведь это довольно затрудняет действия со строками. Например, труднее вернуть подстроку из строки, т.к. нужно вставить завершающий ноль. В плане действий со строками строка как структура из указателя и длины, куда удобнее. Почему же не ее выбрали?

★★★★★
Ответ на: комментарий от stevejobs

Ага, вот в чем дело. Виноват PDP-7, который имел встроенный строковый тип с нулем на конце.

И вот в 2017 году до сих пор тянется наследие архитектурных особенностей PDP-7

cvs-255 ★★★★★
() автор топика
Ответ на: комментарий от cvs-255

экономия 3 байт

Если строки выделяются через какой-нить malloc то там ещё будет вагон и маленькая тележка накладных расходов. Странно что никто об этом не вспомнил :)

true_admin ★★★★★
()
Ответ на: комментарий от stevejobs

Это да. С другой стороны текст храниться может долго, а буфер - временный, по этому в общем зачёте может быть профит. Если всё написано правильно (не хилое такое допущение, как показала практика).

atrus ★★★★★
()
Ответ на: комментарий от cvs-255

А может в дальнейшем еще одну склейку делать? Что, по-новой длину считать?

Если у программиста в голове нет плана выполнения программы и он не знает что хранить, а что нет - да.

Мне кажется, или если строки склеиваются более одного раза, то выигрыш второго метода налицо?

Да много чего на лицо. Есть статья, раскрывающая некоторые подробности (The Most Expensive One-byte Mistake). Это же на PDP-11 было. 128K на первых моделях и многозадачное использование. Тут реально экономили и каждый байт и такт.

Кроме того изначально программы писали на ассемблере, а тут из-за лени и желания оптимизировать уже использовали нуль-терминированные строки. Строковых инструкций ещё не было, но обычно циклы пускают задом наперёд, потому что у процессора есть какая-нибудь инструкция, типа «перейди если в регистре ноль». И если регистр используется в качестве буфера под очередной символ, то...

atrus ★★★★★
()
Ответ на: комментарий от true_admin

Если строки выделяются через какой-нить malloc то там ещё будет вагон и маленькая тележка накладных расходов. Странно что никто об этом не вспомнил :)

Потому что этого не было. Вот исходник malloc PDP-11. Как видите, никакого выравнивания, просто выбирается первый подходящий по размеру свободный блок.

atrus ★★★★★
()
Ответ на: комментарий от i-rinat

Проверяет ZF, не само значение байта.

Да, но это работает во многих случаях. Побайтовый memcpy, например.

hateyoufeel ★★★★★
()
Ответ на: комментарий от atrus

Я не про выравнивание. malloc должен где-то хранить информацию о том что какой-то блок уже занят (struct map). Это тоже требует памяти.

true_admin ★★★★★
()
Ответ на: комментарий от true_admin

По этому в старых менеджерах памяти (хотя может и в современных, не проверял) хранился не список выделенных, а список свободных блоков. В теории это позволяет использовать 100% памяти. :)
Не говоря уже о том, что так проще искать память под запрошенный блок.

atrus ★★★★★
()
Последнее исправление: atrus (всего исправлений: 2)

Допустим, у нас есть словарь. Каждому слову соответствует 1 строка. Положим, средняя длина слова — 6 букв. Хранение длины слова в 4-байтной переменной увеличит размер необходимой для хранения строк памяти на 66%.

В тоже время, С используется не только на x86 архитектурах. Он рассчитан на использования в микроконтроллерах, например. А там, память важнее производительности.

next_time ★★★★★
()
Ответ на: комментарий от next_time

С другой стороны, наиболее часто используемая операция со строками — это не склейка строк, а чтение. Таким образом, и с точки зрения производительности С-строки достаточно эффективны.

next_time ★★★★★
()
Ответ на: комментарий от cvs-255

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

Не забывайте, что С кроссплатформенный настолько, насколько только может быть кроссплатформенным ЯП.

next_time ★★★★★
()
Ответ на: комментарий от PHPFan

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

cvs-255 ★★★★★
() автор топика
Ответ на: комментарий от next_time

В случае словаря, равно как и баз данных, оправдано вообще строка фиксированной длины. И притом длины, кратной 4 байтам (или как выровнена память на целевой архитектуре). Т.е. осмысленно было бы хранить слова в массивах по 64, например, байт. Это неэффективно по памяти, и накладывает ограничение по длине слова, но эффективно по скорости.

cvs-255 ★★★★★
() автор топика
Ответ на: комментарий от next_time

сделает простое сравнение строк чрезмерно дорогостоящей операцией.

Вместо одного сравнения аж два или три? А если окажется что строки одинаковой длины то ваще вешаться, да? :)

true_admin ★★★★★
()
Ответ на: комментарий от true_admin

Именно, если строки одинаковой длины — натурально вешаться, особенно, если строки достаточно длинные: 5-6 байт уже жирновато будет. Для 8-битного процессора и при частых операциях, разумеется.

next_time ★★★★★
()

Ах да, ещё проблема: если хранить длину строки в самой строке, то строки станут несовместимы друг с другом.

Поясню: в С89 нет способа создать, например, 4-байтную целочисленную переменную без undefined behavior. int, согласно стандарту — это вовсе не «4 байта» — это «неизвестно сколько байт, но не менее 1 char». Например, я видел платформы, где int — это 8 байт и где int — 2 байта. Даже размер char в битах и то не определён.

Т.о., С-строки, хранящие длину строки в самой строке и созданные на разных компьютерах, будут несовместимы между собой, даже, если они были созданы утилитой с одним исходным кодом на языке С.

next_time ★★★★★
()
Ответ на: комментарий от redgremlin

Я писал и довольно много, ограничение не сильно мешало.

а как было с открытием файлов, длина пути к которым была более 255 байт?

next_time ★★★★★
()
Ответ на: комментарий от next_time

а как было с открытием файлов, длина пути к которым была более 255 байт?

А никак. В DOS и ранних вендах максимальная длина пути (с именем файла и диском) была 260 байт. Сейчас расширили до ~ 32k байт. Или символов. Не помню.

Кстати, аналогичные ограничения есть и в *nix. В Linix максимальный путь к текущему каталогу - 4096 байт + 255 байт длина имени файла. В macOS - 1024 байт.

atrus ★★★★★
()
Ответ на: комментарий от next_time

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

true_admin ★★★★★
()
Ответ на: комментарий от next_time

наиболее часто используемая операция со строками — это не склейка строк, а чтение

Возьмем такую простую операцию чтения из строки, как чтение числа из строки, при том, что мы имеем указатель на начало числа в строке и указатель на конец числа (парсим файл). Как это предполагается делать? Портить исходную строку нельзя.

cvs-255 ★★★★★
() автор топика
Последнее исправление: cvs-255 (всего исправлений: 3)
Ответ на: комментарий от next_time

сделает простое сравнение строк чрезмерно дорогостоящей операцией.

А вот и нифига! Потому что если строки разной длины, то сравнив 4 байта, можно сразу сказать, что это разные строки, а не идти до самого конца.

cvs-255 ★★★★★
() автор топика

Чем мотивировано задание строки с помощью завершающего нуля?

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

Napilnik ★★★★★
()
Ответ на: комментарий от next_time

а как было с открытием файлов, длина пути к которым была более 255 байт?

find / -type f -print | wc -L
201



Каких, говоришь, файлов?

redgremlin ★★★★★
()
Ответ на: комментарий от cvs-255

мы имеем указатель на начало числа в строке и указатель на конец числа (парсим файл). Как это предполагается делать?

for (p = start; p < end; p++)
  n = 10*n + (char)(*p) - '0';
redgremlin ★★★★★
()
Ответ на: комментарий от n_play

Тогда даже символы экономили, не то что такты (% отсюда и лаконичность синтаксиса C.

aiqu6Ait ★★★★
()
Ответ на: комментарий от i-rinat

До меня начинает доходить, почему x86 - как винтовой цоколь лампочки... Это не легаси, это антропология.

Shadow ★★★★★
()
Ответ на: комментарий от cvs-255

в смысле? перевести строковое представление числа в int? int-а может не хватить, а если хватит, делается тривиально.

next_time ★★★★★
()
Ответ на: комментарий от cvs-255

Не успели продумать, как следует, а потом уже поздно было. Потдверждается тем, что в новых стендартах С/С++ уже всё есть.

Скорее всего, логика была, что если char — это минимально рабочая единица, то сложно добиваться поддержки компилятором uint8_t на тех машинах, где char — 16 бит, к примеру. Ну и с остальными типами по аналогии.

next_time ★★★★★
()
Ответ на: комментарий от true_admin

true_admin> Ну, с другой стороны, сейчас нет проблем с 32-битными микроконтроллерами, так что вешаться не обязательно.

Зато до сих пор 8-битные в ходу. И ещё долго будут.

Quasar ★★★★★
()
Ответ на: комментарий от cvs-255

cvs-255> Фанат AVR?

Ну почему сразу AVR? Ещё как минимум 8051 есть. Используется дофига где. А ещё есть малораспространённые специализированные 8-битные микроконтроллеры. Чаще всего в клавиатурах и мышках найти можно.

Quasar ★★★★★
()
Ответ на: комментарий от cvs-255

мы имеем указатель на начало числа в строке и указатель на конец числа (парсим файл).

Linux устроен так, что ты можешь обратиться по указателю на начало блока памяти и поместить оттуда некоторое количество слов в буфер. Зачем тебе указатель на конец числа? Тогда как использование размера числа правильнее и экономнее (как по памяти, так и по инструкциям).

iVS ★★★★★
()
Ответ на: комментарий от redgremlin

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

iVS ★★★★★
()

c нулем сравнивать удобно.

в pdp11 была инструкция TST которая ставила флажок.

i36_zubov
()
Ответ на: комментарий от Shadow

нет. это тупо требование обратной совместимости. плюс в те времена на чип тупо не помещалось дофига транзисторов.

i36_zubov
()
Ответ на: комментарий от Shadow

Это значит, что основные операции с ноль-завершаемыми строками, такие как strcpy(), записываются в одну ассемблерную команду.

Deleted
()

Потому что в C нет типа «строка». Поэтому и пользуются массивами/указателями на char/wchar_t .

Почему в стандартной библиотеке нет человеческой обёртки для строк - это вопрос из серии: «почему в стандарт C до сих не добавили feature_name?».

omnomnomnus
()
Ответ на: комментарий от cvs-255

В большинстве случаев - нет, не нужно. Если очень хочется, то можно оборачивать строку в структуру, в которой приведёшь длину отдельным поле, это провернуть ничего не стоит.

Bfgeshka ★★★★★
()
Ответ на: комментарий от redgremlin

самому вполне можно написать. Но городить свои велосипеды для стандартной операции - не естьхорошо

cvs-255 ★★★★★
() автор топика
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.