¿Cuál es un ejemplo en el que saber C me hará escribir mejor código en cualquier otro idioma?

En los podcasts de desbordamiento de stack, Joel Spolsky insulta constantemente a Jeff Atwood acerca de que Jeff no sabe cómo escribir código en C. Su statement es que “saber C ayuda a escribir mejor código”. También siempre usa algún tipo de historia que involucra la manipulación de cadenas y cómo saber C le permitiría escribir rutinas de cadenas más eficientes en un idioma diferente.

Como alguien que sabe un poco de C, pero le encanta escribir código en perl y otros lenguajes de alto nivel, nunca me he encontrado con un problema que pude resolver escribiendo C.

Estoy buscando ejemplos de situaciones del mundo real donde conocer C sería útil al escribir un proyecto en un lenguaje de alto nivel / dynamic como perl o python.

Edición: leer algunas de las respuestas que ustedes han enviado ha sido genial, pero todavía no tiene ningún sentido para mí en este sentido:

Tomemos el ejemplo de strcat. Hay una forma correcta y una manera incorrecta de combinar cadenas en C. ¿Pero por qué debería (como desarrollador de alto nivel) pensar que soy más inteligente que Larry Wall? ¿Por qué los diseñadores de lenguaje no escribirían el código de manipulación de cadenas de la manera correcta?

El ejemplo clásico que utiliza Joel Spolsky es el uso incorrecto de strcat y strlen , y la detección de los algoritmos “Shlemiel el pintor” en general.

No es que necesites C para resolver problemas que los lenguajes de nivel superior no pueden resolver, es que saber que C bien te da una perspectiva de lo que está pasando debajo de todos esos niveles de idiomas que te permiten escribir mejor software. Porque una perspectiva de este tipo te ayuda a evitar escribir códigos que, por tu parte, son desconocidos O (n ^ 2), por ejemplo.

Edición: Algunas aclaraciones basadas en comentarios.

Saber que C no es un requisito previo para tal conocimiento, hay muchas maneras de adquirir el mismo conocimiento.

Saber C tampoco es una garantía de estas habilidades. Es posible que seas un experto en C y aún así escribas códigos horribles, maliciosos en cualquier otro idioma que toques.

C es un lenguaje de bajo nivel, sin embargo, aún tiene funciones y estructuras de control modernas, por lo que no siempre se queda atrapado en los detalles complicados. Es muy difícil llegar a ser competente en C sin obtener un dominio de ciertos fundamentos (como los detalles de la gestión de la memoria y los punteros), cuyo dominio a menudo paga grandes dividendos cuando se trabaja en cualquier idioma.

Siempre se trata de los fundamentos.

Esto es cierto en muchas actividades, así como en la ingeniería de software. No son los conjuros secretos los que hacen que los mejores progtwigdores sean los mejores, sino que es un mayor dominio de los fundamentos. La experiencia ha demostrado que el conocimiento de C tiende a tener una mayor correlación con el dominio de algunos de esos fundamentos, y que el aprendizaje de C tiende a ser una de las rutas más fáciles y comunes para adquirir dicho conocimiento.

Es un error suponer que el aprendizaje de C de alguna manera le dará una mejor comprensión de las preocupaciones de progtwigción de bajo nivel. En muchos casos, incluso C es un nivel demasiado alto para brindarle una buena comprensión de los problemas de eficiencia.

Un clásico es i ++ contra ++ i. Está sobre-citado, por lo que quizás la mayoría de las personas conocen las implicaciones sobre el rendimiento entre estas dos operaciones. Pero aprender C no te enseñaría esto por arte de magia.

Supongo que entiendo argumentos sobre cuerdas. Cuando las operaciones de cadena se hacen engañosamente simples, las personas a menudo las usan de manera ineficiente. Pero una vez más, saber que existe Strncat no le da una apreciación completa de las preocupaciones de eficiencia. Es probable que muchos progtwigdores de C ni siquiera hayan pensado en el hecho de que strncat tiene que hacer una operación de strlen internamente.

