LINUX.ORG.RU
ФорумTalks

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

 ,


0

3

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

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

мне от вашего «память дешевая» скоро придется вторую плашку на 8Гб ставить! По рукам бить нужно

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

структура из указателя и длины

8 байт

завершающий ноль

1 байт

Все равно память выделяется выровненными блоками. сделаешь ты malloc(1) или malloc(4) (а может и malloc(8) ), в современных реализациях выделится одно и то же количество памяти.

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

Все равно память выделяется выровненными блоками. сделаешь ты malloc(1) или malloc(4) (а может и malloc(8) ), в современных реализациях выделится одно и то же количество памяти.

Это не повод жрать память не в меру.

Java, например, делает именно так, как ты предлагаешь. Почему бы тебе не пойти и не онаниписать на ней?

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

Вот тебе пример строки

{
   "offset": [0.0, 0.0, 0.0],
   "E2": 0.1,
   "N2": 0.1,
}

ты ее распарсил одним легковесным json парсером, и вот ты хочешь прочитать E2. У тебя один указатель указывает на '0', а другой указатель на ',', идущую за «0.1»

И хочешь сделать atof(), но нет, т.к. atof() хочет завершающий 0

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

Там, где имеют дело с словарями в много сотен тысяч или миллионов слов, производительность итерации по словарю критична. А iter += 64 или iter += str.len + 8 куда быстрее, нежели поиск завершающего нуля. С которым поиск нужного слова может быть довольно долгим процессом. Представь, что гугл тебе дает ответ через 10 минут после запроса. Зато экономит кучу памяти у себя на серверах!

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

Эээээ. Причем тут словари? Словари — абсолютно другая структура данных.

С которым поиск нужного слова может быть довольно долгим процессом.

Хранение данных в неподходящей структуре — действительно долго. Например, хранение структурированных данных в гребаной строке.

Кстати, в go есть прямо то, что ты хочешь — слайсы: https://blog.golang.org/go-slices-usage-and-internals

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

Да понатыкай нулей, лол, у тебя все равно после значения обязательно будет либо какая-то пунктуация, либо конец строки, это ж json

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

Спасибо, понятно. Но дело, однако, в том, что Си не гарантирует, что разность указателей равна размеру первого поля. Даже если выделять сразу память под всю структуру, то разность указателей будет равна максимально возможной длине первого поля.

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

Ну и на правах толстовброса

А вообще, пора прекращать дрочить на указатели и начинать жить. В нормальных языках уже давно можно сделать так:

var data = struct {
   Offset []float64
   E2 float64
   N2 float64
}{}
json.Decode(&data, `{
   "offset": [0.0, 0.0, 0.0],
   "E2": 0.1,
   "N2": 0.1,
}`)

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

derlafff ★★★★★
()

Почему-то никто не упомянул самую главную причину, отчего строки такие.

В Unix (и C) “everything is a file”, то есть последовательность байт неизвестной наперед длины от open() до EOF. Конечно, не обошлось без влияния ленточных накопителей, поэтому всё вертится вокруг потоковой обработки с дешевым последовательным чтением и жутко дорогим rewind.

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

В произвольном бинарном файле может быть сколько угодно нулей в середине. Так что завершающий нуль не отсюда

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

Я разве говорил о каком-то конкретном символе конца файла? На ленте это tape mark, в терминале — Ctrl-D в начале строки, Ctrl-Z в CP/M и DOS, и так далее. Ноль просто удобен именно для строк, благо даже готовые команды в некоторых процессорах уже существовали.

Сама концепция терминированных строк произошла от терминированных файлов в Unix. Точнее, «произошла» — неверное слово. Имеет общие корни, так скажем. Одновременно зародилась.

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

Плохой пример, тут твои строки никаких преимуществ не дают.

switch (s[i]) {
  case ',': 
    s[i] = #0; 
    parse_value(value_begin);
    break;
  case '0'..'9': 
    value_begin = &s[i];
    break;
}
i++;

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

ты портишь исходную строку

Она дорога тебе, как память? Из приведённой задачи совершенно не следует, что её нельзя менять.

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

То, что в C нет встроенного uint16_t, uint32_t, etc, это еще один косяк, которого я не понимаю.

1972 год. Электронные счёты, размером с футбольное поле. Неустаканившиеся стандарты. Байты длиной 4, 5, 6, 7, 8 и 9 бит. (Кстати, 9 далеко не экзотика. 36 и 18 бит компы выпускал DEC и кое-кто ещё. Там байт был как раз 9 бит. И 8/16-бит регистра для переменной такого размера просто не было.)

При этом поймите, что это не настолько далёкое прошлое. В 70-х их выпускали, в 80-х они по инерции продолжали эксплуатироваться, потому что софт был написан. Вот эта катавасия и тянулась вплоть до C89.

Но теперь можете быть довольны. В C99 постановили всё это легаси выбросить и ограничиться только 8-битными байтами. Так что теперь в stdint.h официально определены все эти типы.

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

1 байт

Плохо считаете. 8 и 5 байт. (На 32-битной архитектуре.)

Указатель (4 байт) на структуру (длина (4 байт) + блок символов): 8 байт.

и

Указатель (4 байт) на структуру (блок символов +0x0 (1 байт)): 5 байт.

Ну или 4 против 3-х на 16-битной архитектуре.

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

в современных реализациях выделится одно и то же количество памяти.

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

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

Указатель (4 байт) на структуру (длина (4 байт) + блок символов): 8 байт.

Нет,

Указатель (4 байт) на структуру (длина (4 байт) + указатель на блок символов (4 байт)): 12 байт

В этой вашей сишечке (вроде) можно в саму структуру засунуть только фиксированный по размеру char[].

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

указатель на блок символов (4 байт)

Упрлс? Выделяем блок памяти нужного размера, записываем длину, прибавляем к указателю sizeof(size_t) и возвращаем.

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

Указатель (4 байт) на структуру (блок символов +0x0 (1 байт)): 5 байт.

Всё же отдельного типа в Си нет, а строка, по определению, — вовсе не указатель. Строка определена как массив типа char (какого бы размера он ни был), последний элемент которого равен нулю. Поэтому накладные расходы — ровно один символ на строку.

Альтернативой предлагается агрегатный тип: (длина + указатель на массив) или (длина + массив). Соответственно, в первом случае (sizeof(size_t) + sizeof(char *)), во втором только sizeof(size_t).

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

Вот еще насчет определения их в stdint.h. Зачем их запихнули в него? Всяки же там int, long int, char и прочее, доступны «сразу», без подключения каких-либо заголовочных файлов

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

То сейчас. Сейчас уже есть плюсы, в которых строки сделаны более человеческим образом. А тогда было так: «64 килобайт хватит всем».

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

Только длины: указатель, так-то, и С-строкам тоже нужен. Кстати, на x86 процессорах, забывая о древних ОС и прочем, указатель 8 байт.

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

Зачем их запихнули в него?

7.18.1.1 Exact-width integer types

The typedef name intN_t designates a signed integer type with width N, no padding bits, and a two’s complement representation. Thus, int8_t denotes a signed integer type with a width of exactly 8 bits.

The typedef name uintN_t designates an unsigned integer type with width N. Thus, uint24_t denotes an unsigned integer type with a width of exactly 24 bits.

These types are optional. However, if an implementation provides integer types with widths of 8, 16, 32, or 64 bits, no padding bits, and (for the signed types) that have a two’s complement representation, it shall define the corresponding typedef names.

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

Господи, ты в путях сочинения пишешь, что ли?7?7

redgremlin ★★★★★
()

Почему на арене цирка снова cvs-255?

Xellos ★★★★★
()
Ответ на: комментарий от baka-kun

Поэтому накладные расходы — ровно один символ на строку.

