《C专家编程》笔记第一章 C:穿越时空的迷雾

346 阅读4分钟

前言

从这一章的标题就可以看出,这一章主要讲解了C语言的历史,真正的知识点不是很集中,每个知识点都分散在各个故事里。我把我认为有价值的知识点(其实都很有用)都记录下来并且标记页数,方便以后的查阅。

笔记

P3:

  • 编译器设计者的金科玉律 -- 效率(几乎)就是一切。 当然还有一些其他需要关心的东西,如有意义的错误信息、良好的文档和产品支持。
  • 编译器的效率包括两个方面:运行效率(代码的运行效率)和编译效率(产生可执行代码的速度)

P4 :

该部分粗略地介绍了c语言的一些特点,更为详细的内容将在后面章节具体讲解。

1、数组下标从0而不是1开始

  这一点是c语言初学者都会搞糊涂的地方,尤其是在数组的声明和引用时,声明[]内的数代表着数组的长度,而引用时[]内的数不能大于声明时的数-1
   例:

   int a[3];

声明一个能存储100个int型数据的数组

    a[100] = 0;

这句在编译时不会报错,但是a数组的合法范围是从a[0] ~ a[2], 程序将修改a[100]地址的值,你不知道程序在该地址以前存放什么数据,修改后可能导致程序崩溃,这也是常常出bug的地方。
2、C语言的基本数据类型直接与底层硬件相对应
3、auto关键字显然是摆设

在进入程序块时自动进行内存分配(与全局静态分配或在堆上动态分配相反)。它是缺省的变量内存分配模式。

例:在函数内部int a;auto int a;是等价的。

4、表达式中的数组名可以看作是指针
  首先要清楚的一点是,数组和指针并不是在任何情况下是等价的,所以上面的句子有了一个前提条件“在表达式中”。关于指针和数组的详细内容将在本书的第四章。
5、float被自动扩展为double

这样做的理由从未公诸于众

  原谅我笑了!!!
6、不允许嵌套函数(函数内部包含另一个函数的定义)
7、rigister关键字
 该关键字修饰的变量表示把该变量存放到寄存器中,而不是放到内存中,这样能提高程序里运行速度,因为从寄存器取值比从内存取值要快很多。但是尽量不要用该关键字。

P5 :

C预处理器的3个主要功能:
1、字符串替换
2、头文件包含
3、通用代码模板扩展

  即宏定义的展开,在宏的扩展中空格对扩展的结果这造成很大的影响。

#define a(y) a_expanded(y)
a(X);

被扩展为:
a_expanded(x)
而:

#define a (y) a_expanded(y)
a(X);

被扩展为:
(y) a_expaned(y)(x)

P19

关于形参和实参的问题:

foo(const char **p) { }
main (int argc, char** argv)
{
    foo(argv);
}

编译警告:
warning: argument is incompatible with prototype(参数与原型不匹配)
  形参的传递类似于赋值。

标准中有关赋值部分的约束条件之一:
两个操作数都是指向有限定符或者无限定符的相容类型的指针,左边指针所指向的类型必须具有右边指针所指向类型的全部限定符。

例:

char * cp;
const char *ccp;
ccp = cp; //正确
/*
    左操作数ccp是一个指向char类型的无限定符的指针,右操作数是一个指向const char类型的无限定符的指针
    char 和char类型相容,左操作数ccp所指的类型有const,右操作数所指类型无限定符,所以左操作
    数具有右操作数的全部类型,再加上自己的限定符const,所以上式正确,若反过来如下式,则错误,
*/
cp = ccp;//错误

了解了上面的定义之后再来看书中给出的例子,char**是一个没有限定符的指针,类似地,const char** 也是一个没有限定符的类型。它的类型是“指向一个具有const限定符的char类型的指针的指针”。
由于char**const char**都是没有限定符的类型,但他们所指向的类型不一样(前者指向char*,后者指向const char*),因此他们是不相容的。