深入理解数组与指针的区别

在大一刚开始学习C的我们也许并没有真正的理解数组与指针,其实C的精华部分便是指针与内存的分配这一块。

那是充其量我们能够知道数组与指针肯定不是完全等价的,相同点就是:对数组的引用总是转化为对指针的引用,而不同点呢就是数组名是常量而指针是变量仅此而已,随着我们资历不断的提升,我们么更加进一步的去理解它,从他的本质去即内存的分配与访问去理解它。

好了,首先呢我们必须明白一个概念在C语言中,一个变量的声明和定义有什么区别。

我们知道定义只是一个特殊的声明。

定义:只能出现在一个地方,创建新对象,同时确定对象的类型并分配内存。(注意这里所说的对象跟C++中所说的对象没有任何的关系。)

声明:可以出现多次。 描述对象的类型,用于指代其他地方定义的对象。它所说明的并非本身。

extern 对象声明告诉编译器对象的类型和名字,对象的内存分配则在别处进行,由于并未在声明中为数组分配内存,所以并不需要提供关于数组长度的信息。

所以下来我们看一个例子:

文件1:int mango[100];

文件2:exitern int *mango;

在我们运行时总是会出错,为什么呢?

首先我们在看一下内存对数组和指针的引用:

比如说:char a[9]="abcdefgh"; c=a[i];

编译器符号表具有一个地址 9980

运行时第一步:取i的值,将它与9980相加

运行时第二步:取地址(9980+i)的值。

但是当:char *a="abcdefgh";     c=a[i];时

编译器符号表具有一个a,地址为4624

运行时第一步:取地址 4624的内容,如:5081(注意这里:因为p是一个指针,指针我们知道它存的是地址(也就是4624单元内的内容仍然是一个地址),而相对于本题来说它所存的地址就是数组的地址。)

运行时第二步:去i的值,并将它与5081相加。

运行时第三步:取地址【5081+1】的值。

我们很容易看到上面对数组内存是直接访问的,而对于指针是间接访问的。(虽然最终都可以取得所要的元素,但是访问途径是不一样的!)

当extern char *a;然后用a[i]来引用其中的一个元素时,就是对:char *a="abcdefgh";     c=a[i];的访问步骤。

所以既然把p声明为指针,那么不管p原先是定义为指针还是数组,都会按照指针形式访问元素的,但是只有当p原来定义为指针时这个方法才是有效的。

然后我们分析开始的那个为什么运行时总是出错?mango 被声明为extern char *mango,而它原先的定义却是char mango[100],这种情形,当用mango[i]这种形式提取这个声明的内容时,实际上得到的是一个字符。但按照上面的说法,编译器却把它当成是一个指针,把ACSII码解释为地址,显然是牛头不对马嘴么。

解决方案很容易:即声明为:extern int mango[];

还有就是如我们定义一个:char *p="abcdefgh";

此时这是一个字符串常量,这个字符串常量被定义为只读。如果试图通过指针去修改这个字符串的内容时,就会出现未定义的行为。

与指针相反的是:被字符串常量初始化的数组是可以修改其内容的。

下面说一句C程序员的名言:千万千万不要忘了C语言在表达式中把一个类型为T的数组的左值当作是指向该数组第一个元素的指针。

什么时候数组和指针可以相同呢?

所有作为函数参数的数组名总是可以通过编译器转换为指针。在其他所有情况下(最有趣的情况就是“在一个文件中定义为数组,在另一个文件中声明为指针”),数组的声明就是数组,指针的声明就是指针,两者不能混淆。但在是用数组(在语句或表达式中引用)时,数组总是可以写成指针的形式,两者可以互换。

对编译器而言,一个数组就是一个地址,一个指针就是一个地址的地址。

总结一下相同点的地方和不同点的地方:

声明方面:1、extern 如extern char a[];不能写成指针形式。

2、定义,如 char a[10];不能该写成指针形式。

