A股上市公司传智教育(股票代码 003032)旗下技术交流社区北京昌平校区

 找回密码
 加入黑马

QQ登录

只需一步,快速开始

今天呢 , 我们就来"研究"2个知识点 , 如果你能露骨的认知 ,那么数组的本质和数组指针这个地方对大家也就没问题了
C语言是一门面向过程的语言 , 相比现在比较流行的面向对象 , 有助于提高大家的思维逻辑 , 利于理解语言内在的好处 , 学习C语言应该多理解 , 多思考 。
从一维数组入手 :
C语言的数组名到底是什么 ? 我相信肯定有同学说 : 数组首地址
那么我要准确的回答 , 数组名代表的是数组的地址呢 ? 还是数组第一个元素的地址呢?
那么我们来验证一下 :
一.打印地址
  1. int main(int argc, const char * argv[]) {
  2.    
  3.     int arr[5] = {1,3,5,7,9};
  4.    
  5.     printf("arr = %p\n",arr);
  6.    
  7.     printf("&arr = %p\n",&arr);
  8.    
  9.     printf("&arr[0]=%p\n",&arr[0]);
  10.    
  11.     /*
  12.      arr = 0x7fff5fbff770
  13.      &arr = 0x7fff5fbff770
  14.      &arr[0]=0x7fff5fbff770
  15.      */
  16.    
  17.     return 0;
  18. }
复制代码
你会发现 , arr  ==  &arr == &arr[0]  , 这个也就是我们常说的数组首地址
但是我们并不能验证它到底是怎么呢 ? 接着看 :
二.首先我们先遍历这个数组的元素的地址 :
  1.     printf("------------遍历数组元素地址 :\n");
  2.    
  3.     for (int i = 0 ; i < 5; i++) {
  4.         
  5.         printf("&arr[%d]=%p\n",i,&arr[i]);
  6.         
  7.     }
复制代码
------------遍历数组元素地址 :
&arr[0]=0x7fff5fbff770
&arr[1]=0x7fff5fbff774
&arr[2]=0x7fff5fbff778
&arr[3]=0x7fff5fbff77c
&arr[4]=0x7fff5fbff780
三 , 我们让arr , &arr , &arr[0]都+1
  1.     printf("arr+1 = %p\n",arr+1);
  2.     printf("&arr+1 = %p\n",&arr+1);
  3.     printf("&arr[0]+1=%p\n",&arr[0]+1);
复制代码
arr = 0x7fff5fbff770
&arr = 0x7fff5fbff770
&arr[0]=0x7fff5fbff770
------------遍历数组元素地址 :
&arr[0]=0x7fff5fbff770
&arr[1]=0x7fff5fbff774
&arr[2]=0x7fff5fbff778
&arr[3]=0x7fff5fbff77c
&arr[4]=0x7fff5fbff780
------------ +1 :
arr+1 = 0x7fff5fbff774
&arr+1 = 0x7fff5fbff784
&arr[0]+1=0x7fff5fbff774

这个时候就有意思了 , 你会发现  arr+1 == &arr[0]+1 , 但是&arr + 1却得到这个很奇怪的值 , 这又是什么呢 ?
我们来画个图 :
   
对着这个图 , 你看懂了什么 , 数一下 :
&arr+1 = 0x7fff5fbff784  是不是正好就是越界的地方啊 , 而arr + 1 ,  &arr[0]+1都为0x7fff5fbff774 , 所以我们就可以得到一个结论
arr为数组首元素的地址 , 也就是&arr[0] , arr+1 , 就相当于 &arr[0]+1 , 也就是以第一个元素地址为长度+1 , 跳到第二个元素的地址
&arr为整个数组的地址 , %arr+1 , 相当于以整个数组为长度+1 , 所以就跳到刚好越界的地方了, 同样我们上图理解 :


二维数组深入 :
我们先看图 , 然后思考 :
arr , &arr , arr[0] , &arr[0] , &arr[0][0] , 他们之间的关系
  1. //定义一个二维数组
  2.     int arr[2][3] = {
  3.         1,2,3,  //  arr[0]
  4.         4,5,6   //  arr[1]
  5.     };
  6.    
  7.     printf("------------逐个分析-----------\n");
  8.    
  9.     printf("arr = %p\n",arr);
  10.    
  11.     printf("arr[0] = %p\n",arr[0]);
  12.    
  13.     printf("&arr[0] = %p\n",&arr[0]);
  14.    
  15.     printf("arr[0][0] = %p\n",&arr[0][0]);
  16.    
  17.     printf("&arr = %p\n",&arr);
