为什么说C指针是C语言的灵魂?

来自读者对C和指针的解说

  1. 他可以直接访问硬件,这是灵活性和效率的体现,程序离硬件越近自然效率越高,当然运用不当也可导致效率低下

  2. 难掌握及太危险,如果对指针理解含混,访问过程不当易导致程序奔溃或隐藏潜在危险

  3. 指针作用总的说是调高程序运行效率,原因是它对c语言中定义的各种数据结构进行地址传递,而不需要进行不断地进行值传递。理解起来可以联想一下数据共享与建立副本的区别。

注:欢迎各位程序猿们继续补充

挖掘C语言的灵魂,先看《征服C指针》这本书

enter image description here

读者对本书的评价:

  • 针对性极强,全书以一种精准但又不考究的方式解读C语言指针。看过经典的“C和指针”后再看看日本程序员对指针的理解,也许会获得又一番的感悟。这是最棒的C书 、
  • 日本人都是工匠,从精密机械到各种探测器,从av情色片到美食,小日本干任何事都精益求精。 我看过小日本的几本计算机书,binary hack, debug hack, 以及这本,全都是精品。 他们的书逻辑性不强,特点是人的因素多,注重细节,教给读者诀窍,读来畅快淋漓,爱不释手。 可能我的性格里也有黑客的成份吧,不喜欢那种冷冰冰的知识罗列的书

买前必读:

这是一本关于C 语言的数组和指针的书。

■ 本书的读者群虽然定位于“学习过C 语言,但是在指针的运用上遇到困难”的读者,但还是能随处可见一些高难度的内容。那是因为我也不能免俗,偶尔也喜欢把自己掌握的知识拿出来显摆一下。

■ 对于初学者,你完全没有必要从头开始阅读。遇到还不太明白的地方,也不要过分纠结。阅读中可以跳跃章节。对于第0章和第1章,最好还是按顺序阅读。如果认为第2章有点难度,你可以先去啃第3章。如果第3章也不懂,不妨尝试先去阅读第4章。这种阅读方式是本书最大的卖点。

■ 在本书中,我会经常指出一些“C的问题点”和“C的不足”。可能会有一些读者认为我比较讨厌C语言。恰恰相反,我认为C是一门伟大的开发语言。倒不是因为有“情人眼里出西施”、“能干的坏小子也可爱”这样的理由,毕竟在开发现场那些常年被使用的语言中,C语言还是有相当实力的。就算是长得不太帅,但论才干,那也是“开发现场的老油条”了。

迷你书下载

精彩片段:

应该记住:数组和指针是不同的事物

为什么会引起混乱?

首先,请允许我强调一下本章的重要观点。

C 语言的数组和指针是完全不同的。

大家都说C语言的指针比较难,可是真正地让初学者“挠墙”的,并不是指针自身的使用,而是“混淆了数组和指针”。此外,很多“坑爹”的入门书对指针和数组的讲解也是极其混乱。

比如,K&R 中就有下面一段文字(p.119), C 语言的指针和数组之间有很强的关联关系,因此必须将指针和数组放在一起讨论。

很多C程序员认为“数组和指针是几乎相同的事物”,这种认识是引起C的混乱的主要原因。

从图3-17中可以一目了然地看出,数组是一些对象排列后形成的,指针则表示指向某处。它们是完全不同的。

enter image description here

带着“数组和指针是几乎相同的事物”这样的误解,初学者经常写出下面这样的代码:

int *p;

p[3] = ……     ←突然使用没有指向内存区域的指针
——自动变量的指针在初期状态,值是不定的。
char str[10];
    .
    .
    .
str = “abc”;    ←突然向数组赋值

——数组既不是标量,也不是结构体,不能临时使用。

int p[]; ←使用空的[]声明本地变量 ——只有在“函数的形参的声明”中,数组的声明才可以被解读成指针。 对于数组和指针,它们在哪些地方是相似的,又在哪些地方是不同的?不好意思,可能在下面会出现和前面重复的内容。

表达式之中 在表达式中,数组可以被解读成指向其初始元素的指针。所以,可以写成下面这样:

int array[10];

p = array;         ←将指向array[0]的指针赋予p

可是,反过来写成下面这样,

array = p;

就是不可以的。确实,在表达式中array 可以被解读成指针,可是,本质上它其实是被解释成了&array[0],此时的指针是一个右值*。(此时的指针是右值这个理由之外,在标准中,数组也不是“可变更的左值”。)

比如,对于int类型的变量a,a = 10;这样的赋值是可以的,但肯定没有人想做a + 1 = 10;这样的赋值吧。尽管a 和a + 1 都是int,但是a + 1没有对应的内存区域,只是一个右值,所以不能被赋值。同样的道理,array也不能被赋值。此外,对于下面这个指针,

int *p;

如果p指向了某个数组,自然可以通过p[i]的方式进行访问,但这并不代表p就是指针。

p[i]只不过是*(p + i)的语法糖,只要p正确地指向一个数组,就可以通过p[i]对数组的内容进行访问,如图3-18所示。

enter image description here

如果是“指针的数组”和“数组的数组”,就会有很大的不同。

char *color_name[] = {        ←指针的数组
    “red”,
    “green”,
    “blue”,
};

对以上的代码进行图解(参照图3-19),

![enter image description here][5]

char color_name[][6] = {
    “red”,
    “green”,
    “blue”,
}

对以上的代码进行图解(参照图3-20),

enter image description here

以上两种情况都可以用color_name[i][j]的方式对数组进行访问,但是内存中数据的布局是完全不同的。

声明 只有在声明函数的形参的时候,数组的声明才能解读成指针的声明(参照3.5.1节)。

以上的语法糖,与其说使C变得更加容易理解,倒不如说它使C语言的语法变得更加混乱。是不是有很多人这么想?我就是其中的一个(虽然使用这个语法糖可以让多维数组作为参数被传递时更容易理解……)。而且K&R的说明更是使这种混乱局面雪上加霜。

在不是声明函数的形参的时候,数组声明和指针的声明是不可能相等的。

使用extern 的时候是最容易出现问题的(参照3.5.2节)。另外,声明局部变量或者结构体的成员时,写成

int hoge[];

会引起语法错误(对于结构体的成员,在ISO C99 中是允许这种写法的)。

存在数组初始化表达式的情况下,可以使用空的[],但这是因为编译器能够计算出数组元素的个数,所以可以省略书写元素个数。仅此而已,这种特征和数组扯不上任何关系。

要 点 【非常重要!!】 •数组和指针是不同的事物。

恶名昭著的指针究竟是什么

关于“指针”一词,在K&R 中有下面这样的说明(第5 章“指针和数组”的开头部分): 指针是一种保存变量地址的变量,在C 中频繁地使用。

其实在表达上,这样的说明是有很大问题的。总会让人感觉,一旦提起指针,就要把它当作变量的意思。实际上并非总是如此。

此外,在C 语言标准中最初出现“指针”一词的部分,有这样一段话:

指针类型(pointer type)可由函数类型、对象类型或不完全的类型派生,派生指针类型的类型称为引用类型。指针类型描述一个对象,该类对象的值提供对该引用类型实体的引用。由引用类型T 派生的指针类型有时称为“(指向)T 的指针”。从引用类型构造指针类型的过程称为“指针类型的派生”。这些构造派生类型的方法可以递归地应用。

这段话的内容也许会让你一头雾水(既然是标准,那总要有点标准的范儿吧)。那就让我们先关注第一句话吧,那里出现了“指针类型”一词。

提到“类型”,立刻会让人想起“int 类型”、“double 类型”等。同样,在C 语言中也存在“指针类型”这样的类型。

“指针类型”其实不是单独存在的,它是由其他类型派生而成的。以上对标准内容的引用中也提到“由引用类型T 派生的指针类型有时称为‘(指向)T 的指针’”。

也就是说,实际上存在的类型是“指向int 的指针类型”、“指向double的指针类型”。 因为“指针类型”是类型,所以它和int类型、double类型一样,也存在“指针类型变量”和“指针类型的值”。糟糕的是,“指针类型”、“指针类型变量”和“指针类型的值”经常被简单地统称为“指针”,所以非常容易造成歧义,这一点需要提高警惕*。

(*至少本书还是尽力将这些说法进行区别的,但有时候,无论怎么写也做不到自然地表述想要表达的意思,最后只好投降……非常抱歉。)

要 点

先有“指针类型”。 有了“指针类型”,才有了“指针类型的变量”和“指针类型的值”。

比如,在C中,使用int 类型表示整数。因为int是“类型”,所以存在用于保存int型的变量,当然也存在int型的值。指针类型同样如此,既存在指针类型的变量,也存在指针类型的值。

因此,几乎所有的处理程序中,所谓的“指针类型的值”,实际是指内存的地址。

变量的内容是保存在内存的某个地方的,“某个地方”的说法总是会让人产生困惑,因此,就像使用“门牌号”确定“住址”一样,在内存中,我们也给变量分配“门牌号”。在C的内存世界里,“门牌号”被称为“地址”。

相关阅读: