c/docs/notes/01_c-basic/00_xdx/index.md

69 KiB
Raw Blame History

Important

  • ① C 语言是一门面向过程编译型编程语言,其最大特点在于 运行速度极快,仅次于汇编语言,这使得 C 语言在需要高性能的场景中得到广泛应用,如:操作系统、硬件驱动程序和嵌入式系统。然而,开发效率较低也是 C 语言的一大特点,程序员需要手动管理内存和处理低级别的操作,这对编程能力要求较高。
  • ② C 语言不仅仅是一门编程语言,更是计算机行业的核心语言。它是计算机专业的基础课程,无论是操作系统的构建、硬件驱动的开发,还是数据库系统的实现,都离不开 C 语言的支持。学习 C 语言 不仅是掌握编程技能的必经之路,也是深入理解计算机底层原理的关键。如果忽视了 C 语言的学习,将难以深入理解计算机系统的工作原理,也很难在计算机行业中取得长远的发展。

Note

总结就是一句话C 语言重要(赞),很重要(大赞),非常重要(非常赞),绝对重要(无敌赞),史无前例的重要(一赞到底)!!!

第一章:计算机组成原理

1.1 计算机系统

  • 计算机Computer俗称"电脑",是一种能够接收和存储信息,并按照存储在其内部的程序对海量的数据进行自动、高速的处理,然后将处理结果输出的现代化智能电子设备。
  • 计算机有很多形式,如:台式电脑、笔记本电脑、智能手机、平板电脑等,还有生产环境中提供重要业务支撑的各种服务器。

  • 一个完整的计算机系统硬件Hardware系统软件Software系统两大部分组成,即:

1.2 冯·诺依曼体系结构

  • 冯·诺依曼是一位多才多艺的科学家,他在数学、物理学、计算机科学、经济学等领域都有杰出的贡献。

  • 冯·诺依曼的主要成就:
    • 在计算机科学领域的最著名贡献是提出了冯·诺依曼体系结构1946 年),这是现代计算机设计的基础
    • 促进了计算机的可编程性和通用性,使得计算机能够执行各种复杂的任务。
    • 对核武器设计、自动化控制系统、人工智能等领域的发展产生了重要影响。
    • ……

Important

冯·诺依曼体系结构是现代计算机(量子计算机除外)设计的基础

  • 冯·诺依曼体系结构的理论要点如下:

    • 存储程序程序指令数据都存储在计算机的内存中,这使得程序可以在运行时修改。
    • 二进制逻辑:所有数据指令都以二进制形式表示。
    • 顺序执行:指令按照它们在内存中的顺序执行,但可以有条件地改变执行顺序。
    • 五大部件:计算机由运算器控制器存储器输入设备输出设备组成。
    • 指令结构:指令由操作码和地址码组成,操作码指示要执行的操作,地址码指示操作数的位置。
    • 中心化控制计算机的控制单元CPU负责解释和执行指令控制数据流。

Note

上述的组件协同工作,构成了一个完整的计算机系统:

  • 运算器控制器通常被集成在一起组成中央处理器CPU负责数据处理和指令执行。
  • 存储器(内存)保存数据和程序,是计算机运作的基础。
  • 输入设备输出设备负责与外界的交互,确保用户能够输入信息并接收计算机的处理结果。

1.3 各种硬件处理速度和性能优化

  • 计算机的性能短板:如果 CPU 有每秒处理 1000 个服务请求的能力,各种总线的负载能力能达到 500 个, 但网卡只能接受 200个请求而硬盘只能负担 150 个的话,那这台服务器得处理能力只能是 150 个请求/ 秒,有 85% 的处理器计算能力浪费了,在计算机系统当中,硬盘的读写速率已经成为影响系统性能进一步提高的瓶颈。

img

  • 计算机的各个设备部件的延迟从高到低的排列依次是机械硬盘HDD、固态硬盘SSD、内存、CPU 。

img

  • 从上图中我们可以知道CPU 是最快的,一个时钟周期是 0.3 ns ,内存访问需要 120 ns ,固态硬盘访问需要 50-150 us传统的硬盘访问需要 1-10 ms而网络访问是最慢需要 40 ms 以上。
  • 时间的单位换算如下:
1 秒 = 1000 毫秒,即 1 s = 1000 ms。
1 毫秒 = 1000 微妙,即 1 ms = 1000 us 。
1 微妙 = 1000 纳秒,即 1 us = 1000 ns。
  • 按照上图,将计算机世界的时间和人类世界的时间进行对比,即:
如果 CPU 的时钟周期按照 1 秒计算,
那么,内存访问就需要 6 分钟;
那么,固态硬盘就需要 2-6 天;
那么,传统硬盘就需要 1-12 个月;
那么,网络访问就需要 4 年以上。
  • 所以,对于 CPU 来说,这个世界真的是太慢了!!!
  • 其实,中国古代中的文人,通常以蜉蝣来表示时间的短暂(和其他生物的寿命比),也是类似的道理,即:
鹤寿千岁,以极其游,蜉蝣朝生而暮死,尽其乐,盖其旦暮为期,远不过三日尔。
	                                        --- 出自 西汉淮南王刘安《淮南子》
寄蜉蝣于天地,渺沧海之一粟。 哀吾生之须臾,羡长江之无穷。 
挟飞仙以遨游,抱明月而长终。 知不可乎骤得,托遗响于悲风。
	                                        --- 出自 苏轼《赤壁赋》

Note

对于蜉蝣来说,从早到晚就是一生;而对于我们人类而言,却仅仅只是一天。

  • 存储器的层次结构CPU 中也有存储器,即:寄存器、高速缓存 L1、L2 和 L3如下所示

img

Note

上图以层次化的方式,展示了价格信息,揭示了一个真理,即:鱼和熊掌不可兼得。

  • ① 存储器越往上速度越快,但是价格越来越贵, 越往下速度越慢,但是价格越来越便宜。
  • ② 正是由于计算机各个部件的速度不同,容量不同,价格不同,导致了计算机系统/编程中的各种问题以及相应的解决方案。

Important

  • ① CPU 都是直接和内存打交道的CPU 会直接从内存中读取数据,待数据处理完毕之后,会将结果再次写入到内存中。
  • ② 如果需要将数据持久化(永久)保存(内存是易失性存储器,内存中的数据是以电荷形式存储在存储单元中的。
  • ③ 当计算机关闭或断电时,这些电荷很快消散,导致存储在内存中的数据丢失),那么就需要将内存中的数据刷新到磁盘或硬盘上,即:落盘。

1.4 计算机软件

1.4.1 操作系统的来源

  • 在上古时期,硬件资源不够丰富,计算机设计的也非常简陋。那个时候,很多应用程序都是直接跑在硬件上的,即:一个计算机只能跑一个应用程序。

  • 随着技术的发展,硬件越来越丰富,功能也越来越强大,性能也越来越好。这种情况下,如果一台计算机只能跑一个程序,实在是太浪费了。而且,底层硬件不断丰富,应用程序需要对接的硬件也将越来越多,如果每个应用程序都这么干,不显示工作很重复吗?于是,操作系统应运而生了。

  • 操作系统的功能:
    • 硬件驱动。
    • 进程管理。
    • 内存管理。
    • 网络管理。
    • 安全管理。
    • 文件管理。
  • 那么,操作系统的作用,就是这样的,即:
    • 对下,管理计算机的硬件资源。
    • 对上,提供使用计算机资源的操作方式,有:
      • 系统调用:是一套已经写好的代码接口,应用程序通过调用这些接口来请求操作系统执行特定的硬件操作。它们直接与硬件交互,提供底层功能支持,如:文件操作、进程管理、内存管理等。开发者通过系统调用可以实现对底层资源的直接控制,确保程序能够高效、安全地运行。
      • 终端命令:是一种文本命令接口,通过命令行输入各种指令来控制操作系统和软件的行为。终端命令可以执行文件操作、系统配置、网络管理等各种任务。主要针对开发人员高级用户,他们通过命令行可以快速、精确地完成各种操作,提高工作效率。
      • 图形用户界面GUI是通过图形元素窗口、图标、按钮等与用户进行交互的界面。供直观、易用的操作方式使用户能够通过鼠标点击、拖拽等简单操作完成复杂任务。主要面向普通用户,降低了计算机操作的门槛,提高了用户体验和工作效率。