复制代码
------------逐个分析-----------
arr = 0x7fff5fbff780
arr[0] = 0x7fff5fbff780
&arr[0] = 0x7fff5fbff780
arr[0][0] = 0x7fff5fbff780
&arr = 0x7fff5fbff780

你会发现 : &arr == arr == arr[0] == &arr[0] == &arr[0][0] , 也就是我们俗称的数组首地址 , 那么他们本质真的是同一个东西吗 ? 接下来我们该怎么做 , 没错就是让他们都+1
  1.     printf("------------让他们都+1-----------\n");
  2.    
  3.     printf("arr+1 = %p\n",arr+1);
  4.    
  5.     printf("arr[0]+1 = %p\n",arr[0]+1);
  6.    
  7.     printf("&arr[0]+1 = %p\n",&arr[0]+1);
  8.    
  9.     printf("arr[0][0]+1 = %p\n",&arr[0][0]+1);
  10.    
  11.     printf("&arr+1 = %p\n",&arr+1);
复制代码
------------逐个分析-----------
arr = 0x7fff5fbff780
arr[0] = 0x7fff5fbff780
&arr[0] = 0x7fff5fbff780
arr[0][0] = 0x7fff5fbff780
&arr = 0x7fff5fbff780
------------让他们都+1-----------
arr+1 = 0x7fff5fbff78c
arr[0]+1 = 0x7fff5fbff784
&arr[0]+1 = 0x7fff5fbff78c
arr[0][0]+1 = 0x7fff5fbff784
&arr+1 = 0x7fff5fbff798
这个时候你会发现 arr+1 == &arr[0]+1 都是0x7fff5fbff78c , 解释: 二维数组第一个元素的地址 , 那么二维数组的第一个元素是什么呢 ? 我们可以理解为二维数组的元素是由2个一维数组组成的 , 看图:


arr[0]又是什么 ?    arr[0]+1呢
2种理解方式 :
1. arr[0]是这个二维数组第一行第一列的元素地址 , arr[0]+1 , 则以第一行第一列的元素地址为长度+1 , 跳到第一行第二列的地址上
2. arr[0]是这个二维数组的第一个一维数组里面第一个元素的地址 , arr[0] +1 则跳到这个一维数组的第二个元素的地址上.
arr[0] == &arr[0]

&arr 则是整个二维数组的地址 , &arr+1 , 以整个二维数组为长度+1 , 跳转到越界的地方

总结 :
      &arr  :  整个数组的地址
      arr  :   第一行第一列的元素地址
     &arr[0] :  第一行元素的地址    arr  == &arr[0] , arr+1 = &arr[1]  (第二行) , arr + 2 = &arr[2] (第三行)  ....    以行为长度跳转
      arr[0]  : 第一行第一个元素的地址   
      &arr[0][0] :    第一行第一个元素的地址   arr[0] = &arr[0][0] , arr[1] = &arr[0][1] .....


那么接下来 , 你是否能分别通过下标法和数组名发遍历数组 , 如果能 , 那么此处你将没问题 :

  1.     //遍历二维数组的元素____下标法
  2.     for (int i = 0; i < 2; i++) {
  3.         
  4.         for (int j = 0; j < 3; j++) {
  5.             
  6.             // 下标法 :
  7.             printf("arr[%d][%d]=%d\t",i,j,arr[i][j]);
  8.             
  9.         }
  10.         
  11.         printf("\n");
  12.     }
  13.    
  14.     printf("--------------------------------\n");
  15.     //遍历二维数组的元素的值____数组名法
  16.     for (int i = 0; i < 2; i++) {
  17.         
  18.         for (int j = 0; j < 3; j++) {
  19.             
  20.             // 数组名法  :
  21.             printf("arr[%d][%d]=%d\t",i,j,*(*(arr+i)+j));
  22.             
  23.         }
  24.         
  25.         printf("\n");
  26.     }
复制代码



那么这个数组名法怎么写呢 :  我们来分步骤写 :
  1. arr == &arr[0]
  2. arr+i == &arr[i]
  3. *(arr+i) == arr[i]
  4. *(arr+i)+j == arr[i]+j == *arr[i][j]
  5. *(*(arr+i)+j) == *(arr[i]+j) == arr[i][j]
复制代码
所以 用数组名就是这么遍历数组的

引出指针 , 以及二维数组指针
那么接下来可以引出指针了 , 首先这个是个什么数组 , 二维数组 , 那么我们就会会定义一个二维数组指针
什么是二维数组指针 ?  方便记忆方法 : xxx指针 , 就是指向xxx的指针 , 比如说 函数指针 : 指向函数的指针 , 字符串指针 , 指向字符串的指针 , 数组指针指向数组的指针 , 当然这个只是一个简单暴力的记忆方法 , 要理解
那么怎么定义一个二维数组指针呢 ?
  1. 数据类型 (*指针变量名)[二维数组列数];
  2. 1.先定义后初始化
  3. int (*pa)[3];
  4. pa = arr;

  5. 2. 定义的同时初始化
  6. int (*p)[3] = arr;
复制代码
那么怎么用二维数组指针遍历数组呢 ?

  1. //定义二维数组指针
  2.     int (*p)[3] = arr;
  3.    
  4.     //遍历二维数组的元素的值____指针法
  5.     for (int i = 0; i < 2; i++) {
  6.         
  7.         for (int j = 0; j < 3; j++) {
  8.             
  9.             // 数组名法  :
  10.             printf("arr[%d][%d]=%d\t",i,j,*(*(p+i)+j));
  11.             
  12.         }
  13.         
  14.         printf("\n");
  15.     }
复制代码


那么到这里 , 3中方法遍历数组都说完了, 那么问题来了, 为什么我们要用指针遍历呢 ? 我们不用指针遍历不可以吗?

答案是肯定的 , 我们不用指针 , 用下标法 , 数组名法 , 就是直接获取数组元素的值 , 而用指针法 , 就是间接的获取数组元素的值 ,
打个比方 :
  1. int num = 10;
  2. num = 20 ;
  3. 请问num的值被修改了吗 ?
  4. -----------------------------
  5. int num = 10;
  6. int *p = #
  7. *p = 20;
  8. 请问 , num的值被修改了吗?
复制代码
前者就是直接修改 , 后者, 也就是使用指针是间接修改 , 所以指针的基本用法 , 其实就是间接的获取或者修改变量的值

什么时候用指针而不用数组名呢 ?  再把刚刚的一维数组用3钟方式遍历 :  指针变量  *(p++)  == *(p+i) ;  而  *(arr++)  是错误的  , 因为p是变量 , arr是数组名 , 我们可以认为是常量



好啦 , 差不多到尾声了 , 这个帖子带大家用面向过程的思想 , 理解数组名的意义 , 并引出指针的用法 , 在学习C语言的过程中 , 就应该多思考 , 多理解, 因为C是一门面向过程的语言, 要抓住细节, 以后才更好的理解面向对象的底层 .



43 个回复

倒序浏览
wangchen0357 来自手机 中级黑马 2016-4-26 00:07:25
沙发
不赖,写的。

回复 使用道具 举报
来看看,xue'xi'xue学习学习
回复 使用道具 举报
还可以!!!
回复 使用道具 举报
学习下{:3_46:}
回复 使用道具 举报
这个不错,讲的很详细
回复 使用道具 举报
蒂尔 中级黑马 2016-4-27 19:44:28
7#
必须向大神学习,谢谢分享
回复 使用道具 举报
fkcong 中级黑马 2016-4-27 20:12:27
8#
这个必须学习
回复 使用道具 举报
没看懂,基础不够呀
回复 使用道具 举报
还没学到。。。
回复 使用道具 举报
wuqi 中级黑马 2016-4-27 23:22:42
11#
一语中的啊,一直就记得老师说的就是第一个元素的首地址,根本没有考虑和验证过!
回复 使用道具 举报
一语中的啊,一直就记得老师说的就是第一个元素的首地址,根本没有考虑和验证过!
回复 使用道具 举报
学习了。。。。
回复 使用道具 举报
HSQI 中级黑马 2016-4-28 09:39:36
14#
受教了!!!
回复 使用道具 举报
看的我头昏眼花
回复 使用道具 举报
加油,fighting!!!
回复 使用道具 举报
ios ios ios ios
回复 使用道具 举报
签个到~~~
回复 使用道具 举报
高手啊,
回复 使用道具 举报
加油加油加油加油加油
回复 使用道具 举报
123下一页
您需要登录后才可以回帖 登录 | 加入黑马