在C/C++中,数组名相当于一个指针,指向数组的首地址。这里“相当于”不代表等于,数组名和指针还是有很多区别的,这个在《C陷阱与缺陷》里有详尽的讲述。而这里要说的是对于数组名取地址的这么一个操作。

如果声明有如下数组:

int arr[5];

那么,&arr这个操作得到了什么值呢?

如果简单的认为arr就是一个指向数组首地址的指针的话,那么很自然会想到&arr得到的是一个指向存放arr这个指针的指针,也就是一个二级指针,然而事实却并不是这样。

观察以下代码:

int arr[5];
printf("%p\n%p\n", arr, &arr); int *pointer = new int[5];
printf("%p\n%p\n", pointer, &pointer);

在我的电脑上运行的结果如下:

0024FC08
0024FC08
0042B158
0024FBFC

我们可以看到,这里pointer这个指针确实如我们所想指向了不同的地址,但是,arr这个数组名的表现却和指针不同,arr和&arr指向的地址是一样的。

这里便体现除了数组名和指针的一个不同之处:对数组名进行&操作,并不是取其地址,而是得到了指向整个数组的指针。也就是说,arr与&arr指向的是同一个地址,但是他们的类型不一样。arr相当于&arr[0],类型是int *,而&arr是指向整个数组的指针,类型是int (*)[5]。

而这样的区别显然也会在对指针或者数组名的加减操作中体现出来,看如下代码:

int arr[5] = { 1, 2, 3, 4, 5 };
printf("%d\n", *arr);
printf("%d\n", *(arr + 1));
printf("%d\n", *((int*)(&arr + 1)));
printf("%d\n", *((int*)(&arr + 1) - 1));

输出为:

1
2
-858993460
5

前两个输出是很好理解的,对指针的加一操作会使指针按照指针的类型移动相应的位置。

第三行输出中,首先得到了指向整个数组的指针,对其进行加一操作,指针就指向了整个数组后面的地址,也就是5后面的地址,那么此时将其转化为int*类型再输出,就得到了该未初始化过的值。

第四行输出中,过程为,指针指向5后面的地址,转为int*类型,此时的减一操作使指针移动一个int类型大小的地址空间,那么便指向了5。

这种特性推广到二维至多维指针的情况下都是类似的。

C语言中对数组名取地址的更多相关文章

  1. 搬运1:关于对C语言中数组名取地址加减等操作的一点探究

    对于数组名取地址强制转换的操作 偶然在晚上学了C语言指针后网页闲逛找题时,被一个数组名取地址搞糊涂了,在自己试验加探索后我稍微悟了一点东西. 代码如下: #include<stdio.h> ...

  2. C语言的数组名和对数组名取地址

    http://blog.csdn.net/zdcsky123/article/details/6517811 相信不少的C语言初学者都知道,数组名相当于指针,指向数组的首地址,而函数名相当于函数指针, ...

  3. C语言 对数组名取地址

    作者 : 卿笃军 你有没有想过,对一个一维数组名取地址,然后用这个地址进行加减运算.这会出现什么样的结果呢? 演示样例: int a[5] = {1,2,3,4,5}; int *p = (int * ...

  4. 对数组名取地址 a[ ],&a

    C语言规定,数组名代表数组的首地址,也就是第0号元素的地址.所以a==&a[0] 但对数组名取地址时却要注意了,在理解“对数组名取地址”这一表达式的含义时一定要记住:数组名是“数组”这种变量的 ...

  5. 数组名取地址所算数运算应注意的&quot;trap&quot;

    数组名取地址所算数运算应注意的"trap" 直接看代码: #include <stdio.h> int main() { int array[5]; printf(&q ...

  6. 数组名和数组名取地址&

        在C中, 在几乎所有使用数组的表达式中,数组名的值是个指针常量,也就是数组第一个元素的地址. 它的类型取决于数组元素的类型: 如果它们是int类型,那么数组名的类型就是“指向int的常量指针“ ...

  7. C语言指令数组名和数组名取地址

    以下C语言指令:int a[5] = {1, 3, 5, 7, 9}; int *p = (int *)(&a + 1); printf("%d, %d", *(a + 1 ...

  8. C语言数组名取地址。。。

    int main(){ int a[5] = { 1, 2, 3, 4, 5 }; printf("%08X ,%08X ,%08X ,%08X", a, &a, a + ...

  9. 对数组名取地址&a和 数组首地址a

    #include <iostream> using namespace std; ] = {,,,,}; int main() { cout<<a<<" ...

随机推荐

  1. emWin 界面切换注意事项

    @2018-07-10  emWin 在做界面切换时,须将切换前的界面所有信息 “删除”,否则将造成切换后的界面死机 此 “删除” 对象包括: > 界面上绘制的曲线(随时间一直变化).绘制的2D ...

  2. spring.net框架配置和使用

    spring.net框架学习笔记 spring.net框架是用于解决企业应用开发的复杂性的一种容器框架,它的一大功能IOC(控制反转),通俗解释就是通过spring.net框架的容器创建对象实体,而不 ...

  3. bzoj3114 LCM Pair Sum

    题意:以质因数分解的方式给定n,求所有满足:lcm(a, b) = n的无序数对的价值和.其中(a, b)的价值为a + b 解: 定义首项为a,公比为q,项数为n的等比数列的和为getQ(a, q, ...

  4. C&C++图形图像处理开源库

    Google三维APIO3D O3D 是一个开源的 WebAPI 用来在浏览器上创建界面丰富的交互式的 3D 应用程序.这是一种基于网页的可控3D标准.此格式期望真正的基于浏览器,独立于操作系统之外, ...

  5. Python3:if __name__ == '__main__' 详解

    一般在风格比较好的代码中会有一行if __name__ == '__main__' :代码,这里说明一下这句代码的用处,先上两个代码test1.py和test2.py: # test1.py prin ...

  6. .net MVC框架里怎么写控件

    .net的MVC框架如果选择了Razor引擎就需要自己来写控件了,这里列出了一些简单的控件的书写方法 @*这是TextBox控件的写法*@ @Html.TextBox("textbox1&q ...

  7. JDBC编程示例

    package com.lovo.test; import java.sql.Connection;import java.sql.DriverManager;import java.sql.SQLE ...

  8. vue注册全局属性

    例:统一引用getSpiderToken方法 main.js中相关代码 import { getSpiderToken } from '../static/js/storage' Vue.protot ...

  9. ie6下的line-height属性

    line-height这个属性是被ie6所支持的. 当是当一个父级元素内的子元素,包含了文字,且文字和img,input,label,span这些内联元素连接在一起的时候,你对父级元素设置line-h ...

  10. 20155339 2016-2017-2 《Java程序设计》第8周学习总结

    20155339 2016-2017-2 <Java程序设计>第8周学习总结 教材学习内容总结 第十四章NIO与NIO2 NIO使用频道来衔接数据节点,在处理数据时,NIO可以让你设定缓冲 ...