Там derlafff захотел приплюсовать к ним ссылку на массив, лежащую на стеке. В принципе имеет право, если уж мы до байта тут высчитываем. :)

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

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

А в случае с привычными С-строками, разве, будут совместимы?
Если на разных архитектурах char из разного количества бит, то в конце строки может не получится нулевого байта.

ls-h ★★★★★
()
Ответ на: комментарий от baka-kun

накладные расходы — ровно один символ на строку

Но есть ограничение - строка не может содержать в себе '\0'. Для современного ада состояния с юникодом это важно - utf32-строка например может содержать (и содержит) нулевые байты.

Если держать отдельно размер строки это ограничение снимается, можно хранить любые данные.

Можно, кстати, размер хранить не в фикисрованном количестве байт, а придумать какую-нибудь другую схему. Типа 2 бита отдать под количество байт размера. Ну, если уж экономить байтики

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

Куча реализаций, включая и вариант для «если уж экономить байтики»

Бегло просмотрел. Для «экономить байтики» вижу только вот это: http://www.and.org/ustr/

Какая-то там своеобразная экономия:

By comparison ustr does one allocation of 6bytes by default, in a both 32bit and 64bit environments

Т.е. минимум 6 байт на строку? Там, правда, из этих 6 байт 2 байта reference counter. Ну, ок, экономно

Или я не ту библиотеку смотрел?

Deleted
()

В Паскале посчитали, что 255 символов хватит всем. В Си же строки уже десятки лет ограничены только объемом ОЗУ. Второй подход более перспективный.

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

Нет никакого смысла в строках в память длиной, но с завершающим 0

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

utf32-строка например может содержать (и содержит) нулевые байты.

Строки состоят не из байтов, это массив символов. В UTF-32 строке их размер равен 32 битам. Да, некоторые байты в символе могут быть равны нулю, но это ни на что не влияет.

Если держать отдельно размер строки это ограничение снимается, можно хранить любые данные.

Тебе и так никто не мешает оперировать со строками в своем коде как с массивами и хранить длину. На самом деле, определение строки как «массив char, завершающийся признаком окончания» актуально в первую очередь в качестве универсального представления для общения с внешним миром. Ну нету в текстовых файлах длины перед каждой строкой, и тем более никто заранее не знает (даже я) сколько будет символов в этом предложении.

Это задача программиста — выбрать подходящие структуры для данных приложения. Мне «строки Си» не мешают оперировать со строками как с массивами известной длины, да ещё и размещать их на дереве.

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

Ну так я это же и сделал. :)

Как я понял, derlafff прямо процитировал ТС: «структура из указателя и длины», про указатель на эту структуру он не писал, поэтому разница при наивной реализации будет sizeof(size_t) + sizeof(char *) - sizeof(char). То есть 15 байт у большинства.

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

В UTF-32 строке их размер равен 32 битам. Да, некоторые байты в символе могут быть равны нулю, но это ни на что не влияет.

Тогда получается, что нужно отдельно хранить размер терминатора (он будет разный для ASCIIZ, UTF-16 и UTF-32) или тип строки и собственно этот терминатор. Для UTF-32 overhead будет уже 5 байт. И при этом остается ограничение - в строке не может быть терминирующего символа.

Если мы все еще о том, что null-termination дает экономию в размере, то выходит что экономия сомнительная.

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

Тогда получается, что нужно отдельно хранить размер терминатора…

Нет, просто строки ASCIIZ, UTF-16 и UTF-32 — разные. У них разный базовый тип, разный размер символа.

И при этом остается ограничение - в строке не может быть терминирующего символа.

Так же как в файле на магнитной ленте не может быть tape mark, а в текстовом файле CP/M и, для совместимости, MS DOS символа Ctrl-Z.

baka-kun ★★★★★
()
Ответ на: комментарий от ls-h

Если char (а не int) разный то да. Хотя, 1-байтные будут совместимы с любыми многобайтными.

next_time ★★★★★
()

Почему школьные каникулы всё ещё продолжаются в феврале месяце?

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