你所不知道的C语言:开发工具和规格标准

“If I had eight hours to chop down a tree, I'd spend six hours sharpening my axe.” – Abraham Lincoln

「如果我有8 小时可以砍1 棵树,我会花6 小时把斧头磨利。」(类似汉语「工欲善其事,必先利其器」的精神) – 亚伯拉罕.林肯

语言规格:
C89/ C90 -> C99 -> C11 -> C17/ C18 -> C2x

C 语言老爸的评论

「C 很别扭又缺陷重重,却异常成功。固然有历史的巧合推波助澜,可也的确是因为它能满足于系统软件实践的程序语言期待:既有相当的效率来取代组合语言,又可充分达到抽象且流畅,能用于描述在多样环境的演算法。」

C is quirky, flawed, and an enormous success. Although accidents of history surely helped, it evidently satisfied a need for a system implementation language efficient enough to displace assembly language, yet sufficiently abstract and fluent to describe algorithms and interactions in a wide variety of environments. —— Dennis M. Ritchie

你所不知道的C语言:开发工具和规格标准

为什么我不探讨C++

  • 在台湾发文好像爱用「为什么我不」开头,后面可接「念研究所」、「待在大公司」等描述
  • C++ 自称为面向对象的程序语言,却不愿意对对象在执行时期的表现负责任

若说C 语言给了你足够的绳子吊死自己,那么C++ 给的绳子除了够你上吊之外,还够绑住你身边的朋友

相较之下,Java 让你在吊死自己之际仍有亲友监视着,虽然死不了,但事后会更想死

In Ruby, everything is an object.

In Clojure, everything is a list.

In Javascript, everything is a terrible mistake.

in C, everything is a representation (unsigned char [sizeof(TYPE)]).

  • Linus Torvalds 在2010年的解释(下划线是个链接)

https://www.realworldtech.com/forum/?threadid=104196&curpostid=104208

  • C++ 实际上已经是截然不同的程序语言

C++老爸Bjarne Stroustrup的文章: “ Learning Standard C++ as a New Language ”

文章链接地址:http://www.stroustrup.com/new_learning.pdf

  • 最重要的是,C++ 改版飞快,C++ 17 即将推出,但我还没看懂C++ 98
你所不知道的C语言:开发工具和规格标准

延伸阅读

  • 没有C语言之父,就没有Steve Jobs ( 原文 )

https://buzzorange.com/techorange/2015/10/19/without-dennis-ritchie-there-would-be-no-steve-jobs/

这个链接已无法打开

  • 第一个C 语言编译器是怎样编写的?

https://www.csdn.net/article/2015-11-27/2826350

读规格书可大幅省去臆测

在ISO/IEC 9899 (aka C99 Standard)中5.1.2.2.1内有提到C Standard要求main函数必须这样写

<code>int main(void) { /* ... */};/<code>

或者:

<code>int main(int argc, char *argv[]) { /* ... */ };/<code>

C++之父Bjarne Stroustrup的个人网页内有个FAQ里面有个问题叫Can I write “void main()”?

然而在C++与C的标准中从来没出现过这样的写法,也就是说,void main()这个写法从来没正确过

延伸阅读

  • C 语言中int main() 和void main() 有何区别?(https://www.zhihu.com/question/60047465)
  • C++ 的void main() / int main() … 不要再用void main() 了! | Peter Dave Hello's Blog(https://www.peterdavehello.org/2014/10/void-main-int-main-in-c-and-cpp/)
  • void main(void) - the Wrong Thing(https://www.ty-penguin.org.uk/~auj/voidmain/)

ISO/IEC 9899 (简称“C99”)

  • 从一则笑话谈起“Programming in C: if it doesn't work, just add a star. Or multiple stars. Or ampersands.”
  • 规格书 (PDF)搜寻“ object ”,共出现735处(http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1256.pdf)

搜寻“ pointer ”,共出现637处。有趣的是,许多教材往往不谈object,而是急着谈论pointer,殊不知,这两者其实就是一体两面

object != object-oriented,前者的重点在于「数据表达法」,后者的重点在于“everything is object”

C11 ( ISO/IEC 9899:201x ) (http://port70.net/~nsz/c/c11/n1570.html)

  • 从第一手资料学习:大文豪写作都不免要查字典,庸俗的软件开发者如我们,难道不需要翻阅语言规格书吗?难道不需要搞懂术语定义和规范吗?
  • & 不要都念成and,涉及指针操作的时候,要读为“address of”

C99 标准[6.5.3.2] Address and indirection operators 提到'&' address-of operator

  • C99 [3.14] object

region of data storage in the execution environment, the contents of which can represent values

在C 语言的物件就指在执行时期,数据储存的区域,可以明确表示数值的内容

很多人误认在C 语言程序中,(int) 7 和(float) 7.0 是等价的,其实以数据表示的角度来看,这两者截然不同,前者对应到二进位的“111”,而后者以IEEE 754 表示则大异于“111”

你所不知道的C语言:开发工具和规格标准

A pointer to void shall have the same representation and alignment requirements as a pointer to a character type.

关键描述!规范void *和char *彼此可互换的表示法

<code>void *memcpy(void *dest, const void *src, size_t n);/<code>
  • C99 规格书的解释就比很多书本清楚,何必舍近求远呢?

EXAMPLE 1 The type designated as float *has type "“pointer to float'”. Its type category is pointer, not a floating type. The const-qualified version of this type is designated as float * constwhereas the type designated as " const float *is not a qualified type — its type is "“pointer to const qualified float'” and is a pointer to a qualified type.


EXAMPLE 2 The type designated as " struct tag (*[5])(float)has type “array of pointer to function returning struct tag'”. The array has length five and the function has a single parameter of type float. Its type category is array.

  • Understand more about C提及若干起因于不同的C语言标准,而使得程序码行为不同的案例(https://www.slideshare.net/YiHsiuHsu/understand-more-about-c)

规格不能只看新的,过往也要熟悉

你所不知道的C语言:开发工具和规格标准

  • 空中巴士330 客机的娱乐系统里头执行14 年前的Red Hat Linux,总有人要为「古董」负责
  • 而且空中巴士380客机也是如此

为何C 语言标准函式库里头的函式名称如此简短?像是

  • strcpy
  • strlen

最初链接器有6 到8 个字符的输入限制!

Translation limits
6 significant initial characters in an external identifier

  • 延伸阅读

Why did ANSI only specify six characters for the minimum number of significant characters in an external identifier?(https://stackoverflow.com/questions/38035628/c-why-did-ansi-only-specify-six-characters-for-the-minimum-number-of-significa/38042724#38042724)

Identifier(https://en.cppreference.com/w/c/language/identifier)

The Design and Evolution of C++一书对应的解说:(http://www.stroustrup.com/dne.html)

你所不知道的C语言:开发工具和规格标准

英文很重要

安装cdecl程式,可以帮你产生C程序的声明。

<code>$ sudo apt-get install cdecl/<code>

使用案例

<code>$ cdecl
cdecl> declare a as array of pointer to function returning pointer to function returning pointer to char/<code>

会得到以下输出:

<code>char *(*(*a[])())()
/<code>

把前述C99 规格的描述输入,可得:

<code>cdecl> declare array of pointer to function returning struct tag
/<code>
<code>struct tag (*var[])()/<code>

如果你没办法用英文来解释C 程序的声明,通常表示你不理解!

cdecl 可以解释C 程序声明的意义,比方说:

<code>cdecl> explain char *(*fptab[])(int)
declare fptab as array of pointer to function (int) returning pointer to char/<code>

只用printf 观察数据,有问题吗?

你所不知道的C语言:开发工具和规格标准

  • 只用printf()观察的话,永远只看到你设定的框架(format string)以内的数据,但很容易就忽略数据是否合法、范围是否正确,以及是否看对地方
  • printf()大概是最早被记下来的函数,也困扰很多人,有意思的是,1960年代初期MIT开发的CTSS作业系统中,终端机命令就包含了printf,后者一路从Multics和Unix继承至今
  • 在CTSS原始程序源码com3中可见到这行STMTDC PRINTF,11,T,T25,前一行注解写“The following tables are the dictionaries of statement types”

不要急着打印数据,善用GDB

  • 「学会了GDB,我有种山顶洞人学会用火的感动」 – 张至
  • GDB Rocks!
  • Introduction to gdb
  • Debugging with GDB
  • 除错程序: gdb
  • 透过GDB 学习C 语言

说明:关于GDB这些地址的正常情况下,链接都已经无法访问,所有这里的链接地址我暂时不贴出来


除了Vim,我推荐Visual Studio Code

  • Microsoft 开源Visual Studio Code (VS Code)


分享到:


相關文章: