业界对C语言的相关评测
2022-04-29 09:22:26 阿炯

C 语言撑起操作系统半边天
关于C语言的过时论
C 语言的不败秘诀
50岁的C语言与两位巨人的故事



C语言是一种广泛使用的计算机编程语言,它是一种通用的高级语言,可以用于开发操作系统、编写应用程序和嵌入式系统。然而,尽管C语言在计算机科学领域中扮演着重要的角色,但很少有人真正了解它的内部工作原理和设计思想。在此将介绍C语言的基本概念、历史背景、语法结构和应用领域,以帮助读者更好地了解这门语言。

历史背景

C语言最初由Dennis Ritchie在20世纪70年代初期开发,它是为了开发UNIX操作系统而设计的。其设计目标是提供一种高效、可移植和易于使用的编程语言,以便在不同的计算机系统上编写软件。C语言的设计受到了ALGOL 68和B语言的影响,它采用了B语言的语法结构,并添加了许多新的特性和功能。其发展历程可以分为三个阶段。第一个阶段是C语言的早期发展阶段,从1972年到1977年,Dennis Ritchie和Ken Thompson在贝尔实验室开发了C语言的原型版本。第二个阶段是C语言的标准化阶段,从1983年到1989年,ANSI和ISO组织制定了C语言的标准,称为ANSI C和ISO C。第三个阶段是C语言的现代化阶段,从1990年代开始,C语言的标准不断更新和完善,以适应新的计算机技术和应用领域。

基本概念

C语言是一种结构化编程语言,它的基本概念包括变量、数据类型、运算符、表达式、语句和函数等。C语言的变量是用于存储数据的内存单元,它们可以存储不同类型的数据,如整数、浮点数、字符和指针等。C语言的数据类型包括基本数据类型和派生数据类型,基本数据类型包括整型、浮点型和字符型,派生数据类型包括数组、结构体和联合体等。

运算符包括算术运算符、关系运算符、逻辑运算符和位运算符等,它们用于执行不同类型的运算操作。C语言的表达式是由变量、常量、运算符和函数调用等组成的,它们用于计算和生成值。C语言的语句包括赋值语句、条件语句、循环语句和跳转语句等,它们用于控制程序的执行流程。C语言的函数是一段可重用的代码块,它们用于执行特定的任务,并返回一个值。

语法结构

C语言的语法结构包括预处理指令、函数定义、变量定义和语句等。C语言的预处理指令用于在编译时对源代码进行处理,如#include和#define等。C语言的函数定义包括函数头和函数体两部分,函数头包括函数名、参数列表和返回值类型等,函数体包括函数执行的代码块。

变量定义包括变量类型和变量名等,变量的作用域和生命周期由它们的定义位置和存储类别决定。C语言的语句包括赋值语句、条件语句、循环语句和跳转语句等。赋值语句用于将一个值赋给一个变量,条件语句用于根据条件执行不同的代码块,循环语句用于重复执行一段代码块,跳转语句用于跳转到程序的其他位置。

应用领域

C语言是一种通用的编程语言,它可以用于开发各种类型的软件,如操作系统、编译器、数据库、网络应用和游戏等。C语言的高效性和可移植性使它成为许多应用领域的首选语言,在计算机科学领域中扮演着重要的角色,它是学习计算机科学的基础语言之一。

C语言是一种广泛使用的计算机编程语言,它的设计目标是提供一种高效、可移植和易于使用的编程语言。基本概念包括变量、数据类型、运算符、表达式、语句和函数等,它们用于执行不同类型的计算和操作。语法结构包括预处理指令、函数定义、变量定义和语句等,用于组织和控制程序的执行流程。C语言在各种应用领域中都有广泛的应用,它是学习计算机科学的基础语言之一。


50 岁的 C 语言掌控 Windows、Linux、macOS 等操作系统半边天

诞生于1972年的C语言在2022年已经50岁了,目前来看,它还像20岁的小伙一样活力四射,似乎永不会退休,并且正在赋能全世界重量级应用系统的运行。
作者 | Daniel Munoz 编译 | 梦依丹
出品 | CSDN(ID:CSDNnews)

不久之前,CSDN发布了一篇观点性文章。其中认为,C已经被提升到一个具有威望和权力的角色,它的统治是绝对和永恒的,以至于它扭曲了开发者与该语言之间的对话方式。当下,C是编程的通用语言,大家都必须学C,这也导致C不再只是一种编程语言,它成了每一种通用编程语言都需要遵守的协议。

可以看出,在编程语言众多的今天,C语言依然代表着权威。Meta高性能C++数据仓库工程师Daniel发文表示,尽管C语言面世多年,甚至不少人用暮年来形容它,但它依然在为世界编程赋能,并且还将活跃很长一段时间,因为C语言在某些应用中以压倒性优势领先,且无可匹敌。他列举了C语言是如何渗透到千家万户,影响着世界运行的。对此,笔者对原文进行了编译,与大家共享!

现今存在的很多C语言项目都是几十年前开始的:
开发于1969年的UNIX操作系统,其代码在1972年基于C语言进行了重建,帮UNIX系统代码从汇编转移到更高层次的语言,进而实现用更少的代码完成相同的任务;

开发于1977年的Oracle,其代码也在1983年转向了C语言,Oracle是当下最受欢迎的数据库之一;

发布于1985年的Window 1.0操作系统,尽管源码并未公开,但据说大部分内核代码也是基于C语言构建,还有一部分是汇编;

开发于1991年的Linux,其内核也是基于C语言。Linux在1992年基于GNU重新获得许可,被作为GNU操作系统的一部分使用。GNU系统本身也是使用C和Lisp编程语言构建,所以它的许多组件都是基于C开发。

不少人可能会提出,这些系统都是几十年前的项目,当时的编程语言还不多,可供选择的就更少了。其实不然,C语言不仅仅局限于几十年前的项目,当下不少项目也是基于它启动的。

C语言如何驱动世界

编程语言层出不穷,各种高级语言更是如雨后春笋般涌现。但C语言仍然在为全世界的应用系统赋能,下面分享一些被数百万人广泛使用的C语言构建系统。

各大流行系统所用到的语言

Windows操作系统:据NetMarketShare统计,Windows操作系统份额常年在90%附近徘徊,服务全球数十亿用户,其内核代码大部分是由C语言构建,还有一部分是基于汇编。

Linux:众所周知,Linux内核主要是由C语言撰写,在世界500台最强大的超级计算机中,约有97%运行的是Linux内核,它也被用于许多个人电脑中。

macOS:Mac电脑操作系统也是由C语言驱动,因为OS X的内核大部分是用C语言编写。Mac中的每个程序和驱动程序,就像Windows和Linux电脑一样,都是在由C语言驱动的内核上运行。

移动设备:iOS、Android和Windows Phone的内核也是用C语言编写,它们只是对现有的Mac OS、Linux和Windows内核的移动改编。因此,大家每天使用的智能手机也是运行在C语言之上。

数据库领域:世界上最流行的数据库,包括Oracle、MySQL、MS SQL Server和PostgreSQL,都是用C语言构建(其中前三个实际上是C和C++)。

数据库被用于所有类型的系统:金融、政府、媒体、娱乐、电信、健康、教育、零售、社交网络、网络等等。

3D电影:此类应用程序需要高效、快速。它们需要在数秒内进行许多计算和处理大量数据,这样艺术家和动画师生成的电影镜头所需的时间才越短,公司也能节省更多资金。这类大部分应用也都是基于C和C++制作而成。

嵌入式系统:想象一下,某一天你醒来后的行程:唤醒你的闹钟很可能是用C语言编写,然后你用微波炉或咖啡机来做早餐,它们也是嵌入式系统,因此有可能也是基于C语言构建;你在吃早餐时打开电视或收音机,当你用遥控器打开车库门时,也在使用一个很可能是用C语言编程的嵌入式系统。

然后你准备开车外出。如果它有以下功能,也是用C语言编程的:自动变速器、轮胎压力检测系统、传感器(氧气、温度、油位等)、座椅和后视镜设置的记忆、仪表盘显示、防抱死制动系统、自动稳定控制、巡航控制、气候控制、儿童安全锁、无钥匙进入、座椅加热、安全气囊控制

到商店,停好车,去自动售货机买汽水。那么自动售货机有可能也是基于C构建运行。随后你在商店里买东西,结账,那么收银机也是用C。当你用信用卡付款时?你猜对了:信用卡阅读器也可能是用C语言写的。

所有的这些设备都是嵌入式系统。它们就像小型计算机,里面有一个微控制器/微处理器,在嵌入式设备上运行一个程序,也叫固件。程序必须检测按键并采取相应的行动,同时向用户显示信息。例如,闹钟必须与用户互动,检测用户正在按什么按钮,有时还检测按了多长时间,并对设备进行相应编程,同时向用户显示相关信息。例如,汽车的防抱死制动系统必须能够检测到轮胎的突然锁定,并采取行动,在一小段时间内释放刹车上的压力,解除锁定,从而防止失控打滑。所有这些计算都是由一个编程的嵌入式系统完成的。

尽管不同品牌的嵌入式系统所使用的编程语言可能不同,但由于C语言的灵活性、效率、性能和接近硬件的特点,C语言是开发这些项目的首选。

C语言为什么仍被广泛使用?

在今天,有许多编程语言可以让开发者研发出比C更高效的应用,这些语言拥有丰富的内置库,可以简化与JSON、XML、UI、网页、客户端请求、数据库链接、媒体操作等工作。尽管如此,C依然仍将长期活跃在编程一线,为什么呢?

那让我们一起来看看C语言都有哪些无与伦比的优势。

可移植性和高效

汇编语言的可移植性差,可C语言却是一门可移植性非常好的语言。它尽可能地接近机器,同时它几乎普遍适用于现有的处理器架构。几乎现有的每个架构至少有一个C语言编译器。如今,由于现代编译器产生高度优化的二进制文件,用手写的汇编来改进它们的输出并不是一件容易的事。

由于它的可移植性和效率高效,"其他编程语言的编译器、库和解释器经常用C语言实现"。像Python、Ruby和PHP这些解释性语言的主要实现都是基于C语言,它甚至被其他语言的编译器用来与机器通信。例如,C是Eiffel和Forth的中间语言。意味着这些语言的编译器不需要为每个要支持的架构生成机器代码,而只是生成中间的C代码,由C编译器处理机器代码的生成。

C语言也已成为开发人员之间交流的一种语言。正如Dropbox工程经理、Cprogramming.com创建者Alex Allain所说:C语言作为一门伟大的语言,可以让大多数人以能接受的方式来表达编程中的常见想法。此外,C语言在使用中也有语法结构也会出现在其他语言中,例如,用于命令行参数的argc和argv,以及循环结构和变量类型,因此,即使对方不懂C语言,你也能找到一些共同点来与他们交谈。

内存操作

内存管理和指针运算是C语言的重要特征,使C语言成为系统级编程(操作系统与嵌入式系统)的最佳搭档。

在硬件/软件边界,计算机系统和微控制器将其外设和I/O引脚映射到内存地址。系统应用程序必须读取和写入这些自定义的内存位置,以便与外界进行通信。因此,C语言操作任意内存地址的能力对于系统编程是必不可少的。例如,一个微控制器可以这样设计:每当地址0x40008001的第4位被设置为1时,内存地址0x40008000中的字节就会被通用异步接收/发送器(或UART,一种与外设通信的常见硬件组件)发送,并且在设置后,它将被外设自动取消。下来演示一个C函数代码,它通过该UART发送一个字节:

#define UART_BYTE *(char *)0x40008000#define UART_SEND *(volatile char *)0x40008001 |= 0x08
void send_uart(char byte)UART_BYTE = byte; // write byte to 0x40008000 addressUART_SEND; // set bit number 4 of address 0x40008001

send_uart函数的第一行代码可扩展为:
*(char *)0x40008000 = byte;

这一行代码是告诉编译器将值是0x40008000解释为一个指向char的指针,然后解除对该指针的定义(给出该指针所指向的值)(用最左边的*操作符),最后将字节值分配给该解除定义的指针。换句话说:把变量byte的值写到内存地址0x40008000。

将该函数的下一行代码扩展一下:
*(volatile char *)0x40008001 |= 0x08;

在这行代码中,我们对地址0x40008001和数值0x08(二进制的00001000,即第4位的1)进行了or位运算操作,并将结果存回地址0x40008001。换句话说:我们设置地址为0x40008001的字节的第4位。我们还声明地址为0x40008001的值是易失性的。这就告诉编译器,该值可能会被我们代码外部的进程所修改,所以编译器在写入该地址后不会对该地址的值做出任何假设(在这种情况下,该字节在我们用软件设置后就被UART硬件取消了)。这些信息对于编译器的优化器来说是很重要的。例如,如果我们在for循环中这样做,而没有指定该值是易失性的,编译器可能会认为该值在被设置后永远不会改变,并在第一个循环后跳过执行该命令。

确定资源使用

开发人员进行系统编程不能依赖的一个常见语言特性就是垃圾收集,甚至对一些嵌入式系统来说,只能进行动态分配。嵌入式应用程序在时间和内存资源方面非常有限。对于一些实时的嵌入系统,它们无法承受垃圾收集器的非确定性调用。如果因为内存不足而不能使用动态分配,那么拥有其他内存管理机制就显得尤为重要,比如将数据放在自定义地址中,就像C语言的指针所允许的那样。那些严重依赖动态分配和垃圾回收的语言不适用于资源紧张的系统。

代码大小

C语言有一个非常小的运行时,其代码的内存占用要小于其它语言。例如与C++相比,一个由C语言生成的二进制文件,其体积大约是由类似的C++代码生成的二进制文件的一半。造成这种情况的主要原因之一是异常支持。

异常(Exceptions )机制是C++比C语言多出来的一个不错功能,如果异常不被触发和巧妙的实现,他们实际上是没有执行时间的开销,但代价便是增加代码体积。

引入辅助函数增加了代码的体积,这是C++添加到C语言中的空间开销的一部分,许多嵌入式应用无法负担这种额外的空间。因此,用于嵌入式系统的C++编译器通常有一个禁用异常的标志。在C++中禁用异常是不自由的,因为标准模板库严重依赖异常来告知错误。使用这种修改过的方案,没有异常,需要对C++开发人员进行更多的培训,以检测可能的问题或发现错误。

C++的一个原则就是“开发者无需为不使用的东西付费”。对于其他语言来说,二进制体积的增加会变得非常糟糕,通过其它功能来增加额外开销,虽然这些功能有用,但嵌入式系统却负担不起。虽然C语言不会给你提供这些额外功能,但他可以比其它语言拥有更紧凑的代码足迹(code footprint ),占用更小的磁盘空间。

为什么要学习C语言

C语言并不难学,作为一门老牌编程语言,有关它的教程跟学习资料非常多,那么学习C语言有哪些好处呢?

通用语言

C语言是开发人员的通用语言,网上或者图书里面的不少算法都是基于C语言实现,这也为实现提供了最大的可移植性,开发者也会从中受益。

更好理解机器原理(用C语言思考)

当我们与同事讨论代码的某些部分或其他语言的某些特征时,我们最终会 "用C语言说话":"这部分是向对象传递一个 "指针 "还是复制整个对象?这里会不会发生任何 "转换"?等等。在分析高级语言的一部分代码的行为时,很少讨论(或思考)一部分代码正在执行的汇编指令。相反在讨论机器在做什么时,我们可以用C语言描述(或想)得很清楚。

在许多有趣的C语言项目上工作

从大型数据库服务器或操作系统内核甚至是为了满足个人乐趣而制作的小型家用嵌入式应用,你都可以用C语言实现,并且还可以在网上找到相关Demo。Daniel呼吁大家,不要停止自己喜欢做的事情,比如学习C语言,它古老但小巧,并且是一门经过时间验证的编程语言。

小结

当下许多编程语言在其预设的用途上都要优于C语言,但这并不意味着就能击败C,当考虑性能优先的时候,C依然是王者。世界正运行在C语言驱动的设备上,无论你是否意识到,你使用的诸多设备的的确确都用到了C语言。

原文链接


关于C语言的过时论

优秀的程序员为什么需要学习C语言

对所有的编程语言,它们的最后的目的其实就是两种:提高硬件的运行效率和提高程序员的开发效率。遗憾的是,这两点是不可能并存的!你只能选一样。在提高硬件的运行效率这一方面,C语言没有竞争者!举个简单的例子,实现一个列表,C语言用数组int a[3],经过编译以后变成了(基地址+偏移量)的方式。对于计算机来说,没有运算比加法更快,没有任何一种方法比(基地址+偏移量)的存取方法更快。C语言已经把硬件的运行效率压缩到了极致,这种设计思想带来的问题就是易用性和安全性的缺失。例如,你不能在数组中混合保存不同的类型,否则编译器没有办法计算正确的偏移量。同时C语言对于错误的偏移量也不闻不问,这就是C语言中臭名昭著的越界问题。

第一:相比较其他的编程语言(像C++,JAVA),C语言是个低级语言。从总体上来说,低级的编程语言可以让你更好的了解计算机。

第二:设备驱动程序和操作系统只能用C语言来编写。现在,你可能还从来没有编写过一个设备驱动程序或者一个操作系统,但是如果你需要去修改他们的时候,怎么办?

第三:如果你想要得到一份编写微控制器程序的工作的时候,该怎么办?他们都是用C语言编写的。就因为不想学习一门新的语言,你就准备限制你能得到工作的机会吗?

第四:C的程序比其他用别的语言写的程序,实现相同的功能,它用的代码行数更少,而它带来的运行效率却更快。有时候,你的程序所需要的速度,只有C语言能做到。

