您的位置:首页 > 其它

type conversion of array and pointer in C(1)

2014-08-14 16:51 316 查看
这个问题肯定是初学者的一个非常头痛的问题,甚至一些有编程经验的人也会碰到。我记得大一的时候是专门搞懂了,但是在写昨天那个用矩阵做fibonacci的时候又再次碰到了。所以就跟它斗争了一个晚上,以下是我的总结。

首先是简单的一维数组和一重指针

#include <stdio.h>
void f1(int a[]){
printf("call f1\n");
a[0] = 0;
}
void f2(int* p){
printf("call f2\n");
*(p+1) = 0;
}
void f3(int a[], int b[]){
printf("call f3\n");
b[0] = a[0];
b[1] = a[1];
b[2] = a[2];
}
int * f4(int a[]){
printf("call f4\n");
int* temp = new int[3];
temp[0] = temp[1] = temp[2] = a[2];
return temp;
}
int main(){
int a[3] = {1,2,3};
int *p = a;
printf("%x %x %x %x\n",a,&a,&a[0],p);
printf("%x %x %x %x\n",a+1, &a+1, &a[1],p+1);
printf("%d %d\n",a[0],*p);

f1(a);
for (int i = 0; i < 3; i++) printf("%d ",a[i]);
printf("\n");

f2(p);
for (int i = 0; i < 3; i++) printf("%d ",a[i]);
printf("\n");

int temp[3];
f3(a, temp);
for (int i = 0; i < 3; i++) printf("%d ",temp[i]);
printf("\n");

int *q = f4(a);
for (int i = 0; i < 3; i++) printf("%d ",*(q+i));
printf("\n");
}


稍微解释一下

主函数中第一行打印

printf("%x %x %x %x\n",a,&a,&a[0],p);
的结果应该是相同的,都是数组的地址或者指针中保存的地址。

主函数中第二行打印

printf("%x %x %x %x\n",a+1, &a+1, &a[1],p+1);
的结果a+1, &a[1], p+1应该是第一行的地址加上4, &a+1应该是第一行的地址加上12。

这里涉及到c语言和它的编译问题了。

在C中,数组名的值一般是个指针常量,也就是保存数组第一个元素的地址的变量。它的类型取决于数组元素的类型: 如果数组元素是int类型,那么数组名的类型就是指向int的常量指针。所以对a+1,即这个指针的下一个位置,由于这个指针的类型是数组元素的指针,即int*型,所以a+1被编译器认为是当前地址增加一个int大小的长度到达的新地址,即+4。

但是,在以下两种场合下,数组名并不是用指针常量来表示,就是当数组名作为sizeof操作符和单目操作符&的操作数时。sizeof返回整个数组的长度,而不是指向数组的指针的长度。取一个数组名的地址所产生的是一个指向数组的指针,而不是一个指向某个指针常量的指针。

所以&a返回的指针便是指向整个数组的指针,它的下一个位置是数组起始地址加上整个数组的长度。即+12。

主函数中第三行打印

printf("%d %d\n",a[0],*p);
结果也应该是相同的,都是数组中的第一个元素的值或者说指针指向的第一个元素的值。

f1是传数组进函数,直接对数组修改。

f2是传指针进函数,直接对指针指向的内容修改。

f3是传结果数组进函数,在函数中修改后读取这个结果数组。

f4是传数组进函数,返回一个new的数组的指针。

输出值如图所示:



与我们所料不差,一维数组比较简单,我们再来看看二维数组。二维数组会出现一些一维数组中没有的问题。

首先的一个问题就是我们可以用一个int* 指针指向一维数组,即int* p = arr1,但是我们不能直接用int**指向一个二维数组,即int arr2[2][2]; int** p = arr2。

这点另我们颇为不爽,这样不符合我们喜欢照猫画虎的习惯。但是想想也是合理的,因为二维数组本身就是连续存放,跟一维数组没有区别。所以你可以用一个int* 型的变量来保存这个二维数组的起始地址。回忆一下int** p, 这是一个指向指针的指针,换句话说,int** p = arr2的结果是把arr2的起始地址保存在了*p中。并没有实现把arr+2×1的地址保存在*(p+1)中。所以我们不能直接把一个二维数组赋值给一个二重指针。

其次要考虑的问题是函数的传参和返回的问题,类似于一维数组,我们可以传数组或者指针,返回也可以返回指针或者不返回(数组是无法返回的,只能返回指针,或者说返回数组的地址)。

最后要考虑的问题是new的问题,即如何new一个二维数组。

还是用代码的方式来讲解。

先讲类型推导问题。