3、函数的参数,如fun(char a[])或fun(char *a)随便写,因为当数组作为函数

参数时,编译都将把它转化为指针形式处理。

在表达式中使用:如c=a[i]也可以随便写,数组形式或指针形式都可以。

标准C规定了三条规则:数组与指针相同

1、表达式中的数组名(与声明不同)白编译器当作是一个指向该数组的第一个元素的指针。

2、下标总是与指针的偏移量相同。

3、在函数参数的声明中,数组名被编译器当作指向该数组第一个元素的指针。

在表达式中,指针和数组是可以互换的,因为他们在编译器里的最终形式都是指针,并且都可以进行取下标运算。当然也有几个极少见的例外:在下列情况下,对数组的引用不能用指向该数组第一个元素的指针来代替:

(1)、数组作为sizeof()的操作数显然此时需要的是整个数组的长度,而不是指向第一个元素的指针。

(2)、使用&取数组的地址,它所取的是整体数组的一个地址。

(3)、数组是一个字符串常量初始值时。

C语言把数组小标该写成指针偏移量的根本原因就是指针和偏移量是底层硬件所使用的基本模型。

标准规定作为“类型的数组”的形惨的声明应该调整为“类型的指针”。在函数形惨定义这个特殊情况下,编译器必须把数组形式改写成指向数组第一个元素的指针形式,编译器只向函数传递数组的地址,而不是整个数组的拷贝。

把作为形惨的数组和指针等同起来是处于效率原因的考虑。

在C语言中,所有非数组形式的数据实参均以传值形式(对实参作一份拷贝传递给调用的函数,函数不能修改作为实参的实际变量的值,而是只能修改传递给他的那份拷贝)调用。然而,如果要拷贝整个数组,无论是在时间上还是空间上的开销都是很大的,而且在大多数情况下你并不需要拷贝整个数组,只要告诉函数那个地址就可以了。

我们看一下数组形惨是如何被引用的:

fun(char p[])或fun(char *p)         c=p[i]

编译器符号表显示p可以取址,从堆栈指针SP偏移14个位置

运行时步骤1:从SP偏移14个位置找到函数的活动记录,取出参数

运行时步骤2:取i的值,并于5081相加

运行时步骤3:取出地址(5081+i)的内容

注意:有一样操作只能在指针里进行而无法在数组中进行,那就是修改它的值。数组名是不可修改的左值,他的值是不能改变的。

如下面三个函数在同一个文件中:

1.fun1(int *ptr)

2.{

3.      ptr[1]=3;

4.      *ptr=3;

5.      ptr=array2;//可以把另一个数组名赋给ptr,因为它是一个指针

6.}

7.fun2(int arr[])

8.{

9.      arr[1]=3;

10.      *arr=3;

11.      arr=array2;//也可以,因为arr编译器是按照指针的形式处理的

12.}

13.

14.

15.int arrary[100],array2[100];

16.main()

17.{

18.         array=array2;//编译错误"无法修改数组名"

19.}

有关数组和指针的异同就到此为止吧,其实还有很多内容,它们是C的精华,也是难理解的一部分!

转:http://blog.chinaunix.net/uid-26983585-id-3336911.html

时间: 2024-12-16 20:09:13

深入理解数组与指针的区别的相关文章

数组与指针的区别

数组与指针的区别 1.数组与指针的定义 指针是变量,存储的是一个地址,用来间接访问数据,在32位系统下占用4个字节.指针可以指向任何内存空间,但不是任何内存空间都可以通过指针去访问(不能访问寄存器). 数组是标识符且不占用存储空间,类似于常量,因此数组名不能作为左值. short *sz[4]; //大小为4的一维数组,元素是指针 short (*sz1)[4]; //指向数组的指针 2.extern数组与指针 先看这一段代码: /* * d.c * Created on: * Author:

字符串拼接 strcat ;数组和指针的区别