Incluso usando C, es importante entender lo que sucede detrás de escena si la eficiencia es una preocupación. Las personas que saben C tienden a ver las cosas en una progresión. El ensamblaje y el código de máquina son los componentes básicos de C, mientras que C es un componente básico de los idiomas de nivel superior.

Esto no es específicamente cierto, pero es obvio que C está “más cerca del metal” que muchos idiomas de nivel superior. Esto tiene al menos dos efectos: las preocupaciones de eficiencia no están tan ocultas detrás del comportamiento implícito, y es más fácil de arruinar.

Así que quieres un ejemplo específico de cómo saber C te da una ventaja. No creo que haya uno. Creo que lo que la gente quiere decir cuando dice esto es que saber qué sucede detrás de escena en el idioma en el que escribes te ayuda a tomar decisiones más inteligentes sobre cómo escribir código. Sin embargo, es un error suponer que C es, por ejemplo, “lo que está sucediendo entre bastidores” en Java.

Es difícil de cuantificar con exactitud, pero tener una comprensión de C le dará una mejor idea de cómo se implementan las construcciones de lenguaje de nivel superior y, como consecuencia, podrá usarlas de manera inteligente.

Para darle una razón específica: tener que escribir mis propias rutinas de recolección de basura me ha ayudado a escribir mejor código.

No creo haber encontrado nunca un problema que no haya podido resolver con un lenguaje de nivel superior; pero comenzó aprendiendo C, me ha inculcado un gran número de excelentes prácticas de desarrollo. Saber cómo funcionan las partes rudimentarias del flujo de una aplicación le permitirá poder ver su propio código y tener una buena idea de cómo fluyen los datos y dónde se almacenan. Esto conduce a una mejor comprensión de cómo rastrear la pérdida de memoria, las lecturas de disco lentas, los cachés mal construidos, etc.

Mantener un registro de los punteros … ese es otro que viene a la mente.

Los ejemplos clásicos son cosas que involucran la administración de memoria de nivel inferior, como la implementación de una clase de lista vinculada:

struct Node { Data *data; Node *next; } 

Comprender cómo se utilizan los punteros para recorrer la lista y qué significan en términos de la architecture de la máquina le permitirá comprender mejor su código de alto nivel.

Otro ejemplo al que se refería Joel fue la implementación de la concatenación de cadenas y la forma correcta de crear una cadena a partir de un conjunto de datos.

 // 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 

Saber C le ayuda a escribir mejor código en C. Supongo que el ejemplo de Joel Spolsky es de poca utilidad en C ++ o en Objective-C donde existen clases específicas para manipular cadenas y se han diseñado teniendo en cuenta el rendimiento. Además, el uso de trucos de C en otros idiomas puede ser más productivo.

Sin embargo, el conocimiento de C es muy útil para comprender conceptos generales en otros idiomas y lo que hay detrás del capó en muchas situaciones.

Como alguien que sabe un poco de C, pero le encanta escribir código en perl y otros lenguajes de alto nivel, nunca me he encontrado con un problema que pude resolver escribiendo C.

Estoy buscando ejemplos de situaciones del mundo real donde conocer C sería útil al escribir un proyecto en un lenguaje de alto nivel / dynamic como perl o python.

Es fácil comenzar a escribir código de alto nivel y luego preguntarnos si se está ejecutando lentamente. La verdad es que hay muchas maneras de escribir código perl o python, y algunas son mejores (como en más eficientes) que otras. Si conoce los detalles de bajo nivel de cómo se ejecuta su código en perl o python (los cuales están escritos en C) puede codificar varias ineficiencias, como saber qué construcción de bucle es más rápida, cómo se retiene / libera la memoria, etc. .

Además, al escribir un proyecto en perl o python, a veces se llega a un muro de rendimiento. Los creadores del lenguaje (Guido, al menos) recomiendan que implementes esa parte en C, como una extensión de lenguaje. Para hacer eso, bueno, tendrás que conocer a C.

Por lo tanto, allí.

Para los fines del argumento, suponga que desea concatenar las representaciones de cadena de todos los enteros de 1 a n (por ejemplo, n = 5 produciría la cadena “12345”). Así es como uno podría hacer eso ingenuamente en, digamos, Java.

 String result = ""; for (int i = 1; i <= n; i++) { result = result + Integer.toString(i); } 

Si tuviera que reescribir ese segmento de código (que es bastante atractivo en Java) en C tan literalmente como sea posible, obtendría algo para hacer que la mayoría de los progtwigdores de C se estremezcan de miedo:

 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; } 

Debido a que las cadenas en Java son inmutables, Integer.toString crea una cadena ficticia y la concatenación de cadenas crea una nueva instancia de cadena en lugar de alterar la anterior. No es fácil verlo simplemente mirando el código de Java. Saber cómo dicho código se traduce a C es una forma de aprender exactamente cuán ineficiente es dicho código.

¿Usas arrays mucho? y se encuentra con situaciones en las que necesita que los elementos se almacenen en la memoria sin saber cuántos de ellos (es decir, ¿se basa en una consulta de la base de datos?), entonces supongo que C le enseñaría grandes cosas como stacks, estructuras y listas de enlaces que podrían ayudarte. Saludos, Andy

Saber C realmente no vale mucho. Muchos de los que conocemos a C nos gusta mucho pensar que toda esa visión profunda es valiosa e importante.

Algunos de los que sabemos que C no puede pensar en una sola característica específica de C que sea útil conocer.

Saber cómo funcionan los punteros en C (especialmente con la syntax de C) no es tan útil. En un lenguaje de alto nivel, sus declaraciones crean objetos y gestionan su interacción. Los punteros y las referencias son, quizás, interesantes desde un punto de vista hipotético. Pero el conocimiento no tiene un impacto práctico en cómo usas Java o Python.

Los lenguajes de nivel superior son como son. Saber cómo no cambia esos idiomas; No cambia la forma en que los usas, los depuras o los pruebas.

Saber cómo crear o manipular una lista vinculada no tiene un impacto terrenal en la definición de clase de lista de Python. Ninguna.

Saber la diferencia entre Lista enlazada y Lista de matrices puede ayudarlo a escribir un progtwig Java. Pero la implementación de C no le ayuda a elegir entre Lista vinculada y Lista de matrices. La decisión es independiente de conocer a C.

Un mal algoritmo es malo en todos los idiomas. Conocer los misterios internos de C no hace que un mal algoritmo sea menos malo. Saber C no le ayuda a conocer las colecciones de Java o los tipos incorporados de Python.

No veo ningún valor en aprender C. Aprender Fortran es igual de valioso.

Lo veo así, todo se reduce a C en un nivel de plataforma cruzada y se ensambla de una manera específica de la plataforma. Así que es como ser un piloto de Rally de campo traviesa, y C es la mecánica automotriz básica, puedes ser un gran piloto, pero cuando te metes en problemas al saber C significa que probablemente puedas volver a la carrera, si no te quedas atascado llamando a la mecánica . Y el ensamblaje es lo que saben los mecánicos y los fabricantes, es una buena inversión si eso es lo que quiere hacer, de lo contrario, puede confiar en los mecánicos.

Para aspectos específicos, piense en la administración de memoria, controladores de hardware, motores de física, gráficos 3D de alto rendimiento, stacks TCP, protocolos binarios, software integrado, creación de lenguajes de alto nivel como Perl

No puedes escribir un kernel de SO en Perl; C sería una opción mucho mejor para eso, porque es lo suficientemente bajo para express todo lo que debería hacer el kernel y lo suficientemente portátil para permitirle portar su kernel a diferentes architectures

Saber C no es un requisito para poder usar con eficacia lenguajes de nivel superior, pero ciertamente puede ayudar a comprender mejor cómo funcionan las computadoras y el software. Creo que es similar a una afirmación de que saber algo de lenguaje ensamblador o architecture / hardware de computadora. (y / o / y las puertas, etc.) pueden ayudar a un progtwigdor de C a ser un mejor progtwigdor.

A veces, para resolver un problema, es útil saber cómo están funcionando las cosas “debajo” de lo que estás haciendo.

