注册 登录
编程论坛 C++教室

[原创]谭浩强《C程序设计(第二版)》或《C++程序设计》讲解有误导或者疏漏!

grub2linux 发布于 2005-07-04 16:01, 2407 次点击
谭浩强《C++程序设计》P182(或 C程序设计(第二版)P229 也差不多)中
关于xxx (*p)[n] (xxx为数据类型,n为数组元素个数) 的讲解有误导或者疏漏!

他的概念是“指向由m个元素组成的一维数组的指针变量”
但是下面的代码中却以二维数组的格式才能引用,换句话说,按以上的概念以一维数组的格式
根本就不能引用!(大家可以试试,比如 cout<<*(p+0)或p[0];)

#include <iostream>

using namespace std;

int main()

{

int a[3][4]={1,3,5,7,9,11,13,15,17,19,21,23};

int (*p)[4],i,j;

cin>>i>>j;

p=a;

cout<<*(*p+i)+j)<<endl;  // 此处仅仅是a数组的简单重复,完全可直接用a[i][j] *(*(a+i)+j)来引用

return 0;

}

实际上,xxx (*p)[n] 是指固定以n个元素为一组,一直类推下去。。。这样,实际上内含二维数组的结构。引用时,要指定延伸的次数,
比如:

int a[20]={1,2,3,4,5,6,7,8,9,10,11,12,13......};
// 改成二维数组或多维数组也一样:
// int a[2][10]={1,2,3,4,5,6,7,8,9,10,11,12,13......};
// int a[2][2][5]={1,2,3,4,5,6,7,8,9,10,11,12,13......};

int (*p)[3]=a;  // 以3个元素为一组,一直类推下去。。。
printf("%d",p[2][4] 也可以*(*(p+2)+4)); // 表示要引用,从类推2次后(从0开始)的地址(7的地址)往后数第4个数(从0开始),
就是11。 // 如果改成 printf("%d",p[2](在0到2之内));  编译就会出错,因为仅仅是地址。

(但xxx (*p)[n] 好象在钱能的C++程序设计教程 清华版 里没提到)

[此贴子已经被作者于2005-7-9 17:32:46编辑过]


15 回复
#2
realoneyjw2005-07-04 21:23
这本书没有看过
#3
kai2005-07-04 22:22
To grub2linux, 首先想说的是,我没看过谭先生的这本书,仅从你上面的这段描述,我认为,谭先生没有说错. 引用你的帖子:    他的概念是“指向由m个元素组成的一维数组的指针变量” 这个概念正确! 二维数组 是 一维数组的延伸. 比如 int a[3]; // 一维数组,在内存中开辟的3个integer 所需要的空间, 其首地址为 a, int a[3][4]; // 二维数组, 一维数组的延伸, 这里的3表示存在3个地址,              // 如果有地址变量指向该地址, 那么我们可以直接引用该地址变量, // 在该地址下开辟了4个Integer变量所需要的空间.      int (*p)[4]; p = a; // p 指向了二维数组的首地址, 也可以理解为, 地址的一维数组的起始值赋值于 p // 这样我们便可以对p 的引用而对 二维数组的值予以操作.    
#4
grub2linux2005-07-05 00:55
当然 二维数组 是 一维数组的延伸. 但是,我们在概念上不能混同它们. 他的概念是“指向由m个元素组成的一维数组的指针变量” 在最接近此概念的地方,你自己都说 "p = a; // p 指向了二维数组的首地址, 也可以理解为....等等" 注意, 你这里强调的是"二维数组". 还有,从另一方面说, 对这句话来说, 本身就是多种"歧义"的: 1, 指向一维数组的指针变量,这个一维数组由m个元素组成的 2, 指向一维数组的指针变量,指针变量由m个元素组成的 3, 是指针变量,它(或它们)指向由m个元素组成的一维数组 .... 科研是严谨的,这样表述严谨吗? 再看他的例子: int a[3][4]={1,3,5,7,9,11,13,15,17,19,21,23}; //除此之外,没有说明一维和多维数组都可以..也容易造成误解 int (*p)[4],i,j; //这里的[4]与上面的[4]雷同,(实际上这个数是可以任意指定的!) 这使人误解. 再看看,在此段的讲解中,不难发现: 几乎找不到"二维数组"的概念,绝大多数笔墨是"一维数组"... 补充一下:

int a[3][4]={1,3,5,7,9,11,13,15,17,19,21,23}; int (*p)[4],i,j;

//注意:此处的[4]与上面的[4]一模一样.实际上可以为任意的数!

他这样讲解,没有讲出xxx (*p)[n]这种构造的真正关键之处!

相反会误导..

你当时就理解成可以为任意的数吗?

p=a; //此处赋值也是这样,正好把a数组的第一元素的地址赋给p,实际上可以任意指定位置

(当然必须有效),比如 p=a+1, or p=&a[1][2]等等..这样才能理解真正的含义...

cout<<*(*p+i)+j); //此处是以二维数组的格式引用的,

而概念却是"指向由m个元素组成的一维数组的指针变量." 你没有疑问吗?

而且,经过上面的的任意指定地址后,仍然可以用相当于二维数组的结构的方式来引用!

妙就妙在这里!

这些他都没有讲...

大家没有同感吗?..

[此贴子已经被作者于2005-7-5 14:57:40编辑过]

#5
kai2005-07-05 20:16
To grub2linux, 看了你上面的话,知道你还停留在字面理解,没有明白其实质。 也许你对我上面的这段话会很不服气。 如果你清楚了指针的本质,那么对谭先生的解释,一看便会明白其意。 指针就其本质而言,是地址。如果我们知道了数据的地址,那么我们便可以通过对地址的操作(引用),而实现对数据的操作。 一维数组也好,二维数组也好,或者多维数组。就其本质而言,只是一种数据的存储方式。 一维数组在数据结构中,可以说是最基本的存储方式。二维数组也通常很有用。 但是,如果你清楚OOP的话,通常程序员倾向于使用class 来表达对数据的描述。这是对象化数据描述方式。 其次,像 LinkedList, DoubleLinkedList, Stack, Heap, HashMap, Vector, 等等,这些都被抽象出来,作为某种基本的结构在数据的存储中予以应用。 不过,有一点程序员必须清楚,数据不是漂浮在空气中的。数据的客观存在意味着其地址的客观存在。作为例外,Register没有地址。 如果清楚了这一层,那么你上面讨论的只是技术的支模细节了。 一个合格的程序员应该从宏观着手,把握程序的大局。知道如何将一个问题以模块化方式分析。并将任务分配给小组的每个成员,当然如果你是小组的唯一成员,那么所有的块程序都得你自己写了。这是我想告诉你的无剑胜有剑的道理。 再来说一些技术的枝模细节,二维数组的指针引用是一种丑陋的代码。程序员通常取而代之使用class
#6
grub2linux2005-07-05 21:19
对于已经学成者来说,已经早早走过了这个阶段,
但是他的这个教材是最基本的教材种类,要知道,此书的读者是初学者,
必须要把最基本的枝节弄清楚的
没有局部怎么有宏观?

我的帖子已经很清楚的表书了我的意思, 我觉得他没有讲解到要害处,会使学习者一知半解..


[此贴子已经被作者于2005-7-5 21:20:46编辑过]


#7
yameng2005-07-06 01:45
grub2linux是不是学-钱能C++?

