C中数组和指针到底是否相同? C语言中:数组指针与指针数组有什么区别?

c\u8bed\u8a00\u4e2d\u6570\u7ec4\u548c\u6307\u9488\u7684\u533a\u522b\u4e0e\u8054\u7cfb

\u6570\u7ec4\u662f\u7528\u6307\u9488\u65b9\u5f0f\u5b9e\u73b0\u7684\uff0c\u4f46\u6570\u7ec4\u503c\u662f\u4e00\u4e2a\u5e38\u91cf\uff0c\u5373\u4e0d\u80fd\u6539\u53d8\u6570\u7ec4\u7684\u9996\u5730\u5740\uff0c\u800c\u6307\u9488\u662f\u4e00\u4e2a\u53d8\u91cf\uff0c\u53ef\u4ee5\u6539\u53d8\u5b83\u7684\u503c\uff1b\u5982\uff1a\u6570\u7ec4 a\u548c\u6307\u9488p\uff0cp=a+1\uff1b\u662f\u5408\u6cd5\u7684\uff0c\u4f46a=a+1\uff1b\u662f\u4e0d\u5408\u6cd5\u7684\u3002

\u6570\u7ec4\u6307\u9488\u662f\u6307\u5411\u8fd9\u4e2a\u6570\u7ec4\u9996\u5730\u5740\u7684\u6307\u9488\uff0c\u6307\u5411\u5bf9\u8c61\u662f\u8fd9\u4e2a\u6570\u7ec4\uff1b
\u6307\u9488\u6570\u7ec4\u662f\u5b58\u653e\u4e00\u7c7b\u6307\u9488\u7684\u6570\u7ec4\uff0c\u8fd9\u4e2a\u6570\u7ec4\u7684\u6bcf\u4e2a\u5143\u7d20\u90fd\u662f\u4e00\u4e2a\u6307\u9488\uff1b
\u4f8b\u5b50\uff1a
int a[10];
int* s;
s = a;//\u8fd9\u91ccs\u5c31\u662f\u4e00\u4e2a\u6570\u7ec4\u6307\u9488\uff0c\u5b83\u7684\u503c\u5c31\u662f\u6570\u7ec4a \u7684\u9996\u5730\u5740\uff1b\u5f53\u7136\uff0c\u5982\u679ca\u662f\u4e00\u4e2a\u6574\u6570\uff0c\u90a3s\u5c31\u662f\u4e00\u4e2a\u6307\u5411\u6574\u578b\u7684\u6307\u9488\uff1b

int a;
int* s[5];
for(int i=0;i<5;i++)s[i]=a;
//\u8fd9\u91ccs\u5c31\u662f\u4e00\u4e2a\u6307\u9488\u6570\u7ec4\uff0c\u5b83\u7684\u6bcf\u4e2a\u5143\u7d20\u90fd\u662f\u6574\u578b\u53d8\u91cfa\u7684\u5730\u5740
\u5e94\u8be5\u7b97\u6bd4\u8f83\u660e\u767d\u4e86\u5427
\u8c22\u697c\u4e0a\u63d0\u9192

指针是C/C++的精华,而指针和数组又是一对欢喜冤家,很多时候我们并不能很好的区分指针和数组,对于刚毕业的计算机系的本科生很少有人能够熟练掌握指针以及数组的用法和区别。造成这种原因可能跟现在大学教学以及现在市面上流行的很多C或者C++教程有关,这些教程虽然通俗易懂,但是在很多关键性的地方却避而不谈或者根本阐述不清楚,甚至很多时候阐述的是错误的观点。一般最初学习C/C++的时候接触到的都是这类教程,学习效果可想而知。对于初学者选择好的教程真的很关键,因为先入为主,一旦你接受了错误的观点或者思想即使后来知道了也一时很难纠正过来(我是深有体会),在此我推荐三本很适合于初学者的教程:

《The C Programming Language》Brian W. Kernighan和Dennis M. Ritchie的经典著作(K&R圣经)

《C ++ Primer》Stanley B. Lippman, Josée Lajoie, Barbara E. Moo C++经典权威著作

《Pointers on C》Kenneth A.Reek

很多时候,会有人说“指着和数组是相同的”,这是一种非常危险的说法,并不完全正确。在一定的上下文环境中,指针和数组是等同的,并非所有情况下如此。然而人们很多时候却自然而然忽略了这种情况成立的条件,去假定所有情况下都是如此。下面着重谈一下指针和数组的区别。

