最近看C++编程思想,看到第十三章动态内存管理的时候把自己给绕进去了,主要是在数据和指针这块弄混了。现在把找到的一些资料总结如下:

1. 数组是数组,指针是指针,两者并不等价;

2.数组在作为左值的时候一般是数组自己,而放在右值往往被转换成一个常量指针;

3.下标运算符其实是针对指针的,只是数组变成指针后也适用于了数组;

4.对数组取地址符在很早以前是错误的,因为数组名本身就是数组的地址,对地址去地址显然不对,后来被C++标准定为合法的,表示指向数组对象的指针。

对于 int a[100]; int b[10][100];

1. a表示数组本身,放在右值时是数组第一个元素的地址,其类型是&a[0],因此可以有*(a+1); a[i],但是如果是sizeof(a),则为数组的大小100*4。

2. &a的类型是int (*)[100],因此&a + 1得到的地址是a的地址加上整个数组的大小400,但是sizeof(&a)得到的是a的大小400。

3.下标符只能由指针使用,除非是重载,p[1] 等价于*(p+1),在使用二维数组可能会陷入混乱,例如:

  int **ptr = (int **)&b;

ptr[i]的值类型是指针(int *),但是其值是b的元素值(按照内存中的位置顺序取),如果使用ptr[1][0]很可能是未定义的,因为这时是取了b的元素并把它作为指针再取一次值,除非b的元素本来就是指针。

4. a+1的类型是指向a的行元素的指针,对它取值*(a+1)得到的类型是a的行元素的类型。这里是int型指针,和int型的值。

 如果是b的话,b+1的值和*(b+1)的值相同,但是类型不同,b+1表示的是指向数组的普通指针,而*(b+1)表示的是类型为int [100]的数组。

因此sizeof(b+1)=4, 而sizeof(*(b+1)) = 400,但是两者的值都是相同的地址值。

资料:

C语言中,数组和指针都有a[N]取元素的语法,因此也是初学者容易混淆的地方。这个问题要知其所以然并不容易。理解这个问题,需要涉及到C语言中最基本但也最容易忽略的概念:左值/右值 (lvalue/rvalue)。

在开始这个问题之前,首先要区别“变量”和“值”(/对象)是两个不同的概念。“变量”是一块有名字的存储区域,“值”是某种类型的二进制数据,可能存储在某段内存,或者在寄存器、或者是代码中的字面量。“变量”是对某个值起一个名字,这样可以索引到值的存储位置。

C语言中,表达式(expression)的结果是一个“值”。单独的一个变量名(如a)或者单独的字面量(如1)也是表达式。左值/右值 是值的一个基本属性,C语言中非左值即右值。左值表达式表示了某个值的存储位置,右值表达式表示某个值所存储的数据。

左值性的具体讨论请参看参考链接1。一般来说,赋值操作等号左边是(可修改的)左值,右边取右值。所以

int i,j;
i = 0; // OK: i is lvalue, 1 is rvalue
0 = i; // error: 0 is not lvalue
i = j; // OK. why?

为什么可以写 i = j 这样的赋值操作呢?因为C语言中有一个叫做lvalue-to-rvalue conversion的默认类型转换。当赋值操作右边是一个左值时,会默认转换为右值。这样的一个转换代表了对左值表达式 j 所存储内容的一次“读取”。类似的,C语言中按值传参,用左值表达式作为函数参数时,也会进行 lvalue-to-rvalue conversion。

void foo(int);
foo(0); // OK, need rvalue
foo(i); // OK. lvalue-to-rvalue conversion

讲到这里,我们切入正题:数组、指针是左值吗?答案是当然,因为他们都代表了一个存储位置。不同的是,数组a作为变量,所代表的位置是数组元素的存储位置;指针a作为值(右值),是一个内存地址,而作为变量(左值),所代表的位置是这个地址的存储位置。数组是一个不可修改的左值,指针如果没有指定const,则是可以修改的。

int a[4];
int *p, i, j;
a = 1; // error
p = &i; // OK
p = &(a[1]); // OK

数组和指针的存储区域如图所示(仅为示意,并不代表实际的内存layout)

理解了这个概念,“取地址”怎么用的问题就很显然了。“取地址”的操作就是对于一个左值表达式,取出其表示的内存位置。(仅为示意,并不代表实际的内存layout)

&a; // 0xa000 类型为int (*) [4],数组的地址
&(a[1]); // 0xa004 类型为 int*,某个整型元素的地址
&p; // 0xa010 类型为 int**,指针的地址

现在问题来了,挖掘机..哦不,数组,为什么可以赋值给指针?

p = a; // OK. why?

在C语言中,对于数组类型的左值,lvalue-to-rvalue conversion有一个特例,叫做array-to-pointer conversion(类似的还有function-to-pointer conversion)。在需要右值的场合,如果给出一个数组名,那么数组名会被转换为其首个元素的地址,类型为指向元素的指针。

void foo(int *p);
foo(a); // OK. array-to-pointer conversion
int **q = &a; // warning: initialization from incompatible pointer type

在需要左值的场合,a仍然是数组类型,所以&a的类型是指向数组的指针,而非指向元素的指针!

那么,下标运算符[]是什么?为什么数组和指针兼容下标运算?下标运算符的左侧表达式,是左值还是右值?稍稍想一下会发现,原来下标运算符只需要一个指针类型的右值即可。

int k = a[1]; // equivalent to *(a+1)
int **q;
(*q)[2]; // OK. *q is rvalue of int*

那么结论就很清楚了,在下标表达式中,数组会通过array-to-pointer conversion转换为指针右值,而指针类型,无论左值右值,都可以转换为指针右值。所以下标操作符自古以来就是为指针准备的,数组只是通过内置转换,捎带着实现了下标运算。这个结论,是否有些让人震惊呢?

参考链接:

    1. [FAQ] C/C++的左值 http://www.newsmth.net/nForum/article/CPlusPlus/179727?s=179727
    2. Is array name a pointer in C?

C++中的指针和数组的更多相关文章

  1. 转: 浅谈C/C++中的指针和数组(二)

    转自:http://www.cnblogs.com/dolphin0520/archive/2011/11/09/2242419.html 浅谈C/C++中的指针和数组(二) 前面已经讨论了指针和数组 ...

  2. 转:浅谈C/C++中的指针和数组(一)

    再次读的时候实践了一下代码,结果和原文不一致 error C2372: 'p' : redefinition; different types of indirection 不同类型的间接寻址 /// ...

  3. 浅谈C中的指针和数组(一)

    本文转载地址:http://www.cnblogs.com/dolphin0520/archive/2011/11/09/2242138.html 在原文的基础上加入自己的想法作为修改. 指针是C/C ...

  4. C++中的指针、数组指针与指针数组、函数指针与指针函数

    C++中的指针.数组指针与指针数组.函数指针与指针函数 本文从刚開始学习的人的角度,深入浅出地具体解释什么是指针.怎样使用指针.怎样定义指针.怎样定义数组指针和函数指针.并给出相应的实例演示.接着,差 ...

  5. 深入理解C语言中的指针与数组之指针篇

    转载于http://blog.csdn.net/hinyunsin/article/details/6662851     前言 其实很早就想要写一篇关于指针和数组的文章,毕竟可以认为这是C语言的根本 ...

  6. 深入理解C语言中的指针与数组之指针篇(转载)

    前言 其实很早就想要写一篇关于指针和数组的文章,毕竟可以认为这是C语言的根本所在.相信,任意一家公司如果想要考察一个人对C语言的理解,指针和数组绝对是必考的一部分. 但是之前一方面之前一直在忙各种事情 ...

  7. 浅谈C中的指针和数组(五)

    前面写了一些C指针和数组的一些知识,但是还有一些很重要的知识没有交代,这里做一个补充. 首先看一下,普通变量(指针也是变量)和数组名查看地址的方式是不同的. 查看数组变量的地址,不需要使用 & ...

  8. 浅谈C中的指针和数组(二)

    原文转载地址:http://see.xidian.edu.cn/cpp/html/475.html 在原文的基础上增加自己的想法作为修改 很多初学者弄不清指针和数组到底有什么样的关系.我现在就告诉你: ...

  9. 浅谈C中的指针和数组(三)

    上一个博客我们得到了一个结论: 指针和数组根本就是两个完全不一样的东西.只是它们都可以“以指针形式”或“以下标形式”进行访问.一个是完全的匿名访问,一个是典型的具名+匿名访问.一定要注意的是这个“以X ...

  10. 关于C++中的指针、数组

    C++中指针和数组基本等价的原因在于指针算术和C++内部处理数组的方式:将整数变量加一后,其值将增加1:将指针变量加一后,增加的量等于其指向的数据类型的字节数: 指针中存储的是地址,地址在形式上和整数 ...

随机推荐

  1. A Magic Lamp---hdu3183(链表删除| RMQ)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3183 给你一个长度<1000的数a,和m<len(a); 让把数a删除m个数字之后剩下的数 ...

  2. 爬虫之cookiejar模块

    简介 有时候我们需要爬一些需要登录才能进入网页,这个时候就要用到cookie相关的一些模块来操作了 内置的http包里包含了cookie相关的一些模块,通过她们我们可以自动使用cookie Cooki ...

  3. 现有mysql加入redis

    spring-dao.xml(注意这里必须加上ignore-unresolvedable): redis.properties: redis.hostname=192.168.1.3 redis.po ...

  4. Error-The content of element type "web-app" must match "(icon?,display-

    错误描述 The content of element type "web-app" must match "(icon?,display- name?,descript ...

  5. Mysql数据库常用操作语句大全

    零.用户管理: 1.新建用户: >CREATE USER name IDENTIFIED BY 'ssapdrow'; 2.更改密码: >SET PASSWORD FOR name=PAS ...

  6. 对BeforeSuite和BeforeTest的理解

    在BeforeSuite.BeforeTest.BeforeClass.BeforeMethod及BeforeGroups中,后面三个注解都比较好理解,其实BeforeSuite.BeforeTest ...

  7. APP移动端自动化测试工具选型“兵器谱”一览(主流开源工具)

    (下面大多数工具都是开源工具,在github,码云等开源平台都能找到) "测试那点事儿”在看到360旗下的测试团队整理的关于目前APP移动端自动化相关的工具,觉得总结的很到位,对目前大多数中 ...

  8. 百度NLP一面

    C++ :     1.拷贝构造函数和重载=符分别在什么情况下被调用,实现有什么区别 2.虚函数的目的,虚函数和模板类的区别,如何找到虚函数 常规算法: 1. 如何输出一个集合的所有真子集,递归和非递 ...

  9. 工作笔记——js与文件上传下载

    1 js判断上传文件的后缀名,文件大小 //判断照片大小 function getPhotoSize(obj){ photoExt=obj.value.substr(obj.value.lastInd ...

  10. Flask之wtforms源码分析

    一.wtforms源码流程 1.实例化流程分析 # 源码流程 1. 执行type的 __call__ 方法,读取字段到静态字段 cls._unbound_fields 中: meta类读取到cls._ ...