`
fp_moon
  • 浏览: 970470 次
  • 性别: Icon_minigender_1
  • 来自: 深圳
社区版块
存档分类
最新评论

从编译器角度分析C语言中数组名和指针的区别--转

c 
阅读更多
从编译器角度分析C语言中数组名和指针的区别

数组名和指针是两个往往很容易让人们混淆的概念,很多人以为数组名就是一个指针,也有很多人知道数组名不同于指针但是仅知道数组名的值不能像指针一样改变,例如你可以写出下面这样的代码:
int *p;
p++;
却不能写这样的代码:
int a[];
a++;
那么数组名跟指针之间到底有什么区别呢?

第一,在声明上,除了作为函数参数的数组名总是编译器转化成指针,其他情况下,数组名就是数组名,指针就是指针,二者不能混淆,你不能在一个文件中定义一个数组,而在另一个文件中把它声明成一个指针。
char a[];  //定义一个数组a
 extern char* a; //在另一个文件中将a声明成一个指针
在编译器中,符号表用来存放C语言中有关标识符的属性信息,这些信息集中反应了标识符的特征属性。等到词法分析到代码声称的各个阶段的时候,编译器需要根据源代码提出的要求,从表中获取不同标识符的不同属性。值得注意的是,数组标识符的属性和指针是完全不同的。因此,在一个文件中定义一个数组的时候,编译器会把它记录在符号表中,而在编译器分析另一个文件中的声明时,通过符号的语义检查发现不一致。也许这样的声明可以编译通过(在gcc下通过了),但是把一个数组名当成一个指针来使用,别指望它能运行起来。

第二,指针是一个变量,而数组名不是。数组名是数组的首地址,即它本身就是一个地址,对应到汇编语言级别就是一个常量,一个固定的数(地址)。因此数组名不能进行++,--等运算。
在大多数编译器中,对数组的引用a[i]总是被编译器改写成*(a+i)的格式。也就是说,编译器每遇到a[i],都会把它当作*(a+i)来处理。我们都知道,*addr表示内存中(addr)的位置存储的值,比如*0x8048000就表示地址为0x8048000的内存中所存储的值。所以a[i]就表示a的值加上i所得到的数作为一个内存地址里面所存储的值。
那么a的值是什么呢?编译器在做词法分析和语法分析的时候,遇到一个数组的定义,就会把数组的有关信息汇集在一个叫做“内情向量”或“信息向量”的表各种,其中的信息包括数组的类型,维数,各维的上、下边界,以及数组的首地址,然后将这个“内情向量”相关信息存储在符号表中。数组定义后位置就是固定的,因此其首地址可以在编译阶段得到。当编译器到达代码生成的各阶段时,每次遇到数组名这个标识符,编译器都会从符号表中取出这个数组的首地址,然后用这个地址来替代数组名,例如,假设数组a起始地址是0x8048000,则a[1]就会被编译器转化成*(0x8048000+1),因此在生成的汇编代码中,数组名已经完全被转化成一个常量,一个固定的数(地址)。
但是,对于指针p,它是一个变量,其值存储在地址&p中,这个值在编译时是不能得到的。因为是变量,所以指针可以作为表达式中的左值操作,如++或--,而被认为是常量的数组名却不可以,正如你可以骑走一辆自行车,但是不能骑走一棵树。另外,C语言把数组下标改写成指针偏移量的根本原因是指针和偏移量是底层硬件所使用的基本模型。

第三,对数组的引用,如a[i],或*(a+1),需要访存一次;而对指针的引用,如*(p+1),需要访存两次。
如果理解了第二条的解释,这个应该就不难理解。因为a被认为是常数,所以取*(a+1)的值只需将a所表示的常数加1,然后从得到的地址里访存取一次即可。而对于指针,需要先从&p这个地址里把p的值取出来,然后加1,再从得到的地址里访存取一次,一共需要两次访存。
第四,假设a是一个数组名,而p是一个指针,当你使用 a 和 &a 时,得到值是一样的,都是数组的起始地址。而使用 p 和 &p 时,得到的值是不一样的, p 表示指针 p 所指向的地址,而 &p 表示 p 这个变量的地址。再假设
p = a;
则 p 就表示数组a的起始地址,而&p是存储数组a的起始地址的那个地址。
这是因为编译器把a当成数组首地址,而&a当作数组第一个元素的地址,因此得到的值是一样的。
另外,sizeof(a)得到的是a所表示的数组的大小,而sizeof(p)得到的是指针的大小。
分享到:
评论

相关推荐

    指针数组和数组指针的区别.doc

    这样两者的区别就豁然开朗了,数组指针只是一个指针变量,似乎是C语言里专门用来指向二维数组的,它占有内存中一个指针的存储空间。指针数组是多个指针变量,以数组形式存在内存当中,占有多个指针的存储空间。 还...

    C语言中数组作为函数的参数以及返回值的使用简单入门

    如果想通过一个一维数组作为函数的参数,就必须声明函数形式参数,使用以下三种方式与所有三个声明方法之一产生类似的结果,因为每一种方式告诉编译器,一个整数指针将会要被接收。类似的方式,可以使用多维数组形式...

    C语言入门经典(第4版)--源代码及课后练习答案

    8.5.2 从函数中返回指针值 318 8.5.3 在函数中递增指针 322 8.6 小结 322 8.7 习题 323 第9章 函数再探 325 9.1 函数指针 325 9.1.1 声明函数指针 325 9.1.2 通过函数指针调用函数 326 9.1.3 函数指针数组 ...

    C2j-Compiler:将C语言转换为Java字节码或可以直接解释执行的编译器(将C语言编译成Java字节码的编译器,也可以选择直接解释执行)

    解释器:支持指针,数组,结构和结构数组 编译器:不支持指针 如何使用 1.前往发行版并下载jar 2.转到命令行开始 java -jar C2j-Complier.jar -m interpreter -f test.c java -jar C2j-Complier.jar -m codegen -d ...

    C/C++ 数组和指针及引用的区别

    C/C++ 数组和指针及引用的区别 1.数组和指针的区别 (1)定义 数组是一个符号,不是变量,因而没有自己对应的存储空间。但是,指针是一个变量,里面存储的内容是另外一个变量的地址,因为是变量所以指针有自己的...

    直观理解C语言中指向一位数组与二维数组的指针

    一维数组和指针: 对于一位数组和指针是很好理解的: 一维数组名: 对于这样的一维数组:int a[5]; a作为数组名就是我们数组的首地址, a是一个地址常量 . 首先说说常量和变量的关系, 对于变量来说, 用箱子去比喻再...

    C语言水滴石穿系列文章

    3、水滴石穿C语言之指针、数组和函数 4、水滴石穿C语言程序设计之正确使用const 5、水滴石穿C语言之编译器引出的问题 6、水滴石穿之C语言的底层操作 7、水滴石穿C语言之内存使用 8、水滴石穿C语言之可变参数问题 9、...

    《你必须知道的495个C语言问题》

    书中列出了C用户经常问的400多个经典问题,涵盖了初始化、数组、指针、字符串、内存分配、库函数、C预处理器等各个方面的主题,并分别给出了解答,而且结合代码示例阐明要点。 《你必须知道的495个C语言问题》结构...

    C语言解析教程(原书第4版)(美) 凯利.pdf

    《c语言教程(原书第4版)》是一本优秀的c程序设计语言教材,完整描述了ansi c语言及其语法特性,并对c语言的高级特性和应用作了深入阐述,介绍了从c到c++和java过渡的相关知识。《c语言教程(原书第4版)》的一个...

    你必须知道的495个C语言问题.pdf

    作者在网络版CFAQ列表的基础上进行了大幅度的扩充和丰富,结合代码示例,权威而且详细深入地解答了实际学习和工作中最常遇到的495个C语言问题,涵盖了初始化、数组、指针、字符串、内存分配、库函数、C预处理器等...

    你必须知道的495个C语言问题

    6.12 既然数组引用会退化为指针,如果array是数组,那么array和&array又有什么区别呢? 6.13 如何声明一个数组的指针? 动态数组分配 6.14 如何在运行时设定数组的大小?怎样才能避免固定大小的数组? 6.15 ...

    深入理解c语言数组

    有的资料说:数组名是指向数组首地址的常量指针。 下面我们可以验证一下。我都知道sizeof操作符可以返回一个对象或者类型所占的内存字节数。如:int i=1;那么sizeof(i) 的结果就是4(64位机器下的部分编译器是8) 那...

    编译原理 C编译器

    可将C语言的裴波纳契和pi.c的翻译为汇编语言。满分7分。 上机大作业——简化C编译器实现 总体要求 一、要求实现的语言特性 1. 基本要求 1数据类型:int,char 2语句:赋值(=),if,while,for;赋值 循环 条件判断 ...

    C语言中可移植且可靠的指针运算

    在C语言中,指针变量是强大且实用的功能...利用指针,我们可以编写出许多语法正确的C语言代码,来编译和实现某种功能,但这种功能在不同的C编译器实现中以及不同的目标器件上可能有所不同。甚至可能与我们的期望不同。

    C语言0基础超详细从基础到进阶课程 C语言二级一次通关班课程 附带考试模拟系统!

    C语言0基础超详细从基础到进阶课程 C语言二级一次通关班课程 附带考试模拟系统! (1)\内容 (2)\内容\绝密押题;目录中文件数:4个 ├─二级C语言考前押题(选择题).pdf ├─二级C语言考前预测题.rar ├─二级公共...

    编译原理简易C编译器

    两种语言工具 构造词法分和语法分析部分 passive generate 3编译器演示程序,可将C语言子集测试程序编译为目标代码——汇编程序,用汇编器转换为二进制程序后运行无误,如斐波那契数列程序,应能翻译为正确的汇编...

    如何优化C语言代码(程序员必读)

    对于大部分的编译器,使用指针比使用数组生成的代码更短,执行效率更高。但是在Keil中则相反,使用数组比使用的指针生成的代码更短。。 3、使用尽量小的数据类型 能够使用字符型(char)定义的变量,就不要使用整型...

    C语言FAQ 常见问题列表

    o 7.10 我如何声明一个数组指针 ? o 7.11 我如何在运行期设定数组的大小 ? 我怎样才能避免固定大小的数组 ? o 7.12 我如何声明大小和传入的数组一样的局部数组 ? o 7.13 我该如何动态分配多维数组 ? o ...

Global site tag (gtag.js) - Google Analytics