1.4.2 用户态和内核态

  • 在现代操作系统中,用户态User Mode内核态Kernel Mode是两种不同的执行模式,它们对系统资源的访问权限有着本质的区别。这种区分是为了提供一个稳定和安全的运行环境,防止用户程序直接操作硬件设备和关键的系统资源,从而可能引起系统的不稳定或安全问题。

  • 内核态Kernel Mode VS 用户态User Mode
类型 内核态Kernel Mode 用户态User Mode
权限 内核态是操作系统代码运行的模式,拥有访问系统全部资源和执行硬件操作的最高权限。在这种模式下,操作系统的核心部分可以直接访问内存、硬件设备控制、管理文件系统和网络通信等。 用户态是普通应用程序运行的模式,具有较低的系统资源访问权限。在用户态,程序不能直接执行硬件操作,必须通过操作系统提供的接口(即系统调用)来请求服务。
安全性 由于内核态具有如此高的权限,因此只有可信的、经过严格审查的操作系统核心组件才被允许在此模式下运行。这样可以保护系统不被恶意软件破坏。 用户态为系统提供了一层保护,确保用户程序不能直接访问关键的系统资源,防止系统崩溃和数据泄露。
功能 内核态提供了系统调用的接口,允许用户态程序安全地请求使用操作系统提供的服务,比如:文件操作、网络通信、内存管理等。 用户态保证了操作系统的稳定性和安全性,同时也使得多个程序可以在相互隔离的环境中同时运行,避免相互干扰。

Note

  • ① 操作系统通过用户态和内核态的分离,实现了对系统资源的保护和控制。
  • ② 当用户程序需要进行文件读写、网络通信或其他需要操作系统介入的操作时会发生从用户态到内核态的切换。这通过系统调用System Call实现系统调用是用户程序与操作系统内核通信的桥梁。
  • ③ 执行完毕后,系统从内核态返回用户态,继续执行用户程序。
  • ④ 用户态和内核态的这种分离设计是现代操作系统中实现安全、稳定运行的关键机制之一。
  • 示例:
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;