问题:字符串拼接 strcat 方法1: 开辟新空间,存放结果: #include <stdio.h> #include <stdlib.h> #include <string.h> #include <assert.h> char* _strcat(char* str1, char* str2){ assert(str1 != NULL && str2 != NULL); char* ret = (char*)malloc((strlen(

数组与指针的区别,以及在STL中传递数组/指针

数组和指针在作为实参传入T[] 或T*的形参时没有区别 void f(int pi[]) { cout << sizeof(pi) << endl; } int a[5] = { 1,2,3,4,5 }; f(a); 上述代码输出的是4(32位系统)或8(64位系统),总之不是sizeof(int) * 5(数组大小). 为什么明明形参是数组形式的int [],实际上和指针形式的int *无异呢?关键原因就在于,数组是不能作为左值的. 也就是说,你不能定义两个数组,比如int a[

深入理解数组与指针——原因在于”退化“

(一) 数组与指针本质是不同的.如下图所示 char a[]="hello"; char *p="hello"; 上述代码的初始化结果如下图所示: a就是一个数组变量,表示整个数组.p是一个指针变量,存储的值是地址. 数组变量a和指针变量p,都存储在用户栈中. 而表达式char *p=”hello“中的”hello“是字面量,存储在静态只读存储区 .rodata区域.p存储的是”hello“的首元素地址 由此,可以看出数组和指针的区别有多大!!! (二) 但有时候”

(C语言)数组与指针的区别

以前常常听过这种说法,说数组和指针这两者比较像,但是不能混淆,可是一直没能理解.刚刚在李云的<专业嵌入式软件开发>中,看了讲述数组与指针区别的一章,似乎有所领悟.本着知乎上看到的这张图,我试着将自己的理解写一些. 首先需要了解数组和指针的工作模式. 数组: char name[] = {'T', 'o', 'n', 'y', '\0'}; 此时,这个数组的地址是这样的.其中name指向数组的开头: 指针: char name[] = {'T', 'o', 'n', 'y', '\0'}; ch

引用和指针的区别,数组和指针的区别

一:引用和指针的区别 1:引用并非对象,它只是为一个已存在的对象所起的另外一个名字,必须初始化,而且无法改变它绑定的对象,之后每次使用这个引用都是访问最初绑定的那个对象. 2:指针本身是对象,可以不用初始化,可以改变指针指向的对象 二:指针与数组的区别 指针 数组 保存数据的地址 保存数据 间接访问数据 直接访问 通常用于动态数据结构 通常用于存储固定数目数据类型相同的元素 相关操作malloc(),free()等 隐式分配和删除 同常指向匿名数据 自身即为数据名 在C语言中对于下面的两种情况,

数组与指针的区别?

数组要么在静态存储区被创建(如全局数组),要么在栈上被创建.指针可以随时指向任意类型的内存块.    (1)修改内容上的差别 char a[] = "hello";  //它们有各自的内存空间:a[0] = 'X';char *p = "world"; // 注意p 指向常量字符串p[0] = 'X'; // 编译器不能发现该错误,运行时错误 (2) 用运算符sizeof 可以计算出数组的容量(字节数).sizeof(p),p 为指针得到的是一个 指针变量的字节数,

关于理解数组与指针

指针指向数组问题 int arr1[10]; char arr2[10]; char *parr[10];//指针数组 int(*parr1)[10]=&arr1; int *p=arr1; char *(*pparr[3])[10];//指向数组的指针数组 指向函数的指针 void fun1(char *str) { printf("%s\n",str); } void fun2(char *str) { printf("%s\n",str); } voi

转:数组与指针的区别

转自:二维数组和指向指针的指针 一道面试题引发的问题,首先要知道[]的优先级高于*,题目: char **p,a[6][8]; 问p=a是否会导致程序在以后出现问题?为什么? 直接用程序说明: #include<stdio.h> void main(){ char **p,a[6][8]; p = a; printf("\n");} 编译,然后就会发现通不过,报错:错误 1 error C2440: “=”: 无法从“char [6][8]”转换为“char **” 于是乎