一.指针和数组的定义

指针是指针,指针变量存储的是一个地址,用来间接访问数据,在32位系统下,一个指针变量(包括void指针)始终占4个字节的空间。指针可以指向任何内存空间,但不是任何内存空间都可以通过指针去访问。

数组是数组,定义一个数组之后,编译器便根据该数组元素的类型和个数在内存开辟一段连续的空间来存放数据,从而直接访问数据。

下面看一个例子

在file1.c中有如下代码:

char p[100]="abcdef";

在file2.c中有如下代码:

#include<stdio.h>

extern char *p;

int main(void)
{
printf("%c\n",p[1]);
return 0;
}

复制代码

发现能够编译通过,但是能正确执行么?调试发现:出现下图这个错误,无法计算得到p[1]的值。原因稍后作解释。

从这里就可以看出,指针和数组并不是等同的,数组的定义并不等同于指针的外部声明(注意声明和定义的区别,定义是为一个变量或者对象分配内存空间,而声明只是描述类型)。

二.指针和数组访问时的区别

对数组下标的引用:

对指针的引用:

从上面的图中可以看出,指针和数组根本就是两个完全不一样的东西。对于数组,由于编译器在编译的时候就已经知道每个符号的地址,因此如果需要一个地址来执行某种操作,可以直接进行操作,并不需要增加指令首先取得具体地址,对于数组就是如此;而对于指针,必须在运行时首先取得它当前的具体值然后才能进行引用。从这点就可以解释为什么上面的程序无法正确执行,因为在file1.c中定义的p是一个数组,而在file2.c中却声明的是一个指针。因此在file2.c中引用时默认p是一个指针变量,并且会把指针变量中的任何数据当做地址来处理,因此首先取原数组的前4个字节的内容:0x61 0x62 0x63 0x64构成一个地址(暂不考虑大小端的问题)0x61626364,然后按照char型读取0x61626364这个地址中的内容,但是这个地址可能并不是有效地地址,即使是有效地,也不是我们想要的。大家可以想一下如果在file1.c中将p定义为指针类型,而在file2.c中将p声明为数组类型,会是什么情况?

解决上述问题的办法就是在任何时候保持定义和声明一致。

测试程序:

file2.c

#include<stdio.h>

extern char p[];
extern void print();

int main(void)
{
printf("%x\n",p[0]);
printf("%x\n",p[1]);
printf("%08x\n",p); //注意此时p的值是存储原指针p(file1.c中的p)的内存单元的首地址
print();
return 0;
}

复制代码

file1.c

#include<stdio.h>
char *p="abcdef";

void print()
{
printf("%08x\n",p);
printf("%08x\n",&p);
}

复制代码

执行结果为:

28
20
00424a30
00424a30
00422028
00424a30
Press any key to continue

三.一些应该注意的地方

1.sizeof计算所占空间时的区别。

对于数组,sizeof计算的是整个数组所占的空间,而在32位系统下,sizeof 指针的值始终为4.

2.数组名作为左值时不能被修改,而指针作为左值时可以被赋值。

3.指针可以进行自增(自减)运算(void指针除外,因为void指针无法知道步长),但是数组不能进行自增或者自减运算。
4.理解char *p="abcde"和char str[]="abcde"的区别。
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[3]={1,2,3};
int *p=(a+3);
printf("%d\n",p[-1]);
return 0;
}

复制代码

程序执行结果为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[0]);
}
int main(void)
{
int a[3]={1,2,3};
int *p1,*p2;
int b=4;
p1=a;
p2=&b;
fun(a);
fun(&a[1]);
fun(p1);
fun(p2);
fun(&b);
return 0;
}

复制代码

区分几个表达式的含义:

&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,我认为是错误的,因为其类型是数组指针)

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

#include<stdio.h>

int main(void)
{
int a[3]={1,2,3};
int *p=a;
printf("%08x\n",&p);
printf("%08x\n",p);
printf("%08x\n",&p+1);
printf("%08x\n",p+1);
printf("%08x\n",a);
printf("%08x\n",&a);
printf("%08x\n",a+1);
printf("%08x\n",&a+1); //注意输出结果
return 0;
}

