C语言编程1.3 理解程序开发环

部署运行你感兴趣的模型镜像

在软件开发环境中,主要分为两种类型:

  1. 解释型环境:像Python或Ruby这样的解释型环境,程序可以逐行输入,并且可以随时运行。每一行代码在输入时都会被评估和执行,执行结果会立即返回到控制台。解释型环境因为能提供即时反馈,所以非常适合快速探索算法和程序功能。在这种环境中输入的程序通常也需要解释型环境的支持才能运行。

  2. 编译型环境:在C、C++、C#或Objective-C这样的编译型环境中,程序被输入到一个或多个文件中,然后一次性编译,如果没有发现错误,整个程序就可以运行。这些阶段各不相同,每个阶段都使用不同的程序。由于存在独立的、完整的编译阶段,编译型程序的执行速度通常更快,这个阶段可以独立于解释型环境运行。

就像使用洗发水时,我们习惯于先湿发,然后起泡、冲洗、重复这个过程一样,学习C语言时,我们也将熟悉编辑、编译、运行、验证和重复这个循环。

编辑

程序是通过文件生成的,这些文件的文件名使用预定义的文件扩展名。这些被称为源文件或源代码文件。对于C语言,.c扩展名表示一个C源代码文件。.h扩展名(如我们的Hello, world!程序中所示)表示一个C头文件。编译器在遇到.c和.h文件时会查找它们,因为每种类型的文件都有不同的用途,编译器也会以不同的方式处理它们。其他语言也有自己的文件扩展名;源代码文件的内容应该与编译器期望的语言相匹配。

要创建和修改C文件,你将需要一个纯文本编辑器。这是一个程序,允许你打开、修改和保存纯文本,不包含任何格式设置,如字体大小、字体系列和字体样式。例如,在Windows上,记事本是一个纯文本编辑器,而Word则不是。纯文本编辑器应具备以下功能:

  1. 文件操作:这允许你打开文件、编辑文件、保存文件及其所做的任何更改,并最终以另一个名字保存文件。
  2. 文件导航能力:这允许你在文件中上下左右移动,到行首、行尾、文件开头、文件末尾等。
  3. 文本操作:这允许你插入文本、删除文本、插入一行、删除一行、选择、剪切、复制、粘贴、撤销/重做等。
  4. 查找和替换:这允许你查找文本、替换文本等。

以下功能虽然方便,但不是必须的:

  1. 自动缩进
  2. 特定编程语言的语法着色
  3. 自动定期保存

几乎任何纯文本编辑器都可以使用。不要过分关注任何特定文本编辑器的功能。有些编辑器比其他编辑器好;有些是免费的,而有些则可能较贵且可能一开始不值得这个花费(或许以后某些可能是值得的,但现在不是),没有任何一个编辑器能做到你可能希望它们做到的100%。

这里有一些值得在你的电脑上安装并试用的免费纯文本编辑器:

全平台:Nano,它在终端窗口中运行;它有一定的学习曲线。
Linux/Unix:包括以下内容:

  • a. Vim或vi:这在终端窗口中运行;它有一定的学习曲线。它存在于每个Linux/Unix系统中,因此值得学习如何使用其基本功能。
  • b. gedit:这是一个功能强大的通用编辑器。
  • c. Emacs:这是一个包含一切功能的编辑器;它有一个非常大的学习曲线。

Windows:包括以下内容:

  • a. 记事本:这非常简单——有时对于编程来说太简单了——但包含在每个Windows系统中。
  • b. Notepad++:这是记事本的升级版,具有许多适用于编程的功能。

仅限macOS:运行BBEdit(免费版本),这是一个全功能的GUI编程文本编辑器。
有许多文本编辑器,每个都有其自身的优点和缺点。选择一个文本编辑器并习惯使用它。随着时间的推移,随着你越来越多地使用它,它将变得与你的编程习惯密不可分。

编译

