Какие конкретные примеры того, как знание C делает вас лучшим программистом высокого уровня?

Я знаю о существовании такого вопроса, как этот и этот. Позволь мне объяснить.

После прочтения статьи Джоэла Back to Basics и просмотра множества похожих вопросов по SO, я начал задаваться вопросом, каковы конкретные примеры ситуаций, когда знание таких вещей, как C, может сделать вас лучшим программистом высокого уровня.

Я хочу знать, есть ли много тому примеров. Часто ответ на этот вопрос звучит примерно так: «Знание C дает вам лучшее представление о том, что происходит под прикрытием» или «Вам нужна прочная основа для вашей программы. ", и эти ответы не имеют особого смысла. Я хочу понять различные конкретные способы, которыми вы получите пользу от знания понятий низкого уровня,

Джоэл привел пару примеров: двоичные базы данных против XML и строки. Но два примера на самом деле не оправдывают изучение C и / или Assembly. Итак, мой вопрос таков: Какие конкретные примеры знания C делают вас лучшим программистом высокого уровня?


person Javier    schedule 16.01.2010    source источник
comment
Это как бы суть статьи Джоэла: если вы не знаете, что на самом деле делают высокоуровневые конструкции, вы можете использовать их глупо, не так ли?   -  person dmckee --- ex-moderator kitten    schedule 17.01.2010
comment
да. Я более или менее спрашивал, каким образом вы можете использовать их глупо.   -  person Javier    schedule 17.01.2010


Ответы (10)


Мой опыт обучения студентов и работы с людьми, изучающими только языки высокого уровня, показывает, что они склонны мыслить на определенном высоком уровне абстракции и предполагают, что «все приходит бесплатно». Они могут стать очень компетентными программистами, но в конечном итоге им приходится иметь дело с некоторым кодом, который имеет проблемы с производительностью, и тогда это начинает их укусить.

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

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

Кроме того, и в более общем плане, я считаю, что для опытного программиста важно не только знать нотацию большого O (которую преподают в большинстве школ), но и то, что в реальных приложениях также важна константа (которую школы пытаются игнорировать) . Мой неофициальный опыт показывает, что люди, владеющие обоими языковыми уровнями, как правило, лучше понимают константу, возможно, из-за того, что я описал выше.

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

person Uri    schedule 16.01.2010
comment
Спасибо, это в значительной степени то, что я искал. - person Javier; 16.01.2010
comment
Точно так же знание языка ассемблера (и того, что эффективно в asm, и как компиляторы могут оптимизировать ... например, деление происходит медленно, деление на константы времени компиляции не такое медленное, степени двойки очень эффективны) может сделать вас лучше C программист, особенно если лучше включает здоровую дозу более эффективных. Это очень важно при векторизации чего-либо вручную с помощью встроенных функций, но также и в целом. - person Peter Cordes; 02.12.2016

Знание вещей низкого уровня может очень помочь.

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

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

Если вы понимаете, как работает дисковый ввод-вывод и кеширование, вы можете изменить файловые операции, чтобы они хорошо работали с ним (например, если вы читаете из одного файла и записываете в другой, работа с большими пакетами данных в ОЗУ может помочь уменьшить число конфликтов ввода-вывода. между этапами чтения и записи вашего кода и значительно повысить пропускную способность)

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

Если вы понимаете, как выполняется рисование, вы можете использовать хитрые приемы для повышения скорости рисования. например Вы можете нарисовать шахматную доску, поочередно нарисовав 64 белых и черных квадрата. Но часто бывает быстрее нарисовать 32 белых квадрата, а затем 32 черных (потому что вам нужно изменить цвет рисования только дважды, а не 64 раза). Но на самом деле вы можете нарисовать всю доску черным, затем XOR 4 полосы по доске и 4 полосы по доске белым, и это может быть намного быстрее (смена цвета 2 и рисование только 9 прямоугольников вместо 64). Этот трюк с шахматной доской учит очень важному навыку программирования: боковому мышлению. Хорошо спроектировав свой алгоритм, вы часто можете существенно повлиять на то, насколько хорошо работает ваша программа.

person Jason Williams    schedule 16.01.2010
comment
+1 Хороший пример шахматной доски. Программист, обладающий только языковыми навыками высокого уровня, МОЖЕТ думать о создании наиболее эффективных алгоритмов, но программист с опытом работы с C, скорее всего, ВСЕГДА будет думать об этом. - person Gavin; 17.01.2010

Понимание C или любого другого языка программирования низкого уровня дает вам возможность понять такие вещи, как использование памяти (то есть, почему плохо создавать несколько миллионов тяжелых объектов), как работают указатели / ссылки на объекты и т. Д.

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

Работа на низком уровне с гораздо меньшими системами с ограниченным объемом памяти, такими как Arduino или 8-битные процессоры старой школы, дает огромные преимущества, хотя и не ограничивается C. Это позволяет вам испытать близкое к металлическому кодированию в гораздо более доступном пакете, и, потратив время на сжатие приложений до 512 КБ, вы обнаружите, что применяете эти навыки на более высоком уровне в повседневном программировании.

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

person Bob Palmer    schedule 16.01.2010

Во-первых, знание C помогает понять, как работает память в ОС и на других языках высокого уровня. Когда ваша программа на C # или Java поднимает вопрос об использовании памяти, понимание того, что ссылки (которые в основном являются просто указателями) также занимают память, и понимание того, сколько структур данных реализовано (которые вы получаете, создавая свои собственные на C), помогает вам понять, что ваш словарь резервирует огромные объемы памяти, которые фактически не используются.

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

Я думаю, что C также помог мне понять сетевые протоколы, но я не могу указать на конкретные примеры. На днях я читал другой вопрос SO, где кто-то жаловался на то, что битовые поля C «в основном бесполезны», и я думал, насколько элегантно битовые поля C представляют сетевые протоколы низкого уровня. Языки высокого уровня, работающие со структурами битов, всегда заканчиваются беспорядком!

person SoapBox    schedule 16.01.2010

В общем, чем больше вы знаете, тем лучше будете программистом.

Однако иногда знание другого языка, такого как C, может заставить вас поступить неправильно, потому что может быть предположение, которое неверно для языка более высокого уровня (такого как Python или PHP). Например, можно предположить, что длина списка может быть O (N), где N - длина списка. Однако это, вероятно, не так во многих экземплярах языков высокого уровня. В Python для большинства вещей, похожих на списки, стоимость O (1).

Более подробная информация о специфике языка поможет, но более подробное знание в целом может привести к неправильным предположениям.

person Doug Blank    schedule 16.01.2010
comment
На самом деле это не так для большинства хорошо структурированных программ на C / C ++. Вы сохраните длину после ее вычисления и будете ссылаться на нее вместо того, чтобы каждый раз пересчитывать ее. Это удобная техника, которую многие люди, с которой я работаю, забывают. У них однозначно нет опыта работы с C / C ++. Те, с кем я работаю и имеющие опыт работы с C / C ++, склонны отдавать предпочтение методологии однократного вычисления и повторного использования. Другие, кажется, предполагают (почему я не понимаю), что вычисления производятся бесплатно. - person Jason D; 16.01.2010
comment
Действительно ли много случаев, когда программисты на C делают предположения о реализации конкретной структуры данных? По моему опыту, программисты, знакомые с C, чаще спрашивают, как это реализовано, и с большей вероятностью поймут, что это означает для характеристик производительности. - person Porculus; 16.01.2010

Просто "знание" C не сделает вас лучше.

Но если вы понимаете все, как работают собственные двоичные файлы, как с ними работает ЦП, каковы архитектурные ограничения, вы можете написать код, который проще для ЦП.

Например, как кеши L1 / L2 влияют на вашу работу и как вы должны писать код, чтобы иметь больше попаданий в кеши L1 / L2. При работе с C / C ++ и выполнении серьезных оптимизаций вам придется заниматься подобными вещами.

person BarsMonster    schedule 16.01.2010

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

Для меня приемлем любой язык, если вы понимаете, как компилятор / интерпретатор (в основном) отображает ваш код на машину. Это немного проще сделать на языке, который предоставляет это напрямую, но вы должны иметь возможность, немного почитав, выяснить, как распределяется и организована память, какие шаблоны индексации более оптимальны, чем другие, какие конструкции более эффективен для конкретных приложений и т. д.

Я думаю, что более важным является хорошее понимание операционных систем, архитектур памяти и алгоритмов. Если вы понимаете, как работает ваш алгоритм, почему было бы лучше выбрать один алгоритм или структуру данных над другим (например, HashSet vs. List) и как ваш код отображается на машине, не имеет значения, какой язык вы используете. .

person tvanfosson    schedule 16.01.2010

Это мой опыт того, как я учился и учился программированию, в частности, пониманию C, это восходит к началу 1990-х годов, так что может быть немного антикварным, но страсть и драйв важны:

  • Научитесь понимать низкоуровневые принципы работы компьютера, такие как программирование EGA / VGA, вот ссылка на архив Simtel в руководстве программиста C для ПК.
  • Понимание того, как работает TSR
  • Загрузите полный архив фрагментов кода Боба Стаута, который представляет собой большую коллекцию кода C, который выполняет только одну задачу - изучение они и понимают это, не только это, коллекция сниппетов стремится быть портативной.
  • Посетите международный конкурс запутанного кода C (IOCCC) в Интернете и узнайте, как можно злоупотреблять кодом C и понимать особенности языка. Худшее злоупотребление кодом - победитель! Скачайте архивы и изучите их.
  • Как и я, мне очень понравился печально известный Ponzo's C Tutorial, который мне очень помог, к сожалению, архив очень трудно найти. Если кто-нибудь знает, где их получить, оставьте комментарий, и я исправлю этот ответ, добавив ссылку. Есть еще один, который я могу вспомнить - Учебник Coronado [Generic?] C, опять же, мои воспоминания об этом туманные ...
  • Посмотрите журнал доктора Добба и журнал пользователей C здесь - я не знаю, можно ли еще получить они в печатном виде, но они были классическими, могу вспомнить чувство, когда держал печатную копию в руке и отрывался от дома, чтобы ввести код и посмотреть, что произойдет!
  • Возьмите старую копию Turbo C v2, которую, я думаю, вы можете получить на сайте borland.com, и просто поиграйте с 16-битным программированием на C, чтобы почувствовать и возиться с указателями ... конечно, он старый и старый, но играть с указателями на нем - нормально.
  • Чтобы понять и изучить указатели, перейдите по ссылке здесь, чтобы устаревший Simtel.net - важнейшее звено для достижения C Гуру, если не сказать лучшего слова, также вы найдете множество загрузок, относящихся к языку программирования C - я помню, как на самом деле заказывал архив компакт-дисков Simtel и искал материалы C.