to kai:
同感,grub2linux好象看帖时总是不怎么仔细体会,有时还要针尖对麦芒的争一下。
#8
grub2linux2005-07-06 08:28
以下是引用yameng在2005-7-6 1:45:20的发言: grub2linux好象看帖时总是不怎么仔细体会,有时还要针尖对麦芒的争一下。
嘿嘿,yameng也在这儿..? 没错, 我是程序员论坛的学-钱能C++ 呵呵.. 说实话,你这话我有点不同意的,对学习问题我是追根究底,为了弄清楚疑惑,对解答者的回帖,当然是认真看的 人家好心化时间写帖帮助嘛.当然是珍惜的,不过觉得kai朋友倒是可能没有细看我的对谭老师在这一知识点上的意见... 要说"针尖对麦芒",我觉得研究问题倒是应该提倡的,这样才能精细的互相发现理解不到的地方, 只要对问题不对人,有什么不好呢?

[此贴子已经被作者于2005-7-6 8:33:31编辑过]

#9
apodemas2005-07-21 22:43
我同意楼主的观点,面向大众的教材中的错误是不能以所谓"没有理解本质"来搪塞的
可是...
我还是不明白
我按照楼主的说法去小改了一下
#include &lt;iostream.h&gt;
void main()
{
 int a[2][3]={1,3,5,4,56,7};
 int (*p)[2]=a;
 int i,j;
 cin&gt;&gt;i&gt;&gt;j;
 cout&lt;&lt;*(*(p+i)+j)&lt;&lt;endl;  // 此处仅仅是a数组的简单重复,完全可直接用a[i][j] *(*(a+i)+j)来引用
}
编译就会出错,而把a[2][3]中的2和3颠倒就OK了,是不是哪里理解错了呢?拜托谁告诉我........

[此贴子已经被作者于2005-7-21 22:54:51编辑过]


#10
Antigloss2005-08-04 11:44
楼上的 void main 也是一个错误,请看 https://bbs.bc-cn.net/bbs/dispbbs.asp?boardID=5&ID=24305&page=1 还有,.h早就过时了。请用 #include <iostream> using std::cout; using std::endl; ... ... 或者干脆用 using namespace std; 不过不推荐哦~ 国产的书不是过时,就是错漏百出~要看还是看国外的好~ 就连国内的教师,真正懂 C/C++ 的人我想也没几个。 都是学了几天就来教别人,真是贻笑大方。 当年教我 C++ 的那个老师,竟然连 复制构造函数 和 赋值运算符重载都分不清,竟然说两者取其一即可。真乃大汗!
#11
激情依旧2005-08-06 11:37
      从上面的话可以看出楼主比较偏激................好像有点鸡蛋里头挑骨头吧
#12
Knocker2005-09-13 17:30
[QUOTE]但是下面的代码中却以二维数组的格式才能引用,换句话说,按以上的概念以一维数组的格式
根本就不能引用!(大家可以试试,比如 cout&lt;&lt;*(p+0)或p[0];)[/QUOTE]

注意你的错误,P是个二级指针。
#13
apodemas2005-09-13 20:50
这帖很久后被顶上来了,好象我的问题没人管,记得当时确实按照楼主的说法验证没有成功....
#14
Knocker2005-09-13 22:48
以下是引用apodemas在2005-9-13 20:50:48的发言: 这帖很久后被顶上来了,好象我的问题没人管,记得当时确实按照楼主的说法验证没有成功....

看看谭老师是怎么说的

他的概念是“指向由m个元素组成的一维数组的指针变量”

#15
jerom2013-01-01 14:32
错误或误导的地方确实不少!但由于谭浩强教授的强大影响力,很多东西都已将错就错,被普遍接受了!
因此,在C++考试中,你如果按正确的做很有可能被“砖家”判错!
#16
goldenlane2014-03-16 14:52
楼主,我觉得谭先生说的没错啊。比如:int *p , a,b,c; p = &a;  则可以说p指向一个整型变量,p+1指向哪里呢?下一个整型单元(一般4个字节)。int * (pa)[3]; 定义中pa是一个由三个整型单元构成的一维数组(类型)的指针,也可以说成pa是“指向m个元素构成的一维数组的指针变量”,同理,pa+1是跳过3个整型单元(跳过一个由3个整型元素构成的一维数组),这和一维数组的理解是一致的,只不过二维数组中的数据元素是一维数组。
1