免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 15746 | 回复: 114
打印 上一主题 下一主题

[C++] 关于C语言,我喜欢和讨厌的十件事 [复制链接]

论坛徽章:
2
2015年辞旧岁徽章
日期:2015-03-03 16:54:152015年迎新春徽章
日期:2015-03-04 09:55:28
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2013-11-13 10:18 |只看该作者 |倒序浏览
  
前言:最近有个家伙抱怨道“为什么我还要再用C?”-虽然我不同意他的说法,但至少他随口提到如果你“在一台拇指大小的电脑”上编程,或者为一门语言写引导程序,那么可以用C语言。要我说,写设备驱动,或者特定平台的内核,不管怎么说都可以使用C。
几年之前,我用C语言写下了我的第一个网络程序,但我并不推荐这么做。现在,我只用P打头的,尤其是P-y打头的语言写网络程序(译者注:绕什么圈子,不就是Python嘛…)。但在当时,我刚从DOS和TSRs的世界中出来,在那儿用上10KB的RAM我都会觉得大得惊人。

现在我是一名Web开发者,但是仅限于晚上。白天我为嵌入式微处理器编写固件,因此,C依旧是我所选择的语言。我所说的微处理器是那种嵌入烤面包机,或者其他类似设备中的处理器,只有大概64KB的代码空间以及2KB的RAM。因此,可供选择的语言基本上就只有汇编和C了。(也可以是Forth,不过那是另外的故事。)

然后,我渐渐发现越是多用C,就越不觉得它讨厌了。因此我就想着要给这个世界最常用的系统级程序语言写一些颂词。

以下分别是关于C语言我喜欢五件事和讨厌的五件事。请随意在底下的评论栏里加上你们自己喜欢或讨厌的事情。

1.K&R(喜欢)
Kernighan & Ritchie 写的《C程序设计语言》是关于C语言最好的书,而且我估计它也是关于编程的最好的书之一。简洁明了,都是有用并且重要的例子。这是一本非常好的书,同时也是一个非常好的参考。

甚至就连序言都非常好。在此引用一句,“C不是一门庞大的语言,因此不应该用一本厚重的书来诠释。”如果所有的编程教程都像这本书一样把长度限制到270页,它们会好很多。K&R的简洁明了、点到为止,很可能是C语言的成功所不可或缺的。

另一本给我喜爱的类似的编程教材是Leo Brodie所著的《Thinking Forth》。当然,肯定还有其他非常好的书,像是SICP之类的,只是我还没有读过罢了。

2.它十分简明(喜欢)
事实上,C语言作为一门简明语言是一个实实在在的福利。想要学习C,你只需了解它的类型,熟悉流程控制,处理好指针,然后你基本上就已经掌握它了。剩下的就仅仅是函数了。事实上,K&R利用这个低级的命令式语言,仅花费11行就实现了qsort(),不得不说这是对C语言简明性有力的证明。

3.IOCCC(喜欢)
你或许会觉得我疯了,不过如果你足够上进,International Obfuscated C Code Contest可能那儿是关于计算机科学最好的老师之一。算我开的一个小玩笑,不过我的确认为众多黑客都在不停挑战,并且创造了很多值得一谈的功绩。

其中让我确实学到很多的就是OTCC,Fabrice Bellard所写的“混淆的小型C编译器”。从中我学到了关于编译器设计的知识,主要是C语言编译器不必是340万行代码的庞然大物。同时,我也从Let’s Build a Compiler中获益,并静下来写了一个迷你的由C到Forth的编译器。

4.变量的定义与使用形式相似(喜欢)
这一点对记住如何定义十分复杂的事物非常有用,举个例子,一个指向包含十个整形的数组的指针应该是int *api[10]还是int (*pai)[10]呢?像你使用它的方式一样定义它即可,只需要记住[]操作符的优先级高于*(很自然就可以记住),然后你就明白那个括号是需要的了。(译者注:前者是指针数组,包含十个指向整形的指针。)

5.它编译出的“hello, world”体积很小(喜欢)
尤其是对嵌入式编程,这一点简直棒极了。C语言之上没有一个体积庞大的运行时,在很多嵌入式处理器上,一个什么都不做的程序一般只会编译出3到4个byte。一个完整的“hello, world”程序,甚至是在Windows XP下,都只会编译出1.5KB大小(使用Tiny C Compiler,它非常合适与做小型可运行程序)。

我认为,如果像Python一样的其他语言能够在这一点上赶上C,甚至是C的一部分,他们在嵌入式的世界中就会更加出彩。

6. 全局变量默认是外部的(讨厌)
你会说“用全局变量可不是个好习惯!”。但在嵌入式系统中不同。举个例子,你有一个名为timer.c的文件,其中有个全局变量int counter,在另一个文件state_machine.c中,有另一个counter。如果你碰巧忘记了在它们之前加上’static’,它们就是同一个变量,你根本察觉不到,没有Warning,没有任何提示……

这种行为看起来十分奇怪,尤其是当关键字extern就在手边的时候。不过当你熟悉static的两种不同的意义后,就可以轻易避免这种情况了。不过这依然十分令人讨厌。

7. static的两种不同的意义(讨厌)
有人能解释一下为什么static在函数体中和函数体外有着两种完全不同的意义吗?在函数体中,他表示“静态”——“在函数调用过程中保持这个变量唯一”。但是在函数体外,它的意义完全改变,成了“该变量为该文件私有的”。为什么后者不用private或者intern呢?

8. & 优先级低于 ==(讨厌)
在嵌入式编程中,我们总是喜欢用if ((x&MASK) == 0)这样的语句。但你可能常忘记写里面那对括号,因为感觉上,&的优先级应该比==高。但是事实并非如此,因此必须使用这对多出来的括号。

不过,这个情况有个不错的历史原因。C语言诞生自B语言,而在B语言中只有&而没有&&运算符。当Ritchie引入&&运算符时,他们希望原有的B语言端的代码能够正常运行,因此使&的优先级低于==。

9. 宏的功能并没有那么强(讨厌)
虽然递归的#include是非常棒的点子,但是,要怎么做才能不诉诸一些费脑子的方法,轻易地做预处理循环呢?同样的,有些我常遇到的情况,比如怎么才能给程序int和string两种格式的版本号,而同时只需要修改一个变量呢?

            1
            2

            #define VERSION_INT 209
            #define VERSION_STR "2.09"

用上面的代码,你更新版本号的时候总是需要修改两个地方。而且,特殊的#和##并不能帮上什么忙。我找到的唯一的解决则涉及了一些运行时修改。

10. 它不支持反射(讨厌)
好吧,可能这只是重申了一下第9点——如果宏系统再稍微强大一点,就不需要反射机制了。说不定我还会滥用它。不过我真正想说的是,用C语言,你不能写出生成代码的代码。
为什么不用C语言本身来写预处理器呢?这会给循环展开,更强大的宏机制,甚至更多IOCCC的怪点子提供无穷无尽的可能性。:-)

我认为,C语言之父能够坦然承认C的不足之处是非常可贵的。就像Dennis Ritchie说的一样:

“C语言行为古怪,瑕疵遍布,但却是一个巨大的成功。”

更多关于这点的信息,去读读他的论文 The Development of the C language吧 —— 那真是一篇值得一读的文章。

总而言之,在自己的优势上,C卓尔不群。

原文链接: brush

译文链接: http://blog.jobbole.com/51395/

本文来自ChinaUnix新闻频道,如果查看原文请点:http://news.chinaunix.net/opensource/2013/1113/3011059.shtml

论坛徽章:
0
2 [报告]
发表于 2013-11-13 11:30 |只看该作者
C语言还是很强的

论坛徽章:
15
射手座
日期:2014-11-29 19:22:4915-16赛季CBA联赛之青岛
日期:2017-11-17 13:20:09黑曼巴
日期:2017-07-13 19:13:4715-16赛季CBA联赛之四川
日期:2017-02-07 21:08:572015年亚冠纪念徽章
日期:2015-11-06 12:31:58每日论坛发贴之星
日期:2015-08-04 06:20:00程序设计版块每日发帖之星
日期:2015-08-04 06:20:00程序设计版块每日发帖之星
日期:2015-07-12 22:20:002015亚冠之浦和红钻
日期:2015-07-08 10:10:132015亚冠之大阪钢巴
日期:2015-06-29 11:21:122015亚冠之广州恒大
日期:2015-05-22 21:55:412015年亚洲杯之伊朗
日期:2015-04-10 16:28:25
3 [报告]
发表于 2013-11-13 12:22 |只看该作者
本帖最后由 yulihua49 于 2013-11-13 12:53 编辑
Patagonia 发表于 2013-11-13 10:18
  
前言:最近有个家伙抱怨道“为什么我还要再用C?”-虽然我不同意他的说法,但至少他随口提到如果你“ ...

转帖的吧?
最有感触的是:C没有反射。

反射什么?为什么要反射,没有怎么办?

反射,主要对struct而言。
如果你要写一个通用的程序,比如,将任意一个struct,他的内容写到JSON里边去,如下:

struct_to_JSON(void *struct_val,JSONOBJECT json)
{
}
那么,这个struct_val,因为要通用,你不知道是哪个struct,不知道里边有啥成员,啥类型,多长,在哪。。。。。。
所以只好定义成void *。下边,怎么办呢?

如果是已知的struct ---  后来改称 object了,逐个成员摆到JSON里不就成了吗?不过不通用,也不“优雅”。  ----  顺便回答前些日子的“优雅”问题。


想知道void * 里边的东西?就靠反射了。

没有反射?哪位有办法?

论坛徽章:
59
2015年亚洲杯之约旦
日期:2015-01-27 21:27:392015年亚洲杯之日本
日期:2015-02-06 22:09:41拜羊年徽章
日期:2015-03-03 16:15:432015年辞旧岁徽章
日期:2015-03-03 16:54:152015年迎新春徽章
日期:2015-03-04 09:50:282015元宵节徽章
日期:2015-03-06 15:50:392015年亚洲杯之阿联酋
日期:2015-03-19 17:39:302015年亚洲杯之中国
日期:2015-03-23 18:52:23巳蛇
日期:2014-12-14 22:44:03双子座
日期:2014-12-10 21:39:16处女座
日期:2014-12-02 08:03:17天蝎座
日期:2014-07-21 19:08:47
4 [报告]
发表于 2013-11-13 12:39 |只看该作者
7. static的两种不同的意义(讨厌)
有人能解释一下为什么static在函数体中和函数体外有着两种完全不同的意义吗?在函数体中,他表示“静态”——“在函数调用过程中保持这个变量唯一”。但是在函数体外,它的意义完全改变,成了“该变量为该文件私有的”。为什么后者不用private或者intern呢?

很显然, 这两种情形下的static意义是相同的(至少可以说是相近的)。
其它基本没问题。

此外C不支持lamba表达式(至少是内部函数), 而这在Fortran这类语言中早就支持了。

论坛徽章:
4
天秤座
日期:2013-10-18 13:58:33金牛座
日期:2013-11-28 16:17:01辰龙
日期:2014-01-14 09:54:32戌狗
日期:2014-01-24 09:23:27
5 [报告]
发表于 2013-11-13 12:51 |只看该作者
顶贴吧,感慨颇多,欲语又塞。

论坛徽章:
0
6 [报告]
发表于 2013-11-14 09:34 |只看该作者
Patagonia 发表于 2013-11-13 10:18
  
6. 全局变量默认是外部的(讨厌)
你会说“用全局变量可不是个好习惯!”。但在嵌入式系统中不同。举个例子,你有一个名为timer.c的文件,其中有个全局变量int counter,在另一个文件state_machine.c中,有另一个counter。如果你碰巧忘记了在它们之前加上’static’,它们就是同一个变量,你根本察觉不到,没有Warning,没有任何提示……

...

全局变量不加static限制本来就是外部的,就好象想骂一个人是SB,如果没有SB这个词语,那是语言的问题,现在有SB这个词了,你不用,反而责怪语言设计的不好,你这是SB的表现啊

论坛徽章:
1
2015年辞旧岁徽章
日期:2015-03-03 16:54:15
7 [报告]
发表于 2013-11-14 09:42 |只看该作者
yulihua49 发表于 2013-11-13 12:22
转帖的吧?
最有感触的是:C没有反射。

用宏。宏可以解决绝大部分问题。

论坛徽章:
0
8 [报告]
发表于 2013-11-14 09:43 |只看该作者
Patagonia 发表于 2013-11-13 10:18
  
7. static的两种不同的意义(讨厌)
有人能解释一下为什么static在函数体中和函数体外有着两种完全不同的意义吗?在函数体中,他表示“静态”——“在函数调用过程中保持这个变量唯一”。但是在函数体外,它的意义完全改变,成了“该变量为该文件私有的”。为什么后者不用private或者intern呢?.


SB,你的抽象能力不好。函数和文件都可以抽象为一个区域,函数内的static,就是作用区域限制在函数内,在函数外文件内,作用区域限制在文件内。

论坛徽章:
0
9 [报告]
发表于 2013-11-14 09:51 |只看该作者
Patagonia 发表于 2013-11-13 10:18
  
8. & 优先级低于 ==(讨厌)
在嵌入式编程中,我们总是喜欢用if ((x&MASK) == 0)这样的语句。但你可能常忘记写里面那对括号,因为感觉上,&的优先级应该比==高。但是事实并非如此,因此必须使用这对多出来的括号。..

TMD,&和==都是运算符,是平等的,定为哪个优先都可以,都说不出啥,你感觉哪个优先就应该哪个优先啊,SB

论坛徽章:
0
10 [报告]
发表于 2013-11-14 09:58 |只看该作者
Patagonia 发表于 2013-11-13 10:18
  
9. 宏的功能并没有那么强(讨厌)
虽然递归的#include是非常棒的点子,但是,要怎么做才能不诉诸一些费脑子的方法,轻易地做预处理循环呢?同样的,有些我常遇到的情况,比如怎么才能给程序int和string两种格式的版本号,而同时只需要修改一个变量呢?

            1
            2

            #define VERSION_INT 209
            #define VERSION_STR "2.09"...


一个版本号还定义为整形和字符串两个,这TM是你设计有问题,还TMD怪C的宏不好了,SB
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP