指针和多维数组

数组名是特殊的指针

数组是一个特殊的指针,多维数组也是更为复杂的数组,它们的关系是什么样的呢?

我们通过一个简单的例子来比较形象的了解指针和多维数组:

int a[2][3];

这是一个2*3的二维数组,首先我们清楚数组名就是指向数组首元素的常量指针(它不可以指向其他部分,可以对指向的元素进行任意修改);其次C语言中所谓的多维数组,即是数组的数组,2*3的二维数组,本质上为2个有包含3个int的数据的数组。所以现在我们就可以解释a的含义:

a == &a[0]

那么对于a[0]也有

a[0] == &a[0][0]

这时候我们就也可以得到另一个特殊的结论:a == a[0],字面上看起来很难理解,这是因为对于一个指针它指向一个元素有两个要素:第一为这个元素的首地址,第二为这个元素的类型(这也是我们在使用指针所必要要求的必须指向与它类型相同的元素),所以a是一个指向包含3个int元素的数组指针,a[0]是指向int元素的指针。在比较两者时即比较首地址,无疑都是这个二维数组的首地址。

  1.  #include <stdio.h>
     
     int main (void) {
      int a[2][3];
      if (a == &a[0])
          printf("yes!\n");
      if (a[0] == &a[0][0])
          printf("yes!\n");
      if (a == a[0])
          printf("yes!");
      return 0;
     }

但是两者仍然有差别,这种差别是在类型上的,也可谓是根本上的,通过指针的增加运算我们可以看到他们的不同:

  1.  #include <stdio.h>
     
     int main (void) {
      int a[2][3];
      int *p = a;
      int *pp = a[0];
      if (a == a[0])
          printf("yes!\n");
      if (a+1 == a[0]+1)
          printf("yes!\n");
      else {
          printf("no!\n");
          printf("a = %p\n", a);
          printf("p = %p\n", a+1);
          printf("pp = %p", a[0]+1);
      }
     
      return 0;
     }
     
     /**
      yes!
      no!
      a = 0061FF00
      p = 0061FF0C
      pp = 0061FF04
      因为a为int (*)[3]类型,所以a+1移动了3*4=12个储存单元,末位为C;a[0]为int *类型,所以a[0]+1移动了4个单元,这时候它们不再相等。
     **/

通过指针指向多维数组

因为C语言要求指针在指向一个元素时类型必须同元素一致,所以我们想使用指针指向数组时不能简单使用指向int类型的指针。例如对于二维数组a[2][3]

  1.  int main (void) {
      int a[2][3];
      int (*p)[3] = a;
      // int (*p1)[2] = a;这样是不对的,因为C语言二维数组的声明意义为2个长度为3的整型数组而不是相反的
      return 0;
     }

我们使用int (*p)[3]而不是int *p[3],因为前者表示指向含有三个整数数组的指针,后者为含有3个整型指针的数组。

我们也能通过解引用这样的指针对于数组内容进行访问。

我们通过讨论数组名这个特殊的指针来了解指向多维数组的指针,a == &a[0],所以*a = a[0]为第一个指向长度为3数组的指针;a[0] == &a[0][0],所以*a[0] = a[0][0]这样我们就访问了二维数组的第一个元素,所以**a == a[0][0],我们可以通过多次解引用对多维数组元素进行访问,对于二维数组a[1][2]等价于*(*(a+1)+2)

但是通过这样对一个数组进行访问往往会造成费解,而且有时候会出现不安全的现象:

  1.  #include <stdio.h>
     
     int main (void) {
      int a = 20;
      const int b = 30;
      int *p1 = &a;
      const int *p2 = &b;// 指向常量的指针指向常量
      const int **pp2;// 指向 指向常量的指针 的指针
      // p1 = p2;// 使用普通指针指向指向常量的指针这样的行为并不可行!
      p2 = p1;// 把普通指针赋给指向常量的指针时时没有问题的
     
      // 下面是一种值得警惕的情况
      printf("b = %d\n", b);
      pp2 = &p1;// 这样的行为也是不安全的,称为为嵌套指针类型赋值,这样会使得const修饰符失效
      *pp2 = &b;
      *p1 = 10;
      printf("b = %d", b);
      return 0;
     }
     
     /**
      b = 30
      b = 10
     **/

我们惊奇发现b竟然也被修改了!因为我们让pp2指向指针p1。然后因为pp2是指向常量的指针,所以*pp2也可以指向常量b,但是其实*pp2是p1!这时候我们就能通过p1修改常量b的值,这样行为是很危险的!所以不要使用嵌套指针。