第五:如果你学习过C语言,你就能学习现在任何的高级编程语言。因为所有的高级语言都是以C语言为基础的(像JAVA,C++,C#等等)。

第六:因为C语言已经存在很多年了,它有广泛的使用团体并且有大量的现成代码可以利用。这就使你能在过去程序的基础上,快速和高效的编写新的算法和函数。

第七:C语言是一个开源组织的语言。一个开源组织的产物--LINUX,就是用C语言写的。如果你会C语言,你就能参加这个组织并且还能向众多的开源组织投稿。

第八:C语言是唯一一个向你阐述指针的本质的语言。而C#和Java干脆跳过了指针这个题目。可是指针确实使C语言变得更加强大。

第九:找编程开发方面的工作时,C语言仍然是最普遍需要的语言。所以它值得你花时间去学会它。

第十:任何里面有微处理器的设备都支持C语言。从微波炉到手机,都是由C语言技术来推动的。

说点题外话,C++不会淘汰C语言。有了对象后你会发现再简朴的对象也耗费资源,而且有了对象以后,总是不由自主的去想继承这个事,一但继承实现了,你会发现继承带来的麻烦远超过你的想象。Java的发明人James被问到如果可以从新设计Java语言的话,第一个要做什么事?他说:“去掉对象”!

为C需要被替换掉的观点争辩是简单的。编程语言研究和软件开发实践都暗示了如何比C更好地去做事。但历经数十年的研究和开发,C语言的地位却依旧稳固。很少有其他语言能够在性能、裸机兼容性或通用性等方面击败它。

C vs C++

当然了,C最常被拿来与C++进行比较,正如其名称本身所暗示的那样,C++作为对C语言的扩展而被创建出来。C++和C之间的差异可以概括为C++更加广泛(褒)或更加宽泛(贬),具体取决于这个问题你是问的C还是C++程序员。虽然C++的语法等方面仍然是类C的,但它提供了许多在原生的C中本不可用的非常实用的功能:命名空间(namespace),模板(template),异常(exception),自动内存管理(automatic memory management)等等。需要顶级性能的项目,例如涉及数据库,机器学习系统的项目通常是用C++编写的,以便项目能尽可能地榨取以及利用到每一点性能。此外,与C相比,C++在持续地更加积极地扩展。推出的C++ 20会带来更多功能供开发者享用,包括模块,协同程序,同步库,以及概念,这些使模板更易于使用。C standard的最新版本只进行了少量更新,并侧重于保持向后兼容性。

事实上,C++中的所有附加功能同样也可能成为累赘,而且是很大的累赘。使用的C++专属功能越多,引入的复杂度就越高,对结果的修正就越困难。将自己局限于仅一个C++子集的开发人员可以避免许多开发中严重的坑和额外负担。但是有些团队想要从根儿上防范C++的过度复杂性。坚持使用C能迫使开发人员将自己局限于一个子集。例如,Linux内核开发团队就直接避开了C++。

选C而不选C++对您——以及任何将会维护你代码的开发人员——来说都是可行的,通过采用强制简约主义来避免与C++的复杂性纠缠。当然,C++拥有丰富的高级功能,这是有它自己的道理的。但如果极简主义更适合当前和未来的项目——以及负责项目的团队——那么还是选C更明智一些。

C vs Java

几十年了,Java仍然是企业软件开发的主力军之一——并且也是宽泛而言的开发的主力军之一。许多最重要的企业软件项目都是用Java编写的——包括绝大多数Apache Software Foundation项目——而Java仍然是开发企业级需求项目的可行语言。

Java的语法从C和C++中借鉴了很多东西。但与C不同的是,Java默认情况下不会编译为本机代码。相反,Java运行时环境,JVM,JIT(实时)编译Java代码以在目标环境中运行。在适当的情况下,JIT编译后的Java代码可以接近甚至超过C的性能。其背后的“一次编写,随处运行”的理念也允许Java程序在目标架构上进行相对较少的调整即可运行。相比之下,虽然C已被移植到许多架构中,但任何给定的C程序仍可能需要重新量身定做才能在,打个比方,Windows与Linux,两种不同的os之间正常运行。

这种可移植性和强大性能的结合,以及庞大的软件库和框架组成的生态,使Java成为构建企业应用程序的首选语言。

Java输给C的地方是一个Java从未打算竞争的领域:靠近底层结构运行,或直接与硬件打交道。C代码被编译成机器代码,由进程直接执行。Java被编译成字节码,这是一种随后会被JVM解释器转换为机器代码的中间代码。此外,尽管Java的自动内存管理在大多数情况下都是个优点,但C更适合于必须充分利用有限内存资源的情况。也就是说,在某些方面,Java在速度方面可以接近于C。JVM的JIT引擎在运行时根据程序行为优化例程,允许进行许多类型的优化,而这些优化是在未提前编译的C中无法实现的。虽然Java运行时自动执行内存管理,但一些较新的应用程序可以解决这个问题。例如,Apache Spark部分地通过使用绕过JVM的自定义内存管理代码来优化内存中处理。

C vs C#和.Net

在推出近二十年后,C#和.Net 框架仍然是企业软件世界的主要组成部分。有人说C#和.Net是微软对Java的回应——一个托管代码编译器系统和通用运行库——C和Java之间的许多种对比也适用于C和C#或.Net之间。

与Java(以及某种程度上来说Python也是如此)一样,.Net提供跨各种平台的可移植性和庞大的集成软件生态系统。考虑到.Net世界中有多少面向企业的开发,这些都是不小的优势。当您使用C#或任何其他.Net语言开发程序时,您可以使用为.Net运行时编写的大量工具和库。

.NET另一个类似Java的优势是JIT优化。C#和.Net程序可以按照C语言提前编译,但它们主要由.Net运行时进行即时编译,并使用运行时信息进行优化。JIT编译允许对无法在C中执行的运行着的.Net程序进行各种就地优化。

与C一样,C#和.Net提供各种直接访问内存的机制。堆,栈和非托管系统内存都可以通过.Net API和对象访问。开发人员可以使用.Net中的unsafe模式来实现更高的性能。

但这些都不是没有代价的。托管对象和unsafe对象不能被任意交换,并且它们之间的编组会降低性能。因此,要最大化.Net应用程序的性能需要将托管和非托管对象之间的变动保持在最低限度。如果无法承担托管与非托管内存之间变动造成的性能损失,或者.Net运行时对于目标环境(例如,内核空间)来说是一个糟糕的选择,或者可能根本不可用,那么C就是你所需要的。与C#和.Net不同,C被默认可以解锁对内存的访问权。

C vs Go

Go的语法很大程度上借鉴了C——花括号作为定界符,语句以分号结束,等等。精通C的开发人员通常可以毫不费力地直接使用Go,甚至算上Go的独有功能,如命名空间和包管理,对开发人员来说也并不困难。

代码可读性是Go的指导设计目标之一:让开发人员可以轻松掌握任何Go项目,并在短时间内熟练掌握代码库。C代码库可能很难理解,因为它们很容易聚集大量专属于某个项目或某个团队的宏和和#ifdef。Go的语法及其内置的代码格式以及项目管理工具旨在避免这种结构性问题。

Go还提供了诸如goroutine和channel之类的附加功能,用于处理并发性和组件之间的消息传递的语言级别的工具。C需要开发者手动完成或由外部库提供,但Go提供了开箱即用的这些功能,使得构建需要这些功能的软件变得更加容易。

Go与C最深层次的不同之处在于内存管理方面。默认情况下,Go的对象会被自动管理并自动进行回收。对于大多数编程工作来说,这非常方便。但这也意味着任何需要确定性处理内存的程序都会更难编写。

Go确实包含了用于绕过Go的某些类型处理安全性的unsafe包,例如使用Pointer类型读取和写入任意内存。但unsafe会附带一个warning说用它编写的程序“可能是不可移植的,并且不受Go 1兼容性指南的保护。

Go非常适合构建命令行实用程序和网络服务等,因为这些很少用到太过细致的操作。但如果是低级设备驱动程序,内核空间操作系统组件以及其他需要严格控制内存布局和管理的任务,那么就最好用C来创建。

在嵌入式应用开发上go与c的对比


嵌入式应用中Go与C的再比较

对于嵌入式开发团队来说,更快的嵌入式应用上市时间确实是一个重要的考虑因素。但如何开发一种方法来更快地交付应用程序,同时将质量和安全性作为优先事项?在这种情况下,OTA软件更新经理Mender.io背后的产品工程团队经历了为开发Mender的嵌入式客户端和服务器部分选择最佳编程语言的过程。在这个评估过程中,Go、C和C++入围候选名单。本节总结了从这个评估过程中吸取的一些教训,以及为什么最终选择Go开发Mender的客户端嵌入式应用程序。虽然这可能很主观,但Go对于嵌入式开发来说是一种非常有效的语言。当涉及到网络编程时,尤其如此,网络编程在某种程度上是每个连接的设备或应用程序的一部分。Go是谷歌为满足谷歌开发者的需求而创建的,它的开发主要是为了解决其生态系统中的复杂性爆炸问题。因此,高效编译、高效执行和易于编程是开发Go背后的三个主要原则,因为这三个原则以前并不是在同一种主流语言中都可用。然而要强调的是,Go不能被视为C的替代品,因为在很多地方都需要C,例如在开发实时操作系统或设备驱动程序时。

嵌入式开发的严格要求

建立架构后,修补程序产品工程团队评估哪种语言最适合开发修补程序应用程序。该系统应该由运行在嵌入式设备上的客户端和作为客户端连接的中心点的服务器组成。因此对语言有几个要求:
由于客户端应用程序将在嵌入式设备上运行,编译后的二进制文件需要尽可能小。
它需要与Linux的嵌入式发行版Yocto一起工作。
安装客户端的复杂性应该很低,从而限制外部依赖性和库。
因为它可以在不同的设备上运行,所以该语言必须在不同的架构上编译。
运行修补程序客户端应用程序的设备将是物联网或联网设备,因此该语言需要访问联网库。

选择新语言的要求还包括一些非功能性要求:
我们组织中尽可能多的程序员需要能够理解这种语言。
在用C编写的现有应用程序之间共享和重用现有代码以及在客户端和服务器应用程序之间重用代码必须尽可能容易。
开发速度也是考虑因素之一——我们面临着快速添加新功能的持续压力。

比较Go与C

Go还因为其极其丰富的标准库而被选中进行嵌入式开发,这使得开发速度更快,尤其是与C相比。Go继承了C、C++和Python的许多元素,包括表达式语法、控制流语句、数据结构、接口、指针、按值传递概念、参数解析语法、字符串处理和垃圾收集。通过其优化的编译器,Go可以在嵌入式设备上自然运行。Go确实有一些缺点,它们的分量不足以克服它的优势,比如开发速度和语言构建的简易性,但它们确实是我们决策的考虑因素。

Go与C的大小比较

就大小而言,Go比C更笨重,这是它的几个缺点之一。如果你把“helloworld”作为最小的应用程序,你可以在Go中编写并使用内置的println函数,在去掉调试符号后,它的大小刚好超过600kB。如果包含fmt包及其依赖项,那么大小将增加到1.5MB。与C相比,如果你正在构建一个动态链接库,那么它仅为8kB。如果是静态的,那么大小会增加到800KB以上,这比Go二进制文件还要大得惊人。

Go与C的速度比较

编译的Go代码通常比C可执行文件慢。Go是完全垃圾收集的,这本身会减慢速度。使用C,你可以精确地决定要为变量分配内存的位置,以及是在堆栈上还是在堆上。在Go中,编译器试图对变量的分配位置做出明智的决定。例如,你可以看到变量将被分配到哪里(gobuild-gcflags-m),但不能强制编译器仅使用堆栈。然而,说到速度不能忘记编译速度和嵌入式开发人员速度。Go提供了极快的编译速度;例如,15000行Go客户端代码需要1.4秒才能编译。Go非常适合并发执行(goroutines和channel),前面提到的丰富标准库涵盖了大多数基本需求,因此开发速度更快。

汇编和交叉汇编

有两个Go编译器可以使用:最初的一个叫做gc。它是默认安装的一部分,由Google编写和维护。第二个叫做gccgo,是GCC的前身。使用gccgo,编译速度极快,大型模块可以在几秒内编译完成。如前所述,Go默认情况下是静态编译的,因此只创建一个二进制文件,而不需要额外的依赖关系或虚拟机。可以使用-linkshared标志创建和使用共享库。通常,Go不需要构建文件,可以通过一个简单的“Gobuild”命令进行构建,但也可以将Makefile用于更高级的构建过程。除此之外,在交叉编译方面,Go支持大量的操作系统和体系结构。

Go中的调试和测试

许多开发人员为了在程序执行时了解程序内部的情况,将使用GDB。对于高度并发的Go应用程序,GDB在调试它们时遇到了一些问题。幸运的是,有一个名为Delve的专用Go调试器更适合于此目的。只熟悉GDB的嵌入式开发人员应该能够在大多数情况下使用它而不会出现任何问题。将测试和单元测试添加到Go代码中非常容易。测试内置于该语言中,只需创建一个带有“test”后缀的文件,向函数添加测试前缀,导入测试包,然后运行“gotest”即可。所有测试都将自动从源代码中提取并相应执行。

并发支持

Go中很好地支持并发。它有两个内置机制:goroutine和channel。goroutine是轻量级线程,大小只有2kB。创建goroutine非常简单:只需在函数前面添加“go”关键字,它就会同时执行。Go有自己的内部调度器,goroutine可以根据需要复用到OS线程中。通道是在gorroutine之间交换消息的“管道”,可以是阻塞的,也可以是非阻塞的。

Go利大于弊

使用Go的经验显示了优点和缺点。从负面来说,社区中有很多外部库,但质量参差不齐,你需要非常小心地使用哪种库。随着语言的成熟和被采用,这一点正在大大提高。当Go代码中有一些C绑定时,事情通常会变得更加复杂,特别是交叉编译,如果不导入整个C交叉编译基础结构,就无法轻松完成。使用Go进行嵌入式开发仍然有很多好处。从C和/或Python转换到Go是快速而简单的,并在几天内实现高效。Go提供了非常好的工具和100多个包的标准库。可以轻松地设置和控制运行时设置,例如要使用多少OS线程(GOMAXPROCS),或者是否要打开或关闭垃圾收集(GOGC=off)。库和代码可以很容易地在服务器和客户端开发团队之间共享。

内存管理

Golang在C语言的基础上添加了内存管理机制,这使得C语言开发中让人头大的各种内存问题得以解决。其初始的几个版本内存管理机制被人诟病,垃圾回收会导致程序停顿,但是现在Golang的内存管理已经相当完善。然而,Golang提供内存管理机制的同时。依然保留了C语言的指针变量类型。因为Golang对普通变量采用复制的方式进行使用,而指针是将原始对象的地址进行传递,例如函数传参,普通变量会进行形参向实参的拷贝,函数内部对实参的修改不会影响到函数外的变量,而指针变量只是传递地址,函数内部对实参的修改会直接改变函数外的变量。保留C语言这一概念,为我们的编程带来了极大地灵活性,C语言编程中有一条简单的规则,如果是基本类型(int,float等)建议使用变量,除非需要对变量进行修改;如果使用复合类型(结构体等),如果不是需要深拷贝的场合,建议使用指针,因为这些类型的变量一般占用内存空间比较大,如果使用变量,会多次复制,影响程序性能。Golang保留了这一概念,就让人可以按照自己的需要选择合适的方式——使用变量还是指针,代码设计的灵活性更大,改善代码性能的方式也更简单。

Golang的语句比C语言更加简洁。代码的风格和质量在语法层面上就已经进行了统一方便大家解决这类错误,提供了gofmt工具,运行gofmt直接就会对代码格式进行修正。

Golang不支持动态链接库,Golang编译只会生成一个对应的可执行文件,引用第三方包,使用go get直接拉取源码并统一编译。让项目部署极其简单,可执行文件只有一个,配置好配置文件,资源安排好,直接运行即可。

Golang引入了包体系,更方便代码结构的控制和引用第三方资源。C语言没有包的概念,所以之前文章提到,我们需要自己组织好代码的位置,防止概念混乱。

Golang可以进行面向对象编程,Golang在面向对象方面采取了和C++,Python等相同的理念,Golang提供了面向对象的机制,但是也可以面向过程编程,Golang不限制你使用的编程思想。面向对象语法非常简单,派生采用了Has-a的方式,没有提供Is-a的方式,这有两方面原因,一方面,在C语言中,如果进行派生,可采用一个结构体包含另一个结构体的方式,也就是只能采用Has-a的方式;另外,Is-a的派生方式经常存在问题,如果父类的接口发生变化,经常会直接影响子类的接口也要进行调整,Has-a的方式,父类作为子类的成员变量存在,父类相当于封装在自己的类定义下,代码的变异不容易扩散。

Golang提供了interface,这也是C语言没有的。另外Golang的interface更加易用,不需要显式指定一个类实现了哪个interface,只要是一个类实现了interface中的所有方法,他就可以被认为是这个interface的实现。至于封装,很简单,首字母大写的全局变量,方法和全局函数是公有的,而首字母小写的全局变量,方法和全局函数是包内可访问的。

Golang中的复合类型主要有数组,slice,map和结构体。数组是只读的list,slice则是可变的list,map提供了键值对的结构,结构体是数据的打包。可以看到,Golang没有其他语言的各种容器,泛型等,但是提供的复合类型却足够使用。

Golang被极度赞扬的是它的异步机制(goroutine),如何启动一个异步处理,很简单,将处理封装到函数,然后调用函数时在前面加上go。除了语法上的简洁,goroutine是一个协程,比线程更节省资源,一个线程中可以有多个协程,而且goroutine被分配到多个CPU上运行,是真正意义上的并发。


C vs Rust

在某些方面,Rust是对C和C++创建的内存管理难题的回应,也是对这两种语言的许多其他缺点的回应。Rust编译为本机机器代码,因此就性能而言,它被认为与C相当。但默认情况下,内存安全才是Rust的主要卖点。

Rust的语法和编译规则可帮助开发人员避免常见的内存管理错误。如果程序有一个不符合Rust语法的内存管理问题,它就不会被编译。刚接触这种语言的新手,特别是以前用C语言的开发者,由于C语言为这类bug提供了充足的容错空间,所以他们接触Rust的第一步是学习如何安抚编译器。但Rust的支持者认为,这种短期的痛苦有一个长期的回报:更安全的,不会减缓速度的代码。

Rust还通过其工具改进了C语言。默认情况下,项目和组件管理是Rust提供的工具链的一部分,与Go相同。有一种默认的,推荐的方法来管理包,组织项目文件夹,以及处理C需要单独处理的其他许多事情,每个项目和团队以不同的方式处理它们。然而,在Rust中被吹捧为优势的东西对于C开发者来说可能并没有太大吸引力。Rust的编译时安全功能无法禁用,因此即使是最小的Rust程序也必须符合Rust的内存安全限制。默认情况下,C可能不太安全,但在必要时它更灵活,更宽容。

另一个可能的缺点是Rust语言的大小。即使考虑到标准库,C的功能也相对较少。Rust功能集非常庞大并且还在不断增长。与C++一样,较大的Rust功能集意味着更强大的功能,但也意味着更高的复杂度。C是一种较小的语言,但更容易在头脑中进行建模,因此可能更适合那些对Rust来说太小,不值得大动干戈的项目。

C语言为何值得去学

1.嵌入式领域,C语言依然是首选语言,嵌入式并没有因为其他上层语言的发展而没落,现在嵌入式依然还在其自身的领域展现强大的生命力。手机,电视机,机顶盒,空气净化器等等电子产品都是其领域范畴,从长远看短时间内不可能被消失。而且智能机器人的崛起,C语言的使用频率又开始加大了。

2.操作系统内核代码还是C语言为主打,就语言的灵活性以及执行的效率来看C语言还是最合适的语言,而且在系统层次的代码,C语言还是首选语言。而且现在很多流行语言的底层绝大部分的C语言构建完成。从这个层面讲C语言是永远不会过时的,顶多算是应用范围变窄,但其作用依然强大。

3.C语言的职位比例相对应用级语言是低了点,但是整个软件行业在发展,绝对的C语言编程职位并没有减少。而且对于有志于成为架构师层次的程序员来说,C语言还是必修课,构建软件框架还是需要对底层有所了解。退一步来讲,即使觉得C语言方面的职位比例低一些,不好找工作,可以先从C语言入手,把自己的知识体系建立起来,编程语言属于工具范畴,熟悉一种工具的使用,很容易触类旁通,切换到别的语言也相对轻松些,而且给整个职业生涯起了个好头,打好了基础为更上一层楼做足准备。C语言之所以流行这么年,生命力这么旺盛和本身鲜明的高效,方便灵活挂钩。即使在上层语言使用概率变低,并不妨碍在系统级别继续发挥作用。任何一种语言都有其存在的社会价值所在。C语言还是值得作为入门语言深刻的学习。

4.C/C++程序员的收入没有受到影响,依据100offer的后台数据显现,现在经过100offer入职的程序员年薪最高达47W,最低22.4W,C/C++程序员的收入与其它编程言语的岗位相比处于相等状况,没有呈现下风。跟着C++逐步变成某些特定公司和特定项目所需的言语后,高档C++程序员的收入也会更具有竞争性。

5.在整个游戏产业和嵌入式上依然是主流。首先是游戏范畴,Milo Yip表示——程序员有必要运用C++结构/库,如大多数游戏引擎(如Unreal/Source)及中间件(如Havok/FMOD),尽管有些C/C++库供给别的言语的绑定,但通常原生的API性能最佳、最新。

其次在东西范畴上,无论是网络安全仍是杀毒软件,C/C++仍是干流语言。从应用范畴来说,C/C++适用于高性能计算、嵌入式体系、开发服务器软件、游戏、实时体系等,所以短期内能完全代替C++言语并不存在。C/C++在体系、图形、网络等许多范畴都是较难代替的,它的光芒年月让它的逝世速度得以减少。当某一种编程言语在市场需要显着比另一种言语更强时,强需要言语中的缺陷则简单被淡化,阑珊的言语则不断被人挑出致命硬伤。这种编程言语之间的比较并不公平,且没有意义。

编程言语都是用来表达思想和完结需要的东西,跟着年代的开展,不一样言语在不一样范畴都做出了取舍,代替尽管存在,但不是必定景象。C/C++的需要跟着年代开展会不行避免地越来越少,但不行能完全不见,C/C++程序员也具有不行代替性,在市场上照旧有着竞争性。


C 语言的不败秘诀

生于豪门的C

贝尔实验室作为20世纪最伟大的实验室之一,有着“诺奖摇篮”之称,被人们认为是改变人类命运的地方,里面诞生过 9 项诺贝尔奖、5 项图灵奖。从贝尔实验室走出来的发明今时今日仍然影响着我们的生活:数据型网络、晶体管、移动电话技术、太阳能电池、激光、语音信号数字传输、无线电天文学、UNIX 操作系统,还有今天我们的主角——C语言。

1983年获得图灵奖的肯·汤普逊和丹尼斯·里奇就曾在贝尔实验室共事颇久,创造出许多在计算机科学中发挥着无可替代作用的东西。

1969年,肯·汤普逊为了在 Multics 系统被贝尔实验室放弃后能够继续玩自己的“Space Travel”游戏,他找到了一台老式的 PDP-7 机器,重写了这个游戏。移植好游戏之后,肯·汤普逊着手把自己的一些工具扩充成为一个完善的操作系统。

他和丹尼斯·里奇带着团队实现了文件系统、进程、设备文件、命令解释器和一些小的工具程序,并在布莱恩·柯林汉的建议下将这个系统命名为“UNIX”。在基础开发完成之后肯·汤普逊创造了B语言,应用于系统级的编程活动。但因为该语言过于简单、移植性差的原因,丹尼斯·里奇在这个基础上于1972年开发出了更高效、简洁、易于移植的C语言——伟大的C语言就此诞生。

1973年,UNIX 使用C语言重写。之后这两个伟大的发明便相辅相成,在计算机世界大放异彩。UNIX 系统因为用C语言重写变得效率,更容易移植,很快便流行起来。C语言因为重写了 UNIX 而发展,它也成了系统程序设计的完美选择。

无处不在的C

时至今日,C语言仍然是编程语言中的主流,50年的时光变迁没有让它被遗留在历史中,而是随着时代一同往前。它不仅不是一个“老古董”,随着人工智能、5G等新技术的发展,C语言更在其中发挥着巨大的作用。

强大的操作系统

众所周知,很多操作系统都离不开C语言,比如较早的 UNIX,LINUX;而Windows 部分内核,移动端的Andriod、iOS、鸿蒙系统的内核都是用C写的。每台计算机设备都离不开一个基础系统,要在系统上运行各种应用程序,也就离不开 C 语言。可以说,C语言连接了汇编到 Java、C++等应用层面编程语言,它在计算机体系中占据了一个不可或缺的位置。

编程语言的基础

正是因为它处于系统层面上,所以很多编程语言的编译器或解释器直接使用的就是C语言,并且大量其他编程语言的库都支持C语言,在这样一个日积月累的过程中,C语言就变成了很多其他编程语言的基础。

丰富的应用程序

而在应用层面上C语言也是大放异彩,诸多软件工具、游戏、动画、电影制作等都与C语言紧密相关。比如全世界都在用的图像软件 Adobe Photoshop、数据库 MySQL、Google Chrome 浏览器等。在数百万读者选择的C语言入门书籍《C Primer Plus 第6版 中文版》中作者史蒂芬•普拉达(Stephen Prata)用这张图描述C语言的应用:


史蒂夫·普拉达对C应用的描述

而现在,随着人工智能、物联网等行业的兴起,C语言有了更多的用武之地:


现在C的应用更广了

可以看到,C语言渗透到了几乎所有的计算机领域,与生产生活相关性极大。

与时代同行的C

不过,C语言作为一个已步入“知天命”阶段的编程语言,一直更新的语言标准才是它具有如此强大生命力的重要原因。其标准从最初的 K&R C,到ANSI C 和 ISO C,再到美国国家标准协会(ANSI)发布的 C89 和C90,9年之后又发布了 C99,到现在较为普遍支持的 C11,经过了数次发展。在2018年,C语言新的标准 C17(也叫C18)发布,被用来替代 C11。新标准没有增加新特性,只是对 C11 进行补充和修正。未来,处于草案阶段的 C2x 将成为新的标准,会引入新的特性,拭目以待。

在计算机体系中有如此超然的地位,如果标准一直不变,是必定跟不上技术的发展而被淘汰的——C语言则做到了与时代同行。在较为广泛使用的 C11 标准中,引入了几个有用且关键的特性,比如:字节对齐说明符、泛型机制(generic selection)、对多线程的支持、静态断言、原子操作以及对 Unicode 的支持。

主要内容是:
·对齐处理(Alignment)的标准化(包括_Alignas标志符,alignof运算符,aligned_alloc函数以及头文件)。
·_Noreturn 函数标记,类似于 gcc 的 __attribute__((noreturn))。
· _Generic 泛型宏关键字。
·多线程(Multithreading)支持,包括:
_Thread_local存储类型标识符,头文件,里面包含了线程的创建和管理函数。
_Atomic类型修饰符和头文件。
·增强的Unicode的支持。基于C Unicode技术报告ISO/IEC TR 19769:2004,增强了对Unicode的支持。包括为UTF-16/UTF-32编码增加了char16_t和char32_t数据类型,提供了包含unicode字符串转换函数的头文件.
·删除了 gets() 函数,使用一个新的更安全的函数gets_s()替代。
·增加了边界检查函数接口,定义了新的安全的函数,例如 fopen_s(),strcat_s() 等等。
·增加了更多浮点处理宏。
·匿名结构体/联合体支持。这个在gcc早已存在,C11将其引入标准。
·静态断言(Static assertions),_Static_assert(),在解释 #if 和 #error 之后被处理。
·新的 fopen() 模式,(“…x”)。类似 POSIX 中的 O_CREAT|O_EXCL,在文件锁中比较常用。
·新增 quick_exit() 函数作为第三种终止程序的方式。当 exit()失败时可以做最少的清理工作。


而Linux 表示其内核也会在 5.18 版本中将所使用的 C 标准升级到C11,5.18稳定版将在5月底发布。

持续发展,并且是“有用”的,那么它便会一直走在时代前头,不会落伍。正是因为C语言在计算机领域的应用如此广泛,并且有着强大的生命力,它才会成为绝大多数大学计算机课程的必学编程语言,也是无数入行计算机新人的首选语言。


50岁的C语言与两位巨人的故事

贝尔实验室特别人员奖、美国计算机协会(ACM)的图灵奖、汉明勋章、计算机先驱奖、计算机历史博物馆研究员、哈罗德 · 潘德奖……这些成就全都出自一人,那就是编程界无人能超越的传奇人物也是C语言的创造者——丹尼斯·里奇。


C语言之父:丹尼斯·里奇

计算机历史学家Paul E.Ceruzzi说:里奇不被人们之道。他的名字一点都不家喻户晓,但是如果你有一台显微镜,能在电脑里看到他的作品,你会发现处处都是他的作品。克尼汉也曾如此评价:“牛顿说他是站在巨人的肩膀上,如今,我们都站在里奇的肩膀上。”



1941年,丹尼斯 · 里奇出生在纽约布朗克斯区,父亲是是贝尔实验室的交换系统工程师。里奇从小成绩优异,大学顺利进入了哈佛,在受父亲的影响下,丹尼斯也走上了科学研究之路。

在哈佛读书期间,一次偶然的机会改变了里奇的一生。里奇参加了哈佛计算机系统相关的讲座,从此他开始对计算机疯狂着迷,不仅专门学了一期课程。当时的里奇是一个主修物理的学生,因为对计算机处理的理论和实际问题十分着迷,他在毕业论文中大部分和计算机理论有关(递归函数的层次),这还远远不够,里奇开始花更多的精力在实践上面。

在那个时代,大部分计算机体积十分庞大,占用了整个房间并且还只能进行有限的拨入访问,因此攻克小型台式计算机是当时的工程师们的目标,可是这些计算机没有易于使用的操作系统,于是里奇决定自己做一个。这一决定立即得到了麻省理工学院Honeywell和General Electric的支持。里奇负责多道处理机BCPL语言和GE650的编译器,它们都是属于GECOS系统的。同时,他还写了ALTRAN语言的代数编译器,那视符号计算机的一种语言和系统。

经过这个项目后,里奇毅然决然的放弃了本专业物理学,并决定将计算机作为他的事业。1967年,他加入了贝尔实验室(Bell Labs)。在加入贝尔实验室后,里奇开始和实验室的一位名为Ken Thompson(肯·汤普森)的成员合作。这位Ken Thompson也是对Ritchie 职业生涯影响很大的人。

20世纪70年代,汤普森和里奇在研究如何让早期小型机变得越来越受欢迎。他们认为,所需要的是各种计算机之间更简单,更可行的交互。因为老型计算机要求用户使用操作系统来复制,删除,编辑和打印数据文件,将数据从磁盘移动到屏幕到打印机并返回磁盘进行存储。除了少数专家之外,一旦没有了操作系统,任何人都无法访问计算机。为此,他们花了几个月的时间来提出解决方案,他们完成这个解决方案时已经编写好了影响他们一生的UNIX操作系统。


里奇在1999年的一次采访中表示:“我觉得Linux发展的现象令人高兴,虽然工作站和大型计算机厂商也在提供不同种类的BSD系统,但是在Unix的直接派生品中,Linux应该是最健全的了。”

C++的开发者和设计师比雅尼 · 斯特劳斯普曾说:“假如里奇决定那十年将他的精力花费在稀奇古怪的数学上,那么Unix将胎死腹中。”

事实上,里奇加入贝尔实验室后,发展了C语言和Unix系统,这在电脑工业史上都占据重要的席位。C语言在发展软件和操作系统时是一个非常常用的电脑语言,而现在的编程语言比如C++、C#、Obijective-C、Java和JavaScript拥有极大的影响。


Univac I

1973年,里奇以B语言为基础发展出C语言,在它的主体设计完成后,他和汤普森就用它来完全重写了Unix。Unix最开始是用汇编语言编写的,里奇和汤普森重写了之后于1974年在ACM上发表,正式向外界披露Unix系统。随着Unix的发展,C语言也得到了不断地完善。C语言是一门面向过程地、抽象化的编程语言,广泛应用于底层开发。C语言能用简易的方式编译、处理低级存储器。如此简单,简洁,几乎每个计算机制造商都转向了它,且效果显著。

为了利于C语言的全面推广,很多专家学者和硬件产商联合组成了C语言标准委员会。于是在1989年,第一个完备的C标准诞生了,简称“C89”,截至目前,最新的C语言标准为2017年发布的“C17”。尽管C语言已经如日朝天,但里奇的职业生涯并没没有因此而结束,他于1990年成为朗讯科技计算技术研究部门的领导者。在该职位上,他编写了应用程序并管理已发布的操作系统的增长。

1983年,因为发展了通用操作系统理论并实现了UNIX操作系统,里奇和汤普森二人一起获得了图灵奖。里奇的图灵奖论文题目为《对软件研究的反思》。1990年,二人因“创造UNIX操作系统和C程序设计语言”而获得了IEEE颁发的IEEE汉明奖,1997年获计算机历史博物馆研究员奖,2005年,美国工业研究院授予里奇 IRI成就奖,以表彰他对计算机科学技术做出的贡献,以及UNIX操作系统对社会的广泛影响。2011年,里奇和汤普森二人共同获得了日本国际奖。

但在2011年10月12日,里奇离开了这个世界,离开了他付出一生的C语言和Unix世界,享年70岁,去往另一个地方开始了他的另一场旅行……



从计算机发展以来,编程语言也是层出不穷,但是无论多少“新人”翻涌而出,都无法改变C语言在编程界中德高望重的地位。

C语言到底能做了多少事情?大家经常说的Linux操作系统的内核都是C语言写的,对应的很多嵌入式内核驱动也跑不出C语言范畴,包括大家常用的手机,机顶盒,电视机底层硬件驱动基本上都是C语言完成。可以毫不夸张的说,如果没有C语言,就没有微软的Windows 10 和 Surface Book,也没有安卓智能手机,更没有乔布斯创造的苹果帝国各种产品MAC、iPad。

C语言最牛的地方,几乎现在所有的上层语言的底层语言绝大部分都是C语言大哥做嫁衣给铺垫完成。深刻理解上层语言底层实现,离不开C语言。而且很多大学的计算机专业都会把C语言作为学生入门编程的第一步。因此,很多程序员都把学习C语言当成程序生涯中最基本的事。而C语言为什么能成为最重要、最流行的编程语言之一,这个疑问从斯蒂芬 · 普拉塔在《C Primer Plus第六版》中给我们总结出的C在众多语言中脱颖而出的优点中就能够得到答案。

设计特性
C 语言融合了计算机科学理论和实践的控制特性。C 语言的设计理念让用户能轻松地完成自顶向下的规划、结构化编程和模块化设计。因此,用 C 语言编写的程序更易懂、更可靠。

高效性
在设计上,它充分利用了当前计算机的优势,因此 C 程序相对更紧凑,而且运行速度很快。

可移植性
C 是可移植的语言。这意味着,在一种系统中编写的 C 程序稍作修改或不修改就能在其他系统运行。如需修改,也只需简单更改主程序头文件中的少许项即可。

强大而灵活
C 语言功能强大且灵活。功能强大且灵活的 UNIX 操作系统,大部分是用 C 语言写的。C 程序还可以用于解决物理学和工程学的问题,甚至可用于制作电影的动画特效。

面向程序员
C 语言是为了满足程序员的需求而设计的,程序员利用 C 可以访问硬件、操控内存中的位。C 语言有丰富的运算符,能让程序员简洁地表达自己的意图。



C语言获得如今的成就离不开无数个“里奇”的付出,除了里奇今天还有给大家介绍另一个伴随C语言成长的老人,他为C语言编写了许多经典著作,为无数程序员搭起了攀上巨人肩膀的长梯。他就是斯蒂芬 · 普拉塔,他在美国加州肯特菲尔得的马林学院教授天文、物理和计算机科学。他也是《C Primer Plus》的作者。他编写的《New C Primer Plus》获得了计算机出版联合会1990年度最佳How-to计算机图书奖,《C Primer Plus》获得了计算机出版联合会1991年度最佳How-to计算机图书奖提名。

《C Primer Plus》是程序员学习C语言入门到精通的“宝藏”,如果提到C语言学习,很多人想到的第一本书都会是《C Primer Plus》。


《C Primer Plus第六版》


《C++ Primer Plus》


在《C Primer Plus第六版》中,斯蒂芬 · 普拉塔把编写 C 程序的过程分解成 7 个步骤。让C语言的学习更加简洁和有序,同时通俗易懂,读者阅读时很容易明白而且上手。


这7步怎么用呢?斯蒂芬 · 普拉塔也给我们做了详细的解释。

第 1 步:定义程序的目标
主要是说在动手写程序之前,要在脑中有清晰的思路。想要程序去做什么首先自己要明确自己想做什么,思考你的程序需要哪些信息,要进行哪些计算和控制,以及程序应该要报告什么信息。

第 2 步:设计程序
对程序应该完成什么任务有概念性的认识后,就应该考虑如何用程序来完成它。例如,用户界面应该是怎样的?如何组织程序?目标用户是谁?准备花多长时间来完成这个程序?除此之外,还要决定在程序中如何表示数据,以及用什么方法处理数据等。

第 3 步:编写代码
设计好程序后,就可以编写代码来实现它。也就是说,把你设计的程序翻译成 C 语言。一般而言,使用文本编辑器创建源代码文件。该文件中内容就是你翻译的 C 语言代码。

第 4 步:编译
大家应该知道,C 编译器负责把 C 代码翻译成特定的机器语言。此外,C 编译器还将源代码与 C 库的代码合并成最终的程序。其结果是,生成一个用户可以运行的可执行文件,其中包含着计算机能理解的代码。编译器还会检查 C 语言程序是否有效。如果 C 编译器发现错误,就不生成可执行文件并报错。

第 5 步:运行程序
在常见环境中运行程序要输入可执行文件的文件名,而其他环境可能要运行命令或一些其他机制。例如,在 Windows 和 Macintosh 提供的集成开发环境(IDE)中,用户可以在 IDE 中通过选择菜单中的选项或按下特殊键来编辑和执行 C 程序。最终生成的程序可通过单击或双击文件名或图标直接在操作系统中运行。

第 6 步:测试和调试程序
程序能运行是个好迹象,但有时也可能会出现运行错误。接下来,应该检查程序是否按照你所设计的思路运行。你会发现你的程序中有一些bug,可能忽视了输入检查导致程序瘫痪,可能会把圆括号放错地方,可能误用 C 语言或打错字,等等。

第 7 步:维护和修改代码
创建完程序后,你发现程序有错,或者想扩展程序的用途,这时就要修改程序。例如,用户输入以 Zz 开头的姓名时程序出现错误、你想到了一个更好的解决方案、或者要修改程序使其能在不同的计算机系统中运行等等。如果在编写程序时清楚地做了注释并采用了合理的设计方案,这些事情都很简单。



在“巨人们”的努力下,C语言在编程界成了无法替代的传奇。如这本伴随着无数C语言伙伴成长的图书也成为编程语言图书中的经典,而斯蒂芬 · 普拉塔却成了年过七旬的老人了。

知道斯蒂芬 · 普拉塔与C语言的缘分是怎么开始的吗?其实他最初接近C语言是为了用程序作为技术支撑,便于天文学的研究。他原本就是天文学、物理学的大学教授,是美国天文学会成员,研究计算机语言之后不仅实现了自己的需求,还顺便出版了好几本畅销计算机图书。更巧合的是,C语言之父丹尼斯 · 里奇其实也是专攻物理学,在哈佛读书期间,偶然参与了计算机系统相关的讲座,从此便迷上了编程这个“小妖精”,在毕业之后更是决定将计算机作为他的事业。

斯蒂芬 · 普拉塔刚接触C语言的时候,当时的使用率并不高,市面上的教材稀缺,为了让自己热爱的天文事业能够有所发展,便苦心专研,当他逐渐掌握C语言是时,突然想到,世界上还有许多人因为爱好C语言却没有足够的材料支撑,于是他开始创作友好、方便使用、便于自学的指南,从此我们所了解的《C Primer Plus》系列便因此而诞生了!



C语言经典书单

入门书如何选?首先它不能太厚,动辄上千页可以垫显示器那种完全会直接把人吓跑。其次它内容不能过于表面,这会让新手整个人都处于一种云雾笼罩的状态,对于那些知识全都是似懂非懂,效果很差。

那么,作为C语言入门图书,小异推荐这本整体内容深入浅出、翔实而不累赘的《C Primer Plus 第6版 中文版》成为百万C语言编程入门必选。

作为畅销了40多年的经典C语言入门图书,《C Primer Plus 第6版 中文版》得到了一代又一代的C语言程序员的验证。随着C语言的不断更新,本书也不断再版,以适应新的需求。国内无数读者选择了这本书之后,便立马抛弃了以前那些老旧落伍的教材,真正在本书的引导下无压力畅游C语言编程。作者使用风趣的语言与简洁的图示与代码把那些“高深莫测”的概念与方法娓娓道来,把它们剖析成一个个简单的内容模块让读者较为轻松地理解和掌握。而且,作者不仅告诉读者什么是正确的,更告诉读者什么是错误的,把那些错误的操作展示出来,让读者全方位地去认识和理解C语言。为什么正确?为什么错误?错误会带来哪些问题?这些作者都会详细而清楚地讲解出来。

动手是编程最重要和最快的学习方式,本书中带有大量的示例代码,并且针对C11进行了更新,读者可以照着示例代码自己键入到编辑器中,得到真实确切的运行结果。在操作的过程中,真正去理解代码的作用与对应的知识内容。更重要的是,本书还提供了配套的电子版和在线练习实验环境,轻松方便!此之外,本书的所有章节都附带了对应的习题,读者在学完一章内容之后,可以利用习题检验自己的学习进度,看自己对于这部分内容掌握程度如何。

编程入门,学与做同样重要,缺一不可。当你使用上面两本书真正入门C语言,并且可以熟练地编写C代码之后,就会面对到新的问题——过分依赖if语句,导致代码自己都看不懂;符号使用不规范,找bug半小时后才发现是一个分号用错了等等。这时候,你就需要这本《C 陷阱与缺陷》的帮助了。

《C陷阱与缺陷》


避开C语言编程中间的所有障碍

当然,这本书不是在说C不好,或者说它缺点多,而是用以指导程序员避开在使用C语言进行编程开发过程中容易中的陷阱。

本书作者安德鲁·凯尼格是世界级C编程大师,他是AT&T公司Shannon实验室大规模编程研究部门中的成员,同时也是C++标准委员会的项目编辑,编程经验超过30年。

在多年的使用C语言过程中,他自己和他人都在遇到过各种陷阱与障碍,针对那些问题,在贝尔实验室的时候他发表过一篇论文。后来,他以这篇论文为基础,结合自己的工作经验扩展了那些问题与解决方法,写成了这本指导C语言程序员避开陷阱与障碍的珍贵指南,成为经典。在这本书中,读者可以通过阅读和理解作者在词法陷阱、语法陷阱、语义陷阱、链接、库函数、预处理器、可一致性缺陷等方面的问题讲解,看它们是如何在实际编程工作中发生,又该如何解决的。通过阅读本书,读者可以发现大部分C语言编程过程中可能会遇到的问题,并从书中得到有价值的解决方案。解决这些问题的同时,很多人会遇到头疼的“指针”。

《C和指针》

在很多C程序员间流传这样一句话:不会用指针就不会 C 语言。指针是C语言中精髓!想要学好C语言,指针这关必须要过去。小异推荐的这本《C和指针》,就是专门为解决指针问题的。


指针难?一本书解决!

这本书的作者是肯尼斯·里科(Kenneth Reek),他是美国Rochester工业学院计算机系教授,有着20年的C编程教学经验。

作者在书中将各种指针用法都写出来了,甚至连一些堪称奇葩的指针用法也没有放过,以简洁准确的文字、配合贴切的图示来进行讲解,让广大读者对指针和数组有了更加深刻的认识,特别是多维数组、指针的指针、指向数组的指针和指针的数组这些平日里让人头疼的知识点。

针对这些问题,作者提供了非常多的编程技巧与提示,帮助程序员把指针的强大功能融入到自己的程序中去,真正地掌握指针。同样的,针对每一个问题,章节后都有丰富的练习题,而练习题的答案就在附录之中。有这本书加持,大家在学习C语言过程中的所有指针问题会迎刃而解!逐渐成为一个真正的C语言程序员!当然,每一个程序员都不甘平凡,都想有着更为精进的C编程技巧和方法,成为一个真正的C编程专家!这时候,就轮到这本《C专家编程》出场了。

《C专家编程》

趣话C编程!你也可以是专家

顾名思义,这本书就是为了让你从一个普通C语言程序员成为一个C语言编程专家的。

本书作者彼得·范德林登是一名技术专家和技术作家。他曾在Sun公司和苹果公司工作多年,并曾任摩托罗拉公司首席Android技术布道师、Immersion公司首席Android开发布道师,后在硅谷担任技术顾问。

这本书中充满了各种关于C语言的奇闻轶事,还有曾出现各种匪夷所思的bug,读者在读这本书的时候可以从那些趣事中了解C语言是如何产生并发展的,从一件件切实好玩的案例中真正认识和理解数组、指针到底该怎么用。作者使用幽默的语言和有趣的故事向读者展示了一个充满魅力的C语言世界,认识到学习C语言编程并不是那么枯燥和无聊。作者还从编译器的角度解释C语言的“奇葩设计”,并且把那些先驱和大师在这个过程的探索与抉择描绘得无比精彩生动,读起来非常有意思。正是以这种非常规形式书写,这本书获得了无数读者的喜爱,在豆瓣上大家对它称赞有加,给予了9.2分的高度评价!


很多读者喜爱本书作者的风趣幽默

很多读者在读完之后都给出了诸如“作者太有趣了”、“技术八卦很有趣”、“八卦与技术结合,作者俏皮”之类的评价。在读这本书的时候同时获得了技术思想的提升和八卦之心的满足,读来毫不费劲、趣味十足。当然该书不仅仅讲了C语言特性、声明、数组、指针、链接、运行时、内存的内容,还对如何学习C++进行了深入而专业的分析,给出了细致的建议。使用本书,不仅仅可以成为C语言编程专家,还能够开始学C++,一举两得!