public class Demo {
    public static void writeFile(String filePath, String content) {
        Path path = Paths.get(filePath);
        try {
            Files.write(path, content.getBytes());
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    public static void main(String[] args){
        // 用户态
        int a = 10;                                 
        int b = 20;                                 
        int c = a + b;                              
        string filePath = "c:/demo.txt";           
        string txt = a + b + c;                    
        
        // 从用户态切换到内核态完成文件写入
        writeFile(filePath, a);                     
        
        // 从内核态切换回用户态
        System.out.println(a);                      
        System.out.println(b);                      
        System.out.println(c);                     
    }
}

1.4.3 ISA、ABI 和 API

  • ISA 、ABI 和 API 的参考模型如下:

  • 在底层,硬件模型以指令集架构 ISA 表示该架构定义了处理器、寄存器、存储器和中断管理的指令集。ISA 是硬件和软件之间的接口,对于操作系统 OS 开发人员 System ISA 和直接管理底层硬件的应用程序 User ISA 的开发人员来说非常重要。

Note

  • ① ISA 是计算机体系结构中定义的一组指令它规定了处理器能够执行的操作。ISA 包括指令的编码、寄存器的使用、内存访问模式等。不同的处理器可能有不同的 ISA例如x86、ARM、MIPS 等。
  • ② 在设计一个新的操作系统时,开发者需要确保操作系统能够支持特定的 ISA ,以便在特定的硬件上运行。例如:如果操作系统旨在运行在 ARM 架构的处理器上,那么它必须能够理解和执行 ARM ISA 定义的指令集。
  • 应用程序二进制接口 ABI操作系统层与由操作系统管理的应用程序分开。ABI 涵盖了低级数据类型、对齐方式和调用约定等详细信息,并定义了可执行程序的格式。系统调用在此级别定义。此接口允许应用程序和库在实现相同 ABI 的操作系统之间移植。

Note

  • ① ABI 是指在二进制级别上应用程序与操作系统、库或应用程序的不同部分之间的接口。它定义了数据类型的大小、布局、对齐方式以及函数调用的约定如参数如何传递、返回值如何处理等。ABI 确保了编译后的二进制文件能够在特定的操作系统和硬件平台上正确地运行。
  • ② 在 windows 上的应用程序的运行格式是:PEportable executable格式、.dll dynamic link library格式和 .lib 格式;而在 Linux 上的应用程序的运行格式是:ELFexecutable and linking format格式、.so shared object格式和 .a 格式。
  • ③ 在 Linux 中可以通过 file /bin/ls 命令查看指定可执行应用程序的 ABI 格式;从而也可以论证,在 Windows 上可以运行的程序,在 Linux 上运行不了。
  • ④ 当开发者在 Linux 系统上编写 C 语言程序并使用特定的编译器GCC编译时编译器会遵循 Linux 平台的 ABI 规范来生成二进制文件。这样,生成的可执行文件就可以在任何遵循相同 ABI 规范的 Linux 系统上运行。
  • ⑤ 如果一个应用程序需要跨平台(操作系统)运行,就需要使用一套代码,多平台编译的方式(针对 C 或 C++ 等相同的源代码在不同平台操作系统上使用特定平台的编译器GCC来分别编译成符合自己平台的 ABI 规范的二进制文件。
  • 最高级别的抽象由应用程序编程接口 API 表示,它将应用程序连接到底层操作系统

Note

  • ① API 是一组预定义的函数、协议和工具用于构建软件和应用程序。API 允许不同的软件系统相互交互它定义了软件组件之间如何相互通信。API 可以是库、框架、协议或服务。
  • ② 在 Web 开发中,开发者可能会使用 JavaScript 的 Fetch API 来与服务器进行通信,获取数据或提交表单。这个 API 提供了一种标准化的方式来发送 HTTP 请求和处理响应,而不需要开发者关心底层的网络协议细节。

1.4.4 系统调用System Call和函数库Library Call

  • 在现代操作系统中,应用程序都不能直接作用于硬件,而是运行在操作系统之上。

  • 并且,在上文的图示中,我们也会看到系统调用System Call函数库Library Call的身影,如下:

  • 其实,系统调用System Call函数库Library Call的区别如下:
类型 系统调用System Call 函数库Library Call
定义 系统调用是操作系统提供给程序员的一组接口,这些接口允许用户空间的程序请求操作系统内核提供的服务,比如文件操作、进程控制、通信和内存管理等。 函数库调用是指使用高级语言编写的一组预先编译好的函数,这些函数实现了一些常用的功能,比如:字符串处理、数学计算等。程序员可以在自己的程序中直接调用这些函数,而无需重新实现它们。
权限 执行系统调用时,会从用户态切换到内核态。这是因为系统调用涉及到访问受保护的系统资源,这些操作必须由操作系统控制以确保系统的稳定性和安全性。 函数库调用通常在用户态执行,不涉及到用户态与内核态之间的切换。它们直接使用操作系统通过系统调用提供的服务,或者完全在用户空间内完成计算,不需要操作系统介入。
性能开销 由于涉及到用户态与内核态之间的切换,系统调用的执行成本相对较高。因此,频繁的系统调用可能会影响程序的性能。 相对于系统调用,函数库调用的性能开销较小。因为它们通常不涉及到模式切换,且执行的操作多在用户空间完成。
示例 open()read()write()fork()exec() 等 UNIX/Linux 系统调用。 C 标准库中的 printf() 等函数;数学库中的 sin()cos() 等函数。

Note

  • 执行层级:系统调用直接与操作系统内核交互,执行更底层的操作;而函数库调用运行在用户空间,通常使用系统调用来实现其功能。
  • 性能开销:系统调用由于涉及到用户态与内核态的切换,性能开销相对较大;函数库调用则因为主要在用户态执行,性能开销较小。
  • 使用目的:系统调用提供了访问操作系统资源和服务的能力;函数库调用则提供了方便、高效执行常见任务的手段。

第二章:初识计算机语言

2.1 计算机语言是什么?

  • 人类语言是人和人之间用于沟通的一种方式,例如:中国人和中国人之间使用普通话沟通,而中国人和美国人交流,则可以使用英语。

Note

  • ① 中文有自己的固定格式固定词汇(即:语法规则),英文也是自己的固定格式固定词汇(即:语法规则);同样的道理,法语、韩国等各种人类语言都有自己的固定格式固定词汇(即:语法规则)。
  • ② 在和别的国家的人进行交流的时候,我们必须正确的表达,对方才会理解我们;否则,如果不熟悉对方国家的语言的语法规则,乱用语法规则,可能会贻笑大方,如:中文中的望其项背原指看见对方的背影,形容差距不大,能赶上;但是,很多人却认为这是形容遥不可及或难以企及的目标。
  • ③ 就算和本国家的人进行交流的时候,我们也必须正确的表达,对方才会理解我们;否则,如果乱用语法规则,可能也会让对方感觉奇怪,听不懂我们的意思,如:借我 5000 RMB 买 iphone 或者 5000 RMB 我买 iphone 借
  • 计算机编程语言是人和计算机交流的方式。人们可以使用编程语言对计算机下达命令(指令),让计算机完成人们需要的功能。

Note

  • ① 计算机语言也有自己固定格式固定词汇(即:语法规则),我们必须学习其语法规则,才能控制计算机,让计算机完成我们所需要的功能。
  • ② 计算机语言有很多种C、C++、Java、Go、JavaScript、Python、Scala 等。

2.2 为什么要学习计算机语言?

  • 编程语言到底是什么?编程语言就是由文字和符号组成的,如:
#include <stdio.h> // 这是编译预处理指令

int main() { // 定义主函数

    printf("你好,世界!!!"); // 输出所指定的一行信息

    return 0;  // 函数执行完毕时返回函数值0
}
  • 编程语言就是用于控制计算机,让其完成我们需要的功能。而我们学习编程语言,其实就是学习这些文字和符号编写的规则。
  • 因为 CPU 只能识别二进制的指令,而我们编写的程序叫做源代码,是人类能看懂;但是,计算机却不能识别。那么,我们就需要让计算机能识别我们编写的源程序,就需要将我们编写的源代码交给编译器程序,其会帮助我们将所编写的源代码转换为计算机能够识别的二进制指令。

Note

编译器就是运行在操作系统之上的程序,其作用就是用来将程序员编写的源代码转换为计算机能够识别的二进制指令。

  • 如果我们用 Java 语言编写了程序(源代码),那么编写的程序也是不能直接运行的,需要通过 Java 语言的编译器将 Java 程序编译为计算机能够识别的二进制指令。
  • 如果我们用 Python 语言编写了程序(源代码),那么编写的程序也是不能直接运行的,需要通过 Python 语言的编译器将 Python 程序编译为计算机能够识别的二进制指令。
  • ……

Note

总结:无论我们学习任何一门编程语言,想要将程序运行起来,都必须做如下的两件事情:

  • ① 学习该语言的文字和符号编写的规则,即:语法规则
  • ② 需要在操作系统上安装对应编程语言的编译器程序,将源程序编译为计算机能够识别的二进制指令。

2.3 计算机语言简史

2.3.1 机器语言(相当于人类的石器时代)

  • 1946 年 2 月 14 日,世界上第一台计算机 ENIAC 诞生,使用的是最原始的穿透卡片

  • 这种卡片使用的是用二进制代码表示的语言,和人类语言差别极大,这种语言就称为机器语言,如:
0000,0000,000000010000 代表 LOAD A, 16
0000,0001,000000000001 代表 LOAD B, 1
0001,0001,000000010000 代表 STORE B, 16
  • 这种语言本质上是计算机能识别的唯一语言,人类很难理解;换言之,当时的程序员 99.9% 都是异类!!!

Warning

  • ① 不同类型CPU 架构x86_64、arm 等的处理器有不同的机器语言指令集指令集架构ISA决定了机器语言的具体形式。
  • ② 换言之,机器语言与特定硬件架构紧密相关,机器语言程序几乎没有可移植性。

2.3.2 汇编语言(相当于人类的青铜&铁器时代)

  • 汇编语言使用助记符MOV、ADD、SUB代替二进制操作码使程序更易于人类编写和理解因此汇编语言也被称为符号语言

  • 汇编语言的优点是能编写高效率的程序;但是,缺点和机器语言没什么不同,汇编语言同样依赖于具体的计算机架构(面向机器),程序不具备跨平台的可移植性。

Warning

  • ① 汇编语言,目前仍然应用于工业电子编程领域、软件的加密解密、计算机病毒分析等。
  • ② 汇编语言是编程语言的拓荒年代,它非常底层,直接和计算机硬件打交道,开发效率低,学习成本高。

2.3.3 高级语言(相当于人类的信息时代)

  • 高级语言是一种接近于人们使用习惯的程序设计语言。它允许程序员使用接近日常英语的指令来编写程序,程序中的符号和算式也和日常使用的数学公式差不多,接近于自然语言和数学语言,容易被人们掌握。

  • 高级语言独立于计算机硬件,有一定的通用性;计算机不能直接识别和执行用高级语言编写的程序,需要使用编译器解释器转换为机器语言,才能被计算机识别和执行。

Note

  • ① 普遍使用的高级编程语言C、C++、Java、Python、C#、JavaScript、Go、SQL 等。
  • ② C 语言是“面向过程”的编程语言,已经脱离了计算机硬件,可以用来设计和开发中等规模的程序。
  • ③ Java、C++、Python、C# 等都是“面向对象”的编程语言(它们在“面向过程”的基础上又增加了很多概念),可以用来设计和开发中大型规模的程序。

Important

  • ① C 语言出现的时候,已经度过了编程语言的拓荒年代,具备了现代编程语言的特性,但是这个时候还没有出现“软件危机”,人们没有动力去开发更加高级的语言,所以也没有太复杂的编程思想。
  • ② 之后出现的“面向对象”的编程思想解决了一部分在“软件危机”上的窘境。

2.3.4 总结

  • 编写语言的对比,如下所示:
类别 特征 优点 缺点 示例
机器语言 直接由计算机执行的二进制代码 执行速度快 编写困难,可读性差,与具体硬件强绑定 二进制代码
汇编语言 用助记符代替二进制代码的低级语言 相对机器语言更易编写和理解,允许直接控制硬件资源 依然需要了解硬件,不够抽象,与具体硬件或平台相关 MOVADD 等助记符
高级语言 接近人类语言,提供了更高层次的抽象 易于编写和维护,可移植性好,支持多种编程范式 需要通过编译器或解释器转换为机器语言,可能存在一定的性能损失 CJava Python 等

Important

  • ① 使用机器语言进行编程,对于程序员来说,简直就是噩梦,尤其当功能比较多,程序比较大的时候,不但编写麻烦,需要频繁查询指令手册,而且排查错误非常麻烦,要直接面对一堆二进制数据,想想都令人崩溃(上古程序员,可能真的不是“人”,而是“异类”)。此外,使用二进制指令编程,步骤非常繁琐,要考虑各种边界情况和底层问题,开发效率十分低下。
  • ② 这就倒逼程序员开发出了编程语言提供自己的生产力汇编语言、C 语言、C++ 语言、Java 语言、Go 语言等等,都是在逐步提高开发效率。至此,编程终于不再是只有极客才能做的事情,不怎么了解计算机的初学者在经过一定时间的训练后也可以编写出有模有样的程序。
  • ③ 在实际开发中随着计算机科学的发展现代化的高级编程语言C++ 语言、Java 语言、Go 语言等,因其强大的表达能力、良好的可移植性和易用性,成为了日常软件开发的主流选择。

第三章:初识 C 语言

3.1 C 语言究竟是一门怎样的编程语言?

  • 对于绝大多数程序员而言C 语言是学习编程的第一门语言,很少有不了解 C 语言的程序员。

Note

许多著名编程语言的创造者和计算机科学领域的大佬都有学习和使用过 C 语言詹姆斯·高斯林James GoslingJava 之父比雅尼·斯特劳斯特鲁普Bjarne StroustrupC++ 之父吉多·范罗苏姆Guido van RossumPython 之父林纳斯·托瓦兹Linus TorvaldsLinux 之父)等。

  • C 语言除了能让我们了解编程的相关概念,带领我们走进编程的大门,还能让我们明白程序的运行原理,如:

    • ① 计算机的各个部件是如何协同工作的?
    • ② 程序在内存中是一种怎样的状态?
    • ③ 程序在计算机中到底是如何执行的,以及它的生命周期是什么?
    • ④ 操作系统和应用程序之间,又有着怎样的爱恨情仇?
    • ⑤ ...
  • 这些底层知识决定了我们的发展高度,也决定了我们的职业生涯。如果我们希望能在计算机行业中长远的发展,就必须学习这些相关的底层知识。

Important

这些底层知识,包括不限于:数据结构和算法、计算机组成原理、操作系统、计算机网络、设计模式以及编译原理(了解即可):

  • 数据结构和算法:主要研究数据的组织方式和处理方法,包括:线性表、树、图等数据结构,以及基本的算法思想和分析方法。

  • 计算机组成原理:主要研究计算机系统的硬件组成和工作原理,包括:数字电路、存储器、中央处理器等内容。

  • 操作系统:主要研究计算机系统的软件组成和工作原理,包括:进程/线程/并发(重点)、内存布局和内存管理(重点)、文件系统和磁盘 IO 等内容,以及基本的操作系统概念和设计思路。

  • 计算机网络主要研究计算机之间的通信原理和协议包括网络体系结构OSI 模型)、传输协议、网络安全等内容,以及基本的网络概念和技术。重点学习 TCP/IP 协议栈、socket 通信(三/四次握手、select、poll、epoll、HTTPS/HTTP、长链接等内容。

  • 设计模式:是软件设计中常见问题的通用解决方案。不过,在 C 语言中通常是没有的,因为其是面向过程的编程语言,而 C++ 和 Java 等面向对象的编程语言是有设计模式的。

  • 编译原理:主要研究如何将高级编程语言转换为机器语言,有词法分析、语法分析、语义分析、中间代码生成、优化、代码生成等步骤,了解即可。

  • C 语言的概念少,词汇少,只包含了基本的编程元素,相对比较简单。对于初学者来说,学习 C 语言的时间短,成本小。

Note

  • ① 很多人之所以觉得 C 语言难,就是因为栽倒在 C 语言的指针那边相对比其他的现代化编程语言而言C 语言的指针确实足够底层和麻烦,需要掌握的细节很多)。
  • ② 但是,一旦有所突破,学习之路就会一马平川。

3.2 C 语言的由来

  • 1969 年,美国贝尔实验室的肯·汤姆森Ken Thompson丹尼斯·里奇Dennis Ritchie一起开发了 Unix 操作系统。Unix 最初是使用汇编语言编写的,依赖于计算机硬件。为了程序的可读性可移植性,它们决定使用高级语言重写。但是。当时的高级语言无法满足他们的要求,肯·汤姆森就在 BCPL 语言的基础上发明了 B 语言。
  • 1972 年,丹尼斯·里奇Dennis RitchieB 语言的基础上重新设计了一种新的语言,这种新语言取代了 B 语言,即 C 语言。

  • 1973 年,整个 Unix 系统都使用 C 语言重写(重构),大大增强了 Unix 在不同硬件平台的可移植性,这标志着 Unix 的初步成熟,也标志着 C 语言的初步成熟(因为 C 语言可以用于大型项目开发了)。

Note

C 语言最初是作为 Unix 系统的开发工具而发明的,它的初衷非常简单,就是被设计成一种非常高效的、可以操作硬件的系统级编程语言,然后帮助团队开发 Unix 操作系统。

  • 此后这种语言快速流传广泛用于各种操作系统和系统软件的开发Unix、MS-DOS、Microsoft Windows 以及 Linux 等。

  • 1988 年美国国家标准协会ANSI正式将 C 语言标准化,标志着 C 语言开始稳定和规范化。

3.3 为什么要学习 C 语言?

  • C 语言具有可移植好、跨平台的特点:用 C 语言编写的代码可以在不同的操作系统和硬件平台上编译和运行。

Note

  • ① C 语言的最原始的设计目的,就是为了将 Unix 操作系统移植到其他的计算机架构上,这使得它从一开始就非常注重可移植性。
  • ② 这边所说的 C 语言的可移植性,是和汇编语言相比的;如果 C 语言和现代化的高级编程语言相比可移植性还是很差的Java 的口号是“一次编译到处运行”Go 的口号是“一次编译,到处执行”。
  • C 语言在许多领域应用广泛
    • 操作系统C 广泛用于开发操作系统Unix、Linux 和 Windows。
    • 嵌入式系统C 是一种用于开发嵌入式系统(如:微控制器、微处理器和其它电子设备)的流程语言。
    • 系统软件C 用于开发设备驱动程序、编译器和汇编器等系统软件。
    • 网络C 语言广泛用于开发网络应用程序例如Web 服务器、网络协议和网络驱动程序。
    • 数据库系统C 用于开发数据库系统例如Oracle、MySQL 和 PostgreSQL 。
    • 游戏:由于 C 能够处理低级硬件交互,因此经常用于开发计算机游戏。
    • 人工智能C 用于开发人工智能和机器学习的应用程序,例如:神经网络和深度学习算法。
    • 科学应用C 用于开发科学应用程序,例如:仿真软件和数值分析工具。
    • 金融应用C 用于开发股票市场分析和交易系统等金融应用。
  • ③ C 语言能够直接对硬件进行操作、管理内存以及和操作系统对话,这使得它是一种非常接近底层的语言,非常适合写需要和硬件交互、有极高性能要求的程序。

Note

