LINUX.ORG.RU
ФорумTalks

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

 ,


0

3

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

★★★★★

В плане действий со строками строка как структура из указателя и длины, куда удобнее.

тогда нужно вести подсчет длины где-то. это требует цпу. в отличие от тупых перескоков до «нуля».

тактоэкономия наверно была в головах у людей, ценили машинное время

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

тогда нужно вести подсчет длины где-то. это требует цпу. в отличие от тупых перескоков до «нуля».

Так длину то все равно считать надо для большинства операций со строкой.

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

Или, например, хочешь ты вернуть подстроку из строки. С си-строками тебе придется выделить новую память, скопировать туда подстроку и завершающий 0. А с строкой в виде структуры тебе достаточно вернуть структуру с указателем на начало и длиной. И никаких аллокаций. Надо ли говорить, что аллокация - дело небыстрое?

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

Большинство операций со строками становятся куда проще при явном указании длины строки

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

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

Deleted
()

Почему же не ее выбрали?

Потому что создавалось это на таких древних калькуляторах, что жмотились там из-за каждого такта и байта. Но при этом перспективу уже оценивали.

Вот и выходит, что для нуль-терминированной строки в однобайтной кодировке требуется один байт, чтобы ограничить строку. При этом сама строка может быть любой длины, от одного символа до всей доступной памяти. А вот длина уже ограничивает размер строки. Минимум два байта для компов тех времён. (Для реальных программ.)

Добавьте к этому то, что 80-90% операций над строками это склейка и сканирование и окажется, что неудобствами можно и пренебречь.

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

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

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

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

а может исторически так сложилось, что такой формат строк был где-то. (или «аппаратно» поддерживался чем-то).

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

Добавьте к этому то, что 80-90% операций над строками это склейка и сканирование и окажется, что неудобствами можно и пренебречь.

для си-строк склейка делается так:

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

для строк с явно указанной длиной:

берем уже готовые длины строк, суммируем и дальше аналогично.

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

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

А когда склейка сделана - длину можно и забыть. Дальше она не нужна.

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

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

заговор

откуда по твоему во всяких бд и прочих «хитрых» программах любовь к строкам ограниченной длины?

n_play
()
Ответ на: заговор от n_play

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

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

Ты, наверное, не кодил на Turbo Pascal 7? Там как раз были строки, с длиной в первом байте. Я не писал серьёзных программ, но даже в игрушечных примерах быстро упираешься в лимит в 255 байт.

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

Доводилось в школе. Но, например, 32 бит хватит на строку любой разумной длины. А обработка данных размером более 4 гб это, как правило, не работа со строками, а что-то другое. И там точно стоит хранить длину, т.к. искать завершающий ноль в 4 гб это очень грустное занятие

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

Ты вопрос свой помнишь? Какие 32 бита в то время? Тогда это были бы один или два байта (если они по 8 бит).

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

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

как в примере с возвратом подстроки

А всё-таки, что тебе мешает сделать свой тип для строки, {size_t len; char *data;}, и использовать его?

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

В турбопаскале пацаны уже вовсю на PChar рассекали. Который собственно эцсамое и есть.

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

32 бита это 4 байта. Что на 3 байта длиннее, чем завершающий 0, но зато куда удобнее и экономит ресурсы процессора, которых в то время тоже было не очень.

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

что мешает их определить?

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

труднее вернуть подстроку из строки, т.к. нужно вставить завершающий ноль

Наоборот. Можно вставить '\0' после последнего символа, а затем дать ссылку на начало подстроки через «str + N», где N - смещение укащателя на N символов. Работает для однобайтных кодировок, да.

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

Можно вставить '\0' после последнего символа

Поздравляю, ты испортил исходную строку.

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

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

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

то для выделения подстроки создаётся копия

Я знаю. Что можно извратиться.

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

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

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

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

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

Зачем пересчитывать если операции со строками всё равно побайтовые? Если только для динамического выделения буфера чтобы туда влез результат.

saahriktu ★★★★★
()

Кто мешает тебе определить другой тип строк? С указателем и длиной...

anto215 ★★
()

Операция чтения строки будет выполняться дольше если не использовать завершающий ноль, а хранить длину. Ну и самих строк в памяти можно будет разместить меньше, это кстати до сих пор актуально для всякий устройств где 512kb памяти, а кроме строк нужно разместить и другие данные.

nikita-b
()
Последнее исправление: nikita-b (всего исправлений: 1)

В плане действий со строками строка как структура из указателя и длины, куда удобнее. Почему же не ее выбрали?

А зачем это «выбирать»? Там, где захотелось (см. строки в ядре винды, например), ровно такие строки и есть.

Си это не эти ваши ржавые поделия — тут разработчик волен выбирать то, что хочет.

Deleted
()
Последнее исправление: Deleted (всего исправлений: 1)

Потому что иначе это «тормоз by design» и изличная сущность, если тебе этот конец строки нафиг не сдался (например, ты уже знаешь, сколько байтов с периферного устройства тебе надо считать, и, желательно, по быстрее). В первом месте - изящный универсальный тип для хранения скопления байтов, строка - потом.

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

Так длину то все равно считать надо для большинства операций со строкой.

false. В очень многих типичных случаях знать её совершенно не обязательно. А в тех случаях, когда надо и не один раз, и так делают somestr_len = strlen(somestr); безо всяких отдельных типов.

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

В 90% случаев новая строка больше не модифицируется, так что те же два strlen'а на операцию, что и у твоих строк. Для 10% оставшихся случаев см. выше.

А с строкой в виде структуры тебе достаточно вернуть структуру с указателем на начало и длиной

«Вы прослушали лекцию о правильном отстреливании ног в С».

P.S. Тебя Царь покусал?

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

Я не писал серьёзных программ, но даже в игрушечных примерах быстро упираешься в лимит в 255 байт.

Я писал и довольно много, ограничение не сильно мешало. Только пару программ припомню, которые на PChar'е писались вместо родных строк по этой причине. Больше боли доставляло то, что все внешние-то интерфейсы по большей части сишные, а внутренние на родных строках, вот и гоняешь строки из одного формата в другой.

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

Для выделения подстрок? Нет там необходимости считать. И даже для последовательного вывода строк нет необходимости считать. Можно обходиться и без объединения строк. Пример:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main(int argc, char **argv){
        long long x, d = 2;
        char llbuf[64];
        if(argc < 2){
                printf("usage: fmdl number\n");
                return 1;
        }
        x = atoll(argv[1]);
        fputs(argv[1], stdout);
        fputs(" = ", stdout);
        while (d <= x){
                if(x%d == 0){
                        sprintf(llbuf, "%ld", d);
                        fputs(llbuf, stdout);
                        if(x != d) fputs(" * ", stdout);
                        x /= d;
                        d = 2;
                        continue;
                }
                d++;
        }
        putchar(';');
        putchar('\n');
        return 0;
}
Эта программа раскладывает число на множители и выводит всё это одной строкой вида
52952148 = 2 * 2 * 3 * 3 * 61 * 24113;
И при этом обходится без создания буфера под эту строку и последовательного объединения кучи строк в этот буфер.

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

Практика показывает что ты неправ и дилетант

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

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

эта овечка врёт

Harald ★★★★★
()

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

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

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

Что там гонять-то:

s[Length(s)+1]:=#0;
...
ext_routine(@s[1]);

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

про дельфи имел ввиду

Так там #0 в конце для AnsiString поддерживался компилятором, а также PChar(s) учитывал начало данных строки.
Что гонять-то?

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

часто делаются буферы фиксированной длины, и туда всё пишется (а потом получаются ошибки переполнения, да)

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

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

в настоящих программах всегда так и делается - людям же лень вручную этим заниматься ;)

в топике еще нет этой ссылки! http://russian.joelonsoftware.com/Articles/BacktoBasics.html

Маляр Шлемиэль подрядился красить пунктирные осевые линии на дорогах. В первый день он получил банку краски, поставил её на дорогу, и к концу дня покрасил 300 метров осевой линии. «Отлично!» сказал прораб, «быстро работаешь!» — и заплатил ему копейку.

На следующий день Шлемиэль покрасил 150 метров. «Мда, это, конечно, не так здорово, как вчера, но приемлемо.» — сказал прораб и заплатил ему копейку.

На следующий день Шлемиэль покрасил 30 метров. «Всего лишь 30!» заорал прораб. «Это никуда не годится! В первый день было в десять раз больше! В чём дело?»

«Ничего не могу поделать,» — говорит Шлемиэль. «Каждый день я ухожу всё дальше и дальше от банки!»

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