C++打击有点大, 再翻回C看两眼, 当然只是闲着看, C++还是正事
经常有人说UNIX/Linux的发展历史, C也只是这个时期的一个附属产物, 但是我们今天就来看看C的起源
1. C的起源
首先,在这里致敬C,UNIX伟大的发明者Dennis Ritchie 及 Kenneth Thompson
两位计算机先驱提出了KISS的原则(Keep it simple stupid)
我们在这里也用简洁的语言来组织C的发展历史:
1> C的发展与UNIX息息相关,同期发明,相互依赖
2> 一切的开始是因为Mutics操作系统
3> Mutics失败后, Kenneth想要写一个个人的操作系统, 为了玩游戏,在PDP-7上
4> Ritchie入伙后, 他们为PDP-7编写了可以使用的操作系统UNIX,基于PDP-7 汇编
5> 因为PDP-7 只有8KB内存, Kenneth初始版本是基于解释器的,效率一般
6> 之后Kenneth又写了PDP-11版的UNIX
7> 创造UNIX时, 基于KISS的原则, 所有的软件都由小而简单的组件组成, 吸取了Mutics的教训
8> Ritchie因为解释器效率低, 实现了从BCPL而来的B得进化版,”New B” 基于编译系统, 提高效率
9> 很快, “New B”变成为了, 早期版本的C
C的基础历史就这样,但是他在发展过程中经历了许许多多的问题与困难
同时C引入了类型的概念, 不过是为了支持PDP-11的特性: 支持不同类型字长的数据
2. 早期C体验
C诡异离奇, 缺陷重重, 确取得了巨大成功 –Dennis Ritchie
早期C的体验并不是很好, 它更多的是作为面向编译器设计者的语言, 操作系统设计的语言.
即系统编程语言, 即使到了今天, C在系统编程方面的地位也不是能够轻易被撼动的 !
为了支持系统编程, C支持了一下特性:
-
数组下标从0开始, 为了支持偏移量的概念
-
C语言的基本数据类型与底层硬件对应, 不像Pascal, C11开始才支持复数类型, 浮点数类型也是硬
件支持后,才加入支持的
-
auto关键字是摆设, 与静态区, 动态堆区想对应, 栈区的内存是对象缺省内存分配模式,
-
表达式数组名可以看作指针, 方便了系统编程者, 然而这个坑点, 这么多年死了多少人 !
-
float自动扩展为double, 这个没啥意思, 当年扩展, 现在不扩展, 不过从C++的经验来看,
一般建议直接写double, 两个开销差不了少, 而且double的精度也比较大
-
register关键字, 摆设, 编译器就不鸟你
< C专家编程 >是本好书, 不过要有自己的观点和看法,上面是基于我自己的看法.
3. 标准I/O库与函数库
C语言本身不提供文件I/O, 都是基于标准函数库的
此处略过
但是, 切记 千万不要使用C预处理器,来修改程序已有结构
4. K&R C
< the C Programming Language > 在计算机历史上留下了浓墨重彩的一笔,
不过K&R C 指的是布莱恩.柯尼汉 + 丹尼斯.里奇 (而非是肯.汤普逊)
K&R C的代码风格以及思想很重要, 可以拜读以下大作,
但是, K&R C中有许多已经被修改掉的特性, 这些特性是时代的眼泪, 被删除也无可厚非
所以, 无论怎么说, 我们如果要研究学习,便是ANSI C (C89)
5. ANSI C
任何一门语言, 在发展过程中都会衍生出许多的变体, 如果不加控制, 就会使得这门语言松散
e.g.: Lisp, Basic就是这样凉凉的
所以, ANSI C 作为标准C就出现了
ANSI C标准对于K&R C做了一定程度上的修改, 在进行语言修改, 细节修订的同时, 保持语言特性及思想
不发生重大变化,维护了K&R C的核心思想,ANSI C 也即C89
而所谓的C90是ISO C
垃圾!
不要添乱—–立即解散ISO工作小组 – 匿名人士
6. K&R C与ANSI C的区别
既然K&R C和ANSI C是的两个重要版本, 那么就说说这两个版本的区别吧, 主要体现在下面四个方面上:
-
本质上改变, 就只有原型一个特性
-
新的关键字 , 区别不大, 只是添加了一定类型的支持
-
“Silent Change” 保持原有语言特性不变, 只改变其中一些小细节的实现,
-
其他区别, 这些基本上是一般编程者触碰不到的
那么, 我们就来说说上面三个重大区别
6.1 函数原型
函数原型, 在ANSI C之前是没有的, 之前的都叫作, 函数声明, 这个特性是从C++学习过来的
char *strcpy(); // 函数声明
char *strcpy(char *dest, const char *src); // 函数原型
这是一个重大变化, 主要是用于前置声明
K&R C时,对于函数参数的检查,一直推迟到链接时
而ANSI C使用函数原型, 将参数检查提前到编译时期
同时,进行函数定义时,是这样的:
char *strcpy()
char *dest, const char *src;
{
...
}
char *strcpy(char *dext, const char *src)
{
...
}
另外,我们说函数原型的引入主要是针对前置声明
但是, C11之后,C语言风格也发生转变, 与时俱进, 逐渐向C++/Java等面向对象靠拢
事实上, 不刻意强调, 写前置声明的机会已经很少了, Linux4.x版本后的实现可见一斑
6.2 关键字
关键字其实就那么一回事, const, volatile, signed, void其中常用到的也不多
C11后还加入了 _Generic, _Bool(真正意义上实现内置类型Boolen)
但是,说实话,意义不大(摊手), 一般写个C99就歇菜了
6.3 Silent Change
这一处的改变说大不大, 说小不小.不过还是很有意思的
6.3.1 类型转换
举个例子:
关于类型转换的问题, 众所周知, C是强类型,可以进行自动/强制类型转换
然而, 进行数值运算时, 会进行类型转换, 这里ANSI C与K&R C的区别就体现出来了
对于<=int类型的, K&R C转换为无符号类型, ANSI转换为有符号类型
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
unsigned char i = 4;
if (-1 < i)
printf("ANSI C\n");
else
printf("K&R C\n");
return 0;
}
当然,出现了复杂类型的时候, 比如,int和unsigned int 肯定是符合下述规则的:
执行算术运算时, 如果类型不同, 就会发生转换, 数据类型一般朝着浮点精度更高, 长度更长的方向转换
整型数如果转换为signed不会丢失信息, 就转换为signed, 不然就转换为unsigned
对于, 无符号数和有符号数混用
需要十分注意: 切记不能混用,要不直接不定义无符号,要不全部使用强制类型转换, 否则翻车!
比如下面常见的程序:
int i = -1;
unsigned int j = 34;
if (i < (int)j) // 否则,等着翻车吧!
printf("<\n");
6.3.2 相容性
const char *str;
char c = 'r';
str = &c;
*str = 'a'; // error
c = 'a'; // correct
void test(const char **str)
int main(int argc, char **argv)
{
test(argv); // error
}
上面的测试函数, 就是典型的相容性问题
对于,str形式参数以及argv实际参数, 他们之间的关系还是类似于赋值的
str与argv是可以传参的, 他们都是char ** ,相容
他们指向的, *str, *argv也是相容的,
但是 **str, **argv(是const的对象) 不相容,
也即是说,*str与*argv本身相容, 但是指向对象不相容,
因此传参操作出现错误.
相容性不能传递
注: 此处的对象,指的是操作的数据,具名内存,并非OOP中的对象
7. #pragma
最后,说个好玩的:
GNU C Complier (GCC)在1.34版本实现中,因为设计师讨厌pragma
所以在有pragma的代码时,做了如操作:
do_pragma()
{
close(0);
if (open("/dev/tty",O_RDONLY, 0666) != 0)
goto nope;
close(1);
if (open("/dev/tty", O_WRONLY, 0666) != 1)
goto nope;
exel("/usr/games/hack", "#pragma", 0);
exel("/usr/games/rogue", "#pragam", 0);
exel("/usr/new/emacs", "-f", "hanoi", "9", "-kill", 0);
exel("/usr/local/emacs", "-f", "hanoi", "9", "-kill", 0);
nope:
fatal("you are in a maze of twisty complier features, all different");
}
总之, 这位设计师, 就是不想你使用pragma与编译指令, 实际上pragma还是很好用的,它可以给编译器
完成指定操作,比如: 取消结构体对齐, 要求函数内联等等操作, 多使用, 挺好的.
闲了, C看着还是很有意思, 当然主线不能忘了, 不说了, 又要去板砖了
May 17, 2018 12:25 PM