【C/C++和指针】深度解析---指针与数组 【精华】

一,引例子

二维数组可以使用指向数组的指针代替,而指针数组才可以用指向指针的指针代替。

[html] view plaincopy
  1. #include<iostream>  
  2. using namespace std;  
  3.   
  4. void main()  
  5. {  
  6.      char *a[]={"Hello","the","World"}; //指针数组  
  7.   
  8.      char **pa=a; //指向指针的指针  
  9.   
  10.      pa++;   
  11.   
  12.      cout<<*pa;  
  13. }  

 

类型确定的数组,元素的类型和元素的个数是确定的;数组退化为对应的指针,元素的类型保持一致;

所谓的二维数组只是数组的数组,因此二维数组不能退化为二级指针,而只能退化为指向一维数组的指针

例子:

#include <iostream.h> 
void main()

    int a[2][3];    //二维数组

    int **p=a;      //二维数组不能退化为指向指针的指针

请问为什么是错误的??(请不要说数组名是一个指针这个我知道,我想知道为什么不能用二级指针指向二维数组)

  

解析:首先数组和指针的概念你没分清楚,数组的本质你没搞清楚。这是导致问题出现的根源。

      int x[5]; 这个定义里面,我们说定义了一个数组x,此数组有5个数组元素,元素的类型为int类型。

      首先要问的是,x到底为什么东西?x是数组名,x代表了数组第一个元素的首地址。没错,x确实是数组的名字,x的值也确实是第一个数组元素的地址值。

      注意这里我们说x代表的值与数组第一个元素的地址值相等,但类型不一样

      那么数组x的类型到底是什么呢? 有人说就是int * 类型。有如下语句可以做证:

                         int *p=x; //这句话是正确的。

      数组x的类型真是int *吗?我们说不是,因为下面的语句是不正确的:

                         int a=10; x=&a; // int *类型的变量时可以接受值的。

      所以x不是int*  那么我们可以猜测x的类型是不是 int *const呢?也就是说x是一个地址值不可以改变的指针。这句话貌似有点正确。但是请大家看看下面的例子:

                         int x[5]={0}; int a=sizeof(x); // a的值到底是多少?

     实际上这里a的值是5*4=20 这里是整个数组占用的字节数。 我们不是说x的类型是int * const类型的吗,也就是x应该是一个指针类型,应该是4个字节的啊,为什么sizeof出来时整个数组占用的字节数呢?例如  sizeof(int *)这个的结果就是4。所以有此可以看出,x的类型并不是int*,也不是int * const。  i

       int x[5];中的x到底是什么呢,我们说x是数组,此数组有5个元素,并且每个元素都是int类型。 我们有一个识别数据类型的规律例如: 

       int x; //x类型为int

       int *x;//x类型为int *

       int **x;//x类型为int **

       int (*x)[10];//x类型为int(*)[10]实际上是指向数组的指针

       int (*x)(int ,int);//x的类型为int(*)(int,int)实际上是指向函数的指针 

    由此可以看出,一个符号是什么数据类型,我们只要在其定义的表达式中去掉符号本身剩下的就是符号的类型了。

    照此推断,int x[5];中x的类型应该是 int [5]这个类型,可以看出此类型并不是int *类型。 

    那么int x[5];中的x可以这样赋值: int *p=x; 为什么呢?只能说这里面将x的类型隐式转换为了int *类型。所以这里是可以赋值的,因为进行了类型转换。

   再请看下面的例子: 

         void function(int x[5])

         {  

             cout<<sizeof(x)<<endl; //这里输出4

          }  为什么会输出4,而不是4*5呢,可以看出上面的函数形参实际上类型是int*,并不是数组类型,

       所以我们在定义函数的时候,下面的都是与上面等价的: 

       void function(int x[])//元素个数是多少可以省略 {   cout<<sizeof(x)<<endl; //这里输出4 }

       void function(int *x) //直接写成指针变量也没错 {   cout<<sizeof(x)<<endl; //这里输出4 } 

  

   那么我们看一个类似的问题:

          int x[5]; int **p=&x; //为什么会报错? 因为类型不匹配。 

         p的类型是int **,而&x的类型却不是int **。 &x的类型实际上是int(*)[5],因为取的是x的地址,也就是说这个地址是数组的地址,并不是指向数组第一个元素的指针的指针(也就是二维指针),而是整个数组的地址。

         所以我们可以改成下面的: int (*p)[5]=&x;//这就对了。

 

二,指向数组的指针,和指向数组元素的指针有什么不同? 

    例如int *p;我们要注意的是,p的类型是int*,p占用的空间4个字节,p指向的数据类型是int。P指向的数据类型占用4个字节。

    所以对于指针变量,我们要明白指针变量本身是占用空间的,本身是有类型的,其次指针变量所指向的空间是有类型的,是有空间的。 

    那么int *p; char *p1; 对于指针变量来说p,p1里面都放的是地址值,说白了就是一个数值,他们都占用4个字节的空间,但是他们的类型不一样,p里面的地址指向的是int类型的数据,p1指向的是char类型的数据,这主要体现在p++与p1++中他们在内存中移动的字节数是不一样的,我们假设int占4个字节,char占1个字节。那么对于p来说向前移动了4个字节,p1来说移动了一个字节。这就是他们的类型不同,导致运算过程中的不同。 int x[5];

     int (*p3)[5]; 此时p3指向数组x,那么p3++实际上向前移动了多少呢,可以算出移动了4*5个字节。也就是p3指向的是一个数组,是整个数组,所以p3移动的时候是将一个数组当做一个整体来看待的。所以向前移动了一整个数组的距离。  再看你的问题之前,我们来看一个类似的问题: 

     int a[2][3]; int  **p=&a; //这里我用&a来赋值行不行呢。是不行的。  这里为什么是错误的,原因就是因为&a的类型不是int**类型。所以类型不兼容,导致不能赋值,同时这两种类型是不可以相互转换的。那么&a到底是一个什么样的类型呢。 我们说&a取的是整个数组的地址,那么&a自然就是指向整个数组的指针了。

     int (*p)[2][3]=&a; 此时这样赋值才是正确的。如果我们要用a直接赋值,那该定义一个什么样的变量来接受它呢,首先要明白,数组名代表的地址类型是指向数组的第一个元素的指针,

    例如:  int a[10]; int *p=a; 实际上这里与 int *p=&a[0];是等价的。因为指向a[0]的指针类型就是int*类型。  那么&a的是取数组的地址,其类型是指向数组的指针,而不是指向数组第一个元素的指针,整个是要区别的,他们的类型就不一样。 int(*p)[10]=&a; 

     所以说这里的a和&a绝对不是同一个东西,虽然本质上他们的地址值是一样的,但是他们的类型不一样。就决定他们代表不同的意义。 

     那么刚刚说了对于下面的例子:

            int a[2][3];

            int (*p)[2][3]=&a;

     我们可以定义这样的一个变量p来接受&a的值。 

     那么我们要接受a应该定义一个什么样的变量呢。a[2][3]是一个二维数组,可以看成是这样的a是一个数组,具有两个元素,分别为a[0],a[1]其中这两个元素的值a[0],a[1]他们的值又是一个具有3个元素的数组。此时我们可以将a[0],a[1]看成是数组名,那么a[0][0]就是数组a[0]的第0个元素了。对应关系如下: a[0] ---->  a[0][0],a[0][1],a[0][2] a[1] ---->  a[1][0],a[1][1],a[1][2]  那么a到底是什么,其实a数组有两个元素,a[0],a[1],那么a的值自然就是其第一个元素的地址了,也就是&a[0]了。这是一个什么类型?  我们知道如果我们将a[0]看成一个整体,例如我们用A来代替a[0],那么A[0],A[1]就相当于a[0][0],a[0][1] 。 此时A就是一个int类型的数组,&A,的类型实际上就是 int(*p)[3]这个类型。  所以下面的代码也是正确的:   

      int a[2][3];  int(*p)[3]=a; //所以对于你的问题,可以这样子


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/450710.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Redis常见面试题详解

