知道C会让我用其他任何语言编写更好的代码是什么样的例子?

时间:2009-04-06 01:16:29

标签: c

在Stack Overflow播客中,Joel Spolsky经常对Jeff Atwood强调杰夫不知道如何用C编写代码。他的声明是“知道C可以帮助你编写更好的代码。”他还总是使用某种涉及字符串操作的故事,以及如何知道C将允许您用不同的语言编写更有效的字符串例程。

作为一个知道一点C但又喜欢用perl和其他高级语言编写代码的人,我从未遇到过能够通过编写C来解决的问题。

我正在寻找实际情况的例子,在这种情况下,在用perl或python等高级/动态语言编写项目时,了解C会很有用。

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

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

20 个答案:

答案 0 :(得分:23)

Joel Spolsky使用的典型例子是misuse of strcat and strlen,并且总体上发现了“Shlemiel the painter”算法。

并不是你需要C来解决高级语言无法解决的问题,而是知道C井可以让你了解所有那些级别的语言下面发生的事情,这些语言允许你编写更好的软件。因为只有这样的视角可以帮助你避免编写你不知道的代码,例如O(n ^ 2)。

修改:基于评论的一些说明。

知道C不是这种知识的先决条件,有很多方法可以获得相同的知识。

了解C也不能保证这些技能。你可能精通C语言,但仍然用你触摸的其他语言写出可怕的,笨拙的,kludgy代码。

C是一种低级语言,但它仍然具有现代化的控制结构和功能,因此您并不总是陷入迷人的细节中。如果没有掌握某些基本原理(例如内存管理和指针的细节),就很难熟练掌握C语言,掌握这些基础知识通常会在使用任何语言时获得丰厚的回报。

总是关于基本面。

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

答案 1 :(得分:8)

假设学习C会以某种方式自动让您更好地理解低级编程问题,这是错误的。在很多情况下,即使C级别太高,也无法让您对效率问题有充分的了解。

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

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

即使使用C,如果效率受到关注,了解幕后发生的事情也很重要。知道C的人往往会在一个进程中看待事物。汇编和机器代码是C的构建块,而C是更高级语言的构建块。

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

所以你想要一个知道C如何为你提供优势的具体例子。我不认为有一个。我认为人们意味着什么当他们说这就是知道你正在发生什么语言的幕后发生的事情有助于你做出更明智的决定如何编写代码。但是,例如,假设C是“幕后发生的事情”,这是错误的。

答案 2 :(得分:3)

给你一个特定的理由:必须编写我自己的垃圾收集例程,这有助于我编写更好的代码。

我认为我从未发现过用高级语言无法解决的问题;但是从学习C开始,它已经为我灌输了许多优秀的开发实践。了解应用程序流程的基本部分如何工作将使您能够查看自己的代码并更好地了解数据如何流动以及数据的存储位置。这样就可以更好地理解如何跟踪泄漏的内存,缓慢的磁盘读取,构造不良的缓存等等。

跟踪指针......这是另一个浮现在脑海中的指针。

答案 3 :(得分:3)

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

答案 4 :(得分:2)

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

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

答案 5 :(得分:2)

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

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

答案 6 :(得分:2)

出于参数的目的,假设您想要连接从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是一种方式来了解所述代码的低效性。

答案 7 :(得分:2)

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

我正在寻找实际情况的例子,在这些情况下,在用高级/动态语言(如perl或python)编写项目时,了解C会很有用。

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

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

所以,那里。

答案 8 :(得分:1)

你使用的数组多吗?你是否遇到过需要将项目存储在内存中而不知道有多少项(即基于数据库中的查询?)的情况,然后我想C会教你很多东西,比如堆栈,结构和链接列表帮你。此致,安迪

答案 9 :(得分:1)

了解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同样有价值。

答案 10 :(得分:0)

我认为值得了解一些低级语言,并且有实用选择C的原因:

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

了解整个堆栈很有价值。有时您需要调试一些内容。有时,如果没有低级知识,你就无法解决性能问题(这通常不是 的情况,例如,当性能问题纯粹算法时,但有时候)。

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