person t0mm13b    schedule 16.01.2010
comment
И пока мы этим занимаемся, купите несколько перфокарт и научитесь программировать их вручную. Серьезно, изучение низкоуровневых вещей полезно, но изучение устаревших импровизированных решений проблем, которые больше не существуют, - нет. (И да, в свое время я тоже писал TSR. Но я рад, что сегодня у нас есть многозадачные операционные системы.) - person Niki; 17.01.2010
comment
@nikie: Спасибо. Да, я согласен с вами, но не упустите возможность получить полную власть над ПК у вас под рукой, теперь вам придется пройти через множество сложных вызовов API, чтобы, ммм, вернуть себе контроль над компьютером. ПК от Microsoft .... ';) - person t0mm13b; 18.01.2010
comment
Указатели по-прежнему являются указателями в современных реализациях C на x86-64. Изучение сегментации x86 и дальних указателей, вероятно, не имеет смысла. - person Peter Cordes; 02.12.2016
comment
Если вам нравится иметь полную власть над своим ПК, подумайте об этом с точки зрения максимальной власти в пользовательском пространстве для создания оптимально быстрого кода. Вы все еще можете сделать это в современных ОС. Приложив немного усилий, современные компиляторы во многих случаях могут генерировать почти оптимальный asm. (Знание asm и способов оптимизации для текущих микроархитектур является ключом к пониманию того, в каком направлении двигаться компилятору. Например, см. Почему этот код C ++ быстрее, чем моя рукописная сборка для проверки гипотезы Коллатца?) - person Peter Cordes; 02.12.2016

Пара вещей, с которыми вы должны иметь дело непосредственно в C, которые другие языки абстрагируют от вас, включают явное управление памятью (malloc) и работу непосредственно с указателями.

Моя девушка закончила в одном семестре MIT (где в основном используют Java, Scheme и Python) со степенью в области компьютерных наук, и в настоящее время она работает в компании, кодовая база которой написана на C ++. В первые несколько дней ей было трудно понять все указатели / ссылки / и т. Д.

С другой стороны, мне было очень легко перейти с C ++ на Java, потому что меня никогда не смущало передача ссылок по значению по сравнению с передачей по ссылке.

Точно так же в C / C ++ гораздо более очевидно, что примитивы - это просто компилятор, обрабатывающий одни и те же наборы бит по-разному, в отличие от таких языков, как Python или Ruby, где все является объектом со своими собственными отличными свойствами.

person danben    schedule 16.01.2010

Простой (не совсем реалистичный) пример, иллюстрирующий некоторые из приведенных выше советов. Считайте, казалось бы, безобидные

while(true)
   for(Iterator iter = foo.iterator(); iter.hasNext();)
       bar.doSomething( iter.next() )

или даже более высокий уровень

while(true)
    for(Baz b: foo)
        bar.doSomething(b)

Возможная проблема здесь заключается в том, что каждый раз, когда вокруг цикла while создается новый объект (итератор). Если все, что вас волнует, это удобство программиста, то последнее однозначно лучше. Но если цикл должен быть эффективным или машина ограничена в ресурсах, тогда вы в значительной степени зависите от разработчиков вашего языка высокого уровня.

Например, типичная жалоба на выполнение высокопроизводительной Java - остановка выполнения во время удаления мусора (например, всех выделенных объектов Iterator). Не очень хорошо, если ваше программное обеспечение отвечает за отслеживание входящих ракет, автопилот пассажирского самолета или просто не заставляет пользователя задаваться вопросом, почему графический интерфейс перестал отвечать.

Одним из возможных решений (все еще на языке более высокого уровня) было бы ослабить удобство итератора до чего-то вроде

Iterator iter = new Iterator();
while(true)
    for(foo.initAlreadyAllocatedIterator(iter); iter.hasNext();)
       bar.doSomething(iter.next())

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

person thekindamzkyoulike    schedule 16.01.2010
comment
В C #; C; C ++; и Java, цикл for гарантированно выполняет блок инициализации только один раз! Это все, что написано перед первой точкой с запятой. Так как же вообще можно получить несколько экземпляров итератора каждый раз?!?! - person Jason D; 16.01.2010
comment
Проблема не в цикле for, а в цикле while. - person thekindamzkyoulike; 16.01.2010