![]() |
Модераторы: LSD Страницы: (11) Все « Первая ... 3 4 [5] 6 7 ... Последняя »
( Перейти к первому непрочитанному сообщению ) |
![]() ![]() ![]() |
|
Любитель |
|
|||
Программист-романтик ![]() ![]() ![]() ![]() Профиль Группа: Комодератор Сообщений: 3645 Регистрация: 21.5.2005 Где: Воронеж Репутация: 5 Всего: 92 |
||||
|
||||
J0ker |
|
||||||||
Опытный ![]() ![]() Профиль Группа: Участник Сообщений: 986 Регистрация: 17.9.2008 Репутация: нет Всего: 14 |
знаете, пытались меня всякими пресуппозициями наколоть, но чтоб так нагло - это впервые - зачет ![]() ![]()
а про фанатиков я даже и не говорю
проблема в том, что от нас требуют признания без понимания - это постулат всех современных религий - есть это или этого нет - вопрос В ПРИНЦИПЕ не ставится (причины я могу вам рассказать отдельно ибо это обширная тема) не передергивайте - я говорю НЕ о рациональности вкуса, а о принятии стандартом вкуса большинства - в коммерческом производстве это определенно рационально
оправдана совмещением близости к железу и концепции ЯВУ исторический факт - это лишь обратная совместимость с plain C - многие несуразности посто вынудены поддерживать. Все остальное обусловлено лишь попыткой совместить нативность с высоким уровнем абстракции. Если вам не нужна нативность - вперед на недоделанную джаву. В конце концов и хаскель есть... В plain C-же уровень абстракции на порядок ниже, а нативные проблемы те-же самые. Безусловно добавление уровня абстракции добавляет сложность этой абстракции, но оно так-же добавяет эффекивности и удобтва использования - за все в жизни надо платить. а разве анти-паттерны это не технический вопрос? ![]()
ну давйте попорим - там где-то и раздел наверное подходящий есть только вот сложно о чем-то спорить с тем, кто не признает логических доводов ![]() Это сообщение отредактировал(а) J0ker - 3.10.2008, 20:44 |
||||||||
|
|||||||||
maxim1000 |
|
|||
![]() Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Участник Сообщений: 3334 Регистрация: 11.1.2003 Где: Киев Репутация: нет Всего: 110 |
Ох... ну и мне, что ли поучаствовать в религиозных войнах
![]() Все соображения основаны на личном опыте ну и немного на опыте окружающих и прочитанных статьях всяких. Языки в том виде, в котором они сейчас есть, появились не просто так. У каждого популярного языка программирования есть своё сообщество. И объединяет их набор того, что они ценят в процессе программирования, что и определяет критерии развития того или иного языка. И получается, что даже решая одну и ту же задачу, программисты на разных языках будут решать несколько разные задачи. Потому, что всегда есть критерии "по умолчанию" (скорость, поддерживаемость, масштабируемость), под которыми в каждом сообществе понимаются разные вещи, да и коэффициенты у этих "слагаемых" тоже разные. Взять, например, C++ и С (ниже - не полный список, а только примеры различий): Первым программистам нравится сбрасывать на компилятор (т.е. compile-time) кучу действий, чтобы быть уверенными, что ничего не забыто. Вторым программистам тупо не нравится, когда за безобидной скобкой "}" может скрываться огромная куча кода в деструкторах, которая закрывает подключения к БД, сбрасывает содержимое буферов в файл и вообще выключает комп ![]() и там, и там за чем-то нужно следить, у одних лучше получается следить за неявно вызываемым кодом, у других - за тем, когда нужно освободить память Изначально это было просто из-за специфики задач: одним хочется код, который больше отражает смысл производимого действия, а не всевозможные вариант обработки ошибок, другие не могут рисковать, чтобы в real-time задаче вызывался код, которого не видно, а значит, оценка времени начинает быть нетривиальным занятием. Но сейчас границы между областями размыты. На C пишут графические приложения, на С++ (да и на Java, если уж на то пошло) - программы для мобильных телефонов. Но люди подходят к задачам всё равно со своими критериями, и то, что они выбирают "свой" язык, вполне естественно. И потому сложно объяснить программисту на C, почему ему нужны шаблоны - они ему не нужны, т.к. критерии у него такие, что препроцессор вполне для них подходит. И программисту на C++ - что видеть весь код, который выполняется именно там, где он выполняется, может быть важным. Может, когда-то и сделают язык, который удовлетворяет и те, и те критерии (да и от Java можно немало взять, не говоря уже о функциональных языках). Так что вместо того, чтобы спорить о том, что лучше крылья или плавники, лучше обсуждать, кому где надо передвигаться... Вот где-то так... Изо всех сил старался быть объективным, хотя сам считаю, что тут и спорить не о чем - в C даже нельзя сделать такой банальной вещи, как посчитать факториал в compile-time ![]() -------------------- qqq |
|||
|
||||
J0ker |
|
|||
Опытный ![]() ![]() Профиль Группа: Участник Сообщений: 986 Регистрация: 17.9.2008 Репутация: нет Всего: 14 |
именно поэтому типы в C++ разделяются на Основные (они-же базовые, фундаментальные, POD), Агрегаты (ониже PODS), и Классы. "Скрываться" это может только от ленивого. |
|||
|
||||
UnrealMan |
|
||||||||||||
Опытный ![]() ![]() Профиль Группа: Участник Сообщений: 722 Регистрация: 30.3.2006 Репутация: 0 Всего: 32 |
Ключевое слово здесь "тупо" ![]()
Маленькое уточнение: в большинстве случаев за вызовом деструкторов следить не нужно вообще.
Вот незадача: исходного кода любой функции (хоть явно она вызывается, хоть неявно) не видно в месте её вызова. Такие вот пассажи касаемо связи сокрытия кода с потерей производительности исходят либо от фундаментального непонимания того, откуда в большинстве случаев исходят устранимые потери производительности, или от откровенного лукавства, целью которого является хоть какое-то видимое оправдание своей мышиной возни с C. Я очень сомневаюсь, что медитация над вызовами функций, очищающих ресурсы, способна привести к решению внесения неких изменений, благодаря которым будет достигнуто заметное повышение производительности.
Ну да, каких только критериев не встретишь у C-программимазохистов ![]()
Руководства правилами языка вполне достаточно.
Интересно было бы узнать, где имеет смысл применять такой подсчёт. Это сообщение отредактировал(а) UnrealMan - 3.10.2008, 22:14 |
||||||||||||
|
|||||||||||||
maxim1000 |
|
||||||||
![]() Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Участник Сообщений: 3334 Регистрация: 11.1.2003 Где: Киев Репутация: нет Всего: 110 |
![]() эту фразу стоит рассматривать только в контексте с смайликом, который шёл после неё ![]() -------- честно говоря, возможно, мои примеры преимуществ C могут оказаться несколько надуманны, т.к. я ощущаю себя значительно комфортнее в C++, чем в C но нельзя сбрасывать со счетов, что мы (имеется в виду C++-программисты) уже освоились в той структуре языка, которая сложилась на данный момент, и его сложности нам кажутся проще (а иногда и совсем не сложными), чем другим
Большинство программ развиваются и видоизменяются. В процессе это развития типы могут переходить из одной группы в другую. Например писали функцию для double, а потом стали использовать complex. Писали для структуры, которая содержит координаты точки x,y,z в 3d на какой-то поверхности, перешли на параметризированный вид x,y,f(x,y), а потом и вообще на чтение из какого-то большого файла вместо вычисления f(x,y). [кривовато, конечно, но дальше продолжение ответа и в том числе ответ на реплики]
И для C++ абсолютно нормально, что знание о том, как объект будет удалён находится не возле "}", а в месте объявления объекта (ну а точнее ссылка на это знание). Это отличается от хронологического порядка, часто встречающегося в программировании - если что-то идёт после чего-то другого, то и выполнено оно будет позже. И я вполне мог допустить, что это не для всех комфортно. Необходимые времена жизни объектов не всегда укладываются в идею стека, поэтому и приходится либо расширять их и использовать стековые объекты, либо переходить на ручное управление временем жизни. В таких случаях второй вариант вполне может обеспечить прирост эффективности не только по времени, но и по использованию памяти. И не всегда его можно заметить если алгоритм уже описан в терминах стековых объектов, особенно если вступает в дело взаимодействие соседних итераций одного цикла. В том-то и состоит моя мысль, которую я отчасти хотел описать в первом посте: язык, как набор правил, бесполезно рассматривать в отрыве от идей, принятых в сообществе, которое его поддерживает. Более того, сами правила - всего лишь следствие тех или иных ценностей, принятых в этом сообществе (хотя нельзя не признать, что они же потом на эти ценности и влияют). Когда спрашивают "почему нельзя сделать <...>", ответ "потому, что это запрещено правилами языка (ну или стандартом)", ИМХО, в конре неправилен. Стандарт - не цель, это способ реализовать какую-то общую площадку, на которой можно писать, удовлетворяя принятым в сообществе критериям. Это сообщение отредактировал(а) maxim1000 - 4.10.2008, 00:56 -------------------- qqq |
||||||||
|
|||||||||
UnrealMan |
|
|||
Опытный ![]() ![]() Профиль Группа: Участник Сообщений: 722 Регистрация: 30.3.2006 Репутация: 0 Всего: 32 |
Допустим, "комфорт", связанный с видением всей последовательности операций в соответствующем порядке представляет собой какую-то ценность. Возникает резонный вопрос: какой ценой он достигается? Во-первых, тело функции становится длиннее, а в более длинных функциях менее удобно ориентироваться. Во-вторых, это неизбежно провоцирует ошибки: иногда функцию, заменяющую деструктор, либо вообще забывают вызвать, либо с перепугу вызывают дважды (если функция большая, то такую ситуацию можно и не заметить). Довольно дорогостоящий "комфорт", однако. Когда требуемое время жизни объекта не укладывается во время жизни одного автоматического объекта, в большинстве случаев его следует уложить во время жизни нескольких вспомогательных автоматических объектов. И только если такой подход даёт неприемлемый оверхед, то тогда уже можно мудрить с чисто ручным управлением, которое в C++ доступно ровно в той же степени, как в и C. Собственно, за счёт чего может быть достижим такой прирост? За счёт избавления от копирования объекта? Дык копирование достаточно несложно отслеживается, т.к. ситуации, когда оно осуществляется, вполне чётко очерчены рамками языка.
За этими ценностями должны стоять рациональные мотивы (касающиеся характеристик программного продукта и затрат на его создание), а иначе такие ценности (равно как и выведенные на их основе правила), кроме как сообществу фанатов, никому на фиг не нужны. Это сообщение отредактировал(а) UnrealMan - 4.10.2008, 02:06 |
|||
|
||||
kemiisto |
|
||||||||||||
![]() Дикий Кот. =^.^= ![]() ![]() ![]() ![]() Награды: 1 Профиль Группа: Участник Клуба Сообщений: 3292 Регистрация: 29.7.2007 Репутация: 3 Всего: 160 |
Написано много, долго читал, разделяю точку зрения MAKCim'а. Подолью масла в огонь... Давайте-ка, обратимся к истории вопроса.
![]() Начале 1970-х годов - Кеном Томпсоном и Денисом Ритчи создают C. Для разработки и последующего использования в операционной системе UNIX. 1983 - вышеупомянутые получают Премия Тьюринга (в сфере информационных технологий эта премия имеет статус, аналогичный Нобелевской премии в академических науках) (внимание!):
Конечно же, не будь C - не было бы и UNIX! С ценят за его эффективность и он является самым популярным языком для создания системного программного обеспечения. И всё же, почему так важно, что премию дали не за C? А потому, что годом позже... 1984 - Никлаус Вирт получает Премию Тьюринга (опять же внимание!)
Если бы мы обсуждали "холивар" C vs PASCAL, я бы поставил на этом точку. Но я продолжу... Начало 80-х годов - Бьярне Строуструп придумал ряд усовершенствований к языку С под собственные нужды (как утверждает Народная Энциклопедия). 1998 - ратифицирован первый международный стандарт языка С++. 2001 - знаковое событие - Ole-Johan Dahl и Kristen Nygaard (я позволю себе оставить имена в оригинале, ибо транскрипций много) награждаются Премией Тьюринга:
Лучше поздно, чем никогда! Внимательно вчитайтесь за что дали премию! Можно конечно возразить, мол дали за идеи, а в C++ они были воплощены! НО! Историю не перепишешь! И 2003 год ознаменовался не только выходом нынешней версия стандарта С++ но и вручением Премии Тьюринга Алану Кэю за:
Да будет вам известно, что первая версия Smalltalk'а была разработана в 1971. Страуструп в это время университет то не закончил, а люди в Xerox PARC уже вовсю мыслили объектами! Почитайте хоть немного вот здесь и вы, возможно, многое поймёте... 2005 - в очередной раз Премия Тьюринга вручается за разработку ЯП. На этот раз Питер Наур удостоился этой чести:
А как же Страуструп? Не удостоили. Забыли? Очередь не дошла? А может, попросту, не достоин?! Ещё можно почитать про Oberon. Например, здесь. Обратите внимание вот на что. Вирт постепенно упрощал и до сих пор упрощает синтаксис (убирая элементы, которые, оказались ненужными, либо вызывали неоправданное усложнение реализации компилятор). А мощь языка только растёт! Oberon поддерживает ООП не множа сущности (смотри расширяемые записи), описание языка занимает страницу, производительность на уровне C! Так ещё и есть сборка мусора! А что делают наши старые знакомые-датчане? Первый (Страуструп) усложнает С настолько, что:
Я после третьего правила читать не мог! "Плакалъ"! ![]() ![]() Второй (Хейлсберг) поступает аналогично с PASCAL. Правда, потом пытается (и относительно небезуспешно) иправить ошибки первого под крылом MS... Так что, можно реабилитировать! ![]() Вот, как то так... Это сообщение отредактировал(а) kemiisto - 4.10.2008, 03:05 -------------------- |
||||||||||||
|
|||||||||||||
J0ker |
|
|||
Опытный ![]() ![]() Профиль Группа: Участник Сообщений: 986 Регистрация: 17.9.2008 Репутация: нет Всего: 14 |
тот, кто делает рефакторинг кода должен понимать разницу между типами типов (пардон за тафтологию). Если он этого НЕ понимает, то тут проблема не в сложности языка, а в некомпетентности программиста. Добавлено через 3 минуты и 52 секунды ну на этом фоне изуверства MAKCim'а с goto уже совсем не комильфо ![]() |
|||
|
||||
J0ker |
|
|||
Опытный ![]() ![]() Профиль Группа: Участник Сообщений: 986 Регистрация: 17.9.2008 Репутация: нет Всего: 14 |
kemiisto, кипешь не понят
итак понятно, что премии (в том числе и нобелевские) вручают намного позже факта потом, никто-же не утверждает, что C++ лучший и первый ОО язык - вопрос в приемуществе C++ конкрено над C и потом, пользуясь вашим-же сравнением, по аналогии - Эйнштейну нобелевку вручили вовсе не за Теорию Относительности - так что, порешим значение ТО в современной физике, выражаясь юридическим языком, ничтожным? ![]() |
|||
|
||||
maxim1000 |
|
|||
![]() Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Участник Сообщений: 3334 Регистрация: 11.1.2003 Где: Киев Репутация: нет Всего: 110 |
суть в том, что у них другой "курс обмена" одних сложностей на другие (при чём мы не считаем сложностями первое, а они - второе) Ну, пример придумать можно: есть у нас объекты A, B, C (пусть они переданы в функцию) потом мы делаем преобразование f(A, B, C), которое даёт нам объект D а потом - преобразование g(B, C, D), которое даёт нужный результат объект A на момент второго преобразования уже не нужен, к тому же он занимает много памяти, которая очень нужна для выполнения g. Вот и становится полезно удалить его вручную до вызова g. Конечно, здесь можно придумать какие-то дополнительные стековые объекты, и для программиста на C++ это может оказаться даже полностью естественно и просто, но что может быть проще delete A перед вызовом g? ![]() (да, такая организация программы может потянуть кучу потенциальных проблем, которых мы привыкли избегать (в смысле решать заранее), но и ценность этой простоты сложно отрицать). Добавлено через 4 минуты и 17 секунд ну, там они, насколько я заметил, ведут себя вполне хронологически - наверх не переходят впрочем, там другие проблемы - если в функции будет две ветки if'а, в каждой из которых выделяются свои ресурсы, которые нужно будет освободить, такое решение уже не очень подойдёт Это сообщение отредактировал(а) maxim1000 - 4.10.2008, 10:49 -------------------- qqq |
|||
|
||||
MAKCim |
|
||||||||||||||||||
![]() Воін дZэна ![]() ![]() ![]() ![]() Профиль Группа: Экс. модератор Сообщений: 5644 Регистрация: 10.12.2005 Где: Менск, РБ Репутация: 8 Всего: 207 |
дело не в количестве, а в качестве бывает, код длиной в 5 строчек, труден для восприятия доехать до пункта Б = решить задачу и С без шаблонов, и С++ с шаблонами доедут до пункта Б ![]() шаблоны - это не панацея, есть туева хуча языков без шаблонов, тот же Python к примеру я согласен, что типобезопасность будет снижена, но при ведении грамотной документации и наличии прямых рук вероятность ошибок достаточно низка
объектный подход != ЯП ядро Linux - крупный програмный проект? ![]() VIM - крупный програмный проект? EMACS - крупный програмный проект? Python - крупный програмный проект? GTK - крупный програмный проект? ...
не забываем про производительность ![]() исключения, к примеру, могут очень дорого обойтись в конечном итоге если ты умный человек, ты понял, о чем я ![]()
справедливость - не контекстно-независимый концепт здесь мы ни о чем не договоримся
именно, НО заметь, тут нет никакого намека на UnrealMan тут общий тезис, никого конкретно не затрагивающий у тебя же
тут прокомментирую по отношению к другому фанат может быть необъективным, но об объекте фанатизма он осведомлен в достаточной степени, чтобы быть "грамотным специалистом в своем деле"
опять таки, смотря кому С приграммисту легко, С++ программисту, возможно, тяжело, обратное тоже верно видели мы ууровень лаб ![]() да ниже (читай этот пост сначала, я уже писал об этом) уточни, что подразумевается под отладкой
префиксы/суффиксы имен я С++ знаю ![]() может это и не показатель (скорее всего), но комодераторами тематических разделов на винграде просто так не назначают я просто не знаю другого более менее объективного показателя, с которым могут согласится большинство форумчан наболело ![]() J0ker, вопрос прямой ты профессионально занимался разработкой больших систем на С? (профессионльно = получал деньги, больших = хотя бы > 20000 строк и > 50 модулей (*.h, *.c файлы)) Добавлено через 7 минут и 52 секунды maxim1000, соглашусь по большей части это еще раз оправдывает мой тезис "главное, чтобы человек был профессионал в своем деле" Добавлено через 11 минут и 59 секунд ой как мне не хочится начинать по поводу goto ![]() что в моем примере изуверство? это стандартный способ управлением жизненным циклом объектов без дублирования кода и потери эффективности засчет вызова функций -------------------- Ах, у елі, ах, у ёлкі, ах, у елі злыя волкі © |
||||||||||||||||||
|
|||||||||||||||||||
MAKCim |
|
||||
![]() Воін дZэна ![]() ![]() ![]() ![]() Профиль Группа: Экс. модератор Сообщений: 5644 Регистрация: 10.12.2005 Где: Менск, РБ Репутация: 8 Всего: 207 |
два вложенных if-а - это уже недостаточно хорошо и по возможности от этого надо избавляться (разделяю точку зрения Торвальдса) избавляемся инвертированием условий иначе - вызов функции что-то типа такого
применя правило рекурсивно, можно разрулить любую степень вложенности в ядре Linux эта тема раскрыта в полной мере есть * функции и __* функции вторые - это первые с конкретным инвариантом Добавлено @ 11:28 вообще говоря, крайне глупо сравнивать С и С++ as is принципы эффективного программирование на оных различаются то, что на С++ решается одним методом, на С будет решаться другим, но это не значит, что от отсутствия первого в другом теряется эффективность другого Добавлено через 13 минут и 18 секунд ну и вообще, спасибо тем, кто хоть как-то разделяет мою точку зрения это достаточно рисковано, замахнуться на "святое" Это сообщение отредактировал(а) MAKCim - 4.10.2008, 11:29 -------------------- Ах, у елі, ах, у ёлкі, ах, у елі злыя волкі © |
||||
|
|||||
Lazin |
|
||||
![]() Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Завсегдатай Сообщений: 3820 Регистрация: 11.12.2006 Где: paranoid oil empi re Репутация: 4 Всего: 154 |
Все высокоуровневые средства С++, призваны бороться со сложностью, иногда это обескураживает, например вот строчка кода из одного реального проекта:
это просто вывод в лог, выглядит просто, но на самом деле хрен там: oik_error - макрос, который создает временный объект:
когда объект класса oik::log::logstream создается, он получает ссылку на глобальный объект класса logstream_backend, перегруженый оператор % вызывает шаблонную функцию logstream_backend.write с соответствующим параметром, а в свою очередь обращается к буферу (локальному для каждого потока) и форматирует в него переданое значение, следующий вызов оператора % делает то-же самое, только дописывает свои данные в конец буфера и тд, в результате буфер содержит строку сообщения, которую нужно записать в лог, затем, в деструкторе, при разрушении объекта класса oik::log::logstream вызывается библиотечная функция, которая копирует данные из буфера в "большой и длинный буфер вода вывода"(тм) который имеет размер кратный размеру сектора на диске, а так-же это буфер выровнен по границе страницы, в этот буфер могут писать несколько потоков одновременно не нарушая его структуру, далее, в случае если места в буфере не хватает, он будет сброшен на диск(напрямую, без кэширования), точнее будет сброшена только его часть, кратная размеру сектора, а то, что осталось, будет перемещено в его начало, во время этого процесса, будут заблокированы другие потоки(если они попытаются записать что-то в буфер), после того, как "большой и длинный буфер вода вывода"(тм) будет очищен, сначала будет запущен поток, который запусти этот процесс, а потом остальные, в том порядке, в котором они пытались записать данные. После этого буфер в который форматировались данные то-же будет очищен, далее, в случае если есть сетевое соединение, данные будут посланы клиенту, асинхронно ( есть клиентская программа, с помощью которой можно мониторить активность программ) ![]() наверное иногда не очень хорошо, когда одна строчка делает столько разной херни ![]() Это сообщение отредактировал(а) Lazin - 4.10.2008, 11:57 |
||||
|
|||||
UnrealMan |
|
||||||||||
Опытный ![]() ![]() Профиль Группа: Участник Сообщений: 722 Регистрация: 30.3.2006 Репутация: 0 Всего: 32 |
Если сложить всё вместе, то предлагаемый тобой код заметно труднее для восприятия. И не надо тут прятаться за субъективность - мол, для некоторых людей оно одинаково. С тем же успехом можно было бы утверждать, что психология - наука ни о чём, все люди разные и всё тут.
Выжить = решить задачу. Человек с домом и человек без дома выживают. Итак, ещё раз: не хочешь ли ты стать бездомным? Это одно из преимуществ C++ перед C. Использовать объектный подход на необъектно-ориентированном C затратнее, чем на объекто-ориентированном C++.
И что это доказывает? Что при создании этих проектов разработчикам не пришлось пострадать геморроем?
Могут, если их использовать не по назначению. Ну и что? Я уже давно (ещё до появления этой темы) понял, что литературы, освещающей вопросы архитектуры программ, ты либо вообще не читал, либо читал очень мало. Зато рассуждать горазд.
Ага, т.е. если опустить одного человека - это плохо, а если опустить целую группу людей оптом, то в этом уже не будет ничего зазорного. Забавная у тебя логика ![]() Ты тупо отрицаешь очевидное. Как я уже говорил, убеждать тебя лично я не собираюсь. Те, кто очевидное не отрицает, пусть делают выводы сами. Мда уж, видел я твой уровень знаний. Это сообщение отредактировал(а) UnrealMan - 4.10.2008, 13:07 |
||||||||||
|
|||||||||||
![]() ![]() ![]() |
Правила ведения Религиозных войн | |
|
1. Уважайте собеседника 2. Собеседник != враг 3. Старайтесь воздерживаться от тем вида "Windows Rulez" или "Linux Rulez" С уважением, Smartov. |
0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей) | |
0 Пользователей: | |
« Предыдущая тема | Религиозные войны | Следующая тема » |
|
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности Powered by Invision Power Board(R) 1.3 © 2003 IPS, Inc. |