⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 教学--第十七章 数组(二).htm

📁 《白话c++网页版》是一本用浅显易懂的并具有点幽默的语调来讲述c++的高深的内容
💻 HTM
📖 第 1 页 / 共 5 页
字号:
      <P><IMG height=200 src="教学--第十七章 数组(二).files/ls17.h1.gif" width=200 
      border=0></P>
      <P> </P>
      <P>第一个要注意的的是头两行告诉我们,整个数组变量<SPAN lang=en-us>arr</SPAN>的地址,和第一个元素<SPAN 
      lang=en-us>arr[0]</SPAN>,二者的地址完全一样。</P>
      <P>事实上,数组和元素,是对同一段内存的两种不同的表达。把这一段内存当成一个整体变量,就是数组,把这段内存分成大小相同的许多小段,就是一个个数组元素。</P>
      <P> </P>
      <P>请参看下图:</P>
      <P><IMG height=224 src="教学--第十七章 数组(二).files/ls16002.gif" width=378 
      border=0></P>
      <P>(<SPAN lang=zh-cn>分开一段段看是一个个元素,整体看称为一个数组</SPAN>,<SPAN 
      lang=zh-cn>但二者对应的是同一段内存</SPAN>)</P>
      <P> </P>
      <P>第二个要注意的,大家算算相邻的两个元素之间地址差多少?比如<SPAN lang=en-us> &amp;arr[1] - 
      &amp;arr[0] = 1245028 - 1245024 = 4</SPAN>个字节。这4字节,就是每个数组元素的大小。当然,这里是<SPAN 
      lang=en-us>int</SPAN>类型,所以是4字节,如果是一个<SPAN lang=en-us>char</SPAN>或<SPAN 
      lang=en-us>bool </SPAN>类型的数组,则每个元素的大小是1。</P>
      <P> </P>
      <P>根据这两点,我来提几个问题:</P>
      <P> </P>
      <P>1、如果知道某个<SPAN lang=en-us>int</SPAN>类型数组的地址是 <SPAN 
      lang=en-us>1245024</SPAN>,请问下标为5的元素的地址是多少?</P>
      <P>2、如果知道某个<SPAN lang=en-us>char</SPAN>类型的数组,其下标为4的元素地址为:<SPAN 
      lang=en-us>1012349,</SPAN>请问下标为2的元素地址是多少?</P>
      <P> </P>
      <P>由于可通过<SPAN lang=en-us> sizeof() </SPAN>操作来取得各类型数据的大小,所以我们可以假设有一数组:</P>
      <P> </P>
      <P><SPAN lang=en-us>T arr[N];&nbsp;&nbsp; //</SPAN>类型为T,元素个数为N。</P>
      <P> </P>
      <P>存在:</P>
      <P>&nbsp;<SPAN lang=en-us>&nbsp;&nbsp;&nbsp;&nbsp; &amp;arr[n] = arr + 
      sizeof(T) * n ;&nbsp; (0 &lt;= n &lt; N)</SPAN></P>
      <P>或者:</P>
      <P>&nbsp;<SPAN lang=en-us>&nbsp;&nbsp;&nbsp;&nbsp; &amp;arr[n] = arr + 
      sizeof(arr[0]) * n;&nbsp; (0 &lt;= n &lt; N)</SPAN></P>
      <P> </P>
      <P> </P>
      <H4><B><A name=17.1.4>17.1.4</A> 数组访问越界</B></H4>
      <P> </P>
      <P>上一章我们说过“越界”。由于这一问题的重要性,我们需要专门再说一回。</P>
      <P> </P>
      <P><SPAN 
      lang=zh-cn>越界?越谁的界?当然是内存。一个变量存放在内存里,你想读的是这个变量,结果却读过头了,很可能读到了另一个变量的头上。这就造成了越界。有点像你回家时,走过了头,一头撞入邻居家……后果自付。</SPAN></P>
      <P> </P>
      <P><SPAN lang=zh-cn>数组这家伙,大小不定!所以,最容易让程序员走过头。</SPAN></P>
      <P> </P>
      <P>我们通过数组的下标来得到数组内指定索引的元素。这称作对数组的访问。</P>
      <P>如果一个数组定义为有n个元素,那么,对这n个元素(0 到 n-1)的访问都合法,如果对这n个元素之外的访问,就是非法的,称为“越界”。</P>
      <P>比如,定义一个数组:</P>
      <P> </P>
      <P>int arr[10];</P>
      <P> </P>
      <P>那么,我们可以访问&nbsp; arr[0] ~ arr[9] 这10个元素。如果你把下标指定为 10 ,比如:</P>
      <P> </P>
      <P>int a = arr[10]; //访问了第11个元素。</P>
      <P> </P>
      <P>这就造成了数组访问越界。</P>
      <P> </P>
      <P>访问越界会出现什么结果了? </P>
      <P>首先,它<B>并不会</B>造成编译错误! 
      就是说,C,C++的编译器并不判断和指出你的代码“访问越界”了。这将很可怕,也就是说一个明明是错误的东西,就这样“顺利”地通过了编译,就这样不知不觉地,一个BUG,“埋伏”在你的程序里。</P>
      <P>更可怕的是,数组访问越界在运行时,它的表现是不定的,有时似乎什么事也没有,程序一直运行(当然,某些错误结果已造成);有时,则是程序一下子崩溃。</P>
      <P>不要埋怨编译器不能事先发现这个错误,事实上从理论上编译过程就不可能发现这类错误。也不要认为:“我很聪明,我不会犯这种错误的,明明前面定义了10个元素,我不可能在后面写出访问第11个元素的代码!”。</P>
      <P> </P>
      <P>请看下面的代码:</P>
      <P> </P>
      <P><SPAN lang=en-us>int arr[10];</SPAN></P>
      <P> </P>
      <P><SPAN lang=en-us>for(int i=1; i&lt;=10; i++)</SPAN></P>
      <P><SPAN lang=en-us>{</SPAN></P>
      <P><SPAN lang=en-us>&nbsp;&nbsp; cout &lt;&lt; arr[i];</SPAN></P>
      <P><SPAN lang=en-us>}</SPAN></P>
      <P> </P>
      <P>它就越界了<SPAN lang=zh-cn>,</SPAN>你<SPAN lang=zh-cn>看出</SPAN>原因了吗?</P>
      <P> </P>
      <P> </P>
      <P><SPAN lang=zh-cn>再</SPAN>说上一章的成绩查询<SPAN 
      lang=zh-cn>。我们让用户输入学生编号,然后查该学生的成绩。如果代码是这样:</SPAN></P>
      <P> </P>
      <P>int<SPAN lang=zh-cn> </SPAN>cj[100];&nbsp; </P>
      <P>...</P>
      <P> </P>
      <P>//<SPAN lang=zh-cn>让用户输入学生编号,设现实中学生编号由1开始:</SPAN></P>
      <P>cout &lt;&lt; "<SPAN lang=zh-cn>请输入学生编号(在</SPAN>1~100<SPAN 
      lang=zh-cn>之间):</SPAN>"</P>
      <P>int i;</P>
      <P>cin &gt;&gt; i;</P>
      <P> </P>
      <P>//<SPAN lang=zh-cn>输出对应学生的成绩:</SPAN></P>
      <P>cout &lt;&lt; cj[i-1];</P>
      <P> </P>
      <P><SPAN 
      lang=zh-cn>这段代码看上去没有什么逻辑错误啊。可是,某些用户会造成它出错。听话的用户会乖乖地输入1到100之间数字。而调皮的用户呢?可能会输入101,甚至是-1 
      —— 我向来就是这种用户 ——这样程序就会去尝试输出:</SPAN>cj[100] <SPAN lang=zh-cn>或</SPAN> 
      cj[-2]<SPAN lang=zh-cn>。</SPAN></P>
      <P><SPAN lang=zh-cn>解决方法是什么?这里有一个简单,只要多写几个字:</SPAN></P>
      <P> </P>
      <P>...</P>
      <P>cout &lt;&lt; "<SPAN lang=zh-cn>请输入学生编号(在</SPAN>1~100<SPAN 
      lang=zh-cn>之间</SPAN> <SPAN lang=zh-cn>如果不输入这个范围之内数,计算机将爆炸!):</SPAN>"</P>
      <P>int i;</P>
      <P>cin &gt;&gt; i;</P>
      <P>...</P>
      <P> </P>
      <P><SPAN lang=zh-cn>系主任在使用你的这个程序时,十个指头一定在不停地颤抖……</SPAN></P>
      <P><SPAN 
      lang=zh-cn>理智的作法还是让我们程序员来负起这个责任吧,我们需要在输出时,做一个判断,发现用户输入了不在编号范围之内的数,则不输出。正确答案请看上章。</SPAN></P>
      <P> </P>
      <P>为什么数组访问越界会造成莫名其妙的错误? 
      前面一节我们讲到数组占用了一段连续的内存空间。然后,我们可以通过指定数组下标来访问这块内存里的不同位置。因此,当你的下标过大时,访问到的内存,就不再是这个数组“份内”的内存。你访问的,将是其它变量的内存了。 
      前面不是说数组就像一排的宿舍吗?假设有5间,你住在第2间;如果你晚上喝多了,回来时进错了房间,只要你进的还是这5间,那倒不会有大事,可是若是你“越界”了。竟然一头撞入第6间……这第6间会是什么?很可能它是走廊的尽头,结果你一头掉下楼,这在生活中是不幸,可对于程序倒是好事了,因为错误很直接(类似直接死机),你很容易发现。可是,如果第6间是??据我所知,第6间可能是小便处,也可能是女生宿舍。</P>
      <P> </P>
      <H3><B><A name=17.2>17.2</A> 二维数组</B></H3>
      <P> 
      <P>事实要开始变得复杂。
      <P><SPAN 
      lang=zh-cn>生活中,有很多事物,仅仅用一维数组,将无法恰当地被表示。还是说学生成绩管理吧。一个班级30个学员,你把他们编成1到30号,这很好。但现在有两个班级要管理怎么办?人家每个班级都自有自的编号,比如一班学生编是1~30;二班的学生也是1~30。你说,不行,要进行计算机管理,你们两班学员的编号要混在一起,从1号编到60号。</SPAN>
      <P> 
      <P><SPAN 
      lang=zh-cn>另外一种情况,仍然只有一个班级30人。但这回他们站到了操场,他们要做广播体操,排成5行6列。这时所有老师都不管学员的编号了,老师会这样喊:“</SPAN>第2排第4个同学,就说你啦<SPAN 
      lang=zh-cn>!踢错脚了!”。假设我们的校长大人要坐在校长室里,通过一个装有监视器的电脑查看全校学员做广播体操,这时,我们也需要一个多维数组。</SPAN>
      <P> 
      <H4><B><A name=17.2.1>17.2.1</A> <SPAN lang=zh-cn>二维数组基本语法</SPAN></B></H4>
      <P> 
      <P><SPAN lang=zh-cn>语法:定义一个二维数组。</SPAN>
      <P> 
      <P><SPAN lang=zh-cn>数据类型</SPAN>&nbsp; <SPAN lang=zh-cn>数组名</SPAN>[<SPAN 
      lang=zh-cn>第二维大小</SPAN>][<SPAN lang=zh-cn>第一维大小</SPAN>]<SPAN 
      lang=en-us>;</SPAN>
      <P> 
      <P><SPAN lang=zh-cn>举例:</SPAN>
      <P> 
      <P>int arr[5][6];<SPAN lang=en-us>&nbsp; //</SPAN>注意,以分号结束。
      <P> 
      <P><SPAN 
      lang=zh-cn>这就是操场上那个“5行6列的学生阵”。当然,哪个是行哪个列凭你的习惯。如果数人头时,喜欢一列一列地数,那你也可以当成它是“5列6行”——台湾人好像有这怪僻——我们还是把它看成5行6列吧。</SPAN>
      <P> 
      <P><SPAN lang=zh-cn>现在:</SPAN>
      <P> 
      <P><SPAN lang=zh-cn>第一排第一个学员是哪个?答:</SPAN>arr[0][0]<SPAN 