char a[5]是字符串数组 这个内容可以改变 动态区 随着函数结束而释放

char *a是字符串指针 这个内容不能改变 存在静态区 所以在函数中运行完毕 可以返回指针 指向的内容

不同
a【5】是一个固定长度的字符数组
而 *a="hello"; a只想的是字符串hello的首地址,但是hello字符串末包含‘\n’ 它们在计算机内存中的实际长度是不一样的
虽然两个a都是指向首地址。

首先a是一个数组,有五个储存位置,要知道,在赋值字符数组时,系统会自动的加上\0,hello有5个字符,加上\0就是6个了,超过了5个,这是不行的。。。字符数组在程序运行时有固定的地址,开头的a【0】是首地址,这是一个常值,是不可以改变的,这是和指针不同的地方,上面的p是一个指向字符串的指针,它的值可以改变,比如p=b(b是另一个字符数组),这时,p的值就是b的首地址了;还有就是用指针指向字符串,只能在定义的时候赋值,不能在程序中赋值,但是字符数组可以,这也是区别,例如scanf("%s”,p);,这就是错的。。这样明白不?

就上面这个例子来说,char * a实际还包含一个空格结束符。

指针的应用比数组灵活,同一个指针,按照(int *), (short *)取出来的结果是不一样的。

  • 鍦C 璇█涓 鈥鎸囬拡鍜屾暟缁绛変环鈥 鍒板簳鏄浠涔堟剰鎬?
    绛旓細鍦 C 璇█涓鏁扮粍鍜屾寚閽鐨勫洶鎯戝鏁伴兘鏉ヨ嚜杩欏彞璇濄傝鏁扮粍鍜屾寚閽 鈥滅瓑浠封濅笉琛ㄧず瀹冧滑鐩稿悓锛 鐢氳嚦涔熶笉鑳戒簰鎹傚畠鐨勬剰鎬鏄璇存暟缁勫拰鎸囬拡鐨勭畻娉曞畾涔夊彲浠ョ敤鎸囬拡鏂逛究鐨勮闂暟缁勬垨鑰呮ā鎷熸暟缁勩傜壒鍒湴锛 绛変环鐨勫熀纭鏉ヨ嚜杩欎釜鍏抽敭瀹氫箟:涓涓 T 鐨勬暟缁绫诲瀷鐨勫乏鍊煎鏋滃嚭鐜板湪琛ㄨ揪寮忎腑浼氳湑鍙樹负涓涓寚鍚戞暟 缁勭涓涓垚鍛...
  • C璇█鏁扮粍鍚鍜屾寚閽鐨勫尯鍒?
    绛旓細鏁扮粍鍜屾寚閽堢殑鍖哄埆锛氭暟缁勫悕纭疄琛ㄧず鎸囧悜鏁扮粍棣栧湴鍧鐨勬寚閽堬紝浣嗚繖涓寚閽堝緢鐗瑰埆锛屽畠鐨勫硷紙鎸囬拡鐨勫兼寚鐨勬槸鎸囬拡鎵鎸囩殑鍦板潃锛変笉鑳借鏀瑰啓锛岃兘鏀瑰啓鐨勪粎浠呮槸鍏舵寚鍚戠殑鍐呭锛屾崲鍙ヨ瘽璇达紝鏁扮粍鍚嶅彧鑳芥寚鍚戞暟缁勭殑棣栧湴鍧锛屽鏋滄湁鏁扮粍char a[];閭d箞濡傛灉鍑虹幇a = a+1;杩欐槸缂栬瘧閮介氫笉杩囩殑閿欒銆傝屽浜庝竴涓櫘閫氱殑鎸囬拡鏄彲...
  • C璇█涓竴缁鏁扮粍鍜屾寚閽鏈変粈涔堝尯鍒?
    绛旓細C璇█涓紝涓缁存暟缁鐨勬暟缁鍚嶆寚鍚戜簡杩欎釜鏁扮粍鐨勮捣濮嬪湴鍧锛岃屾寚閽堟槸瀹冩寚鍚戠殑鏌愪釜涓滆タ鐨勫湴鍧銆備緥濡傚畾涔塱nt a[5]锛岃闂浜屼釜鍏冪礌鍙互鐢╝[1],涔熷彲浠ョ敤*锛坅+1锛夈傚綋涓涓寚閽堟寚鍚戞煇涓暟缁勬椂锛屾暟缁勫悕鍜屾寚閽鐨勪綔鐢ㄥ簲璇鏄竴鏍鐨勩備綘瀹氫箟鐨勮繖涓寚閽堟病鏈夋寚鍚戜换浣曚笢瑗匡紝鏄釜閲庢寚閽堬紝涓嶈兘閫氳繃绫讳技浜庢暟缁勭殑鏂瑰紡...
  • c璇█鏁扮粍涓嶅彲浠ュ綋鎴鎸囬拡浣跨敤,浣嗘槸鎸囬拡鍙互褰撴垚鏁扮粍浣跨敤鍚?
    绛旓細2.鏁扮粍鎸囬拡锛氶鍏堝畠鏄竴涓寚閽堬紝瀹冩寚鍚戜竴涓暟缁勩傚湪32 浣嶇郴缁熶笅姘歌繙鏄崰4 涓瓧鑺傦紝鑷充簬瀹冩寚鍚鐨勬暟缁鍗犲灏戝瓧鑺傦紝涓嶇煡閬撱傚畠鏄滄寚鍚鏁扮粍鐨勬寚閽鈥濈殑绠绉般3.鏍稿績鎬濇兂鏄氳繃涓涓猣lag,鏉ョ‘瀹氬崟璇嶇殑璧峰锛 姣忓嚭鐜颁竴涓崟璇嶇殑璧峰锛屽垯琛ㄧず瀛樺湪涓涓崟璇嶃 杩欐牱缁熻鍑虹幇鐨勫崟璇嶈捣濮嬩釜鏁帮紝鍗冲彲鑾峰緱鍗曡瘝鎬绘暟銆4...
  • C璇█:绠杩颁竴涓嬧鏁扮粍鍜屾寚閽鐨勫叧绯烩?
    绛旓細涓銆佹蹇 鏁扮粍锛鏁扮粍鏄鐢ㄤ簬鍌ㄥ瓨澶氫釜鐩稿悓绫诲瀷鏁版嵁鐨勯泦鍚堛鎸囬拡锛氭寚閽堢浉褰撲簬涓涓彉閲忥紝浣嗘槸瀹冨拰涓嶅悓鍙橀噺涓涓鏍锛屽畠瀛樻斁鐨勬槸鍏跺畠鍙橀噺鍦ㄥ唴瀛樹腑鐨勫湴鍧銆備簩銆佽祴鍊笺佸瓨鍌ㄦ柟寮忋佹眰sizeof銆佸垵濮嬪寲绛 1.璧嬪 鍚岀被鍨嬫寚閽堝彉閲忓彲浠ョ浉浜掕祴鍊硷紝鏁扮粍涓嶈锛屽彧鑳戒竴涓竴涓厓绱犵殑璧嬪兼垨鎷疯礉 2.瀛樺偍鏂瑰紡 鏁扮粍锛氭暟缁勫湪鍐呭瓨涓...
  • c璇█涓暟缁璁块棶鍜屾寚閽璁块棶鐨勫尯鍒
    绛旓細涓缁鏁扮粍 绛夋晥浜庡父閲忎竴缁鎸囬拡 浜岃呴櫎浜嗘暟缁勫悕涓嶈兘鏀瑰彉鍊间互澶栵紝璁块棶瀹屽叏鐩稿悓銆備簩缁翠互涓婃暟缁勶紝璁块棶鏃剁洿鎺ユ牴鎹暟缁勯鍦板潃 璁$畻鍑哄亸绉婚噺璁块棶銆傝屼簩缁翠互涓婃寚閽堬紝鍒欐槸鍒嗘鍙栧嚭鍚勪釜涓棿鍦板潃鍊硷紝鐒跺悗鍐嶅彇鍑烘渶缁堝笺
  • C涓暟缁勫拰鎸囬拡鐨勫尯鍒,姹傚簳灞傛暟缁勭粨鏋,璇ュ浣曞鐞
    绛旓細鏁扮粍鎸囬拡鏄鎸囧悜鏁扮粍鐨勬寚閽锛鎸囬拡鏁扮粍鏄暟缁勯噷鐨勫厓绱犲叏鏄寚閽銆傚鏈夛細int a[10],(*p)[10]=&a;鈥斺旇繖閲岀殑(*p)[10]灏辨槸澹版槑浜嗕竴涓暟缁勬寚閽坧锛屼笖p鏄寚鍚戞湁10涓猧nt鍨嬪厓绱鐨勬暟缁鐨勬寚閽堛傝繖涓巃鏄嚑缁寸殑娌℃湁鍏崇郴銆俰nt *p[3];鈥斺斿0鏄巔鏄叿鏈3涓厓绱犵殑鏁扮粍锛屾瘡涓厓绱犻兘鏄痠nt *鍨嬫寚閽堛
  • C璇█涓暟缁勫拰鎸囬拡鐨勯棶棰樸
    绛旓細鏁扮粍鏄暟缁锛鎸囬拡鏄寚閽銆俛鏄痑锛宎[0]鏄痑[0]銆傚彧涓嶈繃鍦ㄤ綔涓簊izeof銆&鐨勬搷浣滄暟绛夋湁闄愬嚑涓乏鍊间笂涓嬫枃涔嬪鐨勫満鍚堟暟缁刟浼氶鍖栵紙闅愬紡杞崲锛岀粨鏋滅殑绫诲瀷鏄厓绱犵殑鎸囬拡绫诲瀷锛屼涪寮冧簡鏁扮粍绫诲瀷涓殑闀垮害锛夋垚鎸囧悜棣栦釜鍏冪礌鐨勬寚閽&a[0]锛屾墍浠ュ鏄撻犳垚璇В銆傚厛鑰冭檻LZ鏈鍚庣殑琛ㄨ堪銆傛敞鎰C璇█涓璞″彲浠ョ悊瑙d负琚崰鎹殑...
  • C璇█涓,鐢鏁扮粍鍚嶄綔褰㈠弬鍜岀敤鎸囬拡浣滃舰鍙傛湁鍖哄埆鍚?
    绛旓細鏁扮粍鍚嶄笉鑳界Щ鍔級锛屼絾鏄畠浠呬粎鍙槸涓涓鎸囬拡锛屼綘鍙互鍦ㄨ皟鐢ㄧ殑鍑芥暟閲鐢╯izeof(num),sizeof(p)姹傚畠浠殑鍊,灏辩煡閬撲负浠涔堟绘槸瑕佸姞涓婁竴涓猧nt n浜嗭紝鍥犱负濡傛灉涓嶅姞鐨勮瘽鍑芥暟灏变笉鐭ラ亾浣犺鎿嶄綔鐨勬暟鐨勪釜鏁帮紝鍙﹀鍒繕浜嗕紶閫鏁扮粍鍜浼犻掍竴鑸殑int锛宒ouble绛夌被鍨嬫槸涓涓鏍风殑锛屼紶閫掓暟缁勪紶閫鐨勬槸鍘熸暟缁勶紝鍑芥暟鐨勬搷浣滃氨...
  • 鎸囬拡銆鏁扮粍鍚嶃佸紩鐢ㄤ笁鑰鐨勭浉鍚鐐涓鍖哄埆?
    绛旓細鏁扮粍鍚嶏細鏄竴娈佃繛缁┖闂村唴瀛樻斁鐨勬暟鍊硷紝濡傛灉瀹氫箟浜唅nt b[10]锛 b[0]鐨勫湴鍧锛&b[0]锛夋槸0X0000锛屽洜涓篿nt鍨嬪崰2涓瓧鑺傦紝閭d箞锛&b[1] = 0X000F锛屼互鍚庣殑浠ユ绫绘帹銆傝屾搷浣滄暣涓暟缁勫氨鍜屾搷浣滀竴缁鎸囬拡鏄竴鏍风殑銆俰nt *p = b锛涳紙涓巌nt *p = &b[0]鏁堟灉鐩稿悓锛夈傚紩鐢細鍏跺疄鏄鐩稿悓鍐呭瓨鍦板潃涓殑鍐呭...
  • 扩展阅读:c位出道对照表 ... 数组本身就是指针 ... c语言用指针遍历数组 ... 数组和指针的优缺点 ... c#数组排序方法 ... 数组相当于指针 ... c中标识符的命名规则 ... 指针和数组的相同点 ... 指针数组和指向数组的指针 ...

    本站交流只代表网友个人观点,与本站立场无关
    欢迎反馈与建议,请联系电邮
    2024© 车视网