文章目录1. Redis1.1 Redis可以用来做什么&#xff1f;1.2 Redis和传统的关系型数据库有什么不同&#xff1f;1.3 Redis有哪些数据类型&#xff1f;1.4 Redis是单线程的&#xff0c;为什么还能这么快&#xff1f;1.5 Redis在持久化时fork出一个子进程&#xff0c;这时已经有两个…

IntelliJ 创建main函数、for循环、输出语句快捷键

前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到教程。 今天偶然发现了IntelliJ中 创建main函数的快捷键&#xff0c;依次还有for循环&#xff0c;System.out.println(); 在编写代码的时候直接…

CentOS新增用户并授予sudo权限

2019独角兽企业重金招聘Python工程师标准>>> 新增用户 添加用户useradd demo设置用户密码passwd demo授予sudo权限 输入以下命令&#xff0c;编辑sudoers配置&#xff1a;visudo找到以下行root ALL(ALL) ALL增加以下内容&#xff1a;demo ALL(ALL) ALL保存后登录dem…

跨站脚本功攻击,xss,一个简单的例子让你知道什么是xss攻击

跨站脚本功攻击&#xff0c;xss&#xff0c;一个简单的例子让你知道什么是xss攻击 一、总结 一句话总结&#xff1a;比如用户留言功能&#xff0c;用户留言中写的是网页可执行代码&#xff0c;例如js代码&#xff0c;然后这段代码在可看到这段留言的不同一户的显示上就会执行。…

用业绩建立信任

当年从香港到美国&#xff0c;对于中学毕业的郭尊华来说&#xff0c;算是一个失败。他尽管家境普通&#xff0c;但因为成绩不佳进不了香港的大学&#xff0c;不得不远赴美国读书。在课余做过挖地、当侍者等多份工作的他&#xff0c;在大三的时候终于得到一份初级工程师的工作。…

idea 快捷键 小结

前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到教程。1.setting 打开快捷键&#xff1a;ctrl alt s 在 file - setting 中。 2.回到默认设置&#xff1a;删除如下路径的文件夹 C:\Users…

Jenkins常用插件

Generic Webhook Trigger Plugin触发器webhook用户触发构建Deploy to container Plugin部署到tomcatGradle Plugin Gradle插件构建项目Maven Plugin Maven插件构建项目Git Plugin Git插件克隆项目GitHub plugin GitHub插件集成githubSSH Slaves plugin SSH插件用于远程登录Buil…

spark广播变量 和 累加器

1 为什么使用广播变量 和 累加器 变量存在的问题&#xff1a;在spark程序中&#xff0c;当一个传递给Spark操作(例如map和reduce)的函数在远程节点上面运行时&#xff0c;Spark操作实际上操作的是这个函数所用变量的一个独立副本。这些变量会被复制到每台机器上&#xff0c;并且…

分布式常见面试题详解

文章目录1. 分布式1.1 什么是CAP原则&#xff1f;1.2 说一说你对高并发的理解1.3 如何实现分布式存储&#xff1f;1.4 说一说你对分布式事务的了解1.5 分布式系统如何保证最终一致性&#xff1f;1.6 谈谈你对分布式的单点问题的了解1.7 HTTP和RPC有什么区别&#xff1f;1.7 HTT…

C11标准委员会成员解读C语言新标准

导读&#xff1a;C语言国际标准新的新草案之前已经公布&#xff0c;新标准提高了对C的兼容性&#xff0c;并将新的特性增加到C语言中。此外支持多线程的功能也受到了开发者的关注&#xff0c;基于ISO/IEC TR 19769:2004规范下支持Unicode&#xff0c;提供更多用于查询浮点数类型…

