c语言判断指针相等,[翻译]C语言中指针和数组是否相等?
虽然看似简单的问题却常常被忽视。特此特意将一篇博客内容进行了翻译,并且内容表述得相当清晰。其原始链接地址为Are pointers and arrays equivalent in C?
C语言中指针和数组是否相等?
简短的答案:不相等。
具体来说,这个问题的答案取决于如何定义'相等'。在某些情况下, 指针运算与数组下标被认为是相同的.然而, 在其他方面, 指针与数组则存在差异.
一个简单的例子展示相等性:
#include
int main()
{
char arr[] = "don't panic\n";
char* ptr = arr;
printf("%c %c\n", arr[4], ptr[4]);
printf("%c %c\n", *(arr+2), *(ptr+2));
return 0;
}
输出结果:
t t
n n
观察到,在数组与指针的环境中使用索引时非常有效;同样地,在处理数组与自身结构时进行操作是可行的。
他们如何是不同的?
在一个很重要和基本的方式中,考虑如下代码:
char array_place[100] = "dont't panic";
char* ptr_place = "don't panic";
int main()
{
char a = array_place[7];
char b = ptr_place[7];
return 0;
}
在为变量a赋值的过程中出现了哪些情况?与对变量b进行赋值有何不同?查看编译器对这些操作的分解有助于理解其机制。
(Visual C++ 2005,x86,Windows XP)
char a = array_place[7];
0041137E mov al,byte ptr [_array_place+7 (417007h)]
00411383 mov byte ptr [a],al
char b = ptr_place[7];
00411386 mov eax,dword ptr [_ptr_place (417064h)]
0041138B mov cl,byte ptr [eax+7]
0041138E mov byte ptr [b],cl
在C语言中,数组的意义在于将名称指向前一个元素的内存地址。因此,在对变量a进行赋值的过程中,请注意以下步骤:首先将第七个位置(即第八个字符)偏移后存储于array_place[7]的位置中;接着将该结果地址中的内容加载到寄存器al中;最后再将其存储到变量a中。
另一方面,在语义上与另一变量存在显著差异。仅作为一个固定值存在,并用于存储另一个内部变量的地址位置。由此可见,在计算字符串中第8个字符相对于起始点的位置偏移量时,CPU将该指针对应的数值加载到一个寄存器中,并对该寄存器进行加法运算以确定偏移量。从而导致额外增加了一条指令的操作步骤。
一个图形化的解释

右侧一列是存储位置,每一个单元格存储着内存中的内容.通过图形展示的是array_place的第一个单词的位置.
观察得知,在内存地址码为十六进制数值为41 7 的位置上存放着一个简单的标签 named array_place。因此访问 array_place[7] 等同于访问内存地址码为十六进制数值为41 以及十进制数为286368 的位置。由此可见,在编译过程中, 编译器仅仅将 array_place[7] 替换为其对应的内存地址码, 即 $41_7_ , 并无需进行地址计算运算。
而指针的工作方式是不同的:

ptr_place仅仅是一个标识符,在内存中占据着特定的位置对应于一个内存地址。该位置对应于该字符串起始位置的第一个字符的位置。通过分析ptr_place[7]的操作序列可深入理解编译器如何生成相应的机器码。
C语言的变量名仅仅是一个标签
那些未深入研究编译原理的程序员常常忽略指针操作这一重要特性。在C语言中,默认情况下变量仅作为内存空间的一个代号存在。在编写代码时,请尽量使用内存地址作为变量名,并通过访问这些地址来操作数据(不直接嵌入固定数值),因为后者是编译器负责处理的任务。
真实地址并非固定不变地以字节形式编码,而是动态变化的过程,这一过程涉及加载和重新分配机制.然而,这些细节超出了我们当前讨论的范围,因此无需深入探讨.
标识符是在编译阶段预先分配内存空间的变量名称。通过分析这些数据结构的使用情况,我们可以深入理解指针与数组之间的本质差异。
在函数参数中数组被转换成指针
先看一段代码:
void foo(char arr_arg[], char* ptr_arg)
{
char a = arr_arg[7];
char b = ptr_arg[7];
}
问题:在这里访问a和b有什么不同吗?
回答:一点也没有!
这是编译器的展开:
char a = arr_arg[7];
00412DCE mov eax,dword ptr [arr_arg]
00412DD1 mov cl,byte ptr [eax+7]
00412DD4 mov byte ptr [a],cl
char b = ptr_arg[7];
00412DD7 mov eax,dword ptr [ptr_arg]
00412DDA mov cl,byte ptr [eax+7]
00412DDD mov byte ptr [b],cl
这是因为数组在函数的参数中总是倍转换成指针。
char arr_place[]的参数声明仅仅是char* arr_place的语法糖。
这里引用K&R2:
在将数组名作为函数参数传递给程序执行的过程中,则它仅仅充当初始化元素的位置。然而,在实际运行中,“当调用该功能模块时”,这个参数会被解析为一个局部变量实例——在这种情况下,“数组名”实际上相当于一个指向内存中特定位置的指针变量——其存储的内容指向内存中的具体地址
如果这令人费解,请耐心思考一番。回想起之前展示的图表可知,在这种情况下作为编译器别无选择:它必须将数组名这个标记转化为指向内存地址的操作符。然而,在编译阶段并未被调用而在运行时才被调用这一特点值得注意。此时在栈中被视为一个参数这一点值得特别关注:尽管如此但该栈操作员却无法理解这些符号背后所隐含的实际含义——即它们代表的是内存地址而不是具体的数值数据。因此在处理带有内部引用的数组标签时该系统便显得力不从心:它无法将这些符号与实际内存地址建立直接联系进而完成必要的转换工作。
