| Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате |
| Форум программистов > C/C++: Для новичков > "Какая угодна длина строки" (с) |
| Автор: Compositum 29.5.2011, 21:44 | ||||||||||||||||||||
| Язык программирования: C (не C++). Книга: "Язык программирования C"; авторы: Брайан Керниган, Деннис Ритчи (второе издание). Потихоньку, не торопясь, последовательно, на совесть выполняю все упражнения книги... Сейчас выполняю упражнение 1.16 из раздела 1.9. (хотя прочитал намного дальше...).
Мои размышления идут по такому пути: строка представляет собой массив объектов char, т.о., теоретически, самый простой вариант отведения необходимого объёма памяти - изначально создать массив char, имеющий максимально допустимую длину - тогда в неё поместится любая возможная строка. Это, конечно, неправильный подход, поскольку получается, что под какую-то единственную строку я могу запросить столько памяти, сколько на моей машине даже физически нет... Но, всё таки любопытно посмотреть, что из этого получится... В разделе 1.6 "Массивы" обозначенной мною книги есть такая фраза:
Из этого текста я делаю вывод, что в качестве размера массива я смогу задать значения таких типов как int, unsigned int, long int, unsigned long int. Проверяю предположение:
Результат:
Как видим - компиляция проходит успешно, а значит - перехожу к следующей стадии своей страшной затеи и сразу же беру быка за рога:
Результат:
Слишком большое... Хорошо, понижаю планку:
Результат:
Как видим, сообщение несколько изменилось, однако результат по прежнему отрицательный... Снова понижаю планку:
Результат:
Как видим - в этот раз компиляция прошла успешно, однако в процессе выполнения получаю ошибку. Из краткого сообщения, делаю предположение, что размер строки не может быть больше размера сегмента. Т.о. мои опасения на тему того, что теоретически я могу отдать под массив char памяти больше, чем у меня есть физически - не оправдались (и слава Богу У меня 64-х разрядная операционная система (Ubuntu 11.04). Как можно программно (на C) получить размер сегмента (я так понимаю, что речь о сегменте памяти)? Правильно ли я понимаю, что под условие задачи (насчёт строки любой длины) я должен задавать размер массива char, равный размеру сегмента памяти (видимо разделение одной строки по нескольким сегментам не допустимо)? Спасибо. |
| Автор: Compositum 30.5.2011, 07:26 | ||||
На данный момент мне быстрее показать скрин, чем набирать исходники (иначе на работу опоздаю) - выложил его http://habreffect.ru/files/11f/5effce3bf/IMG_2625.JPG. Да, это входной поток, но в примере, прежде чем читать его, создаётся переменная longest (массив char, размером 1000). Я так понял, что вместо 1000 нужно подставить др. число, максимально допустимое для длины строки в потоке - вот я и пытался сделать это в первом сообщении топика... |
| Автор: bsa 30.5.2011, 13:48 |
| Compositum, ты пытаешься найти решение несуществующей проблемы. Допустим, у тебя есть строка какого-то размера (он заранее не известен), тебе гарантированно нужно сделать ее копию. Для этого достаточно воспользоваться циклом, realloc и методом выделения памяти, используемом в std::vector - размер выделенной памяти должен быть в 2 раза больше, чем было до этого. Таким образом, если по умолчанию ты будешь делать буфер размером 128 байт, то тебе понадобится всего 3 перевыделения, чтобы получить размер 1024 (128->256->512->1024). |
| Автор: Compositum 30.5.2011, 13:55 | ||
Я всего лишь последовательно выполняю упражнения. На той стадии, на которой находится обозначенная задача, авторами книги ещё не были обозначены никакие realloc и std::vector (повторюсь - это C, а не C++). Исходя из этого, я предполагаю, что авторы книги (они же и авторы языка C), подразумевают какой-то др. способ, доступный читателю на основе ранее изложенного материала... |
| Автор: bsa 30.5.2011, 14:06 |
| Compositum, сделай так, чтобы если getline достигла лимита буфера, то она не ставила конец строки. Таким образом ты сможешь отследить этот момент и корректно вывести и строку, и ее размер. Хранить всю строку в памяти смысла нет никакого. |
| Автор: Compositum 30.5.2011, 14:25 | ||
Но если строка выйдет за границы буфера (т.е. по сути - стека), то она начнёт писать данные поверх кода, стирая его... Или я ошибаюсь? |
| Автор: bsa 30.5.2011, 14:53 |
| Compositum, читать надо кусочками. Прочитал кусок строки размером в MAXLEN, вывел, прочитал другой, тоже вывел, ... прочитал конец, вывел, вывел размер. Это по сути тоже самое, что читать посимвольно и сразу выводить на экран. |
| Автор: Compositum 30.5.2011, 14:57 | ||
Стоп. Хорошо, вот начну я выводить, и вдруг окажется, что следующее слово будет ещё длиннее, а я уже "навыводил"... Тогда как? Я ведь смотрю в разрезе задачи, код которой показан мною на скрине, а в ней требуется из массива строк, поступающего на входе, выбрать самую большую... |
| Автор: bsa 31.5.2011, 12:18 |
| Тебе что, нужно еще и вывести эту самую длинную строку? Или только вывести количество символов в ней? |
| Автор: Compositum 31.5.2011, 12:52 | ||
Я так понял, что показанный мною выше http://habreffect.ru/files/11f/5effce3bf/IMG_2625.JPG ты не смотрел... На нём в комментарии, перед main() озвучена решаемая задача: вывод самой длинной строки в потоке. На том же скрине, в самом низу след. страницы видно задание (упр. 1.16), о котором я собственно и пишу Вот я и думаю, как зная только массивы, суметь обработать входной поток любого размера и вывести строку любой длины, насколько это позволяет текст (главный модуль - это код, показанный на стр. 42 моего скрина). Я не понял фразы "насколько это позволяет текст"... Ведь текст - это массив символов. Массиву можно назначить далеко не каждый размер (это я показал в первом сообщении топика). Как правильно определить максимально возможный размер массива для того или иного типа данных? |
| Автор: bsa 31.5.2011, 17:53 |
| скрин я смотрел, но не читал все. Перечитал все. Я не знаю решения задачи, без использования динамических массивов. Если бы речь шла о чтении файла, то можно было бы решить путем сохранения позиции максимальной строки в файле. Возможно, в книге ошибка. |
| Автор: Compositum 31.5.2011, 18:52 | ||
а я уж было подумал, что это я такой тупой, не могу простую задачку решить (сложную авторы вряд ли выложили бы на этой стадии)... |
| Автор: xvr 1.6.2011, 16:12 | ||
Неправильный вывод (пока оставим за бортом вопрос о существовании сегментов вообще). Твой массив расположен в стеке, вот именно он и переполнился. Сделай массив глобальным (объяви перед main) |
| Автор: Dov 1.6.2011, 17:45 |
| В оригинале задача выглядит так: Revise the main routine of the longest-line program so it will correctly print the length of arbitrarily long input lines, and as much as possible of the text. В моём (не совершенном) переводе это может звучать так: Исправьте функцию main программы так, что бы она корректно выводила длину(т.е. количество символов) самой длинной строки и, как можно большее количество текста(ограниченное, как мы помним, значением MAXLINE). |
| Автор: Compositum 1.6.2011, 20:01 | ||||||||||||||||
Попробовал с глобальными...
Результат
Результат:
Не понял... процесс был "убит" операционной системой? Если да, то почему?
Результат:
Как видим, в последнем случае откомпилировалось и отработало без ошибок, но на компиляцию ушло секунд 7... Почему в случае с локальной переменной это не сработало, а с глобальной - получилось? |
| Автор: xvr 1.6.2011, 21:04 | ||
Локальная переменная располагается в стеке, а его размер ограничен. Глобальная - в обычной виртуальной памяти. Ее размер ограничен только доступной виртуальной памятью (кстати, на 64х битной машине размер виртуальной память меньше - 48 бит) Так что long туда не влез (он занимает 63-64 бита адреса), а int влез (он занимает 31-32 бита) Но учти, что система может иметь свои взгляды на размер памяти, и прибить процесс, который попытается реально обратится к слишком большому объему памяти. Когда кончится своп файл процесс получит sigfault |
| Автор: Compositum 1.6.2011, 21:21 | ||||||||
Спасибо за ответы. Ты пишешь, что размер стека ограничен, а чем обуславливаются размеры/ограничения стека? Я думал, что размер стека вычисляется компилятором на основе размеров переменных, используемых в программе. Я ошибся? Ещё я ведь могу объявить переменную как
В этом случае она будет видна только в рамках этого си-файла - такая переменная так же будет размещаться в обычной виртуальной памяти или в стеке (компилируется и работает без сообщений об ошибке)? Насчёт размера виртуальной памяти не понял - ты имеешь в виду размер указателя на ячейку памяти?
получаю: 8. Т.е. размер указателя - 64 бита. |
| Автор: bsa 2.6.2011, 09:46 | ||
Добавлено через 2 минуты и 51 секунду ты не путай размер указателя и разрядность виртуальной памяти. У указателя много места занимает выравнивание, которое необходимо, чтобы процессор быстрее считывал из памяти (т.е. считывание 8 байт с адреса, выравненного по 8 (addr % 8 == 0) займет 1 машинную операцию, а считывание с невыравненного - 2). |
| Автор: xvr 2.6.2011, 11:45 |
Указатель действительно 64 бита, но могут быть нюансы. Например для x86 64х битной архитектуры обычно только младшие 48 битов (точное количество зависит от процессора) значимые. А оставшиеся старшие биты (64-48=16) должны совпадать друг с другом и с последним значащим битом (47м). В противном случае при попытке что либо прочесть/записать по такому указателю получите ошибку сегментации. Т.е. виртуальное адресное пространство действительно 64х битное, но в его середине есть дыра весьма приличного размера |