如何将拷贝过来的数据 *.ibd 文件生效

1.将拷贝的数据文件 "qqq.idb"放在自己的数据库中. 一般存放在 mysql/ data/ databasename 下 2. "qqq.idb" 改个名字-->"qqq--.idb", 主要是避免冲突&#xff01; 3.执行 create table qqq(...) 语句&#xff0c;此时除了会生成一个 qqq…

windows 下启动zookeeper的zkServer.cmd服务闪退

前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到教程。 解决方案把conf目录下的默认zoo_sample.cfg的文件名字改成zoo.cfg 即可&#xff1a; 以上转自&#xff1a;http://blog.csdn.net/qq4960…

设计模式常见面试真题详解

文章目录1. 设计模式1.1 说一说设计模式的六大原则1.2 说一下六大原则中的开闭原则1.3 手写一个单例模式1.4 手写一个线程安全的单例模式1.5 说一说你对工厂模式的理解1.6 简单工厂模式和抽象工厂模式有什么区别&#xff1f;1.7 如何实现工厂模式&#xff1f;1.8 说一说你策略模…

Android内存优化之内存泄漏

内存泄漏 内存泄漏一般有以下几种情况&#xff1a;单例、静态变量、Handler、匿名内部类、资源使用未关闭 单例导致的内存泄漏 单例的情况主要是因为单例的生命周期比较长&#xff0c;如果引用的一些资源&#xff08;比如Context、图片等&#xff09;没有做特殊处理&#xff0c…

cmd - 使用curl命令的注意点

前言 最近在cmd中使用curl命令来测试rest api&#xff0c;发现有不少问题&#xff0c;这里记录一下。 在cmd中使用curl命令的注意事项 json不能由单引号包括起来json数据里的双引号要用反斜杠\转义json数据里不能带有空格如果想要在json数据里使用空格则必须用双引号将整个json…

指针常见定义

再给出常用的C变量的定义方式&#xff1a;a) 一个整型数&#xff08;An integer&#xff09; b) 一个指向整型数的指针&#xff08;A pointer to an integer&#xff09; c) 一个指向指针的的指针&#xff0c;它指向的指针是指向一个整型数&#xff08;A pointer to a pointer …

场景应用题目常见面试真题详解

文章目录1. 场景应用1.1 微信红包相关问题1.2 秒杀系统相关问题1.3 扫码登录流程1.4 如何实现单点登录&#xff1f;1.5 如何设计一个本地缓存&#xff1f;1. 场景应用 1.1 微信红包相关问题 参考答案 概况&#xff1a;2014年微信红包使用数据库硬抗整个流量&#xff0c;2015…

后Kubernetes时代的微服务

\本文要点\\当前微服务架构依然是最流行的分布式系统架构风格。Kubernetes和云原生运动已大规模地重新定义了应用设计和开发中的一些方面。\\t在云原生平台上&#xff0c;服务仅具备可观测性是不够的。更基本的先决条件是使用检查健康、响应信号、声明资源消耗等手段实现微服务…

Dynamics CRM On-Premise V9安装手记

下载地址&#xff1a; https://download.microsoft.com/download/A/D/D/ADDD6898-4EFA-46FA-80B6-6FE9A3CDED63/CRM9.0-Server-CHS-amd64.exe 安装支持Windows 2016 及SQL Server 2016 SP2以上版本 我想安装了All in one的&#xff0c;就想着用最新的SQLServer 2017&#xff0c…

金山网络CEO傅盛:简约之美

摘要&#xff1a;金山网络CEO傅盛带来了主题为《简约之美》的精彩演讲。他表示由于时代的变迁&#xff0c;红海的竞争&#xff0c;项目的需求等原因&#xff0c;若想项目取得成功&#xff0c;唯有简单才是王道&#xff0c;唯有简单定位才能深入人心。那么&#xff0c;如何做到简…