C 是一个低级别的语言。但在以前不是,当人们还在使用穿孔卡编写程序时,C 是一种可以摆脱汇编(例如 Assembly)这个苦差事的一种方式。

但在现代,当代语言提供了 C 语言发明时所不存在的各种特性,这意味着 C 是一种非常基础的语言并没有很多特性。你可以使用 C 语言为你做任何事情。

那为什么我们今天还在使用 C 语言。

  • 作为一种学习工具:C 不仅是计算历史上令人尊敬的一段,而且它以一种现代语言所没有的方式连接计算机。当你学习 C 语言时,你会了解软件如何在低层次与计算机内存交互。这里没有安全带。你会写出崩溃的软件,这就是乐趣的一部分!
  • 作为有用的工具:C 仍然用于某些应用程序,例如构建操作系统或嵌入式系统。 尽管 Rust 编程语言也在关注这两个领域。

如果你熟悉其它语言,那么你会得 C 语言很简单,C 语言为许多语言提供灵感。你会在 Go、Rust、Swift、Python、JavaScript、Java 和各种语言中看到它的一部分。你可会对 C 语言感到熟悉。

C 语言的一个问题是指针。你可能对 C 语言其他东西都很熟悉,但指针行为非常奇怪。指针背后的概念您可能是已经知道,但 C 语言强制您对指针进行明确理解。

因为一旦你理解指针,它们就会变得容易。但在那一刻之前,它们都是滑溜溜的鳗鱼,让你根本抓不住它。

Hello, World!

这是 C 程序的典型示例。 几乎每个人都知道如何编写它。

#include <stdio.h>

int main(void) { 
  printf("Hello, World!\n"); 
}

让我们看看代码都包含什么,首先 /**/ 之间的任何内容都是注释,编译器将完全忽略它。 // 同行之后任何内容也是如此。这允许你给自己代码添加注释或者留言,当你在遥远的未来回来阅读你的代码时,你就会知道你到底想做什么。

那么,#include 是什么? ,它告诉 C 预处理器提取另一个文件的内容并将其插入到代码中。

你可能会问——什么是 C 预处理器? 好问题。 编译有两个阶段:预处理器和编译器。 任何以井号 #  开头的东西都是预处理器在编译器开始之前运行的东西。常见的预处理器指令是 #include 和 #define。

在 C 预处理器完成所有预处理后,结果就准备好给编译器使用,编译器生成汇编代码机器代码或者任何它要执行的操作。机器代码是 CPU 可以理解的语言,CPU 可以非常快速地理解它。 这是 C 程序之所以快的原因之一。

现在我们不需要理解编译的技术细节; 只需知道您的代码通过预处理器运行,然后它的输出通过编译器运行,最后生成一个可执行文件供您运行。

那剩下的行呢? <stdio.h> 是什么? 这就是所谓的头文件。 是末尾的 .h 泄露它是什么。它将是您将逐渐要了解和喜爱的标准 I/O(stdio) 头文件。 它使我们能够访问一堆 I/O 函数。

对于我们的示例程序,我们正在向终端打印字符串 “Hello, World!”,因此我们需要访问 printf() 函数来执行此操作。

<stdio.h> 文件为我们提供这种访问能力。如果我们尝试在没有 #include <stdio.h> 的情况使用 printf(),编译器会向我们抱怨(扔一个错误给你)。

我如何知道我需要为 printf() 添加头文件 #include <stdio.h>? 这个答案可以文档里找到。如果你使用 Linux/Unix 系统,man 3 printf 它会在手册页的顶部告诉你需要哪些头文件。

下一行是 main()。 这是  main()  函数的定义; 大括号 {} 之间的所有内容都是函数定义的一部分。

main 函数在很多方面都是一个特殊的函数,它是程序开始执行时将自动调用的函数。在 main() 之前,你的任何东西都不会被调用。

一旦程序执行到  main() 的末尾,也就是在大括号的结尾处,程序就会退出,你会回到命令提示符下。

所以现在我们知道程序引入一个头文件 stdio.h,并声明了一个将在程序启动时执行的 main() 函数。

让我们接下来看看 main 函数内部有什么,在 main 函数内部您向函数 printf() 传递一个参数,这是调用它时要打印的字符串。

字符串中的大多字符将像存储时一样打印出来。 但是有些字符无法在屏幕很好地打印,但它们都使用反斜杠进行转义。最流行的一个是 \n(读作“反斜杠-N”或简称为“换行符”),它对应的是一个换行符,就像在行尾按回车键。

最后将代码复制到 hello.c 文件。 并在类 Unix 平台(例如 Linux、BSD、Mac 或 WSL)上构建,您将从终端使用运行命令 gcc -o hello hello.c 构建 hello.c 文件:

gcc -o hello hello.c

这意味着编译 hello.c 文件并生成可执行程序 hello 。编译完成后,你的当前目录存在 hello 文件,你可以运行命令 ./hello 执行该程序。

./hello

前导 ./ 告诉 Shell 从当前目录运行。你将会看到在终端屏幕打印 Hello, World!。

Hello, World! 

编译

让我们看看如何构建 C 程序,以及幕后发生的事情。编译是将 C 源码转换为操作系统可以执行的程序的过程。

JavaScript 和 Python 则没有编译步骤,因为它在幕后进行编译, Python 允许您将源码编译成 Python 虚拟机可以执行的字节码。Java 开发者则需要编译程序,这将为 Java 虚拟机生成字节码。

在编译 C 时,会生成机器码。 这是 CPU 可以直接快速执行的 1 和 0。通常不需要编译的语言称为解释语言,也称为脚本。

正如我们上面所说的,gcc 是许多类 Unix 操作系统的编译器。你通常在终端运行 gcc 命令构建 C 语言程序。

Gcc 编译

如果你的当前目录有一个 hello.c 文件,你可以运行命令 gcc -o hello hello.c 编译 hello.c

gcc -o hello hello.c

gcc 的 -o 选项指定输出的文件,最后的 hello.c 你想要编译的文件。

如果您的源代码被分成多个文件,您可以将所有 .c 文件放在命令行将它们全部编译在一起,就像它们是一个文件,但规则实际上比单个文件更复杂:

gcc -o awesomegame ui.c characters.c npc.c items.c

并且它们将一起构建成一个更大的可执行文件。这足以你应付多数场景,  稍后我们将讨论多个源文件、目标文件和各种有趣内容。

Clang 编译

在 Mac,标准编译器不是 gcc,而是 clang。但是它还安装了一个封装器,因此你仍然可以运行 gcc 编译源码文件。

您也可以使用 Homebrew 或其他方式安装 gcc 编译器。

IDE 构建

如果您使用的是集成开发环境 (IDE),则不必从命令行构建。在 Visual Studio 快捷键 CTRL-F7 即可构建,  CTRL-F5 构建并运行程序。

在 VS Code,您可以按 F5 使用调试器构建并运行。但您必须安装 C/C++ 扩展并正确配置 c 语言项目。

在 XCode,您可以使用快捷键 COMMAND-B 构建,COMMAND-R 运行。要获取命令行工具,请在 Google 搜索 XCode 命令行工具 ,然后您会找到安装它们的说明。

对于入门,通常不建议使用重量级的IDE,例如 XCode ,Visual Studio,你可以选择VS Code 或者 命令行。这对于你深入理解才会有帮助。

C 语言版本

多年来,C 已经取得了长足的进步,它有许多命名的版本号来描述您正在使用的语言的版本。

C 语言的版本使用年份进行规范。最著名的是 C89、C99、C11 和 C2x。在教程中,我们将重点关注后者。

您可以通过指定 Gcc 的 -std= 选项强制 GCC 使用这些版本之一。如果您希望你源码仅支持指定的版本,请添加 Gcc 的 -pedantic 选项。

例如:

gcc -std=c11 -pedantic foo.c

在本教程中,编译的程序都是使用的 c语言 C2x 版本并且打开所有警告。

gcc -Wall -Wextra -std=c2x -pedantic foo.c