数组名和指针的区别

近段时间在整理自己大学几年来学习C++的点点滴滴,写这篇文章源于看林锐博士的《高质量C++指南》时,看到文章直接把指针和数组名视为同一东西,原文为“数组名本身就是一个指针,是一个指针常量,即a等价于int * const a,因此你不能试图修改数组名的值”,摘自第三版127页第7.2.1小节。

    自己初学C++时也被数组和指针搞得昏头转向过,通过多方求证和对比实验已把指针和数组弄得稍明白,但是今天又看到一个人说“数组名就是指针”这样的言论,我就有这个想法把自己对指针和数组名的理解写出来,我不敢说我的理解是正确的,但是,至少没那么彻底的祸害人。


   数组名绝对不等于指针,而且不是指针,这是我的观点

首先看C标准对指针的解释为:指针是一个变量,变量的值是另外一个变量的地址。那么,既然指针是变量,那么指针必然有自己的存储空间,只不过是该存储空间内的值是一个地址值,而不是别的内容。如果按照林博士的观点“数组名就是指针”,想必,数组名也会是一个变量,而且有自己的存储空间?下面上汇编代码反驳之,汇编代码没有骗我们,数组名仅仅是一个符号,不是变量,它没有自己的存储空间,而指针实实在在的是个变量,有自己的空间:

下面是C++代码:

#include <iostream>
using namespace std;


int main()
{
	int i = 10;
	int *pi = &i; //(1)测试指针pi是否有自己的空间


	int a[2];     //(1)的对比实验,测试数组名是否有自己的空间
	a[0] = 5;
	a[1] = 15;
	
	int *pa = a;  //(2)测试对数组名的引用的寻址方法
	pa = a+1;


	int j = a[0]; //(3)对比测试对数组名的引用和对指针变量的引用的不同
	int k = *pa;


	//++a;        //(4)对比测试对指针变量的修改和对数组名的修改的不同
	++pa;


	return 0;


}
这段代码的目的是通过4个对比实验逐一证明指针和数组名不是用一个东西,请看下面的汇编代码便可一目了然,每个对比实验的结果都能说名:数组名不是指针,它就是一个符号。
6:        int i = 10;
00401048   mov         dword ptr [ebp-4],0Ah    //(1)
7:        int *pi = &i;
0040104F   lea         eax,[ebp-4]           
00401052   mov         dword ptr [ebp-8],eax    //(2)
8:
9:        int a[2];    
10:       a[0] = 5;
00401055   mov         dword ptr [ebp-10h],5 //(3)

11:       a[1] = 15;
0040105C   mov         dword ptr [ebp-0Ch],0Fh
12:
13:       int *pa = a;  
00401063   lea         ecx,[ebp-10h]            //(4)
00401066   mov         dword ptr [ebp-14h],ecx
14:       pa = a+1;
00401069   lea         edx,[ebp-0Ch]           //(5)
0040106C   mov         dword ptr [ebp-14h],edx
15:
16:       int j = a[0]; 
0040106F   mov         eax,dword ptr [ebp-10h]  //(6)
00401072   mov         dword ptr [ebp-18h],eax
17:       int k = *pa;
00401075   mov         ecx,dword ptr [ebp-14h]
00401078   mov         edx,dword ptr [ecx]
0040107A   mov         dword ptr [ebp-1Ch],edx
18:
19:       //++a;        
20:       ++pa;
0040107D   mov         eax,dword ptr [ebp-14h]  //(7)
00401080   add         eax,4
00401083   mov         dword ptr [ebp-14h],eax
21:
22:       return 0;
00401086   xor         eax,eax
23:
24:   }

简单学过汇编的人都基本能看懂上面的代码,下面逐一分析(1)-(7)的汇编代码告诉我们的事实:

(1)、i是个整型变量,可以看出编译器为i分配了空间,地址为ebp-4

(2)、先lea取i的地址,然后存入到ebp-8的空间中,这就是pi的地址,足矣证明pi是个变量,有自己的空间

(3)、重点之处,定义了一个数组,但是,汇编代码告诉我们,编译器只给a[0]和a[1]分配了内存空间,a[0]的地址为:ebp-10,a[1]为:ebp-0C,却没有给,数组名a也分配空间,这里起码可以断定数组名a不是一个变量只是个符号

(4)、从代码lea ecx,[ebp-10h]可以看出,ebp-10是a[0]的地址,对数组名a的引用已经被替换为对数组首元素的地址了

(5)、a+1的汇编代码也是编程了对ebp-0c地址单元的引用,而这个地址是a[1]的地址,更进一步说明了问题

(6)、是为了对比,对数组的引用和对指针的引用不同之处,通过下面三行汇编代码可以看出:

17:       int k = *pa;
00401075   mov         ecx,dword ptr [ebp-14h]
00401078   mov         edx,dword ptr [ecx]
0040107A   mov         dword ptr [ebp-1Ch],edx

对指针pa的引用是一个间接访问,epb-14h是pa的地址,而mov ecx,dword ptr [ebp-14h]是先取得pa的地址,然后[ecx]取该地址中的内容,并mov到ebp-1c中,改内存就是变量k的内存空间首地址,这样就完成了对pa的引用

(7)这个是重点分析的地方,很对多人对于数组名就是指针持赞同观点的一个冠冕堂皇的证据是“数组名不能被修改,因为数组名是一个常量指针”,也就是不能执行 a = a+1;这句话对一般,错一半,对的是,数组名确实不能被修改,错的是,不能被修改的原因不是因为数组名是常量指针,而是因为数组名只是一个符号,不是一个变量,因此不能作为一个左值,因此不能被修改,这里又涉及到左值和右值的问题,就不再赘述,网上资料很多。


通过上述几个对比实验,能够非常清晰的看到,数组名和指针的本质区别:指针是一个变量,有自己对应的存储空间,而数组名仅仅是一个符号,不是变量,因而没有自己对应的存储空间,到此已经可以得出结论,“数组名永远不等于指针”。


我认为,林博士的意思是说,数组名和指针在参与表达式运算时效果是一样的,但是不能简单粗暴的说两者是相等的


上述如有错误之处,恳请指出,我必会虚心听取,认真论证修改,以免更多的人困惑


上述观点有不少不妥之处,讨论在此http://topic.youkuaiyun.com/u/20120316/18/f845d4b5-12b2-4467-9946-d31d3c0fd208.html,可自行结合自己的知识进行分析



<think>嗯,用户想了解C/C++数组名指针区别。好的,这个问题我之前学过,但可能有些细节需要回忆一下。首先,我记得数组名在大多数情况下会被编译器转换为指向数组首元素的指针,但数组名本身并不是指针变量,而是一个常量指针。对吗? 根据引用内容,比如引用[1]提到,数组名会被编译器自动替换为指向首元素的指针,比如int arr[]的情况下,arr会被当作&arr[0]。所以当输出arrp的时候,地址是一样的。这说明数组名指针在某些情况下行为相似。但引用[3]里提到数组指针指向整个数组的地址,而数组名可能更接近首元素的地址?这里需要区分数组指针指针数组的不同。 然后,数组名是常量,不能被重新赋值,而指针是变量,可以指向其他地址。例如,像arr = another_array;这样的操作是非法的,而指针p可以指向其他地方。这点应该正确,用户可能需要知道这一点区别。 还有sizeof操作符的结果不同。对于数组名,sizeof返回整个数组的大小,而指针的话则是指针本身的大小,比如在64位系统上是8字节。引用中可能没有直接提到这点,但根据常识,这是关键区别之一。 另外,取地址操作符&的结果不同。对数组名使用&,得到的是整个数组指针,类型是数组指针(比如int (*)[n]),而对指针变量使用&,得到的是指针变量的地址,类型是int**。引用[3]提到数组指针需要解引用才能访问元素,所以这部分可能相关。 类型信息方面,数组名保留了数组的类型长度信息,而指针只保存地址。例如,当传递数组名给函数时,它退化成指针,函数内部无法知道数组的长度,这也是为什么需要传递长度参数的原因。引用[5]中的例子显示,使用数组指针传递到函数时,可能需要解引用才能正确访问元素。 还有,数组名作为函数参数时会退化为指针。比如在函数参数中声明int arr[]其实等同于int* arr。这点引用中可能提到过,比如引用[1]中的编译器自动替换。 总结起来,数组名指针区别主要有:是否为常量、sizeof结果、&操作的结果、类型信息保留情况、以及作为函数参数时的退化行为。需要将这些点整理清楚,用例子说明,比如代码示例中的sizeof差异,或者尝试修改数组名的地址导致编译错误的情况。 可能还需要提到指针数组数组指针区别,但用户的问题主要是数组名指针的关系,所以这部分可能稍微带过。引用[5]中的例子展示了数组指针的使用,可以用来解释数组名指针的不同使用场景。 用户可能在实际编程中遇到数组名被当作指针使用的情况,但需要理解背后的原理,比如为什么不能对数组名进行赋值,或者为什么传递数组到函数后长度信息丢失。这些实际应用中的问题都需要在回答中覆盖。 最后,要确保引用的内容正确对应到各个区别点,比如引用[1]说明数组名被替换为指针,引用[3]说明数组指针的解引用方式,引用[5]中的代码示例展示数组指针的使用方法。结合这些引用内容,组织一个结构清晰的回答,分点列出区别,并给出代码示例注意事项。</think>### 数组名指针区别(C/C++) 在C/C++中,**数组名****指针**在语法上有相似行为,但本质存在重要区别。以下是核心差异的详细分析: --- #### 1. **本质属性不同** - **数组名**:是**常量标识符**,代表一段连续内存空间的起始地址,不可修改。例如: ```cpp int arr[5] = {1,2,3,4,5}; arr = nullptr; // 编译错误:数组名不可重新赋值[^1] ``` - **指针**:是**变量**,存储某个内存地址,可以重新指向其他地址。例如: ```cpp int* p = arr; p = &arr[2]; // 合法操作 ``` --- #### 2. **`sizeof` 运算结果不同** - **数组名**:`sizeof(arr)` 返回**整个数组占用的字节数**。例如: ```cpp int arr[5]; cout << sizeof(arr); // 输出 5*sizeof(int),如20(int为4字节时) ``` - **指针**:`sizeof(p)` 返回**指针变量本身的字节数**(如32位系统为4字节,64位系统为8字节)。 --- #### 3. **取地址操作(`&`)的语义** - **数组名**:`&arr` 的类型是**数组指针**(如 `int(*)[5]`),指向整个数组的地址。例如: ```cpp int arr[5]; int (*p_to_array)[5] = &arr; // 数组指针类型[^3] ``` - **指针**:`&p` 返回指针变量自身的地址,类型为 `int**`(二级指针)。 --- #### 4. **类型信息保留** - **数组名**:包含**数组长度元素类型**信息。例如: ```cpp void func(int arr[5]) { // 实际传递的是指针数组长度信息丢失[^1] } ``` - **指针**:仅存储地址,无法直接推导指向内存的完整类型长度。 --- #### 5. **函数参数传递时的行为** - **数组名**:作为函数参数时,**退化为指向首元素的指针**。例如: ```cpp void Print(int *p) { /* 实际接收的是指针 */ } Print(arr); // 等价于 Print(&arr[0])[^5] ``` - **指针**:直接传递指针变量的值,行为一致。 --- #### 示例代码:数组名指针的对比 ```cpp #include <iostream> using namespace std; int main() { int arr[5] = {1,2,3,4,5}; int* p = arr; cout << "sizeof(arr): " << sizeof(arr) << endl; // 输出20(假设int为4字节) cout << "sizeof(p): " << sizeof(p) << endl; // 输出8(64位系统) cout << "arr+1地址偏移: " << (void*)(arr+1) - (void*)arr << endl; // 4字节(int大小) cout << "&arr+1地址偏移: " << (void*)(&arr+1) - (void*)&arr << endl; // 20字节(整个数组大小) return 0; } ``` --- ### 注意事项 1. **禁止对数组名赋值**:`arr = new int[5];` 是非法操作。 2. **多维数组指针操作**:需使用数组指针(如 `int(*)[5]`)来正确访问元素。 3. **类型匹配**:数组指针与普通指针类型不兼容(如 `int(*)[5]` vs `int*`)。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值