#include <stdio.h>
int main(){
    int a[3][2] = {1,2,3,4,5,6};
    //TYPE 1
    //int **p = a 编译不通过
    
    //TYPE 2
    int (*p)[2] = a;
    
    //TYPE 3
    //int *q1 = a; 编译不通过
    //int *q2 = &a; 编译不通过
    //int *q3 = &a[0]; 编译不通过
    int *q4 = &a[0][0];
    //int *q5 = p; 编译不通过
    int *q6 = *p;
    int *q7 = a[0];
    
    //打印
    printf("%x %x %x %x %x %x %x %x %x %x\n",a,&a,&a[0],&a[0][0],&p, p,*p,q4,q6,q7);
    printf("%x %x %x %x %x %x %x %x %x %x\n",&a+2*1+1, a+2*1+1, &a[0]+2*1+1, &a[0][0]+2*1+1, &a[1][1], *(p+1)+1, p+2*1+1, q4+2*1+1,q6+2*1+1, q7+2*1+1);
}
上面的代码输出如下:



这是一个非常有趣的结果,我来解释一下。

1. a是数组a的地址,它是一个指针常量,由于a这个二维数组的元素是长度为2的数组,故a在编译时的递增量为+2*4=+8

2. &a是一个指向长度为6的数组的指针,值也是数组a的地址, 故&a在编译时的递增量为+4*6=+24

3. &a[0]是一个指向长度为2的数组的指针,值也是数组a的地址, 故&a[0]在编译时的递增量为+2*4=+8

4. &a[0][0]中保存的是数组的起始地址,由于a[0][0]并非数组而是数组中的一项,故&a[0][0]在编译时的递增量为+4

5. &p是指针p的地址,和数组a的地址无关

6. p指向的是长度为2的数组,*p就是长度为2的数组。故p在编译时的递增量为+4*2 = +8, *p在编译时的递增量为+4

7. q1编译不通过因为左边是int *,而右边是指向长为2的数组的指针,类型不相符。

8. q2编译不通过是因为左边是int *,而右边是指向长为6的数组的指针,类型不相符。

9. q3编译不通过因为左边是int *,而右边是指向长为2的数组的指针,类型不相符。

10. q5编译不通过因为左边是int *,而右边是指向长为2的数组的指针,类型不相符。

11. q4编译通过因为等式两边都是指向一个int型的数,q4在编译时的递增量为+4

12. q6,q7编译通过因为左边是int*,而右边是一个数组,这个赋值是允许的,但是如果右边加了&符号就不行,因为加完以后表示一个指向长度为2的数组的指针,跟左边是不相符的。q6,q7没有指向数组,它们本来就相当于一个长为2数组,指向的是数组中的数,所以它们在编译时的递增量为+4

由第一行结果可以知道,数组a的地址是bfa3e318,我们来分析实验结果,看看是不是跟我上面的地址递增量推测相符

&a+2*1+1的地址递增了HEX(60-18) = HEX(48)=DEC(72)单位递增量为72/3=24,和上面的分析相符

a+2*1+1的地址递增了HEX(30-18) = HEX(18)=DEC(24)单位递增量为24/3=8,和上面的分析相符

&a[0]+2*1+1的地址递增了HEX(30-18) = HEX(18)=DEC(24)单位递增量为24/3=8,和上面的分析相符

&a[0][0]+2*1+1的地址递增了HEX(24-18) = HEX(c)=DEC(12)单位递增量为12/3=4,和上面的分析相符

&a[1][1]的地址递增了HEX(24-18) = HEX(c)=DEC(12),因为&a[1][1]是{1,1}这个位置上的元素的地址,所以应该是递增(1×2+1)×4 = 12个byte

*(p+1)+1的地址递增了HEX(24-18) = HEX(c)=DEC(12),因为p指向的是长度为2的数组,所以p+1的地址递增2×4=8,*(p+1)是a数组第1行的地址,*(p+1)+1是a数组第一行第一列的地址,地址递增了8+4 = 12

p+(2*1)+1的地址递增了HEX(30-18) = HEX(18)=DEC(24)单位递增量为24/3=8,和上面的分析相符

q4+2*1+1的地址递增了HEX(24-18) = HEX(c)=DEC(12)单位递增量为12/3=4,和上面的分析相符

q6, q7的地址递增了HEX(24-18) = HEX(c)=DEC(12)单位递增量为12/3=4,和上面的分析相符

总结一下就是,指针的类型要明确,到底是指向什么东西,是一个数,还是二维数组中的一个一维数组,还是指向整个二位数组。类型不同不能互相赋值,另外编译器处理指针的递增量不同,访问数组需要搞清楚递增量。

今天不知不觉就这么晚了,剩下的部分明天再写吧。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