C语言中指针和多维数组的更多相关文章

  1. c语言中指针和多维数组的理解

    1.复习指针和数组之间的特殊关系:不带方括号的数组名是一个指针,指向该数组的第一个元素. 2.多维数组: ][];//声明一个二维数组作为举例 a.理解方式1:可以将数组看成行和列构成,即理解成2行4 ...

  2. 以杨辉三角为例,从内存角度简单分析C语言中的动态二维数组

    学C语言,一定绕不过指针这一大难关,而指针最让人头疼的就是各种指向关系,一阶的指针还比较容易掌握,但一旦阶数一高,就很容易理不清楚其中的指向关系,现在我将通过杨辉三角为例,我会用四种方法从内存的角度简 ...

  3. C语言中如何将二维数组作为函数的参数传递

    今天写程序的时候要用到二维数组作参数传给一个函数,我发现将二维数组作参数进行传递还不是想象得那么简单里,但是最后我也解决了遇到的问题,所以这篇文章主要介绍如何处理二维数组当作参数传递的情况,希望大家不 ...

  4. 论C语言中二级指针和二维数组之间的区别

    刚开始学习C语言的时候,觉得一个数组可以定义一个一级指针去访问,想当然的就觉得可以定义一个二级指针去访问二维数组.很显然这是错误的. 我们来看看C语言的数组在内存中的存储方式. 实际上C语言中的数组, ...

  5. C语言中指针和数组

    C语言数组与指针的那些事儿 在C语言中,要说到哪一部分最难搞,首当其冲就是指针,指针永远是个让人又爱又恨的东西,用好了可以事半功倍,用不好,就会有改不完的bug和通不完的宵.但是程序员一般都有一种迷之 ...

  6. 图解c/c++多级指针与“多维”数组

    声明:本文为原创博文,如有转载,请注明出处.若本文有编辑错误.概念错误或者逻辑错误,请予以指正,谢谢. 指针与数组是C/C++编程中非常重要的元素,同时也是较难以理解的.其中,多级指针与“多维”数组更 ...

  7. 图解C/C++多级指针与多维数组

    声明:本文转自 chenyang_yao ,欢迎阅读原文. 指针与数组是C/C++编程中非常重要的元素,同时也是较难以理解的.其中,多级指针与“多维”数组更是让很多人云里雾里,其实,只要掌握一定的方法 ...

  8. 编程基础-c语言中指针、sizeof用法总结

    1.指针 学习 C 语言的指针既简单又有趣.通过指针,可以简化一些 C 编程任务的执行,还有一些任务,如动态内存分配,没有指针是无法执行的.所以,想要成为一名优秀的 C 程序员,学习指针是很有必要的. ...

  9. <转载>c++中new一个二维数组

    原文连接 在c++中定义一个二维数组时有多种方式,下面是几种定义方式的说明:其中dataType 表示数据类型,如int  byte  long... 1.dataType (*num)[n] = n ...

随机推荐

  1. Atcoder ABC161 A~E

    传送门 A - ABC Swap 1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 ...

  2. Codeforces Round #655 (Div. 2) A. Omkar and Completion (构造)

    题意:构造一个长度为\(n\)的序列,要求所有元素总和不大于\(1000\),并且任意两项的和不等于另外一项. 题解:全构造\(1\)就好了. 代码: int t; int n; int main() ...

  3. .net core 更换yum源 / “No package libgdiplus-devel available.” 错误解决方法

    安装 libgdiplus-devel yum install libgdiplus-devel 如果出现错误 No package libgdiplus-devel available. 原因可能是 ...

  4. 用阿里云ecs部署kubernetes/K8S的坑(VIP、slb、flannel、gw模式)

    1 阿里云ecs不支持keepalived vip 1.1 场景描述 本来计划用keepalived配合nginx做VIP漂移,用以反代多台master的apiserver的6443端口,结果部署了v ...

  5. Eclipce怎么恢复误删类

    选择误删除文件在eclipse所在包(文件夹) 在包上单击右键. 选择restore from local history... 在弹出的对话框中选择需要恢复的文件

  6. 014.NET5_MVC_Razor扩展Html控件02

    第二种方法: 通过一个后台方法,返回一个不存在的html标签字符串,在读取的时候,通过后台方法去渲染成需要的标签和内容: 1. 定义一个普通类,类名称建议以TagHelper结尾,并且给类添加特性[H ...

  7. PPT online viewer

    PPT online viewer PPT 在线查看器 SpeakerDeck https://speakerdeck.com/xgqfrms/python?slide=3 SlideShare ht ...

  8. 图解 git workflow

    图解 git workflow 图解 git 工作流 git-flow https://www.git-tower.com/learn/git/ebook/cn/command-line/advanc ...

  9. Web Performance API

    Web Performance API 性能监测/性能优化 https://developer.mozilla.org/en-US/docs/Web/API/Performance https://d ...

  10. git cli all in one

    git cli all in one https://www.atlassian.com/git/tutorials/learn-git-with-bitbucket-cloud git create ...