转自:http://www.cnblogs.com/dolphin0520/archive/2011/11/09/2242419.html

浅谈C/C++中的指针和数组(二)

前面已经讨论了指针和数组的一些区别,然而在某些情况下,指针和数组是等同的,下面讨论一下什么时候指针和数组是相同的。

C语言标准对此作了说明:

规则1:表达式中的数组名被编译器当做一个指向该数组第一个元素的指针;

注:下面几种情况例外

1)数组名作为sizeof的操作数

2)使用&取数组的地址

规则2:下标总是与指针的偏移量相同;

规则3:在函数参数的声明中,数组名被编译器当做指向该数组第一个元素的指针。

规则1和规则2结合在一起理解,就是对数组下标的引用总是可以写成“一个指向数组的起始地址的指针加上偏移量”。如a[i]总是被编译器解析为*(a+i)的形式。

规则1:表达式中的数组名总被编译器解析为指针,因此如下语句int a[3];int *p=a;是可以正确编译执行的。在表达式中a被解析为指向数组第一个元素的指针,那么赋值符号两边的类型匹配,因此可以正确编译执行。

规则2:下标总是和指针的偏移量相同。C语言中将数组的下标改写成指针偏移量的主要原因在于指针和偏移量是底层硬件所使用的基本类型。如a[i]中的i总被编译器解析为偏移量,所以a[i]总是被改写成*(a+i)的形式(因此a[i] 和 i[a] 是等价的),a是指向数组第一个元素的指针,加上偏移量i,表示该指针向后移i个步长,然后取a+i所在单元的内容。由此就可以解释为什么C语言中数组的下标可以为负,而且在我看来,C语言中不检查数组的下标是否越界同样跟这个有关,如下面这段程序:

#include<stdio.h>

int main(void)
{
int a[3]={1,2,3};
int *p=(a+3);
printf("%d\n",p[-1]);
return 0;
}

程序执行结果为3,虽然下标为-1,但是被编译器解析为偏移量,因此相当于*(p-1)。

规则3:在函数参数的声明中,数组名被编译器当做指向该数组第一个元素的指针。在C语言中将形参的数组和指针等同起来是出于效率的考虑。假如不这么做,将整个数组的每个元素的值都拷贝一份进行传递,这样无论在时间上还是空间上的开销都可能是非常大的。但是又要能操作到数组中的元素,只需将数组第一个元素的地址传递给调用函数,然后通过指针去访问想要访问的空间,这样一来时空消耗将大大减少。因此在函数内部,编译器始终把参数中声明的数组名当做一个指向数组第一个元素的指针,这样一来,编译器可以产生正确代码,并不需要对数组和指针这两种情况作区分。因此void fun(int a[]);和void fun(int *a)两种形式的效果完全等同,在函数内部去引用a的话,始终都会被编译器认为是指针。因为void fun(int a[]);这种形式最终还是会被编译器解析为void fun(int *a);这种形式告诉我们调用时必须传递一个指向整型数据的指针。所以下面这段代码可以正确编译和执行:

#include<stdio.h>

void fun(int a[])
{
printf("%d\n",a[0]);
}
int main(void)
{
int a[3]={1,2,3};
int *p1,*p2;
int b=4;
p1=a;
p2=&b;
fun(a);
fun(&a[1]);
fun(p1);
fun(p2);
fun(&b);
return 0;
}

区分几个表达式的含义:

&p,p,a,&a

&p:表示取存储指针变量p的内存单元的地址;  sizeof(&p)=4;

p:表示取指针变量p存储的地址;                     sizeof(p)=4;

a:表示取数组第一个元素的地址;                    sizeof(a)=3*4=12;

&a:表示取整个数组的首地址;                        sizeof(&a)=4(在VC++6.0中该值为12,我认为是错误的,因为其类型是数组指针)

虽然a和&a的值相同,但是所表达的含义完全不同,a表示取数组第一个元素的地址,而&a表示取数组的首地址。它们所代表的类型也完全不同,a是一个int型指针,而&a是一个int (*p)[]型指针,即数组指针(在后续文章中会作解释)。所以a+1和&a+1得到的结果不同,a+1表示将指向该数组的第一个元素的指针向后移一个步长(这里的步长为数组元素类型所占的字节数);而&a+1表示将指向该数组的指针向后移动一个步长(而此处的步长为数组元素个数*元素类型所占的字节数)。

#include<stdio.h>

int main(void)
{
int a[3]={1,2,3};
int *p=a;
printf("%08x\n",&p);
printf("%08x\n",p);
printf("%08x\n",&p+1);
printf("%08x\n",p+1);
printf("%08x\n",a);
printf("%08x\n",&a);
printf("%08x\n",a+1);
printf("%08x\n",&a+1); //注意输出结果
return 0;
}
作者:海子

    

    

本博客中未标明转载的文章归作者海子和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

