上一个博客我们得到了一个结论:

指针和数组根本就是两个完全不一样的东西。只是它们都可以“以指针形式”或“以下标形式”进行访问。一个是完全的匿名访问,一个是典型的具名+匿名访问。一定要注意的是这个“以XXX 的形式的访问”这种表达方式。


下面就说说两种东西在“以指针形式”访问和“以下标形式”访问的两种情况在指针和数组上的应用。

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+i所在单元的内容。由此就可以解释为什么C语言中数组的下标可以为负,而且在我看来,C语言中不检查数组的下标是否越界同样跟这个有关,如下面这段程序:

#include<stdio.h>

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

程序执行结果为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[]);
}
int main(void)
{
int a[]={,,};
int *p1,*p2;
int b=;
p1=a;
p2=&b;
fun(a);
fun(&a[]); //注意这句话输出的是2
fun(p1);
fun(p2);
fun(&b);
return ;
}

区分几个表达式的含义:

&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,我认为是错误的,因为其类型是数组指针,就是一个指针,编译时期确定,大小就是4)

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

#include<stdio.h>
#include<stdlib.h> int main(void)
{
int a[]={,,};
int *p=a;
printf("%08x\n",&p);
printf("%08x\n",p);
printf("%08x\n",&p+);
printf("%08x\n",p+);
printf("%08x\n",a);
printf("%08x\n",&a);
printf("%08x\n",a+);
printf("%08x\n",&a+); //注意输出结果
system("pause");
return ;
}

浅谈C中的指针和数组(三)的更多相关文章

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

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

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

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

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

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

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

    原文转载地址:http://see.xidian.edu.cn/cpp/html/476.html 在原文的基础上增加自己的思想作为自己的修改 指针数组和数组指针的内存布局 初学者总是分不出指针数组与 ...

  5. 浅谈C中的指针和数组(七)

    现在到揭露数组名本质的时候了,先给出三个结论: (1)数组名的内涵在于其指代实体是一种数据结构,这种数据结构就是数组: (2)数组名的外延在于其可以转换为指向其指代实体的指针,而且是一个指针常量: ( ...

  6. 浅谈C中的指针和数组(六)

    数组和指针,原本不想在写了,觉得这部分差不多了,但是自己在写程序的时候还是发现了一个错误.首先说一下我的要求: 给一个函数传递一个二维数组,然后我想在这个函数里面计算这个数组的行数. 写个类似的错误D ...

  7. 浅谈C语言 extern 指针与数组

    /* * d.c * * Created on: Nov 15, 2011 * Author: root */ #include "apue.h" int a[] = {3,2}; ...

  8. 浅谈Linux中的信号处理机制(三)

    一晃眼,已经到9月底了,都来不及去感慨时间匆匆.最近常常会想明年的今天我将会在那里干着什么样的工作?对未来又是憧憬又是担忧,压力山大.无论如何现在还是踏踏实实的学习吧,能这样安安静静学习的日子也不多了 ...

  9. 浅谈musql中using的使用---高性能(三)

    转载地址:https://blog.csdn.net/lppl010_/article/details/79429657 mysql中using的用法为: using()用于两张表的join查询,要求 ...

随机推荐

  1. ORA-32004: obsolete or deprecated parameter(s) specified for RDBMS instance

    1.启动报错SQL> startupORA-32004: obsolete or deprecated parameter(s) specified for RDBMS instanceORAC ...

  2. ChildNodes详解及其兼容性处理方式

    写在前面:在做insertBefore插入节点练习时发现一个问题,插入childNodes[0]和childNodes[1]时插入的位置是一样的!于是有了childNodes的了解,有了这篇文章,欢迎 ...

  3. Majority Element,Majority Element II

    一:Majority Element Given an array of size n, find the majority element. The majority element is the ...

  4. Ubuntu 13.04下安装Vmware tools 9.2.3

    更新13.04后 VmwareTools安装会出现三个问题 找不到generic kernel headers头文件 编译vmci出错 编译vmhgfs出错 第一个问题是系统的文件位置换了,而VMTO ...

  5. 南阳师范学院ACM集训队博客使用方法

    南阳师范学院ACM集训队博客使用方法 为方便大家交流,我们使用的是同一个用户名和密码,所以请不要随意修改用户名和密码,不然大家都登不上了,谢谢! 首先进入主页:http://www.cnblogs.c ...

  6. js数组(一)

    一.创建数组两种方法: 1Array构造 var colors = new Array();2.字面量表示什么事字面量,如 var colors = ["red","gr ...

  7. UML中的交互图<转>

      转自>>http://blog.csdn.net/mingxuanyun/article/details/8572128 交互图用来描述系统中的对象是如何进行相互作用的,即一组对象是如 ...

  8. AlarmManager与PendingIntent

    1.AlarmManager的作用与PendingIntent的关系 顾名思义,就是“提醒”,是Android中常用的一种系统级别的提示服务,在特定的时刻为我们广播一个指定的Intent.简单的说就是 ...

  9. 获取Json对象的长度或计数

    最近又开始写博客了.因为最近的工作又开始与技术方面接口了.现在在开发WEB的时候,经常会遇到JSON对象的传递,JSON是个好东西,但是它却没有提供一些简单便捷的处理方法,其中获取JSON对象的长度就 ...

  10. J2SE知识点摘记(三)

    1.         在java只允许单继承,而不允许多重继承,也就是说一个子类只能有一个父类.但在java中却允许多层继承. 2.         非内部Class只能被public或者defaul ...