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

今天呢 , 我们就来"研究"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 个回复

正序浏览
赞赞赞赞赞赞赞赞赞赞赞赞赞赞啧啧啧
回复 使用道具 举报
q为什么要点招,好害怕
回复 使用道具 举报
说的我上了2周的都怕了
回复 使用道具 举报
为什么要点招,好害怕
回复 使用道具 举报
Mark   Mark
回复 使用道具 举报
加油,加油。
回复 使用道具 举报
考试,不开心,一周小考。
回复 使用道具 举报
瞧一瞧  看一看  
回复 使用道具 举报
厉害啊!
回复 使用道具 举报
Mark  //Mark
回复 使用道具 举报
牛逼!!!
回复 使用道具 举报
表示已经露骨的认知
回复 使用道具 举报
五一快乐
回复 使用道具 举报
来逛逛,签个到
回复 使用道具 举报
来逛逛,签个到
回复 使用道具 举报
来逛逛 签个到
回复 使用道具 举报
放假的我也不忘来黑马论坛水一贴。
回复 使用道具 举报
个人觉得C语言很厉害。
回复 使用道具 举报
C语言,哪有我大java厉害!
回复 使用道具 举报
看来对c深有研究哈
回复 使用道具 举报
123下一页
您需要登录后才可以回帖 登录 | 加入黑马