转: 浅谈C/C++中的指针和数组(二)的更多相关文章

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

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

  2. 浅谈c/c++中的指针问题

    首先给出几种指针类型来作出区分,不看后面的解析如果可以自己分辨正确那么就算对指针有一个很好的掌握了,就没有必要再去看后面的解析,如果不能完全区分,那么就有必要仔细看看后面解析. 1 Char * p  ...

  3. 浅谈 Swift 2 中的 Objective-C 指针

    浅谈 Swift 2 中的 Objective-C 指针 2015-09-07  499 文章目录 1. 在 Swift 中读 C 指针 2. 在 Swift 中创建 C 指针 3. 总结 作者:Ja ...

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

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

  5. c#Winform程序调用app.config文件配置数据库连接字符串 SQL Server文章目录 浅谈SQL Server中统计对于查询的影响 有关索引的DMV SQL Server中的执行引擎入门 【译】表变量和临时表的比较 对于表列数据类型选择的一点思考 SQL Server复制入门(一)----复制简介 操作系统中的进程与线程

    c#Winform程序调用app.config文件配置数据库连接字符串 你新建winform项目的时候,会有一个app.config的配置文件,写在里面的<connectionStrings n ...

  6. 浅谈C++11中的多线程(三)

    摘要 本篇文章围绕以下几个问题展开: 进程和线程的区别 何为并发?C++中如何解决并发问题?C++中多线程的基本操作 浅谈C++11中的多线程(一) - 唯有自己强大 - 博客园 (cnblogs.c ...

  7. 浅谈C++11中的多线程(二)

    摘要 本篇文章围绕以下几个问题展开: 进程和线程的区别 何为并发?C++中如何解决并发问题?C++中多线程的基本操作 浅谈C++11中的多线程(一) - 唯有自己强大 - 博客园 (cnblogs.c ...

  8. 转载 浅谈C/C++中的static和extern关键字

    浅谈C/C++中的static和extern关键字 2011-04-21 16:57 海子 博客园 字号:T | T   static是C++中常用的修饰符,它被用来控制变量的存贮方式和可见性.ext ...

  9. 浅谈C语言中的强符号、弱符号、强引用和弱引用

    摘自http://www.jb51.net/article/56924.htm 浅谈C语言中的强符号.弱符号.强引用和弱引用 投稿:hebedich 字体:[增加 减小] 类型:转载 时间:2014- ...

随机推荐

  1. 手把手教你用C++ 写ACM自动刷题神器(冲入HDU首页)

    转载注明原地址:http://blog.csdn.net/nk_test/article/details/49497017 少年,作为苦练ACM,通宵刷题的你 是不是想着有一天能够荣登各大OJ榜首,俯 ...

  2. selenium帮助手册以及 webdriver的各种driver

    帮助手册 http://selenium-python.readthedocs.io/locating-elements.html 转载于:http://blog.csdn.net/five3/art ...

  3. libevent安装及使用

    一.安装libevent 官网:http://libevent.org/ 选择最新版本下载,我选择的是libevent-2.0.22-stable.tar.gz,然后安装README文件中描述的方法编 ...

  4. isp和3a的联系与区别是什么?

    Willis Zen上善若水 2 人赞同 你说的这个问题,不是很多人能够回答的,我也只能把我知道的告诉你.isp 是image signal processing,用于图像处理,比如gamma调整,d ...

  5. request、response 中文乱码问题与解决方式

    request乱码指的是:浏览器向服务器发送的请求参数中包含中文字符,服务器获取到的请求参数的值是乱码:   response乱码指的是:服务器向浏览器发送的数据包含中文字符,浏览器中显示的是乱码: ...

  6. python中zip函数

    zip函数接受任意多个(包括0个和1个)序列作为参数,返回一个tuple列表.(在海豚实习时自己写了一个要用到zip的函数,那个例子非常代表性) 示例1 for i,j in zip(range(3) ...

  7. android 入门 007(界面跳转)

    一.隐式跳转(自定义界面) 界面层: <Button android:id="@+id/sencond_contact" android:layout_width=" ...

  8. CODEVS 3145 汉诺塔游戏 递归

    题目描述 Description 汉诺塔问题(又称为河内塔问题),是一个大家熟知的问题.在A,B,C三根柱子上,有n个不同大小的圆盘(假设半径分别为1-n吧),一开始他们都叠在我A上(如图所示),你的 ...

  9. SqlSever基础 dateadd month 增加五个月

    镇场诗:---大梦谁觉,水月中建博客.百千磨难,才知世事无常.---今持佛语,技术无量愿学.愿尽所学,铸一良心博客.------------------------------------------ ...

  10. 【leetcode❤python】Move Zeroes

    #-*- coding: UTF-8 -*- #filter()函数可以对序列做过滤处理,就是说可以使用一个自定的函数过滤一个序列,#把序列的每一项传到自定义的过滤函数里处理,并返回结果做过滤.最终一 ...