Какие конкретные примеры существуют, когда вы знаете, что C делает вас лучшим программистом на высоком уровне?
Я знаю о существовании такого вопроса, как этот и этот. Позвольте мне объяснить.
Давайте читаем статью Джоэля Обратно к основам и видя много похожих вопросов на SO, я начал задаваться вопросом, каковы конкретные примеры ситуаций где знание таких вещей, как C, может сделать вас лучшим программистом на высоком уровне.
Я хочу знать, есть ли много примеров этого. Много раз ответ на этот вопрос был похож на " Знание C дает вам лучшее представление о том, что происходит под обложками" или ". Вам нужна прочная основа для вашей программы", и эти ответы не имеют большого значения. Я хочу понять различные конкретные способы, с помощью которых вам будет полезно знать концепции низкого уровня,
Джоэл привел несколько примеров: двоичные базы данных против XML и строки. Но два примера на самом деле не оправдывают обучение C и/или Ассамблее. Поэтому мой вопрос таков: Какие конкретные примеры существуют для того, чтобы знать, что C делает вас лучшим программистом на высоком уровне?
Ответы
Ответ 1
Мой опыт преподавания студентов и работы с людьми, которые изучали только языки высокого уровня, состоит в том, что они склонны думать на определенном высоком уровне абстракции, и они предполагают, что "все идет бесплатно". Они могут стать очень компетентными программистами, но в конечном итоге им приходится иметь дело с каким-то кодом, который имеет проблемы с производительностью, а затем он укусит их.
Когда вы много работаете с C, вы действительно думаете о распределении памяти. Вы часто думаете о макете памяти (и о местоположении кэш-памяти, если это проблема). Вы понимаете, как и почему некоторые графические операции стоят дорого. Насколько эффективны или неэффективны некоторые типы поведения сокетов. Как работают буферы и т.д. Я чувствую, что использование абстракций на языке более высокого уровня, когда вы знаете, как оно реализуется под обложками, иногда дает вам "этот лишний секретный соус", когда вы думаете о производительности.
Например, у Java есть сборщик мусора, и вы не можете напрямую напрямую передавать вещи в память. И все же вы можете сделать определенные варианты дизайна (например, с настраиваемыми структурами данных), которые влияют на производительность по тем же причинам, что и в C.
Кроме того, и в целом, я считаю, что для программиста по питанию важно знать не только нотацию Big-O (которую большинство школ обучают), но и в реальных приложениях постоянная также важна (какие школы пытаются игнорировать). Мой анекдотический опыт заключается в том, что люди, обладающие навыками на обоих языковых уровнях, как правило, лучше понимают константу, возможно, из-за того, что я описал выше.
Кроме того, многие системы более высокого уровня, которые я видел, взаимодействуют с библиотеками нижнего уровня и инфраструктурами. Например, некоторые коммуникации, базы данных или графические библиотеки. Некоторые драйверы для определенных устройств и т.д. Если вы являетесь программистом по питанию, вы, возможно, должны отправиться туда, и это помогает, по крайней мере, иметь представление о том, что происходит.
Ответ 2
Знание материала низкого уровня может помочь.
Чтобы стать гонщиком, вам нужно научиться и понять основную физику о том, как шины захватывают дорогу. Любой человек может научиться ездить довольно быстро, но вам нужно хорошее понимание материала "низкого уровня" (силы и трение, гоночные линии, тонкое управление дроссельной заслонкой и тормозом и т.д.), Чтобы получить эти последние несколько процентов производительности, которые позволят вам выиграть гонку.
Например, если вы понимаете, как архитектура ЦП работает на вашем компьютере, вы можете написать код, который лучше работает с ним (например, если вы знаете, что у вас есть определенный размер кэша процессора или определенное количество байтов в каждой строке кэша ЦП, вы можете упорядочить свои структуры данных и способ доступа к ним, чтобы наилучшим образом использовать кеш - например, обработка многих элементов массива по порядку часто быстрее, чем обработка случайных элементов из-за кэша ЦП). Если у вас многоядерный компьютер, то понимание того, как низкоуровневые методы, такие как работа с потоками, может дать огромные преимущества (так же, как не понимание низкого уровня может привести к катастрофе в потоковом режиме).
Если вы понимаете, как работают дисковые операции ввода-вывода и кеширования, вы можете модифицировать операции с файлами, чтобы хорошо работать с ним (например, если вы читаете из одного файла и пишете другому, работа с большими партиями данных в ОЗУ может помочь уменьшить I/O между этапами чтения и записи вашего кода и значительно повысить пропускную способность)
Если вы понимаете, как работают виртуальные функции, вы можете создать высокоуровневый код, который хорошо использует виртуальные функции. Если они используются неправильно, они могут серьезно затруднить работу.
Если вы понимаете, как обрабатывается рисунок, вы можете использовать умные трюки, чтобы улучшить скорость рисования. например Вы можете нарисовать шахматную доску, поочередно рисуя 64 белых и черных квадрата. Но часто бывает быстрее рисовать 32 белых квадрата, а затем 32 черных (потому что вам нужно изменить цвет чертежа дважды, а не 64 раза). Но на самом деле вы можете нарисовать всю доску черным, затем XOR 4 полосы по всей доске и 4 полосы вниз по доске в белом, и это может быть намного быстрее (изменения 2 цвета и всего 9 прямоугольников для рисования вместо 64). Этот трюк с шахматной доской учит вас очень важному навыку программирования: боковое мышление. Хорошо спроектировав свой алгоритм, вы часто можете иметь большое значение для того, насколько хорошо работает ваша программа.
Ответ 3
Понимание C, или, если на то пошло, любого языка программирования низкого уровня, дает вам возможность понять такие вещи, как использование памяти (то есть, почему это плохо, чтобы создать несколько миллионов тяжелых объектов), как работают указатели/ссылки на объекты, и др.
Проблема заключается в том, что, поскольку мы создали все возрастающие уровни абстракции, мы делаем много программ программирования "lego block", не понимая, как фактически функционируют legos. И имея почти бесконечные ресурсы, мы начинаем рассматривать память и ресурсы, такие как вода, и, как правило, решаем проблемы, бросая больше железа в ситуацию.
В то время как не ограничивается C, существует огромная польза для работы на низком уровне с гораздо меньшими системами с ограничением памяти, такими как 8-разрядные процессоры Arduino или старой школы. Он позволяет вам приблизиться к металлическому кодированию в гораздо более доступном пакете, и, потратив время на сжатие приложений на 512 КБ, вы обнаружите, что применяете эти навыки на более высоком уровне в своем повседневном программировании.
Таким образом, сам язык не важен, но с более глубоким пониманием того, как все биты объединяются и как эффективно работать на уровне, близком к аппаратным, - это набор навыков, полезных любому разработчику программного обеспечения.
Ответ 4
Во-первых, знание C помогает понять, как память работает в ОС и на других языках высокого уровня. Когда ваши программы на С# или Java запускаются в памяти, понимая, что ссылки (в основном, просто указатели) также занимают память и понимают, как много структур данных реализовано (которое вы получаете от создания собственного на C) помогает понять, что ваш словарь резервирует огромные объемы памяти, которые фактически не используются.
Для другого, знание C может помочь вам понять, как использовать функции операционной системы более низкого уровня. Вам это не нужно часто, но иногда вам могут понадобиться файлы с отображением памяти или использовать сортировку на С#, а C значительно поможет понять, что вы делаете, когда это произойдет.
Я думаю, что C также помог мне понять сетевые протоколы, но я не могу указать на конкретные примеры. Я читал еще один вопрос на тот день, когда кто-то жаловался на то, что бит-поля C "в основном бесполезны", и я думал, насколько элегантно битовые поля C представляют собой низкоуровневые сетевые протоколы. Языки высокого уровня, связанные со структурами бит, всегда заканчиваются беспорядком!
Ответ 5
В общем, чем больше вы знаете, тем лучше программист вы будете.
Однако иногда знание другого языка, такого как C, может заставить вас поступать неправильно, потому что может быть допущение, что это неверно на языке более высокого уровня (например, Python или PHP). Например, можно предположить, что поиск длины списка может быть O (N), где N - длина списка. Однако, вероятно, это не так во многих языковых версиях высокого уровня. В Python для большинства подобных списков стоимость O (1).
Знание больше о специфике языка поможет, но знание большего в целом может привести к ошибочным предположениям.
Ответ 6
Просто "знание" C не сделает вас лучше.
Но, если вы все понимаете, как работают родные двоичные файлы, как работает с ним процессор, каковы ограничения архитектуры, вы можете написать код, который проще для CPU.
Например, как кеши L1/L2 влияют на вашу работу, и как вы должны писать свой код, чтобы иметь больше обращений в кешках L1/L2. Когда вы работаете с C/С++ и делаете большие оптимизации, вам придется пойти на такие вещи.
Ответ 7
Это не столько знание C, сколько потому, что C ближе к голым металлам, чем к многим другим языкам. Вам нужно больше знать, как распределять/освобождать память, потому что вам нужно это делать самостоятельно. Выполнение этого помогает вам понять последствия многих решений, которые вы принимаете.
Для меня любой язык приемлем, если вы понимаете, как компилятор/интерпретатор (в основном) сопоставляет ваш код на компьютере. Это немного проще сделать на языке, который раскрывает это напрямую, но вы должны с небольшим чтением выяснить, как распределена и организована память, какие шаблоны индексирования более оптимальны, чем другие, какие конструкции более эффективны для конкретных приложений и т.д.
Более важным, я думаю, является хорошее понимание операционных систем, архитектур памяти и алгоритмов. Если вы понимаете, как работает ваш алгоритм, почему лучше выбрать один алгоритм или структуру данных над другим (например, HashSet vs. List) и как ваш код будет отображаться на машине, не имеет значения, какой язык вы используете.
Ответ 8
Это мой опыт того, как я учился и учил программированию, в частности, понимая C, это происходит в начале 1990 года, поэтому может быть немного антиквариатом, но страсть и драйв важны:
- Научитесь понимать принципы низкого уровня работы компьютера, такие как программирование EGA/VGA, здесь ссылка в архив Simtel на C на ПК.
- Понимание того, как работает TSR
- Загрузите весь архив фрагменты Bob Stout, который представляет собой большую коллекцию кода C, которая делает только одну вещь - изучает их и понимает, а не только, что коллекция фрагментов стремится быть переносимой.
- Перейдите на международный конкурс Obfuscated C Code (IOCCC) и посмотрите, как можно использовать код C и понимать интраузлов языка. Худшее злоупотребление кодом - победитель! Загрузите архивы и изучите их.
- Как и я, мне нравился печально известный Ponzo C Tutorial, который очень помог мне, к сожалению, архив очень трудно найти. Если кто-то знает, где их получить, оставьте комментарий, и я исправлю этот ответ, чтобы включить ссылку. Есть еще один, который я помню - Coronado [Generic?] C Tutorial, опять же, моя память об этом туманна...
- Посмотрите на журнал доктора Добба и C User Journal здесь - я не знаю, можете ли вы все еще напечатать их, но они были классик, может вспомнить ощущение наличия печатной копии в руке и отрыва дома, чтобы ввести код, чтобы узнать, что произойдет!
- Возьмите древнюю копию Turbo C v2, которую я считаю, что вы можете получить с borland.com и просто играть с 16-битным программированием на C, чтобы получить чувство и беспорядок с указателями... уверен, что это древний и старый, но игра с указателями на нем в порядке.
- Поймите и узнайте Указатели, Simtel.net - ключевая ссылка на достижение C Guru'ship из-за отсутствия лучшего слова, также вы найдете множество загрузок, относящихся к языку программирования C, - я помню, что на самом деле заказывал архив Simtel CD и искал C материал...
Ответ 9
Несколько вещей, с которыми вам приходится иметь дело непосредственно в C, которые отделяют вас от других языков, включают явное управление памятью (malloc
) и непосредственное обращение с указателями.
Моя подруга - один семестр от окончания MIT (где они в основном используют Java, Scheme и Python) со степенью "Computer Science", и в настоящее время она работает в компании, чья кодовая база находится на С++. В течение первых нескольких дней ей было трудно понять все указатели/ссылки и т.д.
С другой стороны, я очень легко перешел с С++ на Java, потому что меня никогда не путали в отношении pass-reference-by-value vs pass-by-reference.
Аналогично, в C/С++ гораздо более очевидно, что примитивы - это просто компилятор, обрабатывающий одни и те же множества бит по-разному, в отличие от языка, такого как Python или Ruby, где все является объектом с его собственными различными свойствами.
Ответ 10
Простой (не вполне реалистичный) пример, чтобы проиллюстрировать некоторые из приведенных выше рекомендаций. Рассмотрим, казалось бы, безобидный
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. Удобство всегда стоит где-то, и знание более низкого уровня может помочь вам определить и смягчить эти затраты.