我对C语言的理解

C 语言是值得学习的,即便现在有很多声音说 C 语言缺乏内存回收以及直接暴露指针的概念是很危险的,而且由于缺乏操作方便的操作符以及各种遍历的库导致写 C 程序很繁琐。从这些方面来说 C 当然是无法比拟 Python 、Java、C# 甚至 C++ 这些后起之秀。值得学习的原因在于几乎所有重要的基础软件都是用 C 写的,这些重要的软件包括操作系统、网络底层、编译器、数据库,几乎所有考虑性能第一的软件都会首选 C 或者 C++ 语言。云风和 Linus 都曾经表示过 C 比 C++ 少了很多心智负担,因而用来起来更加容易,毕竟写严肃的软件不是为了显示自己的智力多强大,KISS 原则依然是编程中的第一原则。

C 是一门以简约著称的通用语言,只拥有很少的特性,这些特性比其它的高级语言更加细粒度。这些特性仅仅包含必要的表达式、控制流和数据接口以及丰富的操作符。C 语言既不是抽象程度高的语言,也不是一门大语言,并且也没有被限制在特定的领域。而且由于 C 的指针的缘故使得它可以做很多别的语言做不了的事,指针的存在使得 C 语言几乎可以深入到机器的任何地方。《UNIX编程艺术》以及很多别的书籍都提到,C 语言是在机器底层上的一层非常薄的粘合剂,只拥有非常非常少的抽象,这导致其能力无限。C++ 试图在贴近机器以及高级抽象上做到完美导致了现在的复杂度。

随着对 C 语言的使用,会越来越认同 C 语言的哲学。

为什么我要做这件事?是因为我自己有一个情结。我在大学毕业时期自学过 C 语言,用的就是 《The C Programming Language》第二版,而且我看的英文版,但是由于本身缺乏基础以及英语一般,虽然我坚持看完了整本书,但是最终迫于就业的压力,我选择了 Java 做了第一工作语言。现在我已经有了将近五年的工作经验,期间也了解过不少语言,对编程本身有了更深入的理解,并且更加了解 GNU/Linux 开源软件的技术和哲学,甚至英语在这一大段的时间内由于看英文文档都得到了很大的提高。现在是时候学我最想学好的语言,我的目标不高就是深入到其内部,理解优秀的开源软件的代码。正如《三体》里边说的当处于四维时可以在不经过三维的表面就能达到其内核。

C 语言概述

C 语言的指令能够直接的翻译为汇编语言,就是这层薄的抽象导致 C 语言的广泛应用于底层。而且所有的 Linux 操作系统接口都是以 C 接口提供的,这使得 C 语言天然适合底层开发。C 语言支持对内存的直接访问,依赖于非常少的运行时,这使得它不仅可以在主机在运行,甚至能够在嵌入式系统中运行。而且由于 C 标准的建立,如果程序中使用的特性不超出 ISO C 的范围,那么程序可以在做非常少的修改甚至不做修改的情况下就能够编译于很多平台和机器。早期的很多 C 开源程序都是以源码的形式发布的,用户直接在自己的机器上编译。由于不同的机器在很多方面都不一致,包括库函数的名字、需要参数的数量、字长度以及大小端等等,而 C 语言是直接编译为对应机器的机器码,不像现在的跨平台语言使用虚拟机的形式而将源码编译成字节码的中间形式,从而导致 C 语言的跨平台编译是挺难的。这就导致了 C 语言对预编译器以及 Autotools 这类工具的依赖。从而导致了 C 工程的特殊工具链。

C 语言早期和 Unix 系统的契合是很深的,而不同的供应商在实现 API 上由有所不一致,导致跨平台是很难的。这就导致了后来的 POSIX 标准,定义了一整套包括工具和 API 的标准,只有满足了此标准的系统才能对外宣称自己是符合 POSIX 的系统,从而只使用 POSIX 标准的 C 程序可以无需做任何改动就能一直到对应的平台。

C 语言设计之初就是为了编写 Unix 操作系统,并且由于语言小巧,实现编译器也简单直接,是的 C 语言很快流行开来。早期 C++ 的编译器就是将 C++ 的代码翻译为 C 代码,再编译成机器代码。

早期的 C 语言与现代 C 语言还是有一些差距的:

  • 早期的复合操作符是 =op 而不是现在 op=
  • 早期只有返回非 int 类型的函数才需要声明返回类型,事先未声明而使用的函数会被假定为返回 int 类型。
  • 早期 struct 和 union 不能直接复制,跟数组一样通过指针来传递。
  • 早期的 C 语言声明并不包含参数的类型。
  • 早期的 C 语言也没有枚举类型。
  • 早期的 C 语言没有 void 类型函数。

后来的 ISO C 是早期 C 的超集,所以所有早期的 C 都是兼容于当前标准。现在几乎所有的编译器都支持到 C89,关于 C89、C99 和 C11 标准之间的差异查看对应的文档。这里只说一下 C99 开始才有单行注释 //,可变长度数组(variable-length arrays) 和 flexible array members,以及可变参数宏(variadic macros),从 C99 开始为声明的类型将不再假定为 int 类型了。值得说的是 Microsoft Visual C++ 只支持到 C89。

C 语言的重要特性

C 语言中 Array 不是第一等公民,所以数组是不能直接复制的,传递数组名其实传递的是数组的头元素地址,而且这个地址是常量地址,意味着不能被赋予其它地址。枚举也不像别的语言的中的枚举是一个标签,C 语言中的枚举其实就是具名的整数而已,每个枚举值都可以和 int 类型值互换,意味着可以出现整型值的地方都可以出现枚举值。C 语言中的字符串不是一种单独的类型,而是由语言定义的以 \0 结尾的字符数组。C 语言中的指针内容就是内存中的机器地址,指针和数组的统一是 C 语言最大的特点,而函数名也是函数的指针,这个 C 语言很大的灵活性。

C 语言以文件分模块,每个 C 文件单独编译为对象文件,最后链接在一起形成一个可执行程序。C 程序还有动态链接,这些不在此介绍。函数与全局变量天然是外部可见的,通过 extern 来引用,而声明为 static 的函数和变量则是内部可见。

1 thought on “我对C语言的理解”

Leave a Reply

Your email address will not be published. Required fields are marked *