  • C 语言毕竟诞生的时间非常早20 世纪 70 年代),属于 70 后了,有点落后于现在的时代,虽然执行效率高(仅次于汇编语言),但是开发效率低。
  • 随着时间的推移,人们在 C 语言的基础上增加了面向对象的机制(软件危机),形成了一门新的编程语言,它的名字是 C++ 。
  • 但是C++ 的特性实在是太多了(因为 C++ 兼容 C并增加了很多自己独有的特性可以是当今最复杂的编程语言没有之一于是人们在 C++ 的基础上,删减了一些非必要的特性,就形成了 Java 和 C# ,也可以认为 Java 和 C# 是 C++--。
  • 当然近年来Go 语言也很火,它的设计者之一就是 Unix 操作系统的的开发者肯·汤姆森Ken ThompsonGo 诞生的背景据说是肯·汤姆森Ken Thompson在 C++ 委员会在为其演讲 C++ 新特性的时候,觉得 C++ 新特性太多太复杂,于是就开发了 Go 语言所以Go 语言中有很多 C 语言的身影。
  • 其实,在 C 语言之后的许多编程语言,或多或少都参考了 C 语言;所以,编程界流传了一句话:汇编生 C C 生万物。
  • 学习 C 语言有助于快速上手其他编程语言C++(原先是 C 语言的一个扩展,在 C 语言的基础上嫁接了面向对象编程思想、C#、Java 等,这些语言都继承或深受 C 语言的影响和启发。
  • ⑤ C 语言长盛不衰。C 语言至今,依然是最广泛使用、最流行的编程语言之一,包括很多大学将 C 语言作为计算机教学的入门语言,拥有庞大而活跃的用户社区,这意味着有许多资源和库可供开发人员使用。
  • ⑥ C 语言容易学习,适合入门。和 Java、C++、Python 等更加现代化的高级编程语言相比C 语言涉及的概念少,附带的标准库小,整体比较简单,容易学习,非常适合初学者入门。

3.4 计算机语言排行榜

  • TIOBE 是一个流行编程语言排行每月更新。排名权重基于世界范围内工程师数量Google、Bing、Yahoo! 、Wikipedia、Amazon、Youtube 和百度这些主流的搜索引擎,也将作为排名权重的参考指标。

  • 计算机语言走势图:

3.5 C 语言到底能做什么?

3.5.1 概述

  • 我们知道,这个世界上的编程语言太多太多。其中,最流行的编程语言就是 TOBIE 榜单上的前 50 名。

  • 从应用的范围上来讲,编程语言大致可以分为以下两类,如下所示:

    • 通用型编程语言这些语言设计的目的是能够用于开发多种类型的应用程序不局限于某个特定领域。它们通常拥有丰富的库和框架支持可以应用于系统编程、应用开发、Web开发、数据分析等多个领域。
    • 专用型编程语言:这些语言是为了解决特定领域的问题而设计的,通常在该领域中表现出色。虽然它们可以在一定程度上用于其他领域,但其核心设计和优化是针对某个特定应用场景的。
  • 常见的专用型编程语言,如下所示:

专用型编程语言 描述
SQL 专门用于数据库查询和管理,尽管可以嵌入其他编程语言中,但其主要应用是数据库操作。
R 主要用于统计分析和数据可视化,广泛应用于数据科学和研究领域。
MATLAB 主要用于数值计算、算法开发和数据可视化,广泛应用于工程和科学领域。
VHDL/Verilog 用于硬件描述和数字电路设计,主要应用于电子工程领域。
  • 常见的通用型编程语言,如下所示:
通用型编程语言 描述
Python 广泛应用于Web开发、数据科学、自动化脚本、人工智能等。
Java 用于企业级应用、移动应用开发Android、Web 开发等。
C++ 可用于系统编程、游戏开发、嵌入式系统等多个领域。
JavaScript 最初用于 Web 前端开发现在也广泛用于服务器端Node.js、桌面应用开发Electron等。

Note

C 语言是一门通用型的编程语言并没有针对某个领域进行优化。但是C 语言也有很多常见的应用领域。

3.5.2 C 语言的常见应用领域

  • C 语言的常见应用领域,如下所示:
    • ① 单片机或嵌入式开发单片机通常是一个集成度较高的微控制器资源有限家电控制、自动化设备、传感器数据采集、智能玩具、汽车电子。嵌入式系统是一个计算机系统嵌入在更大设备中用来执行专门的任务。它通常包含硬件和软件两部分具有专用性、实时性、资源受限等特点消费电子智能手机、智能手表等、医疗设备、工业控制系统、汽车电子系统ECU、ABS、物联网设备等。
    • 上位机开发(桌面软件开发)其实这部分的市场已经被其它编程语言蚕食了C++ 的 QT、GTKJava 的 Swing 、JavaFxDart 的 Flutter ,因为 C 语言太过于底层,本身不直接支持跨平台,而且没有丰富的库和框架支持。
    • ③ 系统组件开发:
      • 基础组件文件系统、进程管理、用户界面CLI + GUI等。
      • 核心算法,如:加密/安全算法MD5、SHA、AES、RSA、SSL、调度算法进程和线程调度、内存页面置换、LZ 压缩算法、CRC 等数据校验算法、随机数生成算法等。
      • 硬件驱动声卡驱动、显卡驱动、网卡驱动、蓝牙驱动、键盘和鼠标驱动、扫描仪驱动、打印机驱动、USB 驱动等。
      • 通信协议TCP/IP 协议族UDP、DNS、路由选择、HTTP/HTTPS 、SMTP/POP3/IMAP、FTP、NFC 等。
    • ④ 开发操作系统:这是 C 语言的初衷它就是为开发操作系统而生的UNIX 内核、Linux 内核以及 Windows 内核,主要就是使用 C 语言开发。
    • ⑤ 开发其它编程语言:
      • 有些编程语言的编译器(解释器)和标准库就使用 C 语言开发,如: Python、PHP、Rust、Perl。
      • 有些编程语言是在 C 语言的基础上进行的扩展,如: C++、Objective-C、Swift。
    • ⑥ 信号处理C 语言在电气工程领域也有很多用途,它可以使用信号处理算法来管理微处理器、微控制器等集成电路。
    • ⑦ 音视频处理C 语言的速度非常快,能够快速地对音频和视频数据进行处理。音频和视频数据通常比较大,需要高效的算法和数据结构来处理,而 C 语言运行速度非常快能够及时处理这些数据。C 语言提供了丰富的底层库和工具FFmpeg、OpenCV 等,这些库和工具可以方便地对音频和视频数据进行编码、解码、剪辑、处理和转换等操作。
    • ⑧ 数据库开发:数据库是软件领域的基础设施,它的性能直接影响整个应用程序的运行效率,所以必须使用一种高效的语言进行开发。使用 C 语言开发的数据库有MySQL、SQLite、PostgreSQL 等。
    • ⑨ ...

Important

虽然 C 语言的开发场景貌似看起来很多;但是,使用 C 语言的场景几乎就是:底层/系统开发 + 关键组件/模块的开发 + 贴近硬件的开发,这些开发场景非常关注运行效率,或者响应时间,或者硬件资源。

3.6 不学 C 语言,行吗?

  • C 语言相比于其它的现代化高级编程语言而言Java、Go 等,实在是太老了;如果不打算搞嵌入式开发,即便学了,一时半会也用不上。但是,几乎所有大学的计算机/软件专业都将 C 语言作为必修课,这又是为什么呢?
  • 其实,是因为 C 语言是一门基础语言,很多其它的课程都依赖 C 语言。如果你不了解 C 语言,很多课程你是理解不了的,如下所示:
    • ① 编译原理课程通常是以 C 语言为例进行讲解,因为 C 语言的编译过程相对简单、规范和透明,适合教学。
    • ② 数据结构课程通常也是使用 C 语言进行编程,因为 C 语言比较底层能够让大家看到数据结构的各种细节。另外数据结构是一种被频繁调用的组件必须要追求效率C 语言再合适不过了。
    • ③ 学习操作系统原理(内存、进程、线程、通信等)也要具备 C 语言基础,否则是学不明白的。
    • ...
  • C 语言是一门面向计算机的语言它能帮助我们快速了解底层而其它的高级语言Python、Java、C# 等是面向用户的它能让我们快速上手搞出点实用的工具来比如桌面软件、网站、APP 等。

Note

  • ① 借助 C 语言学习原理,相当于修炼内功;使用其它语言开发程序,相当于精通招式。
  • ② 一个既有扎实“内功”又精通“招式”的程序员,无疑能够更好地应对各种编程挑战。
  • 从整体上讲,计算机软件大概可以分为两种:
    • 一种是基础设施,如:操作系统、数据库、浏览器、云计算系统、大数据系统、编译器/编程语言、通信协议、区块链、标准库/运行库、算法实现等。
    • 一种是应用软件桌面软件、APP、网站、小程序等。

Important

  • ① 内功不扎实的话,开发一般的应用软件可能没什么问题。但是,如果想要开发高性能的软件,或者开发基础设施,那是绝对是不行的。
  • ② 万丈高楼平地起,勿在浮沙筑高台!!!

3.7 C 语言的版本选择

3.7.1 概述

  • C 语言是在 B 语言的基础上改进而来的,目的是为了更好地开发 Unix 操作系统。到了 1973 年Unix 的大部分功能都被 C 语言重写,这标志着 C 语言的初步成熟,因为它可以用于大型项目了。
  • 后来C 语言被多次改进,越来越强大,为了规范 C 语言的特性和功能,人们发布了多个 C 语言标准。

3.7.2 版本 1KR C

  • K&R C 指的是 C 语言的原始版本。1978 年C 语言的发明者布莱恩·柯林Brian Kernighan和丹尼斯·里奇Dennis Ritchie合写了一本著名的教材《C 编程语言》The C programming language

Note

由于 C 语言还没有成文的语法标准,这本书就成了公认标准,以两位作者的姓氏首字母作为版本简称 “K&R C”。

3.7.3 版本 2ANSI C又称 C89 或 C90

  • 到了 80 年代C 语言越来越流行,广泛被业界使用,从大型主机到小型微机,各个厂商群雄并起,推出了多款 C 语言的编译器。这些编译器根据行业和厂商自己的需求进行了各种扩展C 语言进入了春秋战国时代,逐渐演变成一个松散杂乱的大家族。
  • 为统一 C 语言版本1989 年美国国家标准协会ANSI制定了一套 C 语言标准并于次年被国际标准化组织ISO通过。它被称为 “ANSI C”也可以按照发布年份称为 “C89 或 C90”。

Note

目前常用的编译器MSVCMicrosoft Visual C++、GCC、LLVM Clang 等,都能很好地支持 ANSI C 的内容。

3.7.4 版本 3C99

  • C 语言标准的第一次大型修订,发生在 1999 年,增加了许多语言特性,如:双斜杠( // )的注释语法,可变长度数组、灵活的数组成员、复数、内联函数和指定的初始值设定项,这个版本称为 C99是目前最流行的 C 版本

Note

这个时候的 C 语言编译器基本已经成熟,各个组织对 C99 的支持所表现出来的兴趣不同:

  • 当 GCC 和其它一些商业编译器支持 C99 的大部分特性的时候。
  • 微软和 Borland 却似乎对此不感兴趣,或者说没有足够的资源和动力来改进编译器。

最终导致不同的编译器在部分语法上存在差异。典型的例子就是ANSI C 规定,只能用常量表示数组的长度,而 C99 取消了这个限制,数组的长度也可以用变量表示(可变长度数组)。对于 C99 的这个新改动GCC 和 Clang 是支持的,而 MSVC 却不支持。

3.7.5 版本 4C11

  • 2011 年,标准化组织再一次对 C 语言进行修订增加了_Generic、static_assert 和原子类型限定符,这个版本称为 C11。

Note

  • ① 需要强调的是,修订标准的原因并不是因为原标准不能用,而是需要跟进新的技术。
  • ② 支持此标准的主流 C 语言编译器有 GCC、LLVM Clang、Intel C++ Compile 等。

3.7.6 版本 5C17

  • C11 标准在 2017 年进行了修补,但发布是在 2018 年。新版本只是解决了 C11 的一些缺陷,没有引入任何新功能,这个版本称为 C17。

3.7.7 版本 6C23

  • 2023 年发布,计划进一步增强安全性,消除实现定义的行为,引入模块化语言概念等新特性,使 C 语言在安全和可靠性方面有重大提高。

第四章C 语言的学习技巧

4.1 如何学习 C 语言?

  • 对于大部分的初学者, 学习 C 语言的目的可能是为了成为一名合格的程序员,开发出优秀的软件。但是,在学习了 C 语言的基本语法后,却发现只能在控制台黑底白字)上玩玩,没有漂亮的用户界面以及人性化的交互。于是,开始学习数据结构、算法、数据库、操作系统,越陷越深,越来越迷茫,不知道学习 C 语言能做什么,认为学习编程很难,开始怀疑自己,直到放弃!!!
  • 其实C 语言本身就是一门非常简单的语言,提供的实用功能不多,大部分的时候需要借助操作系统、第三方库以及以及一些硬件,如:单片机等,才能发挥它的威力!!!

Important

  • ① 学习 C 语言仅仅是让你踏上程序员之路的第一步而已,只学习 C 语言也做不了什么。
  • ② 系统、扎实的学习 C 语言可以让你了解底层硬件、一些简单的数据结构和算法,并培养计算机思维。
  • 前文我们提过C 语言是一门通用性的语言并没有针对某个领域进行优化。并且在实际项目中C 语言主要用于比较底层的开发,例如:
    • Windows、Linux、Unix 等操作系统的内核 90% 以上都使用 C 语言开发Rust 语言有望未来,在操作系统开发中占据一席之地,特别是在对安全性和性能要求极高的领域)。
    • 开发硬件驱动,让硬件和操作系统连接起来,这样用户才能更有效的使用硬件。
    • 单片机和嵌入式属于软硬件的结合,是使用 C 语言最多的地方。
    • 开发系统组件或服务,用于支撑上层应用。
    • 如果对软件某个模块,例如:算法和搜索部分的效率要求较高,也可以使用 C 语言来开发。
    • ……
  • 貌似感觉 C 语言的应用还是很多的啊,那为什么感觉学习 C 语言还是做不了什么?原因除了 C 语言通常都是开发最底层的应用之外,最重要的一点就是生态

Important

现代化的高级编程语言的流行程度,除了和编程语言的设计是否优秀有关,最主要的原因就是生态

  • ① 很多编程语言都自带标准库语言本身提供的开箱即用Java、Go 等。
  • ② 很多编程语言都有自己的包管理器用于管理第三方库解决方案Java 中的 Maven、Gradle、Go 中的 go modules JavaScript 的 npm 等。

遗憾的是C 语言的标准库非常简单,只有输入输出文件操作日期时间字符串处理内存管理,对于网络编程GUI数据库并发需要大量的第三方库操作系统的功能来扩展 C 语言的功能Java 语言、Go 语言等其他的现代化高级编程语言都是直接将这些常见的开发场景内置到标准库中开箱即用极大的降低了软件开发的难度。C 语言的第三方库也不是很多和其它现代化高级编程语言相比C 语言的大多数的第三方库都是底层库,支持应用开发的库寥寥无几,只有一个 GTK 库能够开发出桌面软件,几乎没有网站开发以及 APP 开发相关的库),社区也不是很活跃(和其它现代化高级编程语言相比),更别提缺少自己的包管理器。