编译器是一种程序,它的任务是接收输入的源代码文件——在我们的例子中,是.c和.h文件——将这些文本形式的源代码转换成机器语言,并将所有预定义的部分链接起来,以便程序能够在特定的计算机硬件和操作系统上运行。编译器生成的是一个包含机器语言的可执行文件。

机器语言是一系列指令和数据,特定的中央处理单元(CPU)知道如何从程序执行流中获取这些指令和数据,并逐一在计算机上执行。每个CPU都有自己的机器语言或指令集。通过使用一种通用语言,如C语言编程,程序员可以不必关心机器语言的细节;这些知识已经体现在编译器中。

有时候,汇编语言被称为机器语言,但这并不完全准确,因为汇编语言仍然包含文本和符号,而机器语言只由二进制数字组成。如今,很少有人具备直接阅读机器语言的技能;曾经,有更多的程序员能够做到这一点。时代已经变了!

当我们编译程序时,我们调用编译器来处理一个或多个源文件。这个调用的结果要么是成功,生成一个可执行文件,要么会识别出编译过程中发现的编程错误。编程错误可能从简单的名称拼写错误或遗漏标点符号到更复杂的语法错误不等。通常,编译器会尝试理解它发现的任何错误;它试图为发现的问题提供有用的信息。需要注意的是,尝试和尝试只是目标;实际上,编译器可能会因为一个错误而输出许多行错误信息。此外,编译器在调用时会处理整个源代码。你可能会在每次编译器调用时在程序的不同部分发现许多不同的错误。

一个完整的、可运行的程序由我们编译的源代码——我们编写的代码——以及操作系统提供的预定义编译例程组成,即操作系统作者编写的代码。预定义的程序代码有时被称为运行时库。它包含一组可调用的例程,这些例程知道如何与计算机的各个部分进行详细交互。例如,在Hello, world!程序中,我们不需要知道将字符发送到计算机屏幕的详细指令——我们可以简单地调用一个预定义的函数,printf();,让它为我们完成这个任务。printf()是C运行时库的一部分,还有许多其他例程,我们将在后面发现。在下一章中,我们将探讨函数的作用。将文本发送到控制台的方法可能因每个操作系统而异,即使这些操作系统运行在相同的硬件上。因此,程序员不仅被屏蔽了机器语言的细微差别,还被屏蔽了计算机本身不同的实现细节。

由此可以推断,对于每个操作系统,都有一个特定的编译器和运行时库。为某个操作系统设计的编译器很可能无法在另一个操作系统上工作。如果某个操作系统的编译器碰巧或似乎在不同的操作系统上运行,那么生成的程序及其执行将是高度不可预测的。混乱是可能的。

运行

一旦编译成功完成,就会生成一个可执行文件。除非我们为它提供一个明确的名称,否则这个可执行文件将被命名为a.out。通常,可执行文件会在调用编译器的同一目录中创建。在大多数情况下,我们会确保当前工作目录与源文件的位置相同。

运行可执行文件是通过从命令行调用它来完成的。当被调用时,可执行文件被加载到计算机的内存中,然后成为CPU的程序执行流。一旦加载到内存中,CPU从特殊的保留字,即main()开始,并继续执行,直到遇到return;或一个闭合的}字符。程序停止,然后可执行文件从内存中卸载。

要运行可执行文件,打开命令提示符(在Windows上)或终端窗口(在Linux和Mac上),使用cd导航到可执行文件的目录,然后简单地输入可执行文件的名称(例如,a.out或你指定的任何名称)。

注意

如果你成功导航到与可执行文件相同的位置,并且你已经验证它存在,但是你从命令解释器得到了一个错误消息,那么你很可能遇到了命令解释器的内置PATH变量的问题。为了快速解决这个问题,输入$ ./a.out命令来运行它。这指示命令解释器在当前目录中查找名为a.out的文件。

当程序运行时,任何输出都将被定向到终端或控制台窗口。当程序结束时,命令解释器将给你一个新的命令提示符。

验证

在这个阶段,你可能会感觉到,只要你的程序编译时没有错误,并且运行时没有使你的计算机崩溃,就意味着你已经完成了。然而,事实并非如此。你必须验证你认为你的程序应该做的事情实际上它是否做到了。你的程序是否解决了它应该解决的问题?结果是否正确?

因此,你需要回到编写你最初的程序,然后将其输出与程序给出的输出进行比较。如果你预期的结果与实际结果匹配,你的程序就是正确的;只有到那时,你才算真正完成了。

随着我们编写更复杂的程序,我们将发现一个合格或良好的程序应具备以下几个特征:

  1. 正确性:程序做了它应该做的事情。
  2. 完整性:程序做了它应该做的所有事情。
  3. 简洁性:程序不做超出其应有功能的事情,并尽可能高效地完成任务。
  4. 清晰性:程序对使用者和维护者来说都容易理解。

在本专栏的大部分内容中,我们将主要关注正确性、完整性和清晰性。目前,hello1.c既不完整也不清晰,我们将很快了解原因。

重复

与我们之前提到的洗发水比喻(湿发、起泡、冲洗、重复)不同,不是只重复一次指令,而是要多次重复这个周期。

很少有一次就能顺利完成程序开发周期的。很可能,你需要多次重复其中的某些部分。例如,你可能编辑源代码,编译它,然后发现编译器失败了。在这种情况下,你需要回到编辑源代码并重新编译,每次都要找出哪里出了问题,然后修复它。一旦成功编译,你将进入运行和验证阶段。如果输出不正确或程序崩溃,你需要找出哪里出了问题,然后再次开始编辑源代码。

这听起来是不是很令人沮丧?确实可以 – 特别是当你不知道为什么出了问题,或者你无法理解编译器或计算机告诉你哪里出了问题时。

这个故事的要点是让你放心,你会犯错误,主要是遗漏或错误的标点符号或拼写错误的变量,你会感到沮丧。学习编程的一部分就是学习如何处理你的挫折感,以及如何成为一个侦探,追踪那些琐碎的错误。

关于调试

当你经历程序开发周期,并且随着你对开发语言、开发工具以及你自己(是的,你在编程的同时也在了解自己)越来越熟悉,这一切都会变得自然而然,就像它应该的那样。当你打错字,或者当你得到一个明显错误的结果时,这些不是bug – 它们只是错误。Bug要微妙得多。

有一个更深的陷阱,对于大多数初学者程序员来说很难察觉;那就是,他们对应该发生的事情的假设,而没有任何实际发生的事情的证据。我在代码中引入的最难的bug,大多数都是那些我假设程序会以某种方式工作,但我没有验证的。当我最终回到我的假设,并在代码中证明它们时,我才能够超越我自己造成的bug。

那么,你能避免这个陷阱吗?

可以。在本专栏中,我们将尝试通过一种我们将用于开发程序的方法来缓解这个微妙的问题。随着我们前进,我们将使用试错法、引导发现和通过观察获得的证据。有时,我们会故意破坏我们的程序,看看会发生什么。此外,我们将尝试证明每个概念,以便预期的行为与实际行为相匹配。

这并不是说,即使采用这种方法,bug就不会悄悄进入。它们会的。但是,通过对你假设的仔细关注、观察到的行为以及你收集的证据来证明任何假设,大多数bug都可以避免。

既然我们已经介绍了程序开发周期,你现在就可以开始编写你的第一个程序了。随着你通过本专栏的进展,你将对这些周期变得非常熟悉。

您可能感兴趣的与本文相关的镜像

Python3.8

Python3.8

Conda
Python

Python 是一种高级、解释型、通用的编程语言,以其简洁易读的语法而闻名,适用于广泛的应用,包括Web开发、数据分析、人工智能和自动化脚本

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

用哲学编程

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值