最近看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. Python 3.5 中的异步HTTP请求写法

    Python 3.5 增加了对async def and await的支持,同样的异步代码看起来干净了很多,也更易读. import aiohttp import asyncio async def ...

  2. django将数据库中数据直接传到html

    1.当然,前提是建立和配置好django数据库啦~ 2.在python后台函数中引入需要的表 #要读取home这个APP下的models.py文件,引入其中的Student_message_unedi ...

  3. JS获取浏览器信息及屏幕分辨率

    因为vue有自己的生命周期,初始化数据的时候,可以在钩子函数created()函数里初始化数据,也可以在mounted()函数里获取,但是两者是不同的,获取浏览器和屏幕分辨率的时候,不能在create ...

  4. mysql ERROR 1264 (22003): Out of range value for column 'x' at row 1 错误

    mysql> insert into t1 values (-129), (-128), (127),(128);ERROR 1264 (22003): Out of range value f ...

  5. Powerdesigner显示列名

    设置要修改的列 点击ok即可.

  6. Mysql-xtrabackup 与MySQL5.7 binlog 实现数据即时点恢复

    Mysql-xtrabackup 与MySQL5.7 binlog  实现数据即时点恢复 一.数据库准备 1. rpm -e mariadb-libs postfix tar xf mysql-5.7 ...

  7. appium ios 自动化测试

    iOS自动化测试:Appium 从入门到实践https://www.jianshu.com/p/43f858180557appium自动化测试iOS Demohttps://www.jianshu.c ...

  8. oracle 将当前系统时间戳插入timestamp字段

    oracle 将当前系统时间戳插入timestamp字段 --insert records 精确到秒:insert into userlogin_his(usrname,logintime) valu ...

  9. iOS跳转到Touch ID设置界面

    1.首先去info.plist 设置: 2.代码 NSURL *url = [NSURL URLWithString:@"App-Prefs:root=TOUCHID_PASSCODE&qu ...

  10. 特性(property)/静态方法(staticmethod)/类方法(classmethod)/__str__的用法

    property是一种特殊的属性,访问它时会执行一段功能(函数)然后返回值 1 import math 2 class Circle: 3 def __init__(self,radius): #圆的 ...