Important

  • ① 换言之在实际工作中C 语言几乎是不用做软件、网站、APP 等这些应用层开发,其它的编程语言能够更好地完成任务,没必要非得使用 C 语言C 语言基本都是用来做底层开发,也就是看不见摸不着的、在后台默默提供服务的那些项目,而这样的项目对初学者来说基本没有实用价值,初学者也不知道它们该怎么使用。

  • ② 初学者想要的 C 语言没有C 语言能做的初学者用不到,就是这种矛盾,导致初学者非常迷茫。

  • ③ 有人可能会问C 语言不是还可以用来开发单片机或者嵌入式吗是的没错但是这个方向是软硬件结合的不是在我们的电脑上进行开发而是在特殊的板子上进行开发并且还需要学习数字电路、模拟电路、8051/ARM、RTOS、嵌入式 Linux 等方面的知识,只学 C 语言也没有用武之地。

  • ④ 如果你觉得学了 C 语言没用,那么恭喜你,你是对的,应用层的开发一般真的用不上它。

  • ⑤ 但是,没用也要学,学习 C 语言并不一定是要应用它C 语言可以夯实你的编程基础,尤其是数据结构、算法、内存、线程、进程、通信、操作系统、编译原理等底层的计算机知识,没有 C 语言基础是学不好的。

  • ⑥ 这些底层知识并不一定能够直接应用在实际开发中,但是它们会让你有底气,会让你透彻地理解编程概念,会让你站的“低”看得远,会让你避免很多低级错误,会让你心中有“架构师”的思维。

Important

不过,现在 C 语言社区也开始诞生了一些包管理器Conan 和 vcpkg 也有自己的项目构建工具cmake 、xmake 等。

Note

JavaScript 的作者 Brendan Eich布兰登·艾奇 曾经这么说:“与其说我爱 JavaScript不如说我恨它。它是 C 语言和 Self 语言一夜情的产物(致敬 C 语言)。十八世纪英国文学家约翰逊博士说得好:"它的优秀之处并非原创,它的原创之处并不优秀。"”

4.2 项目构建工具和包管理器

4.2.1 概述

  • 项目构建工具包管理器在软件开发中扮演着不同的角色,它们虽然有时会有重叠的功能,但主要关注的点是不同的。

4.2.2 项目构建工具

  • 项目构建工具是用于自动化编译、测试、打包、部署等一系列任务的软件工具。它们帮助开发者简化和管理整个软件开发生命周期中的各个步骤,尤其是在构建过程中的复杂性管理上。
  • 其功能有:
    • 编译代码:自动编译源代码(如 .java.c 等)为可执行文件或中间文件(如:.class 文件)。
    • 运行测试:集成单元测试、集成测试,自动运行测试用例并生成报告。
    • 打包将编译后的代码、依赖库、资源文件等打包成可分发的格式JAR、WAR、可执行文件等
    • 依赖管理:自动下载、更新和管理项目所需的第三方库(这部分功能有时与包管理器重叠)。
    • 部署:将打包后的应用程序自动部署到测试环境、生产环境等。
    • 任务自动化:除了基本的构建流程外,还可以自动化执行一些常见任务,如:代码检查、文档生成等。
  • 常用的项目构建工具:
    • MavenJava一个流行的构建工具和依赖管理工具广泛用于 Java 项目。
    • GradleJava、Kotlin、Groovy一个灵活的构建工具支持声明式的构建脚本和多种语言。
    • MakeC/C++):一个经典的构建工具,使用 Makefile 来定义构建规则和依赖关系。
    • CMakeC/C++一个跨平台的构建系统帮助生成标准的构建文件Makefile 或 Visual Studio 项目文件。
    • ……

4.2.3 包管理器

  • 包管理器是用于自动化安装、更新、配置管理软件包及其依赖关系的工具。它主要关注于获取和管理项目所需的第三方库或工具包,并确保它们正确地集成到项目中。
  • 其功能有:
    • 依赖管理:根据项目配置文件(如:package.jsonrequirements.txt)自动下载和安装项目所需的依赖包。
    • 版本控制:管理包的版本,允许开发者指定某个特定版本或版本范围,确保项目中的库版本一致性。
    • 包的发布和共享:开发者可以通过包管理器发布自己的库,并且共享给社区或组织内部的其他项目使用。
    • 环境隔离:有些包管理器提供虚拟环境功能,可以将不同项目的依赖隔离开,避免版本冲突。
    • 更新和卸载:包管理器可以自动更新依赖包到最新的兼容版本或卸载不再需要的包。
  • 常见的包管理器:
    • npmNode.js用于管理 JavaScript 和 Node.js 项目的包和模块。
    • pipPython用于安装和管理 Python 的软件包。
    • CargoRustRust 编程语言的包管理器和构建工具。
    • YarnJavaScript是 npm 的替代品,提供更快和更可靠的包管理体验。
    • HomebrewmacOS用于 macOS 系统下的命令行工具和库的管理。
    • ……

4.2.4 注意事项

  • 对于 Java 项目中的 MavenGradle 而言,其不仅是项目构建工具也是包管理工具

Note

  • ① Gradle 也支持原生项目的开发C 和 C++ 。
  • ② 不过,目前而言,业界开发 C/C++ 项目时,使用最多的项目构建工具和包管理器是 Cmake 和 Conan 。

4.3 C 语言为什么没有应用层开发的库?

  • C 语言是一门“古老”的语言了它只支持面向过程编程不支持面向对象编程和泛型编程在中大型的应用层项目开发中C 语言已经显得捉襟见肘了C++、Java、Python、C#、JavaScript 等其他编程语言能够更好地胜任,为 C 语言开发应用层的库简直是费力不讨好,所以几乎没人这么做。
  • GTK 算是一个应用层的库,它使用 C 语言开发但是为了适应市场GTK 也提供了其它编程语言的接口C++、Python 等。
  • 先不用管面向过程、面向对象、泛型这些晦涩的编程概念。简单地理解就是C 语言支持的特性少,用起来费劲,开发效率低,而 C++、Java、Python、C#、JavaScript 等支持的特性多,用起来方便,开发效率高。

Important

  • ① C 语言的优势是运行效率极高,这正是底层开发所看重的。
  • ② 底层开发有时候就是一个模块,或者是一个服务,规模不算大,但是对效率有严格的要求,此时用 C 语言就非常合适,所以针对底层开发的 C 语言库较多,因为它们有非常大的实用价值。

第五章:附录

5.1 嵌入式领域中的 C 语言

5.1.1 概述

  • C 语言在 C51、STM32 和 ARM 平台上的应用场景非常广泛,涵盖了各种嵌入式系统的开发需求。

5.1.2 C518051 系列微控制器)

  • 背景8051 是由 Intel 于 1980 年设计的一种 8 位微控制器架构。它具有指令集简单、结构紧凑的特点,广泛应用于低端嵌入式系统中。
  • 开发工具C51 是指针对 8051 系列微控制器的 C 语言编译器Keil C51。这种编译器将 C 语言代码编译为适合 8051 架构的汇编代码。
  • C 语言的作用C 语言在 8051 微控制器上的应用使得开发更加高效和可维护。尽管 8051 的硬件资源有限,但 C 语言仍然能够在不损失性能的前提下提供高级编程的便利。
  • 应用场景
    • 简单的控制系统:家用电器(微波炉、洗衣机、空调)的控制板等。这些设备通常不需要复杂的运算能力,但要求可靠和稳定的控制。
    • 低功耗传感器接口C51 微控制器常用于低功耗传感器的数据采集和传输,如:温度、湿度、压力传感器。
    • 工业自动化设备:用于简单的工业自动化控制,如:小型电机驱动、工业传感器数据处理和传输。
    • 电子玩具:许多简单的电子玩具使用 8051 系列微控制器来控制声音、LED 灯光、显示屏等。

Note

总结C51 微控制器适用于资源受限、需要低成本的简单控制系统,非常适合使用 C 语言来进行开发!!!

5.1.3 STM32STM32 系列微控制器)

  • 背景STM32 是意法半导体STMicroelectronics推出的一系列基于 ARM Cortex-M 内核的 32 位微控制器。它们广泛用于需要高性能和低功耗的嵌入式应用中,如:工业控制、消费电子和物联网设备。
  • 开发工具:开发 STM32 微控制器通常使用 Keil、IAR Embedded Workbench 或 STM32CubeIDE 等开发环境。这些环境中使用的编程语言主要是 C有时也包括 C++)。
  • C 语言的作用C 语言在 STM32 上的应用非常广泛,开发者可以利用它直接控制硬件寄存器,同时也能方便地使用 STM32 提供的 HAL硬件抽象层库或 LL低层库进行开发。C 语言在这个平台上不仅能实现底层控制,还能编写复杂的应用逻辑。
  • 应用场景
    • 物联网IoT设备STM32 微控制器常用于各种物联网设备智能家居控制系统、环境监测设备、可穿戴设备等。这些设备通常需要低功耗和强大的处理能力并且需要支持多种通信协议Wi-Fi、Bluetooth、LoRa。
    • 消费电子:智能手表、健身追踪器、电子书阅读器、无人机等,这些设备需要具备实时处理能力、低功耗和良好的外设支持。
    • 医疗设备STM32 微控制器被广泛应用于便携式医疗设备中,如:血糖监测仪、心率监测器、便携式超声设备等,这些设备需要精确的传感器数据采集和处理。
    • 工业自动化控制PLC可编程逻辑控制器、工业机器人、伺服电机控制等STM32 能够处理复杂的控制算法和实时任务。
    • 汽车电子:用于汽车中的传感器管理、车载信息娱乐系统、车身控制系统(车窗、电动座椅调节等)。