lang=zh-cn>;</SPAN>
      <P><SPAN lang=zh-cn>第二排第三个学员是?答:</SPAN>arr[1][2]<SPAN lang=zh-cn>;</SPAN>
      <P> 
      <P><SPAN 
      lang=zh-cn>也不并不困难,对不?惟一别扭的其实还是那个老问题:现实上很多东西都是从1开始计数,而在C里,总是要从0开始计数。</SPAN>
      <P> 
      <P><SPAN lang=zh-cn>接下来,校长说,第一排的全体做得很好啊,他们的广播体操得分全部加上5分!程序如何写?答:</SPAN>
      <P> 
      <P>for(int col=0; col&lt;6; col++)
      <P>{
      <P>&nbsp;&nbsp; arr[0][col] += 5;
      <P>}
      <P> 
      <P><SPAN lang=zh-cn>对了,这里我没有用</SPAN> i <SPAN 
      lang=zh-cn>来作循环的增量,而是用</SPAN>col<SPAN lang=zh-cn>。因为</SPAN>col<SPAN 
      lang=zh-cn>在英语里表示“列”,这样更直观对不?下面要用到行,则用</SPAN>row<SPAN lang=zh-cn>。</SPAN>
      <P> 
      <P><SPAN 
      lang=zh-cn>广播操做到“跳跃运动”了,校长大人在办公室蹦了两下,感觉自已青春依旧,大为开心,决定给所有学员都加1分,程序如何写?答:</SPAN>
      <P> 
      <P>for(int row = 0; row &lt; 5; row++)
      <P>{
      <P>&nbsp;&nbsp;&nbsp; for(int col = 0; col &lt; 6; col++)
      <P>&nbsp;&nbsp;&nbsp; {
      <P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; arr[row][col] += 1;
      <P>&nbsp;&nbsp;&nbsp; }
      <P>}
      <P> 
      <P><SPAN lang=zh-cn>看明白了吗?在二维数组,要确定一个元素,必须使用两个下标。</SPAN>
      <P><SPAN lang=zh-cn>另外,这个例子也演示了如何遍历一个二维数组:使用双层循环。第一层循环让</SPAN><SPAN 
      lang=en-us>row </SPAN>从<SPAN lang=en-us> 0</SPAN>到<SPAN lang=en-us> 
      4,</SPAN> 用于遍历每一行;<SPAN lang=en-us>col</SPAN>从0到5,遍历每一行中的每一列。
      <P><SPAN lang=en-us>(</SPAN>遍历:访问某一集合中的每一个元素的过程<SPAN lang=en-us>)</SPAN>
      <P> 
      <P><SPAN lang=zh-cn>大家把这两个程序都实际试一试.</SPAN>
      <P> 
      <H4><B><A name=17.2.2>17.2.2</A> <SPAN lang=zh-cn>二维数组初始化</SPAN></B></H4>
      <P> 
      <P><SPAN lang=zh-cn>一维数组可以定义时初始化:</SPAN>
      <P> 
      <P>int arr[] = {0,1,2,3,4};
      <P> 
      <P><SPAN lang=zh-cn>二维数组也可以:</SPAN>
      <P> 
      <P>int arr[5][6] =
      <P>{
      <P>&nbsp;&nbsp;&nbsp;&nbsp; { 0, 1, 2, 3, 4, 
      5},&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
      <P>&nbsp;&nbsp;&nbsp;&nbsp; {10,11,12,13,14,15},
      <P>&nbsp;&nbsp;&nbsp;&nbsp; {20,21,22,23,24,25},
      <P>&nbsp;&nbsp;&nbsp;&nbsp; {30,31,32,33,34,35},
      <P>&nbsp;&nbsp;&nbsp;&nbsp; {40,41,42,43,44,45},
      <P>};<SPAN lang=en-us>&nbsp; //</SPAN>注意,同样以分号结束
      <P> 
      <P>初始化二维数组使用了两层{},内层初始化第一维,每个内层之间用逗号分隔。
      <P> </P>
      <P><B>例二:</B></P>
      <P> 
      <P>我们可以把这个数组通过双层循环输出:
      <P> 
      <P><SPAN lang=en-us>for(int row = 0; row &lt; 5; row++)</SPAN>
      <P><SPAN lang=en-us>{&nbsp;&nbsp; </SPAN>
      <P><SPAN lang=en-us>&nbsp;&nbsp;&nbsp; for(int col = 0; col &lt; 6; 
      col++)</SPAN>
      <P><SPAN lang=en-us>&nbsp;&nbsp;&nbsp; {</SPAN>
      <P><SPAN lang=en-us>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; cout 
      &lt;&lt; arr[row][col] &lt;&lt; endl;</SPAN>
      <P><SPAN lang=en-us>&nbsp;&nbsp;&nbsp; }</SPAN>
      <P><SPAN lang=en-us>}</SPAN>
      <P> 
      <P>这段代码会把二维数组<SPAN 
      lang=en-us>arr</SPAN>中的所有元素(5*6=30个),一行一个地,一古脑地输出,并不适于我们了解它的二维结构。我们在输出上做些修饰:
      <P> 
      <P><SPAN lang=en-us>for(int row = 0; row &lt; 5; row++)</SPAN>
      <P><SPAN lang=en-us>{&nbsp;&nbsp; </SPAN>
      <P><SPAN lang=en-us>&nbsp;&nbsp;&nbsp; cout &lt;&lt; "</SPAN>第<SPAN 
      lang=en-us>" &lt;&lt; row + 1 &lt;&lt; "</SPAN>行<SPAN lang=en-us>: 
"</SPAN>
      <P> 
      <P><SPAN lang=en-us>&nbsp;&nbsp;&nbsp; for(int col = 0; col &lt; 6; 
      col++)</SPAN>

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -