再谈数组与指针

来源:互联网 发布:编程语言薪资排行榜 编辑:程序博客网 时间:2024/05/21 03:56

     在以前的一遍博文中,有谈到数组与指针,经过了一段时间,我对数组与指针的认识又更深入了一些………

 

 

 

(一)、数组是什么?

 

     其实数组也是一种数组类型,对于元素是 int 类型的数组的类型就是 int []

     我们在进行函数声明的时候有时候会省略形参的标识符,只写上形参的类型,对于数组类型的形参我们就是写成这样 int []。

     例如: int funtion(int [], int , int );(尽管在函数体内部会退化成指针。)

 

     还有我可以对一个类型进行 sizeof 运算 sizeof(int) ,我们也可以对数组类型进行sizeof 运算,例如: sizeof(int [10]) (数组类型有多少元素要指出)。

 

     用typedef 可以对数组类型取一个别名:

     typedef    int     vegtable[10];

 

     vegtable arr;     <=====> int arr[10];

 

 

 

 

 

(二)、指针是什么?

 

     指针是C/C++的最特别的地方,也是最难学好的地方!

    

     但指针也没有那么晦涩难懂。

 

     指针的本质是 变量和其他的变量一样,能被运算,真正区别于其他变量的是,指针存储的内容是地址。

 

     那变量总有个数据类型的,指针的类型: 其实也就是地址的类型,而地址的类型取决于地址里存放的数据的类型!

 

     指针的偏移步长,是指针指向地址的里数据类型进行 sizeof 运算的大小。

 

     int *p;               指针p的步长就是 sizeof(int);

     int (*p)[10];        指针p的步长就是 sizeof(int [10]);

     int (*p)[10][10]; 指针p的步长就是 sizeof(int [10][10]);

 

     有时我们对指针进行强制类型转换,转换的本质是使指针指向的地址里的数据类型改变,这样指针的步长也会发生变化。

 

     int (*p)[10];        步长是 sizeof(int)      对指针p进行强制类型转换  int *pp = (int *)p;  现在pp的步长就是 sizeof(int)。

 

 

 

     如何定义一个指向某个数据类型的指针(看一个指针指向什么数据类型)?

 

     1)、是指针;                           (*p)

     2)、指向什么类型数据;            例如: 指向整型 int (*p) ;  指向数组类型(元素是int,有10个元素)   int (*p)[10];

                                                            指向函数(返回值是int,两个形参是int)     int (*p)(int ,int);

 

     3)、再给指针赋值。

 

     (看一个指针指向什么数据类型 也类似上面的分析方法)

 

 

 

 

(三)、数组与指针什么时候通用,什么时候不通用?

 

     数组与指针在一些地方是通用的,也有一些地方是不能互换的。

    

     数组与指针在通用的时候,数组是退化成指针,我们对数组的操作就可以转化成对指针的操作。

     例如对数组里元素的访问就 a[i] 实际上编译会之后会变成 *(a+i), 即使通过指针来访问元素的;数组作为形参传递给函数……

 

     那数组在哪些情况不会退化指针呢?有三种情况下数组不会退化成指针的:

     1)、sizeof        对数组进行 sizeof 运算的时候,数组不会退化成指针;

     2)、&               对数组进行 & 运算时

     3)、(自己暂时还未理解,理解就马上添加上)

 

     除上面的三种情况外,数组都会退化成指针的。

 

 

 

 

(四)、数组指针与指针数组

 

     我觉得中文有些饶人,不算是问题,如果你看英语对这俩概念的描述,或许你会很直观的区分它们:

 

     数组指针: A pointer to an array                   int (*p)[10];

     指针数组: An array of poiners to integers   int *p[10];

 

     我们看看对数组名进行 & 运算:

 

     int  a[10];

     int  *p = a;                        是把 &a[0] 赋给 p,p+1 的偏移是sizeof(int)          p指向数组的第一个元素

     int  (*p)[10] = &a;             是把 &a      赋给 p,p+1 的偏移是sizeof(int [10])   p指向整个数组    这就是一个数组指针

 

 

 

 

 

 

(五)、数组作为参数传递给函数

 

     数组除上面讲的三种情况除外,数组都会退化成指针。

 

     所以数组作为函数的参数时,数组会退化指针。

 

     一维数组作参数的写法:

 

     int fun(int a[10]);

     int fun(int a[]);

     int fun(int *a);

 

     一维数组:上面这些都是传递首个元素的地址 &a[0],指针在函数体内的步长是 sizeof(int) 。

 

 

 

     二维数组作参数的写法:

       

     int fun(int a[10][10]);

     int fun(int a[][10]);

     int fun(int (*p)[10]);

     

     二维数组:上面这些都是传递首行的地址 &a[0],也就是传递的是行地址,指针在函数体内的步长是 sizeof(int [10])。

 

 

     现在来分析下  二维数组的 int fun(int a[][10]);  为什么是这种, 而像 int fun(int a[10][]); int fun(int a[][]);的传递方式不可以呢?

     int a[10][10];

     看二维数组前中括号里的数字(以下简称前数字)和后中括号里的数字(以下简称后数字)所代表的意义。

 

     前数字 是数组的行数, 后数字 是代表的数字的每行的列数。

     上面这句或许你认为我说了跟没说似的,但你看二位数组传入给函数的是首行的地址,在函数体内会对指针操作进行偏移,这时候就要步长了,而步长就是每行的列数,所以说必须传入每行的列数,即后数字,这样函数体内就知道二维数组传入的地址指针的偏移步长。

     也需你会问,那行数为什么不用传入呢?

     我们来对比一维数组,int fun(int a[]);一维数组没有传入的是元素的个数,在函数体内部,对于传入的指针来说是没有传入对指针操作的边界,这样就好理解了二维数组为什么没有传入行数,行数即是二维数组的行指针的操作边界。

     我们会经常看见,一维数组作为函数参数时,还另外再传入一个参数(数组的元素的个数),这样就告诉函数对指针的操作边界。

     所以在二维数组作为函数参数是,我们也可以再传入一个参数(数组的行数)告诉函数对指针的操作边界。

 

     这样我们就可以类比多维数组作函数参数怎么传递了, 一定要传入对指针操作的步长。

     例如: int fun(int a[][10][10])               步长是 sizeof(int [10][10])。

 

     另:二维数组的作函数参数还有两种非常危险的写法  int fun(int **a); int fun(int *a[10]);(多维数组类似,至于为什么你自己可以分析到的)。

 

 

    以上都是我个人对与数组与指针的理解,若有什么不对或不足,欢迎指出,我会及时更正!转载请说明出处!

原创粉丝点击