Note

总结STM32 微控制器在物联网、消费电子、医疗设备和工业控制等领域表现出色,非常适合使用 C 语言来进行开发,因为 C 语言允许直接进行硬件控制并支持复杂的应用开发。

5.1.3 ARM 架构(特别是 ARM Cortex 系列)

  • 背景ARM 是一种广泛使用的处理器架构特别是在嵌入式系统中ARM Cortex 系列处理器(如 Cortex-M、Cortex-R 和 Cortex-A非常流行。Cortex-M 系列主要用于微控制器Cortex-R 用于实时系统Cortex-A 则用于高性能嵌入式系统。
  • 开发工具:针对 ARM 架构的开发,常用工具包括 ARM Keil MDK、IAR、GCC for ARM 和 ARM Development Studio。这些工具均支持使用 C 语言进行开发。
  • C 语言的作用C 语言在 ARM 架构上的应用广泛。它被用于操作系统内核(如 FreeRTOS、Zephyr、设备驱动、应用层逻辑等。在 ARM Cortex-M 和 Cortex-R 系列中C 语言的高效性和低级别硬件访问能力是开发实时、低延迟系统的关键。
  • 应用场景
    • 高级嵌入式操作系统ARM Cortex-A 系列处理器广泛用于运行 Linux、Android 等操作系统的嵌入式设备,如:智能手机、平板电脑、智能电视和车载娱乐系统。
    • 实时系统ARM Cortex-R 系列处理器用于实时系统,如:汽车的 ABS防抱死制动系统、ESC电子稳定控制系统以及航空电子设备这些系统要求极低的延迟和高可靠性。
    • 高性能物联网网关Cortex-A 系列处理器可以用来开发支持多协议、多设备管理的物联网网关,这些网关通常需要强大的计算能力和多线程处理能力。
    • 边缘计算设备在边缘计算场景中ARM Cortex-A 处理器用于执行本地数据处理和决策,如:视频分析、图像处理、语音识别等。
    • 智能家居设备ARM Cortex-M 系列微控制器广泛应用于智能家居产品,如:智能灯泡、智能音箱、家庭安全系统,这些设备需要高效的处理能力和低功耗。
    • 机器人控制系统ARM Cortex-M 和 Cortex-A 系列处理器用于机器人系统的控制和通信,如:无人机、工业机器人、服务机器人等,处理复杂的运动控制、路径规划和传感器数据融合。

Note

总结ARM Cortex 系列适用于从实时系统到高级嵌入式操作系统的各类应用,支持从低功耗控制到高性能计算的多种需求,非常适合使用 C 语言来进行开发,因为 C 语言不仅用于控制硬件,还广泛应用于操作系统和应用程序的开发。

5.2 C 和 C++ 的关系

  • 起源和发展:
    • C 语言C 语言由丹尼斯·里奇Dennis Ritchie在 1972 年开发,最初用于开发操作系统,特别是 UNIX。它是一种结构化的编程语言提供了对硬件的低级访问且效率高因此在系统编程中广泛使用。
    • C++ 语言C++ 由比雅尼·斯特劳斯特鲁普Bjarne Stroustrup在 20 世纪 80 年代初开发,作为 C 语言的扩展。它引入了面向对象编程的概念,同时保留了 C 语言的高效性和底层操作能力。C++ 最初被称为 “C with Classes”带类的 C后来发展成一种独立的编程语言。
  • 兼容型:
    • 语法兼容C++ 几乎完全兼容 C 语言的语法,这意味着大多数 C 代码可以在 C++ 编译器下直接编译运行。C++ 可以看作是 C 语言的超集(虽然不完全是 100% 兼容,但差异较少)。
    • 扩展性C++ 在 C 的基础上增加了许多新特性,如:类和对象、继承、多态、模板和异常处理等。这使得 C++ 不仅适合系统编程,还可以用于开发复杂的应用程序。
  • 编程范式:
    • C 语言:主要是面向过程编程,强调的是函数调用和控制结构。
    • C++ 语言:支持多种编程范式,包括:面向过程、面向对象和泛型编程,使得开发者可以更灵活地选择适合的编程风格。
  • 性能与应用:
    • 性能:由于 C++ 包含了更多的高级特性,它的编译时间和运行时开销可能比 C 稍高。但得益于其优化机制C++ 仍然可以实现与 C 语言相近的性能。
    • 应用场景C 语言仍然在嵌入式系统、驱动程序开发和其他对性能要求极高的场合中占据重要地位。而 C++ 则被广泛用于游戏开发、图形处理、大型软件系统等需要复杂结构和抽象的领域。

Note

C++ 是 C 语言的继承者和扩展,它保留了 C 语言的优势,并引入了更多现代编程语言的特性,使得它在更广泛的应用领域中得到应用。

5.3 C 语言和 Java 语言的对比

  • C 语言 和 Java 语言的软件设计思想对比,如下所示:

Note

  • ① Java 语言编写的应用是直接运行在 JVMJava 虚拟机)上,体现了分层的软件设计思想;并且,这种分层的软件设计思想给 Java 语言带来了跨平台性自动内存管理等方面的功能。
  • ② C 语言编写的应用是直接运行在操作系统之上,其设计哲学是:简洁、高效、直接控制底层

  • 虽然 C 语言和 Java 语言有很多相似之处,但在设计理念、运行环境、内存管理等方面有显著差异,如下所示:
对比角度 C 语言 Java 语言
编译与解释 编译型语言,源代码编译为机器码直接执行,效率高。 解释与编译结合,源代码编译为字节码,通过 JVM 解释运行。
平台依赖性 与操作系统和硬件平台紧密结合,跨平台能力较弱。 通过 JVM 实现跨平台,"编写一次,到处运行"。
内存管理 手动管理内存,需显式分配与释放,容易出现内存泄漏。 自动垃圾回收,简化内存管理,降低内存泄漏风险。
指针 支持指针,能直接操作内存,但可能导致安全问题。 不支持指针操作,避免内存安全隐患。
编程范式 过程化编程,主要通过函数调用组织程序。 完全的面向对象编程,一切皆为对象。
异常处理 不支持异常处理,错误处理通过返回值或全局变量实现。 提供强大的异常处理机制,通过 try-catch-finally 块处理。
标准库与生态 标准库简洁,需大量使用第三方库开发复杂应用。 标准库丰富,拥有庞大生态系统和社区支持。
运行时性能 直接编译成机器码,性能高,适合高性能要求的场景。 运行在虚拟机上,性能稍低,但通过 JIT 等优化提升效率。
多线程支持 依赖操作系统 APIpthread多线程编程较为复杂。 内置多线程支持,提供 Thread 类和并发工具类。
应用场景 系统级编程,如操作系统、嵌入式系统、驱动程序等。 企业级应用、Web 开发、Android 开发、大数据处理等。

Important

总结而言:

  • ① C 语言适合底层编程、系统级开发,性能高、控制力强,但对程序员的要求也更高。而 Java 语言则更适合应用层开发,拥有丰富的库和工具支持,开发效率更高,并且由于自动内存管理和异常处理,编写的代码通常更加安全和健壮。
  • ② 两者各有优势,选择使用哪种语言应根据项目需求和开发环境来决定。