《C专家编程》:C:穿越时空的迷雾

1 C语言的史前阶段:

C语言的出现证明了一件事儿,那就是失败时成功之母。因为一直被人们朝圣的C语言竟然源于一个失败的项目。1969,通用电气、麻省理工工学院和贝尔实验室联合创立了一个庞大的项目--Multics。该项目的目的是创建一个操作系统,但是最后失败了。当心灰意冷的贝尔实验室的专家们撤离Multics工程后,他们又去寻找其他的任务。其中一个叫Ken Thompson的研究人员对另一个操作系统很感兴趣。所以Thompson和Denis Ritchie自娱自乐,把Thompson的“太空旅行”软件移植到不太常用的PDP-7(由迪吉多公司所研发的一款迷你电脑)系统上。与此同时,Thompson为PDP-7编写了一个简单的OS,它比Multics简单的多,并取名为UNIX。

2 编译器设计者的金科玉律:效率(几乎)就是一切

在编译器中,效率几乎就是一切。编译器效率包括两个方面:运行效率(代码的运行速度)和编译效率(产生可执行代码的速度),除了一些开发和学习环境之外,运行效率起决定性作用。效率几乎就是一切但也不是绝对的道理,如果得到的结果是不正确的,那么效率再高又有什么意义呢?

3 C语言的早期体验

C语言的许多特性是为了方便编译器设计者而建立的,因为开始几年C语言的主要客户就是那些编译器设计者,根据编译器设计者的思路而发展形成的语言特性有:

  • 数组下标从0而不是从1开始
  • C语言的基本数据类型直接与底层硬件相对应
  • auto关键字显然是摆设
  • 表达式中的数组名可以看作是指针
  • float被自动扩展为double
  • 不允许嵌套函数(函数内部包含另一个函数的定义)
  • register关键字,程序中的那些变量经常被使用,这样就可以把它们存放到寄存器中,简化了编译器,却把包袱丢给了程序员

4 C预处理器实现的3个主要功能

  • 字符串替换
  • 头文件包含
  • 通过代码模板的扩展

在宏的扩展中,空格对扩展的结果造成很大的影响

#define a(y) a_expanded(y)
a(x);
被扩展为a_expanded(x)
#define a (y) a_expanded(y)
被扩展为(y) a_expanded(y)(x)

4 原型的形成

原型的目的是当我们对函数作前向声明时,在形参中增加一些信息(函数名,返回类型,参数名称,参数类型),这样,编译器就能够在编译时对函数调用中的实参和函数声明中的形参之间进行一致性检查

老式的函数申明方法:char * strcpy(det,src)
                  char *det,*src;{}
现在:char *strcpy(char *det,char *src){}
可以省略参数名称,只保留参数类型

5 char* 与 const char*,char**与const char**

可以把一个类型为char*的值赋给一个const char*类型的对象,但反过来就不能进行赋值。

要想赋值形式合法,必须满足下列条件之一:

两个操作数都是指向有限限定符或无限限定符的相容类型的指针,左边指针所指向的类型必须具有右边指针所指向类型的全部限定符

char *cp;
const char *cpp;
cpp = cp;//可以赋值
//左操作数是一个指向有const限定符的char的指针
//右操作数是一个没有限定符的char指针
//char类型与char类型是相容的,左操作数所指向的类型具有右操作数所指向类型的限定符(无),再加上自身的限定符(const)
cp = cpp//结果产生编译警告

为什么反过来就不能进行赋值呢?

//假设反过来能够赋值,那么我们就可以修改b的值了,显然是矛盾的
int *a; 
const int b = 1;
a = &b;
*a = 2;
printf("%d\n",b);//输出2

char**与const char**之前都不能互相赋值

char**指向char*,const char**指向const char *,char*与const  char*是不相容的,同样,我们也可以采纳反正法来证明

char b;
const char c = b;
char *p1;
const char **p2 = &p1;//用char **的值赋给const char **
*p2 = &c;//用const char * 给const char *赋值,完全没问题
*p1 = 'y';//给char *指向的内容赋值,没问题!但这儿会发生什么事情
printf("%c\n",c);

6 算术转换

当执行算术运算时,操作数的类型不同,就会发生转换,数据类型一般朝着浮点精度更高、长度更长的方向转换,整数型如果转换为signed不会丢失信息,就转换为signed,否则转换为unsigned

7 一个微妙的bug

int array[] = {23,34,12,17,204,99,16};
#define TOTAL_ELEMENTS (sizeof(array) / sizeof(array[0]))

main ( )
{

    int d = -1,x;
    if(d <= TOTAL_ELEMENTS - 2) {
	    x = array[d + 1];
    }
}

-1被转换成unsigned int的结果为一个非常大的正整数,致使表达式的值为假。

将条件表达式修改为

d <= (int)TOTAL_ELEMENTS - 2

尽量不要在代码中使用无符号类型

你可能感兴趣的:(C语言)