以下通常在C中实现:

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

我不是硬件用户,但我认为C也大大影响了CPU设计。

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

作为一个警告,我认为值得学习汇编程序。虽然C接近金属,但在我不得不做一些汇编之前我并不完全理解C.偶尔有助于理解函数调用是如何实际执行的,如何实现for循环等等。不重要但也很有用的是必须(至少一次)处理没有虚拟内存的系统。在Windows,Unix和某些其他操作系统上使用C时,即使是简陋的malloc也会做很多工作,如果您不得不手动处理,这些工作更容易理解,调试和/或调整锁定和解锁内存区域(不是我建议定期这样做!)

答案 11 :(得分:0)

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

答案 12 :(得分:0)

知道C很棒,因为它背后没有任何作用(GC,边界检查等)。它只会完全按照你的说法进行操作。没有任何暗示。甚至C ++也会用RAII做你不会告诉它的事情(当然,暗示当对象超出范围时会被破坏,但你实际上并没有写出来)。 C是一种很好的方式来学习计算机“引擎盖下”的内容,而无需编写程序集。

答案 13 :(得分:0)

在Python中,假设你有一个功能

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

在某些版本的Python上,大约一年前可用,运行foo()一段时间,你会得到一个非常有趣的结果(即["bar","bar","bar","bar])。

似乎有人将默认参数实现为静态变量(并且没有重置它),因此会发生意外结果。

也许我的例子是做作的 - 我的一个朋友真正喜欢Python发现了这个奇怪的错误,但事实是所有这些语言都是用C或C ++实现的。不了解和不理解对基础语言至关重要的概念意味着您将无法深入理解基于此构建的语言。

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

答案 14 :(得分:0)

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

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

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

编写真正可移植的C代码通常要求程序员不要对主机系统做出很多假设 - 想想sizeof(),CHAR___BITS,unsigned long,UINT_MAX。虽然这并没有帮助我在其他语言中编写更好的代码,但它帮助我思考了可能的替代实现:微小的微处理器如何仍然可以运行我的C代码,为我简单的单行语句生成大量的RISC指令。 (这是另一回事;没有多少其他语言可以很容易地在我的头脑中映射到给定的汇编语言。然后,这可能只是我。)

当然,这些论点都不仅仅适用于C. @ S.Lott有一个有效的观点 - Fortran可能是一个同样好的选择。但是有很多C代码!从上到下的整个个人计算机系统 - 从库到驱动程序到内核的应用程序都可以在C语言的源代码中找到。如果你无法阅读它,那将是一种浪费。

答案 15 :(得分:0)

我认为没有任何具体的例子。

学习C为您做的是为您提供洞察力,扩展思维,以及计算机(和软件)的工作方式。这是一个非常抽象的事情......

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

Wedge对Joel的文章提到画家Shlemiel的提法是一个有趣的,但在这里没有任何意义。该算法不以任何特定方式与C相关联(尽管它以空终止字符串表示)。

Python的字符串无论如何都是不可变的,与C的字符串模型完全不同,所以我不太清楚这种关系。

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

考虑一下:

a = a + b 

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

如果a变得相当大,并且你不断添加小东西,那么画家Shlemiel就会表现出来。

然而再一次,知道这与知道C无关,只知道你的语言如何在低级别实现。 (这是C语言经验对你有帮助的地方)。

答案 16 :(得分:0)

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

答案 17 :(得分:0)

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

有时为了解决问题,有助于了解事情是如何在你正在做的事情下工作的。

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

答案 18 :(得分:0)

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

答案 19 :(得分:0)

我认为这样,所有内容都归结为跨平台级别的C,并以特定于平台的方式进行组装。所以这就像是一个越野赛车拉力赛车,而C是基本的汽车机械师,你可以成为一名优秀的车手但是当你遇到麻烦时知道C意味着你可能会让自己重新参加比赛,如果不是你就会被称为机械师。装配是机械师和制造商所知道的,如果这是你想做的事情,这是一项值得投资的事情,否则你就可以信任这些机制。

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