No creo que esto signifique que un progtwigdor debe conocer C para ser un buen progtwigdor, pero creo que saber C puede ser útil para casi cualquier progtwigdor.

Al no conocer bien a Perl, me pregunto si ahora es posible distribuir la carga del procesador a más de un núcleo físico con varios subprocesos creados en un solo progtwig en Perl, sin generar procesos adicionales

No creo que pueda haber ningún ejemplo específico.

El aprendizaje que C hace por usted es brindarle una perspectiva, una ampliación de la mente, sobre cómo funcionan las computadoras (y el software). Es una cosa muy abstracta ..

No te hace escribir mejor código en python, solo te hace más científico informático.

La referencia que hizo Wedge al artículo de Joel sobre el pintor Shlemiel es interesante pero no tiene relevancia aquí. Ese algoritmo no está vinculado a C de ninguna manera en particular (aunque se manifiesta en cadenas terminadas en nulo).

Las cuerdas de Python son inmutables de todos modos, y son completamente diferentes del modelo de cuerdas de C, así que no veo la relación.

Supongo que un ejemplo concreto es la optimización de un analizador o un lexer o un progtwig que continúa escribiendo en un buffer de cadena todo el tiempo. Si usa cadenas normales en lugar de un búfer de cadenas, encontrará un problema cuando construya cadenas muy grandes.

Considere eso:

 a = a + b 

hace una copia de a y b . No cambia la cadena a la que hace referencia a, crea una nueva cadena, asigna más memoria, etc.

Si a vuelve considerablemente grande, y sigues agregándole pequeñas cosas, entonces Shlemiel, el pintor, se manifestará.

Pero nuevamente, saber esto no tiene nada que ver con conocer C, solo saber cómo su lenguaje implementa las cosas en el nivel bajo. (Aquí es donde tener una experiencia en C te ayudará).

Técnicamente, todas las deficiencias de C te obligarían a codificar a su alrededor; Haciéndote escribir más código -> Haciéndote más experimentado en general. Al carecer de un número entero portátil más grande que 32 bits, por ejemplo, C me ha hecho, en el pasado, escribir mi propia biblioteca bignum.

La falta de memoria implícita, gestión de recursos y errores (recolección de basura, RAII, constructores / destructores llamados automáticamente, tal vez excepciones) obliga a los usuarios de C a escribir mucha inicialización, manejo de errores y código de limpieza. Puede que solo sea yo, pero nunca estoy cansado de escribir tal código. Voy y leo la documentación de cada función externa a la que llamo, vuelvo a mi código y verifico cada valor de retorno y otras cosas indicativas de falla. ¡Incluso me hace sentir seguro!

Este último punto es probablemente el más grande que se ha hecho a favor del argumento. ¡Solo puedes escribir tantos pares malloc () / free () antes de comenzar a analizar la vida útil de cada variable que encuentres en cada idioma! Los objetos de almacenamiento automático de C ++ tampoco ayudan a este desorden.

Escribir un código C verdaderamente portátil a menudo requiere que el progtwigdor no tenga muchas suposiciones sobre el sistema host: piense en sizeof (), CHAR___BITS, sin signo largo, UINT_MAX. Si bien esto no me ha ayudado a escribir mejor código en otros idiomas, me ha ayudado a pensar en posibles implementaciones alternativas: cómo un pequeño microprocesador aún podría ejecutar mi código C, generando una gran cantidad de instrucciones RISC para mi simple statement de una línea. (Eso es otra cosa; no hay muchos otros idiomas que se mapeen desde y hacia un lenguaje ensamblador dado tan fácilmente en mi cabeza. Por otra parte, tal vez sea yo).

Por supuesto, ninguno de estos argumentos es válido solo para C. @ S.Lott tiene un punto válido: Fortran podría ser una alternativa igualmente buena. ¡Pero hay tanto código C alrededor! Un sistema de computadora personal completo de arriba a abajo, de aplicaciones a bibliotecas, de controladores a kernel, está disponible en código fuente en C. Sería un desperdicio si no pudiera leerlo.

