写《品悟C》的薛非,怒了!
【转】踢爆IT劣书出版黑幕——由清华大学出版社之《C语言入门很简单》想到的
原作 = 薛非(《品悟C》著者)
1.前言与作者
首先声明,我是由于非常偶然的机会获得《C语言入门很简单》这本书的,绝对不是买的。买这种书实在丢不起那人。
去年这书刚出版时,在CU论坛举行试读推广 ,我当时随口说了几句(没说一句好话),没想到最后居然获送了一本。
对于我来说,这书连洗脚水都不如,根本不可能去读它。用它来擦屁股纸又嫌太硬,更怕这本劣书的油墨污染自己的屁股。所以我一直想不出应该如何处置这本书。想来想去,这书的唯一用途是拎出来当反面教材,给容易上当受骗的初学者们提个醒,顺便给大家讲点劣书是如何炮制的,都有哪些特征,也算是物尽其用了。
CU论坛这个地方有个特点,就是毁书不倦。一般的书到那里去推广,基本上就是去找虐。能在那里立足的书,至少得有点真东西。如果是垃圾书到那里,几个帖子立马就现出原形,比如陈冰 编辑、陈良乔 写的《我的第一本C++书》,就是首先在CU被戳穿的。但在博客园,你会看到一群傻瓜在抢赠书。(见 [最后召集:西瓜糖、editasp]微软MVP呕心力作《我的第一本C++书》新鲜出炉,围观即有机会获新书免费送 )
这本《C语言入门很简单》也是如此。试读活动开始的时候,我只看了前言(参见 C:劣书简易鉴别法 方法2),就感觉到这是一本劣书。
因为在前言的最后写着
引用 本书作者
本书由马磊主笔编写。其他参与编写的人员有陈世琼、陈欣、陈智敏、董加强、范礼、郭秋滟、郝红英、蒋春蕾、黎华、刘建准、刘霄、刘亚军、刘仲义、柳刚、罗永峰、马奎林、马味、欧阳昉、蒲军。
这是一伙什么人呢?我也说不上来,他们对外往往自称编辑,也有人叫他们“书商”,其实混在出版圈外围的出版贩子这个说法可能比较更恰当。不信你从中找几个名字,比如“陈世琼、陈欣 ”,google一下或者百度一下,你就会发现他们什么书都出,什么书都敢出。
这一伙人的书基本都是在清华大学出版的,所以很多人都发现清华大学出版社出的书大多数很烂,原因就在于此。我的感觉也大抵如此。通常我对各出版社的C语言书比较关注,我发现除了我那本《品悟C》 ,清华大学出版社没有任何一本像样的有技术含量的C语言书。
当然其他出版社也出烂书,但基本上都还能拿得出一两本比较好的C语言书。我不会因为清华大学出版社出版了我的《品悟C》就不顾事实地说他们好话,其实找他们出版并不是因为觉得他们是一家好的出版社,而是因为谭浩强的《C程序设计》是他们出版的,而我的《品悟C》 是揭露谭浩强《C程序设计》错谬百出、误人子弟的,我只是想要得到让他们自己打自己脸的这个效果。更何况我根本就不欠他们什么,我没义务为他们粉饰什么。
这些出版贩子一般和出版社很熟悉,能提前预知出版社的选题动态、出版计划,当然他们自己也会根据对市场的了解进行选题。由于他们和出版社的关系,因此他们的选题一般能很快通过审核拿到书号。拿书号是必须通过出版社的,因为在我们这个国家,除了通过出版社,任何人或组织都没有出版的权力。否则,轻则抓你非法出版收缴你的书,重则可能判你徒刑。
选题之后,出版贩子自己并不写,他们自己也不会写。而是遍地撒网找作者。他们找作者一般是利用有些技术人员想出书的心理(很多技术人员都有出书的冲动或愿望,尤其是有技术含量的技术人员),钓你上钩。利用一般人不了解出版行情的弱点,告诉你写一页(或每千字)给你多少钱(这个是远远低于出版社的价格的,他们赚差价)。运气好,他们能钓到一位不太会写书也不懂出版界内幕的高手。这个过程是神神秘秘的,严重信息不对称。有时即使你没上钩,他们可能也已经把你的部分内容骗去了,比如目录(写过书的人都知道这个东西有多重要)。运气不好就随便拉一个人写,有时甚至找在校的大学生。而这些大学生有很多都狗屁不通,专业课都没学过。于是抄袭,拼凑、胡编乱造,完活。
出版贩子拿到书稿之后,最多只是进行一些文字性编辑工作(利用一般人Word用得不熟以及可能文笔不够流畅的弱点),加点口水显得亲切,再取个煽情的名字,一本劣书就炮制完了。再之后,他们就把书交给出版社出版印刷。
出版社的编辑由于不用做什么工作就完成了社里的任务,因而也就乐得其成,甚至帮劣书的忙忽悠读者。劣书就这样流向了市场。
从这里不难看出,那些“出版贩子”对于书作者来说是剥削者,对于出版社来说是外围打工的,对读者来说,则是垃圾书市场的真正推手。而出版社的很多编辑则是寄养在“书号”这种国家垄断资源下的寄生虫和生产垃圾书的帮凶。可笑的是很多出版人员自以为是文化人,喜欢别人称呼他们为“老师”,其实出版社很多编辑的良知比制造地沟油的都少,从某种意义上来说,他们比流氓更流氓。郭德纲不是早就说过吗,流氓其实不可怕,就怕流氓有文化。说了你可能不相信,编辑中的有些败类是货真价实的流氓,各种意义上的。我自己对此是有切身体会的。比如,清华大学出版社的前任编辑,现图灵公司副总编陈冰 就对我发出过赤裸裸的人身攻击和人身威胁。(参见 315,关于《C程序设计伴侣》一书致人民邮电出版社的公开信 )
所以我每看到这种前言中列出一大堆编写人员的书,基本连目录都不用看就知道这书是怎么回事。
目录
只凭前言评价一本书,想必难以服人。
因此,讲完前言中的“作者”的奥秘,下面再从技术角度来讲一下这本书的目录。
目录是一本书的骨架,基本能确定一本书的好坏。目录好,书一般怎么也不会写得太差,差也是细节的问题;目录差,一本书无论如何也写不好。这就跟盖房子的道理一样,你搭了一个猪圈的架子,永远不可能建起一座别墅。
《C语言入门很简单》这本书的目录如下:
第1篇 一切从基础开始
第1章 概述
1.1 c语言简介
1.1.1 c语言的位置
1.1.2 c语言的优缺点
1.1.3 c适合什么开发
1.2 c语言的开发环境
1.2.1 编辑器、编译器和链接器
1.2.2 集成开发环境
1.3 visual studio使用简介
1.3.1 visual studio版本
1.3.2 visual studio的安装
1.3.3 新建项目
1.3.4 编写代码
1.3.5 编译链接
1.3.6 运行可执行程序
1.4 如何学好c语言
1.5 小结
1.6 习题
第2章 开始c语言之旅
2.1 为什么要写代码
2.1.1 为什么要写程序
2.1.2 从本书开始学编程
2.1.3 从一个现实的例子开始
2.2 编程的核心——数据
2.2.1 数据从哪里来
2.2.2 数据的表示
2.2.3 数据类型面面观——精度和范围
2.2.4 c语言基本数据类型
2.2.5 数据的变与不变——变量、常量
2.3 使用变量和常量
2.3.1 变量的使用
2.3.2 命名的方式
2.3.3 关键字
2.3.4 常量的使用
2.4 小结
2.5 习题第2篇 简单程序的构建
第3章 简单数学运算
3.1 什么是赋值
3.1.1 赋值的作用——把数据存起来
3.1.2 赋值运算的形式
3.1.3 赋值表达式
3.1.4 机动灵活的赋值——scanf()
3.1.5 看看我们的劳动成果——printf()
3.1.6 赋值的重要性
3.2 开始赋值——整型赋值
3.2.1 整数在计算机中的表示——二进制
3.2.2 更先进的表示方法——八进制和十六进制
3.2.3 进制之间的转化——以二进制为桥梁
3.2.4 给整型赋值
3.3 浮点型赋值
3.3.1 小数在计算机的表示
3.3.2 给浮点型赋值
3.4 字符型赋值
3.4.1 字符在计算机中的表示——ascii
3.4.2 给字符赋值
3.5 类型转化
3.5.1 什么是类型转换
3.5.2 类型转换的利弊
3.5.3 隐式类型转化和显示类型转换
3.5.4 赋值中的类型转换
3.6 基本数学运算
3.6.1 数学运算和数学表达式
3.6.2 商与余数
3.6.3 位运算
3.6.4 优先级的奥秘
3.6.5 数学运算中的类型转换
3.7 复合赋值运算
3.7.1 复合赋值运算
3.7.2 自增自减运算——特殊的复合赋值
3.7.3 自增自减的使用
3.8 小结
3.9 习题
第4章 程序结构
4.1 语句和语句块
4.1.1 简单语句
4.1.2 语句块
4.2 变量的作用域
4.2.1 局部变量的声明定义位置规则
4.2.2 局部变量的作用域规则
4.2.3 嵌套语句块的同名变量作用域规则
4.3 最常见的语句执行顺序——顺序结构
4.4 判断结构
4.4.1 判断的基础——逻辑真假
4.4.2 基础的判断——关系运算
4.4.3 复杂的判断——逻辑运算
4.5 if判断结构
4.5.1 基本if结构
4.5.2 …结构
4.5.3 另类的条件判断——?运算符的使用
4.5.4 …else结构
4.5.5 嵌套的if结构
4.6 switch判断结构
4.6.1 switch基本结构
4.6.2 果断结束——break的使用
4.7 循环结构
4.7.1 while循环结构
4.7.2 for循环结构
4.7.3 goto语句
4.7.4 循环嵌套
4.7.5 break和continue
4.8 真正的程序——三种结构的揉和
4.9 小结
4.10 习题第3篇 复杂数据的表示
第5章 数组
5.1 数组简介
5.1.1 数组的用途
5.1.2 数组变量的定义
5.2 数组变量初始化和赋值
5.2.1 数组的初始化
5.2.2 数组的下标
5.2.3 给数组赋值
5.2.4 数组元素的引用
5.3 二维数组
5.3.1 数组的维
5.3.2 二维数组表示和含义
5.3.3 二维数组的初始化
5.3.4 二维数组的赋值
5.3.5 二维数组的引用
5.4 多维数组
5.4 小结
5.5 习题
第6章 字符的数组——字符串
6.1 字符数组
6.1.1 字符数组的表示
6.1.2 字符数组的初始化
6.1.3 字符数组的赋值和引用
6.2 字符串
6.2.1 字符串的c语言表示
6.2.2 使用字符串为字符数组初始化
6.2.3 字符串的保存形式
6.3 字符串的输入输出——scanf和printf字符串
6.3.1 输入输出字符串的c语言表示
6.3.2 scanf()函数对字符串的特殊处理
6.4 小结
6.5 习题
第7章 指针
7.1 地址的概念
7.1.1 地址的含义
7.1.2 为什么要用地址
7.1.3 地址的表示与取址运算
7.2 指针和指针变量
7.2.1 指针的含义和用途
7.2.2 指针类型
7.2.3 指针变量的定义和使用
7.2.4 void指针
7.3 指针运算
7.3.1 取指针元素
7.3.2 指针的自加自减
7.3.3 指针的类型转换
7.4 数组和指针
7.4.1 数组名也是指针
7.4.2 数组名是指针常量
7.4.3 使用数组名访问数组元素
7.4.4 三种访问数组元素的方法
7.4.5 数组指针和指针数组
7.5 多重指针和多维数组
7.5.1 多重指针
7.5.2 取多重指针元素运算
7.5.3 多维数组名和各维元素
7.5.4 使用指针访问多维数组
7.6 字符串和指针
7.6.1 字符指针
7.6.2 字符指针和字符串
7.6.3 scanf()、printf()函数和字符指针
7.7 小结
7.8 习题
第8章 结构体
8.1 结构体的含义
8.2 结构体类型的表示
8.2.1 结构体类型的一般格式
8.2.2 结构体的成员变量
8.2.3 复杂的结构体
8.3 结构体变量
8.3.1 结构体变量的声明定义
8.3.2 结构体变量初始化
8.3.3 取结构体成员运算
8.4 结构体数组
8.5 结构体指针
8.5.1 一重结构体指针
8.5.2 使用结构体指针取结构体数据
8.5.3 结构体指针例子
8.6 回到问题
8.7 小结
8.8 习题
第9章 共同体类型
9.1 共同体的含义与表示
9.1.1 共同体的用途
9.1.2 共同体的表示
9.1.3 复杂的共同体
9.2 共同体变量
9.2.1 共同体变量
9.2.2 共同体成员变量的相互覆盖
9.2.3 使用共同体变量
9.3 共同体数组
9.4 共同体的指针
9.4.1 一重共同体指针类型
9.4.2 共同体指针变量
9.4.3 完整的例子
9.5 小结
9.6 习题
第10章 枚举类型
10.1 枚举类型的含义与表示
10.1.1 枚举类型的含义
10.1.2 枚举类型的表示
10.2 枚举常量和枚举变量
10.2.1 枚举常量
10.2.2 枚举变量的定义
10.2.3 枚举变量的使用
10.3 枚举数组和指针
10.3.1 枚举数组
10.3.2 枚举指针
10.3.3 用枚举指针来访问枚举数组
10.4 typedef类型定义符
10.5 小结
10.6 习题第4篇 复杂功能的实现
第11章 函数
11.1 函数的意义
11.2 函数的形式
11.2.1 函数的一般形式
11.2.2 函数的参数列表
11.2.3 函数的返回值类型
11.3 函数声明和定义
11.3.1 函数的声明
11.3.2 函数的定义形式
11.3.3 函数的形参
11.3.4 return返回值语句
11.4 自己动手写一个函数——加法函数
11.4.1 确定加法函数的样子
11.4.2 实现加法函数体
11.4.3 完整的加法函数定义
11.5 函数调用
11.5.1 函数的调用作用
11.5.2 函数调用表达式
11.5.3 函数的实参
11.5.4 简单函数的调用
11.6 复杂参数
11.6.1 数组参数
11.6.2 指针参数
11.6.3 结构体、共同体和枚举参数
11.7 小结
11.8 习题
第12章 特殊的函数——main()函数
12.1 main()函数的作用
12.2 main()函数的声明定义
12.2.1 main()函数的声明形式
12.2.2 main()函数的参数
12.2.3 main()函数的返回值
12.3 小结
12.4 习题
第13章 局部变量和全局变量
13.1 变量的作用域和生命周期
13.2 函数内的局部变量
13.2.1 局部变量的作用域
13.2.2 局部变量的生命周期
13.2.3 局部变量的覆盖作用
13.3 函数外的全局变量
13.3.1 全局变量的作用域
13.3.2 全局变量的生命周期
13.3.3 局部变量对全局变量的覆盖作用
13.4 变量修饰符
13.4.1 使用修饰符改变变量的作用域和生命周期
13.4.2 c语言中常用变量修饰符的作用
13.5 小结
13.6 习题第5篇 c语言的高级内容
第14章 预处理命令、文件包含
14.1 预处理命令的作用
14.1.1 程序预处理
14.1.2 预处理命令
14.1.3 c语言的几类预处理命令
14.2 c语言中的宏定义
14.2.1 c语言的宏定义形式
14.2.2 不带参宏定义
14.2.3 带参数的宏定义
14.3 预编译控制
14.3.1 c语言预编译控制
14.3.2 三种预编译控制组合形式
14.3.3 一个简单的例子
14.4 文件包含
14.4.1 头文件和源文件的文件名
14.4.2 头文件和源文件的内容
14.5 include包含头文件
14.5.1 自定义头文件和系统头文件
14.5.2 文件包含的两种形式
14.5.3 完整的circle例子
14.5.4 c语言中的标准头文件
14.6 小结
14.7 习题
第15章 文件操作
15.1 文件
15.1.1 重新认识文件
15.1.2 计算机眼里的文件
15.1.3 开发人员能对文件干些什么
15.2 文件的打开关闭
15.2.1 文件指针
15.2.2 文件打开函数
15.2.3 文件关闭函数
15.3 文件读写
15.3.1 读写一个字符
15.3.2 读写一个字符串
15.3.3 读写一个数据块
15.4 文件的其他操作
15.4.1 随机读写文件
15.4.2 出错检验
15.5 小结
15.6 习题
这个目录安排得如此的海立云垂、奇诡无比,绝对算得上是C语言书籍中荒诞派的代表作。其荒谬绝伦的程度令人惨不忍睹,有屎以来,前无来者。读过之后,顿感虎躯一震,三观俱覆。
最奇葩之处就在于它在接近全书结束的第4篇的第11章才开始讲“函数”,而在此之前却在第8章已经早早地把结构体讲完了。
初学者不可能知道这样有什么不妥,但是任何一个懂得程序设计(不一定是C语言)的人看到这些立刻就会明白,这个作者绝对是一个外行,这样写给初学者的书绝对是在坑爹。
为什么呢?因为这不符合C语言的基本特点,也违背了程序设计的基本原则。你见过小学数学先讲小数再讲整数的吗?你见过没学过代数直接上微积分的吗?《C语言入门很简单》就是这种荒唐透顶的本末倒置的C语言版。谭浩强的书都不敢这么写。
为什么不能把函数放在结构体后面讲呢?因为在C语言中,如果不懂得函数的基本理论并能熟练构造、使用函数,结构体这种东西根本就没用!你一定要用,那就跟在炕上用挠炕席的方法学游泳一样。只有谭浩强那样的“大师”才会在程序中只写一个main()函数,同时又使用结构体。(参见 用驴子拖宝马——怎样滥用结构体 ) 仅此一点,就足以说明《C语言入门很简单》作者的编程水平连学得比较好的大一学生都不如,搞不好他的C编程就是跟老 谭学出来的。
函数一定要放在结构体前面讲,而且应该尽可能地早讲。原因是它非常重要。它是实现结构化程序设计思想最重要的手段,是组织代码最重要的利器(对不起,不是指针,指针是用来组织数 据的)。
Dijkstra当年说过,“一个有过 BASIC 编程经历的人是很难学会好的编程习惯的。作为一个潜在的程序员,他们已经被脑残并且无法修复。”(It is practically impossible to teach good programming to students that have had a prior exposure to BASIC: as potential programmers they are mentally mutilated beyond hope of regeneration. )
Dijkstra为什么要这样说?就是因为BASIC(当时的)根本就没有像样的现代意义的函数,函数调用是通过滥用全局变量实现的,整个程序是用一种傻逼精神完成的。Dijkstra实在看不下去这帮 脑残的代码了,不得不跳出来大声疾呼。自此,人们才开始懂得什么叫做结构化程序设计。