从编译器角度分析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)得到的是指针的大小。
分享到:
相关推荐
在C语言中,数组、指针与地址的关系是学习C语言的重要组成部分,它们构成了C语言的特色和难点。数组是一种构造类型数据,由相同类型的元素按照特定顺序组成,数组名实际上是指向数组第一个元素的地址,即首地址。...
本次实验旨在掌握C语言中数组、结构体和指针的基本用法,以及初步理解链表的概念和操作。实验内容涉及设计一个结构体来存储学生信息,包括学号、姓名、三科成绩和平均成绩。其中,平均成绩需要根据输入的成绩计算...
**多维数组指针:** ```c int (*p)[3]; // p是指向含有3个整型元素数组的指针 int (*p)[3] = &a; // 将数组a的地址赋给p ``` **函数指针:** 函数指针是一种特殊的指针类型,它可以存储函数的地址。 ```c int add...
数组与指针紧密相关,C语言的数组名在大多数情况下会被编译器解释为指向数组首元素的指针。因此,可以使用指针操作来访问数组中的元素。在使用指针访问数组时,有两种基本方法:一种是直接使用下标来访问数组中的...
在C语言中,多维数组和多级指针是高级数据结构的重要组成部分,它们在软件开发中用于处理复杂的数据组织。本篇文章主要探讨二维数组和二级指针,因为理解这两个概念有助于理解更复杂的多维数组和多级指针。 二维...
【C语言数组初学者知识点详解】 C语言中的数组是一种非常基础且重要的数据结构,它允许存储一组具有相同数据类型的元素。对于初学者来说,理解数组的概念、定义和使用方法是学习C语言的关键步骤。 1. **数组的概念...
本文档详细介绍了如何在C语言中使用数组与指针,并通过实例进行了具体分析。 首先,我们来讨论数组。在C语言中,数组是一组数据类型相同的有序集合,它们在内存中占据连续的存储单元。数组可以是一维的也可以是多维...
通过以上几点,我们可以看出字符数组和字符指针在C语言中的区别和联系。在实际编程中,应根据需要选择合适的类型,并注意它们在内存中存储和操作的差异。正确地使用字符数组和字符指针,可以帮助我们编写出更高效、...
数组指针和指针数组是C语言中关于指针和数组概念的重要知识点,它们虽然听起来相似,但其实是完全不同的两个概念,涉及到内存布局、指针的优先级问题、数组指针的定义、以及指针与整数运算的规则等方面。 首先,...
在C语言中,数组是一种非常基础且重要的数据结构,它允许我们存储一组相同类型的元素。在本详细解读中,我们将深入探讨C语言中的数组...通过深入学习和实践,你可以掌握C语言数组的精髓并将其应用于各种实际场景中。
从提供的文件内容来看,文档是关于C语言数组的练习题目,其中包含了一定数量的数组定义、初始化、输入输出以及多维数组的使用等基础知识点。以下是对文件内容的详细解读: 1. 数组定义与声明: 文档中出现了不同...
在C语言中,数组的下标越界不会导致语法错误,这是由于数组的本质——数组名在本质上是一个指向数组首元素的指针。例如,`b[3]`在C语言中等同于`(b + 3)`,这里的b是一个地址,没有所谓的边界限制。因此,如果尝试...
【C语言数组结构】 在C语言中,数组是一种非常重要的数据结构,用于存储一组相同类型的元素。数组由连续的内存空间组成,通过下标来访问其中的元素。 1. **一维数组**: - 一维数组的引用是通过数组名加上下标来...
在编译器设计中,指针可以用来实现语法分析和语义分析等。 七、指针的注意事项 在使用指针时,需要注意一些问题,例如,指针的初始化、指针的越界访问、指针的赋值等问题。如果不正确地使用指针,可能会导致程序...
标题: C语言中指针与数组的恩怨浅析 知识点总结: 1. 指针的概念和特性 在C语言中,指针是一种保存变量地址的变量,其本身也是一种类型,称为“指针类型”。指针类型的大小通常是固定的,比如在32位系统中通常占4...
本实例"301-用指针数组作为函数的参数显示多个字符串"旨在教授如何利用指针数组在单片机环境中传递和处理字符串。在深入探讨这个实例之前,我们先来理解几个关键概念。 **指针**: 在C语言中,指针是变量的地址,...
《灵活使用C语言指针--深入理解C语言指针》这篇文章详细探讨了C语言中指针的使用,这是C语言核心特性之一。指针在C语言中扮演着至关重要的角色,因为它允许直接操作内存地址,提供了高效的数据操作和强大的功能。 ...
C语言变长数组是C99标准引入的一个新特性,使得在运行时可以确定数组长度成为可能,打破了传统C语言中数组长度必须在编译时固定的限制。然而,与C++等其他支持变长数组的现代编程语言相比,C语言中的变长数组使用受...