Что такое пример, в котором знание C заставит меня написать лучший код на любом другом языке?
В подкастах Qaru Joel Spolsky постоянно наводит на Джеффа Этвуда о Джеффе, не зная, как писать код на C. Его утверждение гласит, что "знание C помогает вам писать лучший код". Он также всегда использует какую-то историю, связанную с манипуляциями с строками, и как знание C позволит вам писать более эффективные строковые подпрограммы на другом языке.
Как кто-то, кто знает немного C, но любит писать код в perl и других языках высокого уровня, я никогда не сталкивался с проблемой, которую я смог решить, написав C.
Я ищу примеры реальных ситуаций, когда знание C было бы полезно при написании проекта на высокоуровневом/динамическом языке, таком как perl или python.
Изменить: чтение некоторых ответов, которые вы, ребята, представили, было замечательным, но в этом отношении мне все равно не имеет смысла:
Возьмем пример strcat. Там правильный путь и неправильный способ комбинировать строки в C. Но почему я должен (как разработчик высокого уровня) думать, что я умнее Ларри Уолл? Почему разработчики языка не будут правильно писать код манипуляции строкой?
Ответы
Ответ 1
Классический пример, который использует Джоэл Спольски, находится на неправильном использовании strcat и strlen и определении алгоритмов Shlemiel the painter в целом.
Это не то, что вам нужно C для решения проблем, которые языки более высокого уровня не могут решить, это то, что знание C хорошо дает вам представление о том, что происходит под всеми этими уровнями языков, что позволяет вам писать лучшее программное обеспечение. Поскольку такая перспектива помогает избежать написания неизвестного вам кода, например, O (n ^ 2).
Изменить: Некоторые пояснения, основанные на комментариях.
Знание C не является обязательным условием для такого знания, существует много способов получить те же знания.
Знание C также не является гарантией этих навыков. Вы можете быть опытными на C и все же писать ужасный, громоздкий, kludgy код на любом другом языке, который вы касаетесь.
C - язык низкого уровня, но он все еще имеет современные структуры управления и функции, поэтому вы не всегда попадаете в затруднительное положение. Очень сложно овладеть C, не овладевая определенными фундаментальными особенностями (такими как детали управления памятью и указателями), мастерство которых часто приносит богатые дивиденды при работе на любом языке.
Он всегда об основных принципах.
Это верно во многих целях, а также в разработке программного обеспечения. Это не секретные заклинания, которые делают лучших программистов лучшими, скорее это более полное мастерство основ. Опыт показывает, что знание C имеет тенденцию к более высокой корреляции с овладением некоторыми из этих основ и что обучение C имеет тенденцию быть одним из более простых и более распространенных путей получения таких знаний.
Ответ 2
Ошибочно предположить, что обучение C каким-то образом даст вам лучшее понимание проблем программирования на низком уровне. Во многих случаях даже C является слишком высоким уровнем, чтобы дать вам хорошее представление об эффективности.
Классика - это я ++ или ++ i. Это чрезмерно цитируется, поэтому, возможно, большинство людей знают о последствиях производительности между этими двумя операциями. Но изучение C не волшебным образом научит вас этому самому.
Думаю, я понимаю аргументы о строках. Когда операции с строкой сделаны обманчиво простыми, люди часто используют их неэффективно. Но опять же, зная, что strncat существует, не дает вам полной оценки эффективности. Многие программисты из C, вероятно, даже не думали о том, что strncat должен выполнять операцию strlen внутри.
Даже используя C, важно понять, что происходит за кулисами, если эффективность является проблемой. Люди, которые знают С, склонны рассматривать вещи в прогрессии. Сборочный и машинный код являются строительными блоками C, а C - строительным блоком языков более высокого уровня.
Это не совсем верно, но очевидно, что C "ближе к металу", чем многие языки более высокого уровня. Это, по крайней мере, два эффекта: проблемы с эффективностью не так скрыты позади неявного поведения, и это легче испортить.
Итак, вам нужен конкретный пример того, как знание C дает вам преимущество. Я не думаю, что он есть. Я думаю, что люди имеют в виду, когда говорят, что это означает, что знание того, что происходит за кулисами на любом языке, который вы собираетесь писать, помогает вам принимать более разумные решения о том, как писать код. Однако ошибочно предположить, что C - это "то, что происходит за кулисами" в Java, например.
Ответ 3
Трудно точно определить количественно, но понимание C даст вам более полное представление о том, как реализованы языковые конструкции более высокого уровня, и, как следствие, вы сможете лучше использовать конструкции интеллектуальным образом.
Ответ 4
Чтобы дать вам конкретную причину: мне пришлось писать свои собственные программы сбора мусора, которые помогли мне написать лучший код.
Я не думаю, что когда-либо обнаружил проблему, которую я не смог решить с помощью языка более высокого уровня; но начавшись с обучения C, он привил мне целый ряд отличных методов развития. Знание того, как рудиментарные части потока работы приложения позволят вам взглянуть на свой собственный код и получить хорошее представление о том, как потоки данных и где они хранятся. Это приводит к лучшему пониманию того, как отслеживать утечку памяти, медленные чтения дисков, плохо сконструированные кеши и т.д.
Отслеживание указателей... это еще один, который приходит на ум.
Ответ 5
Классическими примерами являются вещи, относящиеся к управлению памятью более низкого уровня, такие как реализация связанного класса списка:
struct Node
{
Data *data;
Node *next;
}
Понимание того, как указатели используются для повторения списка, и то, что они означают с точки зрения архитектуры машины, позволит вам лучше понять ваш код высокого уровня.
Другим примером, на который ссылался Джоэл, была реализация конкатенации строк и правильный способ создания строки из набора данных.
// this is efficient
for (int i=0; i< n; i++)
{
strcat(str, data(i));
}
// this could be too, but you'd need to look at the implementation to be sure
std::string str;
for (int i=0; i<n; i++)
{
str+=data(i);
}
Ответ 6
Зная C, вы можете написать лучший код на C. Я предполагаю, что пример Джоэла Спольского мало используется в С++ или Objective-C, где существуют определенные классы для манипулирования строками и созданы с учетом производительности. Более того, использование трюков C на других языках может быть продуктивным.
Тем не менее, знание C очень полезно для понимания общих понятий на других языках и того, что находится за капотом во многих ситуациях.
Ответ 7
Как кто-то, кто знает немного C, но любит писать код на perl и других языках высокого уровня, я никогда не сталкивался с проблемой, которую смог решить, написав C.
Я ищу примеры реальных ситуаций, когда знание C было бы полезно при написании проекта на высокоуровневом/динамическом языке, таком как perl или python.
Легко начать писать код высокого уровня, а потом удивляться, что мы работаем медленно. Правда, есть много способов написать perl или код python, а некоторые лучше (как в более эффективном), чем другие. Если вы знаете информацию о низком уровне того, как ваш код выполняется в perl или python (оба из которых написаны на C), вы можете кодировать несколько неэффективных функций - например, знание того, какая конструкция цикла выполняется быстрее, как память сохраняется/освобождается и т.д..
Кроме того, при написании проекта в perl или python вы иногда попадаете в стену производительности. Создатели языка (по крайней мере, Гвидо) выступают за то, чтобы вы реализовали эту часть на языке C как расширение языка. Для этого вам нужно знать C.
Итак, там.
Ответ 8
В целях аргумента предположим, что вы хотели бы объединить строковые представления всех целых чисел от 1 до n (например, n = 5 создаст строку "12345" ). Вот как это можно было бы наивно реализовать, скажем, в Java.
String result = "";
for (int i = 1; i <= n; i++) {
result = result + Integer.toString(i);
}
Если вы должны переписать этот сегмент кода (который довольно хорошо выглядит на Java) на C как можно более буквально, вы получите что-то, что заставит большинство программистов C сжиматься от страха:
char *result = malloc(1);
*result = '\0';
for (int i = 1; i <= n; i++) {
char *intStr = malloc(11);
itoa(i, intStr, 10);
char *tempStr = malloc(/* some large size */);
strcpy(tempStr, result);
strcat(tempStr, intStr);
free(result);
free(intStr);
result = tempStr;
}
Поскольку строки в Java неизменяемы, Integer.toString
создает фиктивную строку, а конкатенация строк создает новый экземпляр строки вместо изменения старого. Это нелегко понять, просто глядя на Java-код. Знание того, как указанный код переводится на C, является одним из способов точно узнать, насколько неэффективен указанный код.
Ответ 9
Вы много используете массивы? и вы сталкиваетесь с ситуациями, когда вам нужны элементы, которые нужно хранить в памяти, не зная, сколько из них (т.е. основано на запросе из базы данных?), тогда я полагаю, что C научит вас отличным вещам, таким как стеки, структуры и списки ссылок, которые могут помочь тебе. С уважением, Энди
Ответ 10
Знание C действительно не стоит того. Многие из нас, которые знают C, глубоко любят думать, что все это глубокое понимание ценно и важно.
Некоторые из нас, кто знает C, не могут придумать одну специфическую особенность C, которая будет полезной для понимания.
Знание того, как работают указатели на C (особенно с синтаксисом C), не все так полезно. На языке высокого уровня ваши утверждения создают объекты и управляют их взаимодействием. Указатели и ссылки - возможно, интересны с гипотетической точки зрения. Но знание не оказывает практического влияния на то, как вы используете Java или Python.
Языки более высокого уровня такие, как они. Знание того, как эти языки не меняются; он не изменяет, как вы их используете, отлаживаете или проверяете.
Знание того, как создавать или манипулировать связанным списком, не оказывает никакого влияния на определение класса списка Python. Никто.
Знание разницы между Linked List и Array List может помочь вам написать Java-программу. Но реализация C не поможет вам выбирать между Linked List и Array List. Решение не зависит от знания C.
Плохой алгоритм плохой на каждом языке. Знание внутренних тайн C не делает плохой алгоритм менее плохим. Знание C не помогает вам знать коллекции Java или встроенные типы Python.
Я не вижу никакой ценности в обучении C. Обучение Fortran так же ценно.
Ответ 11
Я вижу это так: все сводится к C на кроссплатформенном уровне и сборке на платформе определенным образом. Так что, как быть гонщиком раллийного ралли, а C - это основная автомобильная механика, вы можете стать отличным водителем, но когда вы попадаете в неприятности, зная, что C означает, что вы, вероятно, можете вернуться в гонку, если не застряли, вызывая механику, И сборка - это то, что знают механики и производители, это достойная инвестиция, если это то, что вы хотите сделать, иначе вы можете просто доверять механике.
В частности, рассмотрим управление памятью, жесткие драйверы, физические двигатели, высокопроизводительные 3D-графики, стеки TCP, двоичные протоколы, встроенное программное обеспечение, создание языков высокого уровня, таких как Perl
Ответ 12
Вы не можете написать ядро ОС в Perl; C был бы намного лучшим выбором для этого, потому что он достаточно низкоуровнев, чтобы выразить все, что должно делать ядро, и достаточно портативный, чтобы вы могли переносить ядро на разные архитектуры.
Ответ 13
Знание C не является обязательным требованием к эффективному использованию языков более высокого уровня, но, безусловно, может помочь в общем понимании того, как работают компьютеры и программное обеспечение - я думаю, что это похоже на утверждение о том, что знание языка ассемблера или компьютерной архитектуры/аппаратная логика (и/или/nand gates и т.д.) могут помочь программисту C быть лучшим программистом.
Иногда для решения проблемы это помогает узнать, как все работает "под", что вы делаете.
Я не думаю, что это означает, что программист должен знать C, чтобы быть хорошим программистом, но я думаю, что знание C может быть полезно почти любому программисту.
Ответ 14
Не зная Perl, мне интересно, возможно ли теперь распределять нагрузку процессора на более чем одно физическое ядро с несколькими потоками, созданными в одной программе на Perl, без появления дополнительных процессов.
Ответ 15
Я не думаю, что может быть какой-то конкретный пример.
Какое обучение C для вас дает вам понимание, расширение ума, как работают компьютеры (и программное обеспечение). Это очень абстрактная вещь.
Это не заставляет вас писать код лучше на python, а просто делает вас более ученым-компьютером.
Ссылка, сделанная клином на статью Джоэля, в которой упоминается Шлемиеля, является интересной, но здесь не имеет значения. Этот алгоритм никак не связан с C (хотя он проявляется в строках с нулевым завершением).
Строки Python в любом случае неизменяемы и полностью отличаются от C строк строк, поэтому я не вижу отношения.
Я предполагаю, что один конкретный пример - оптимизация парсера или лексера или программы, которая постоянно записывает в строковый буфер. Если вы используете обычные строки вместо строкового буфера, вы столкнетесь с проблемой при создании очень больших строк.
Рассмотрим, что:
a = a + b
делает копию как a
, так и b
. Он не меняет строку, на которую ссылается a, она создает новую строку, выделяет больше памяти и т.д.
Если a
становится значительно большим, и вы продолжаете добавлять к нему небольшие вещи, тогда Шлемиэль проявит себя.
Но опять же, зная, что это не имеет никакого отношения к знанию C, просто зная, как ваш язык реализует вещи на низком уровне. (Здесь вам поможет эксперимент на C).
Ответ 16
Технически, все недостатки C заставят вас закодировать вокруг них; заставляя вас писать больше кода → делая вас более опытными в целом. Не имея какого-либо портативного целого, превышающего 32 бита, например, C в прошлом заставлял меня писать свою собственную библиотеку bignum.
Отсутствие неявной памяти, управление ресурсами и ошибками (сбор мусора, RAII, автоматически называемые конструкторы/деструкторы, а также исключения) заставляют пользователей C писать много операций инициализации, обработки ошибок и очистки. Это может быть только я, но я никогда не устаю писать такой код. Я читаю документацию по каждой внешней функции, которую я вызываю, возвращаюсь к своему коду и проверяю каждое возвращаемое значение и другие отказоустойчивые материалы. Это даже заставляет меня чувствовать себя в безопасности!
Этот последний пункт, вероятно, самый большой, который можно сделать в пользу аргумента. Вы можете написать столько пар malloc()/free(), прежде чем приступать к анализу жизненного цикла каждой переменной, с которой вы сталкиваетесь на всех языках! Объекты автоматического хранения С++ также не помогают этому беспорядку.
Написание действительно портативного кода C часто требует, чтобы программист был свободен от многих предположений о хост-системе - подумайте sizeof(), CHAR___BITS, unsigned long, UINT_MAX. Хотя это не помогло мне написать лучший код на других языках, это помогло мне подумать о возможных альтернативных реализациях: как крошечный микропроцессор все еще мог запустить мой код C, генерируя gazillion инструкции RISC для моей простой однострочной инструкции. (Это другое дело: не многие другие языки так легко отображают язык ассемблера и из него, что может быть только мне.)
Конечно, ни один из этих аргументов не подходит только для C. @S.Lott имеет действительную точку - Fortran может быть одинаково хорошей альтернативой. Но есть так много C-кода! Вся персональная компьютерная система сверху донизу - приложения к библиотекам для драйверов для ядра - доступна в исходном коде на C. Это будет такая потеря, если вы не сможете ее прочитать.
Ответ 17
В Python, скажем, у вас есть функция
def foo(l=[])
l.append("bar")
return l;
В некоторой версии Python, доступной около года назад, при запуске foo() для времени вы получите действительно интересный результат (т.е. ["bar","bar","bar","bar]
).
Похоже, что кто-то реализовал параметры по умолчанию как статическую переменную (и без ее перезагрузки), так что неожиданные результаты происходят.
Возможно, мой пример был изобретен - мой друг, который действительно любит Python, нашел эту странную ошибку, но факт этого вопроса - все эти языки реализованы на C или С++. Незнание и не понимание понятий, которые имеют основополагающее значение для базового языка, означает, что у вас не будет глубокого понимания языков, которые построены поверх этого.
Я нахожу все "зачем беспокоиться о проблемах с C/С++/ASM глупо". Если вы достаточно склонны изучать язык, это означает, что вы достаточно любопытны, чтобы попасть в него на первое место. Зачем останавливаться перед C?
Ответ 18
Знание C отлично, потому что оно ничего не делает за вашей спиной (GC, проверка границ и т.д.). Это делает только то, что вы говорите. Ничего не подразумевается. Даже С++ делает то, что вы не рассказываете об этом тоже с RAII (конечно, подразумевается, что объект разрушается, когда он выходит из области видимости, но на самом деле вы этого не пишете). C - отличный способ узнать, что происходит "под капотом" компьютера, без необходимости писать сборку.
Ответ 19
неэффективный код (например, петли строки + =), как правило, неэффективны на любом языке. какая разница, если кто-то объясняет, почему он неэффективен на одном языке или другом? зная C, но не понимая, что метод неэффективен, ничем не отличается от знания python и не осознает того же.
Ответ 20
Я думаю, что стоит знать какой-то язык низкого уровня, и есть прагматические причины выбора C:
- Он низкоуровневый, рядом с ассемблером
- Широко распространено
Понимание всего стека ценно. Иногда вам нужно отлаживать что-то кишки. Иногда вы не можете исправить проблему производительности без низкоуровневых знаний (это часто бывает не так, например, когда проблема производительности является чисто алгоритмической, но иногда она есть).
Почему C широко рассматривается как квинтэссенция "дна стека", а не какой-либо другой язык (ы)? Я думаю, это потому, что C - низкоуровневый язык программирования, а C выиграл. Прошло уже некоторое время, но C не всегда был доминирующим. Чтобы взять только один известный пример, сторонники Common Lisp (у которых были свои собственные способы написания низкоуровневого кода) надеялись, что их язык будет популярен и, в конечном итоге, потеряно.
Обычно в C:
- операционные системы (Unix-варианты, Windows, многие встроенные операционные системы)
- языки программирования более высокого уровня (многие популярные реализации Java, Python и т.д.)
- (очевидно) множество популярных проектов с открытым исходным кодом
Я не специалист по аппаратным средствам, но я понимаю, что C тоже сильно повлиял на дизайн процессора.
Итак, если вы верите в понимание всего стека, изучение C с прагматичной точки зрения является лучшим выбором.
В качестве предостережения, я думаю, что стоит также изучить ассемблер. Хотя C близко к металлу, я не совсем понял C до тех пор, пока мне не пришлось делать ассемблер. Иногда полезно понимать, как фактически выполняются вызовы функций, как реализуются циклы for
и т.д. Менее важным, но также полезным, является (по крайней мере один раз) обращение к системе без виртуальной памяти. При использовании C в Windows, Unix и некоторых других операционных системах даже скромный malloc
выполняет большую работу под обложками, которые легче оценить, отладить и/или настроить, если вам когда-либо приходилось иметь дело с ручным блокированием и разблокировка областей памяти (не то, что я рекомендовал бы делать это регулярно!)