En Python, digamos que tienes una función

 def foo(l=[]) l.append("bar") return l; 

En alguna versión de Python, disponible hace aproximadamente un año, ejecutando foo () por veces, obtendrías un resultado realmente interesante (es decir, ["bar","bar","bar","bar] ).

Parece que alguien implementó los parámetros predeterminados como una variable estática (y sin restablecerla), por lo que se producen resultados inesperados.

Tal vez mi ejemplo fue artificial: un amigo mío a quien realmente le gusta Python encontró este error peculiar, pero el hecho es que todos estos lenguajes están implementados en C o C ++. No saber y no entender los conceptos que son fundamentales para el lenguaje base significa que no tendrá una comprensión profunda de los idiomas que están construidos sobre eso.

Encuentro todos los “por qué molestarse con la pregunta C / C ++ / ASM tonta”. Si estás lo suficientemente inclinado a aprender un idioma, eso significa que tienes la curiosidad de entrar en el primer lugar. ¿Por qué detenerse justo antes de C?

Saber C es genial porque no hace nada detrás de tu espalda (GC, verificación de límites, etc.). Sólo hace exactamente lo que le dices también. Nada está implícito. Incluso C ++ hace cosas que no se le dicen también con RAII (por supuesto, está implícito que el objeto se destruye cuando se sale del scope, pero en realidad no se escribe eso). C es una excelente manera de aprender lo que sucede “debajo del capó” de la computadora, sin tener que escribir el ensamblaje.

El código ineficiente (p. ej., bucles de cadena + =) es típicamente ineficiente en cualquier idioma. ¿Qué diferencia hay si alguien explica por qué es ineficiente en uno u otro idioma? conocer C, pero no darse cuenta de que un método es ineficiente, no es diferente a conocer Python y no darse cuenta de lo mismo.

Creo que vale la pena conocer un lenguaje de bajo nivel, y hay razones pragmáticas para elegir C:

  • Es de bajo nivel, cerca del ensamblador.
  • Esta muy extendido

Entender la stack completa es valioso. A veces necesitas depurar las entrañas de algo. A veces no puede solucionar un problema de rendimiento sin un conocimiento de bajo nivel (este no suele ser el caso, por ejemplo, cuando el problema de rendimiento es puramente algorítmico, pero a veces lo es).

¿Por qué C es ampliamente considerado el “fondo de la stack” por excelencia, y no otro (s) idioma (s)? Creo que esto es porque C es un lenguaje de progtwigción de bajo nivel, y C ganó . Ha sido un tiempo ahora, pero C no siempre fue tan dominante. Para tomar solo un famoso ejemplo, los defensores de Common Lisp (que tenían sus propias formas de escribir código de bajo nivel) esperaban que su lenguaje también fuera popular, y que finalmente se perdiera .

Los siguientes se implementan generalmente en C:

  • sistemas operativos (variantes de Unix, Windows, muchos sistemas operativos integrados)
  • lenguajes de progtwigción de nivel superior (muchas implementaciones populares de Java, Python, etc.)
  • (Obviamente) resmas de proyectos populares de código abierto.

No soy una persona de hardware, pero supongo que C también ha influido mucho en el diseño de la CPU.

Entonces, si crees en entender a toda la stack, aprender C es, desde una perspectiva pragmática, la mejor opción.

Como advertencia, creo que también vale la pena aprender el ensamblador. Aunque C está cerca del metal, no entendí completamente C hasta que tuve que hacer un ensamblador. Ocasionalmente es útil comprender cómo se realizan realmente las funciones de las llamadas, cómo se implementan los bucles, etc. Menos importante, pero también útil, es tener que (al menos una vez) lidiar con un sistema sin memoria virtual. Al usar C en Windows, Unix y ciertos otros sistemas operativos, incluso humble malloc hace mucho trabajo bajo las coberturas que es más fácil de apreciar, depurar y / o sintonizar si alguna vez ha tenido que lidiar con el locking y deslocking manual de la memoria. regiones (no es que recomiendo hacerlo regularmente)