【问题标题】:What is an example in which knowing C will make me write better code in any other language?什么是知道 C 会让我用任何其他语言编写更好的代码的例子?
【发布时间】:2009-04-06 01:16:28
【问题描述】:

在 Stack Overflow 播客中,Joel Spolsky 不断向 Jeff Atwood 抱怨 Jeff 不知道如何用 C 编写代码。他的说法是“了解 C 可以帮助您编写更好的代码”。他还总是使用一些涉及字符串操作的故事,以及了解 C 如何让您用不同的语言编写更有效的字符串例程。

作为一个懂一点 C,但喜欢用 perl 和其他高级语言编写代码的人,我从来没有遇到过可以通过编写 C 来解决的问题。

我正在寻找在使用 perl 或 python 等高级/动态语言编写项目时了解 C 的实际情况的示例。

编辑:阅读你们提交的一些答案非常棒,但在这方面对我来说仍然没有任何意义:

以 strcat 为例。在 C 语言中组合字符串有正确的方法和错误的方法。但为什么我(作为高级开发人员)应该认为我比 Larry Wall 聪明?为什么语言设计者不能以正确的方式编写字符串操作代码?

【问题讨论】:

  • "为什么语言设计者不能以正确的方式编写字符串操作代码?"一般来说,这没有多大意义。它在 C 编程的上下文中没有任何意义。也许这应该是一个完全不同的问题——一个被重写的问题,以便更清楚地知道你想知道什么具体的事情。

标签: c


【解决方案1】:

Joel Spolsky 使用的经典示例是在 misuse of strcat and strlen 上,并且通常会发现“画家什莱米尔”算法。

并不是说您需要 C 来解决高级语言无法解决的问题,而是了解 C 可以让您了解所有这些语言级别下正在发生的事情,从而使您能够编写更好的软件。因为只有这样的视角可以帮助您避免编写您不知道的代码,例如实际上是 O(n^2)。

编辑:基于 cmets 的一些说明。

了解 C 并不是这些知识的先决条件,有很多方法可以获取相同的知识。

了解 C 也不能保证这些技能。你可能精通 C 语言,但仍然用你接触过的所有其他语言编写可怕、怪诞、笨拙的代码。

C 是一种低级语言,但它仍然具有现代控制结构和功能,因此您不会总是被繁琐的细节所困扰。如果不掌握某些基础知识(例如内存管理和指针的细节),就很难精通 C,而掌握这些基础知识在使用任何语言时通常会带来丰厚的回报。

始终关注基本面。

在许多追求以及软件工程中都是如此。让最优秀的程序员成为最优秀的不是秘密咒语,而是对基础知识的更好掌握。经验表明,C 知识往往与掌握其中某些基础知识具有更高的相关性,而学习 C 往往是获得此类知识的更容易和更常见的途径之一。

【讨论】:

  • 正确。要制作出色的程序,您需要全局,而高级语言是其中的重要组成部分。如果您不了解 C(甚至更好的汇编),您就会错过整个部分。
  • 了解 C 并不是识别 Sclemiel 算法的先决条件。 8 年前,当我只有 3 年的经验时,我正在使用 Perl/DBI 将 50 万条记录从 Oracle 表移植到 MySQL 表,我确定了自己的 Shlemiel 算法,修复了它,并在 0.5 小时内移动了所有记录
  • 他不是说这是先决条件,只是C可以帮助你看到这些东西。
  • @PintSizedCat:看不出了解 C 语言如何帮助您发现糟糕的算法。糟糕的 Schlemiel 算法存在于 C 之外。事实上,了解 C 经常使人们感到困惑,因为他们认为他们可以对 ++ 操作进行微优化以加速愚蠢的 Schlemiel 算法。
  • @S.Lott 这将是不了解您的语言的问题,而不是了解 C 的问题
【解决方案2】:

认为学习 C 会自动让您更好地理解低级编程问题是错误的。在很多情况下,甚至 C 级别都太高,无法让您很好地理解效率问题。

经典是 i++ 与 ++i。它被过度引用了,所以也许大多数人都知道这两个操作之间对性能的影响。但是学习 C 语言本身并不能神奇地教给你这个。

我想我理解关于字符串的论点。当字符串操作看似简单时,人们经常以低效的方式使用它们。但同样,知道 strncat 存在并不能让您完全理解效率问题。很多 C 程序员可能甚至没有想过 strncat 必须在内部进行 strlen 操作。

即使使用 C,如果关注效率,了解幕后发生的事情也很重要。了解 C 的人倾向于以渐进的方式看待事物。汇编和机器代码是 C 的构建块,而 C 是高级语言的构建块。

这并不是特别正确,但很明显,C 比许多高级语言“更接近金属”。这至少有两个影响:效率问题没有隐藏在隐式行为背后,而且更容易搞砸。

因此,您需要一个具体的例子来说明了解 C 如何为您带来优势。我不认为有一个。我认为人们 的意思 是说了解幕后发生的事情,无论您使用哪种语言编写代码,都可以帮助您就如何编写代码做出更明智的决定。然而,假设 C 是 Java 中的“幕后发生的事情”是错误的。

【讨论】:

  • 我希望我能不止一次地竖起大拇指...这是我在这件事上看到的最好的答案。
【解决方案3】:

很难准确量化,但是了解 C 语言会让您更深入地了解高级语言结构是如何实现的,因此您将能够更好地以智能方式使用这些结构。

【讨论】:

  • 我 100% 同意你的观点,但我正在寻找的是一个具体的例子来说明它是如何有用的。 Joel Spolsky(在播客中)总是声称它可以让你编写更高效的代码或其他东西。这就是我想要弄清楚的。
【解决方案4】:

给你一个具体的原因:必须编写自己的垃圾收集例程有助于我编写更好的代码。

我认为我从未发现过使用高级语言无法解决的问题;但是从学习C开始,它给我灌输了很多优秀的开发实践。了解应用程序流的基本部分如何工作将使您能够查看自己的代码并很好地了解数据的流动方式以及数据的存储位置。然后,这有助于更好地了解如何追踪内存泄漏、磁盘读取速度慢、缓存结构不佳等。

跟踪指针...这是另一个想到的问题。

【讨论】:

  • 指针被诽谤;但它们是 CPU 自行车的轮子。
  • 这是一个高级语言无法解决的问题:Lua 中的小技巧
【解决方案5】:

经典示例是涉及较低级别内存管理的事情,例如链表类的实现:

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

了解指针是如何用于迭代列表的,以及它们在机器架构方面的含义将使您更好地理解您的高级代码。

Joel 提到的另一个例子是字符串连接的实现,以及从一组数据创建字符串的正确方法。

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

【讨论】:

  • strcat 在这样的循环中是构建字符串的一种非常低效的方法的示例。使用 C 字符串,您需要在 cat 上之后将其挂在字符串的末尾,因此您不必再次找到它,就像您的示例一样。 std::string 循环可能有效。
  • 哈哈是的,我是从不需要执行任何额外分配的角度考虑的
【解决方案6】:

了解 C 可以帮助您用 C 编写更好的代码。我猜 Joel Spolsky 的示例在 C++ 或 Objective-C 中几乎没有用处,因为存在用于操作字符串的特定类并且在设计时考虑了性能。此外,在其他语言中使用 C 技巧可能会提高效率。

尽管如此,C 知识对于理解其他语言中的一般概念以及许多情况下的幕后知识非常有帮助。

【讨论】:

    【解决方案7】:

    作为一个懂一点 C,但喜欢用 perl 和其他高级语言编写代码的人,我从来没有遇到过可以通过编写 C 来解决的问题。

    我正在寻找在使用 perl 或 python 等高级/动态语言编写项目时了解 C 的实际情况的示例。

    开始编写高级代码很容易,然后我们会怀疑它运行缓慢。事实上,编写 perl 或 python 代码的方法有很多,有些方法比其他方法更好(因为效率更高)。如果您知道如何在 perl 或 python 中执行代码的低级细节(两者都是用 C 编写的),您可以围绕几个低效率进行编码——比如知道哪个循环结构更快、如何保留/释放内存等.

    此外,在使用 perl 或 python 编写项目时,您有时会遇到性能障碍。该语言的创建者(至少是 Guido)主张您在 C 中实现该部分,作为语言扩展。要做到这一点,你必须知道 C。

    所以,那里。

    【讨论】:

      【解决方案8】:

      出于论证的目的,假设您想连接从 1 到 n 的所有整数的字符串表示形式(例如,n = 5 将产生字符串“12345 ”)。以下是在 Java 等语言中如何天真地做到这一点。

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

      如果您要尽可能用 C 重写该代码段(在 Java 中非常好看),您会得到一些让大多数 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 列表类定义没有实际影响。没有任何。

          了解链表和数组列表之间的区别可能有助于您编写 Java 程序。但是 C 实现并不能帮助您在 Linked List 和 Array List 之间进行选择。该决定与知道 C 无关。

          糟糕的算法在每种语言中都是糟糕的。了解 C 的内在奥秘并不会让糟糕的算法变得更糟糕。了解 C 并不能帮助您了解 Java 集合或 Python 内置类型。

          我看不出学习 C 有什么价值。学习 Fortran 也同样有价值。

          【讨论】:

          • “糟糕的算法在所有语言中都是糟糕的。”是的,了解 C 可以让您识别高级语言在后台使用哪些算法,从而做出正确的选择。没有 C,你只剩下语言文档,几乎从不提及它们(只是说“足够快”)
          • Java 程序员如何在不知道引用是什么以及至少知道它们如何工作的情况下高效?如果没有这些知识,即使理解 someString.equals(someOtherString) 和 (someString == someOtherString) 之间的区别也会变得相当困难。
          • 了解哪些参考文献很重要。知道 C 不是一种学习方法。您可以通过 C 程序员在学习 C 时使用的相同简单图片来了解这一点。C 本身并没有帮助。
          • 我不确定 C 如何帮助您识别错误的排序算法。一个愚蠢的排序在 Java 中和在 Python 中一样愚蠢,与 C 无关。在 HashMap 和 TreeMap 之间进行选择时,C 完全没有优势。
          • @S.Lott:我明白了——我被“指针和引用......从假设的角度来看很有趣”弄糊涂了。我读起来好像它是“指针和 Java 引用......”,但我发现我误读了。
          【解决方案11】:

          从技术上讲,C 的所有缺陷都会迫使您围绕它们编写代码;让你编写更多代码 -> 让你更有经验。例如,由于缺少任何大于 32 位的可移植整数,C 过去让我编写了自己的 bignum 库。

          缺乏隐式内存、资源和错误管理(垃圾收集、RAII、自动调用的构造函数/析构函数,可能还有异常)迫使 C 用户编写大量初始化、错误处理和清理代码。可能只是我,但我从不厌倦编写这样的代码。我去阅读我调用的每个外部函数的文档,返回我的代码并检查每个返回值和其他指示失败的东西。它甚至让我感到安全!

          最后一点可能是支持该论点的最重要的一点。在开始分析每种语言中遇到的每个变量的生命周期之前,您只能编写这么多的 malloc()/free() 对! C++ 的自动存储对象也无助于这种混乱。

          编写真正可移植的 C 代码通常需要程序员摆脱对主机系统的许多假设——想想 sizeof()、CHAR___BITS、unsigned long、UINT_MAX。虽然这并没有帮助我用其他语言编写更好的代码,但它帮助我思考了可能的替代实现:一个微型微处理器如何仍然可以运行我的 C 代码,为我的简单单行语句生成大量 RISC 指令。 (这是另一回事;在我的脑海中,没有多少其他语言能如此轻松地与给定的汇编语言相互映射。再说一次,这可能就是我。)

          当然,这些论点中没有一个只适用于 C。@S.Lott 有一个有效的观点 - Fortran 可能是一个同样好的选择。但是周围有很多 C 代码!一个完整的个人计算机系统,从上到下——应用程序到库到驱动程序再到内核——都可以在 C 源代码中找到。如果你看不懂,那就太浪费了。

          【讨论】:

            【解决方案12】:

            我认为值得了解一些低级语言,并且有务实理由选择C:

            • 它是低级的,接近汇编程序
            • 很普遍

            了解整个堆栈很有价值。有时你需要调试一些东西的胆量。有时您无法在没有底层知识的情况下解决性能问题(通常不是这种情况,例如,当性能问题纯粹是算法问题时,但有时确实如此)。

            为什么 C 被广泛认为是典型的“堆栈底部”,而不是其他一些语言?我认为这是因为 C 是一种低级编程语言,并且 C 赢了。现在已经有一段时间了,但 C 并不总是那么占主导地位。举一个著名的例子,Common Lisp(有自己的编写低级代码的方式)的支持者希望他们的语言也能流行起来,最终lost

            以下通常在C中实现:

            • 操作系统(Unix 变体、Windows、许多嵌入式操作系统)
            • 高级编程语言(Java、Python 等的许多流行实现)
            • (显然)大量流行的开源项目

            我不是硬件专家,但我推测 C 语言也对 CPU 设计产生了重大影响。

            因此,如果您相信了解整个堆栈,那么从实用的角度来看,学习 C 是最佳选择。

            作为警告,我认为学习汇编程序也是值得的。尽管 C 接近于金属,但直到我不得不做一些汇编程序时,我才完全理解 C。有时了解函数调用的实际执行方式、for 循环的实现方式等有时会有所帮助。不太重要但也很有用的是必须(至少一次)处理没有虚拟内存的系统。在 Windows、Unix 和某些其他操作系统上使用 C 时,即使是不起眼的 malloc 也会在幕后做很多工作,如果您曾经不得不处理手动锁定和解锁内存区域(我不建议定期这样做!)

            【讨论】:

              【解决方案13】:

              我是这样看的,一切都归结为跨平台级别的 C,并以特定于平台的方式组装。所以这就像成为一名越野拉力赛车手,而 C 是基本的汽车机械师,你可以成为一名出色的车手,但是当你遇到麻烦时,知道 C 意味着你可能会让自己重新回到比赛中,否则你会被困在打电话给机械师.组装是机械师和制造商所知道的,如果这是您想做的事情,这是一项值得投资的事情,否则您可以信任机械师。

              具体考虑内存管理、硬件驱动程序、物理引擎、高性能 3D 图形、TCP 堆栈、二进制协议、嵌入式软件、创建 Perl 等高级语言

              【讨论】:

                【解决方案14】:

                您不能在 Perl 中编写操作系统内核; C 将是一个更好的选择,因为它足够低级来表达内核应该做的所有事情,并且足够便携,可以让您将内核移植到不同的体系结构

                【讨论】:

                • 是的,但我不想用 Perl 编写内核。我只是想完成我的工作。我已经知道 C,但我从来没有遇到过这样的情况,我在编写 Perl 时会想,“我的天哪,我应该用 C 来写这个”。我总是假设拉里沃尔比我聪明。
                【解决方案15】:

                了解 C 并不是能够有效使用高级语言的必要条件,但它肯定可以帮助人们大致了解计算机和软件的工作原理 - 我认为这类似于断言了解某些汇编语言或计算机体系结构/硬件逻辑(和/或/与非门等)可以帮助 C 程序员成为更好的程序员。

                有时为了解决一个问题,了解你正在做的事情的“底层”是如何运作的会有所帮助。

                我不认为这意味着程序员必须了解 C 才能成为一名优秀的程序员,但我认为了解 C 对几乎所有程序员都有帮助。

                【讨论】:

                  【解决方案16】:

                  不太了解 Perl,我想知道现在是否可以通过在 Perl 的单个程序中创建多个线程将处理器负载分配到多个物理内核,而无需产生额外的进程

                  【讨论】:

                    【解决方案17】:

                    我认为不可能有任何具体的例子。

                    学习 C 语言对您的作用是让您深入了解计算机(和软件)的工作原理,拓宽思路。这是一个非常抽象的东西..

                    它不会让你用 python 编写更好的代码,它只会让你更像一个计算机科学家。

                    Wedge 对 Joel 的文章中提到的画家 Shlemiel 的引用很有趣,但与此无关。该算法与 C 没有任何特定的联系(尽管它以空字符结尾的字符串表现出来)。

                    无论如何,Python 的字符串是不可变的,并且与 C 的字符串模型完全不同,所以我不太明白其中的关系。

                    我想一个具体的例子是优化解析器或词法分析器或一直写入字符串缓冲区的程序。如果您使用普通字符串而不是字符串缓冲区,则在构建非常大的字符串时会遇到问题。

                    考虑一下:

                    a = a + b 
                    

                    复制ab。它不会更改 a 引用的字符串,它会创建一个新字符串,分配更多内存等。

                    如果a 变得相当大,而你不断往里面添加一些小东西,那么画家施莱米尔就会现身。

                    但话又说回来,知道这与了解 C 无关,只是了解您的语言如何在低级别实现事物。 (这就是拥有 C 经验的地方会对您有所帮助)。

                    【讨论】:

                      【解决方案18】:

                      在 Python 中,假设你有一个函数

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

                      在大约一年前推出的某个版本的 Python 上,运行 foo() 多次,您会得到一个非常有趣的结果(即 ["bar","bar","bar","bar])。

                      似乎有人将默认参数实现为静态变量(并且没有重置它),所以出现了意想不到的结果。

                      也许我的例子是人为的——我的一个真正喜欢 Python 的朋友发现了这个特殊的错误,但事实是所有这些语言都是用 C 或 C++ 实现的。不了解或不理解基础语言的基本概念意味着您不会深入了解基于此构建的语言。

                      我发现所有“为什么要麻烦 C/C++/ASM 问题很傻”。如果你足够倾向于学习一门语言,那意味着你有足够的好奇心首先进入它。为什么要停在 C 之前?

                      【讨论】:

                        【解决方案19】:

                        了解 C 非常棒,因为它不会在你背后做任何事情(GC、边界检查等)。它也只做你告诉它的事情。没有任何暗示。甚至 C++ 也做了一些你不会用 RAII 告诉它的事情(当然,这暗示对象在超出范围时会被破坏,但你实际上并没有写出来)。 C 是了解计算机“幕后”发生的事情的好方法,无需编写汇编。

                        【讨论】:

                          【解决方案20】:

                          低效代码(例如字符串+= 的循环)在任何语言中通常都是低效的。如果有人解释为什么用一种语言或另一种语言效率低下,会有什么不同?懂 C 却没有意识到一个方法效率低下,与懂 python 却没有意识到这一点没有什么不同。

                          【讨论】:

                            猜你喜欢
                            • 2017-06-04
                            • 1970-01-01
                            • 2019-07-23
                            • 2018-06-30
                            • 2011-04-16
                            • 1970-01-01
                            • 1970-01-01
                            • 2013-09-06
                            • 1970-01-01
                            相关资源
                            最近更新 更多