C语言难点之数组与指针的爱恨情仇

提到数组和指针,是C语言的精髓。下面就来讲一讲它们之间的爱恨情仇。

数组与指针的定义

对于数组来说,它的定义是在内存中拿出一块连续的内存作为整个数组的空间,数组名对应与整个数组的首地址,相当于是一个常量的指针。
对于指针来说,它的定义只是在内存中开辟出四个字节(32位系统)。
首先在定义上两个就不一样,只是在功能上可以进行互换。

    char * ch=NULL;
    char num[10]={0};
	ch=num;
	ch[1]=10;
	num[1]=20;

在上面的情况下,两个是完全一样的可以进行互换。但是如果是下面这种情况就不同了。

   *(++ch)=10;
   *(++num)=20; 

你觉得这时候num[1]会等于几呢?
当然是编译器报错了呀,为什么?这就是数组名和指针的不同之处了,数组名是作为一个常量指针来看待的,它本身的值不可以改变,但是可以进行指针的运算,而ch作为指针,就可以随便改变它本身的值了,也就是可以作为左值。

数组作为函数的参数

将数组作为函数的参数传递进函数时,为什么还要把数组的大小做为另一个参数呢?比如

int countnumOfzero(int num[10],int n);

这不是已经把数组的大小10,传递进去了吗?如果你这么觉得,那可是大错特错了。其实数组在作为函数的参数传递进函数时,它的类型就变为了指针了。举个实例解释一下。

#include <stdio.h>
int countnumOfzero(int num[10],int n)
{
	printf("%d\n",sizeof(num));
    return 0;

}
int main(void)
{
    int num[10]={0};
    printf("%d\n",sizeof(num));
    countnumOfzero(num,10);
    return 0;
}

vc6.0编译输出如图所示。
在这里插入图片描述
从输出可以看到确实传进去的是指针,而不是整个数组了,如果使用下面的函数声明,与上面的函数也是一致的。

int countnumOfzero(int *num,int n);

所以其实传递数组作为函数的参数也是传值传递参数,只是可以通过指针的间接访问修改数而已。

数组的几个相关符号傻傻分不清


int a[10]={0};

那么a,a[0],&a[0],&a都代表什么意思呢?
这里还是介绍一下指针的运算,我觉得明白了指针的运算才能明白&a指的是什么。

unsigned short b=10;
unsigned short *a=&b;

假设a的地址为100,那么a+0x1等于多少呢?(unsigned int *)a+0x1又等于多少呢?
让我们来解析一下,对于指针的运算,它是按照它所指向的类型来进行运算的,而不是真的加1.它是加上了指针所指向类型所占的字节数。前面的例子a指向的是无符号的short类型,在32位系统下占两个字节,那么a+0x1就相当于a+1*2,a+0x2就相当于a+2*2。就是说指针的运算每次运动的步长是它指向类型所占的内存字节作为基数的。
在这里插入图片描述
所以可以得出(unsigned int *)a+0x1就是104,因为此时a被强转为int类型的指针,就是它指向的元素是int型,而int型占4个字节,所以此时a+1,相当于加了4.

有了上面的知识,我们来分析数组的几个相似的符号,对于a和&a[0]还有a[0],其实都比较好理解,就是&a比较难,a和&a[0]都是数组的首地址,而且地址的类型都是int型的,对它们执行运算的话都是加1相当于加4.a[0]就不是地址了,它就是访问数组的第一个元素。那么&a怎么理解呢?它表示的是指向了整个数组的指针,这个时候,上一篇文章《C语言难点之复杂声明与定义》就有用了,就是相当于声明了一个数组指针,int (*b)[10] ,这里b不就是指向整个数组的指针嘛,&a就是这样的指向了整个数组的指针,虽然它的值与a和&a[0]的值都是相等的,但是如果对其进行加1操作,效果可就大不相同了。

#include <stdio.h>

int main(void)
{
    int a[10]={0};
    int (*b)[10]=&a;
    printf("%d %d\n",b,(b+1));
    return 0;
}

在这里插入图片描述
因为b指针指向的元素是一个10个int元素的数组,所以它加1相当于加了40,与前面对于指针的运算是一致的。


#include <stdio.h>

int main(void)
{
    int a[10]={0};
    printf("%d %d\n",&a,(&a+1));
    return 0;
}

在这里插入图片描述
重点看加1之后,地址增加了多少,每个编译器分配的地址不同,但是增量是相同的。再来看a与&a的对比。

#include <stdio.h>

int main(void)
{
    int a[10]={0};
    printf("%d %d\n",&a,(&a+1));
	printf("%d %d\n",a,(a+1));
    return 0;
}

在这里插入图片描述
你懂了吗?&a与a的区别!

一个方法让你可以分析无数维的数组

对于多维的数组,其实你就可以把它作为一维数组来看,因为不论多少维,他都是按照线性内存分配的,其实前面就说过,想学好C语言,内存首先要搞懂。可以稍微参考这篇文章一篇文章帮你规划C语言从入门到进阶学习之路
当我们把多维数组看成一维的数组就比较好分析了。

int a[3][2];

数组名是一个指向第一维第一个元素的指针,它的类型是指向第二维元素数组的指针,就是指向数组的指针,和前面的分析是一样的,a的元素类型是指向一个2个int元素的数组,所以对于a+1相当于地址加2*4.如果对a+1解析访问即*(a+1),它仍然是一个指针,不过此时它的指针类型为指向整型元素的指针,对它加1操作,相当于加1*4 ,如果此时在进行解析 * (* (a+1)+1),就是访问它的数据了因为没有下一维的数组了,相当于a[1][1],就是这样层层递进的去分析,不论多少维的数组都可以解决.数组名就是指向第一维的指针,指针所指向的元素类型就是第二维的数组,如果执行加1操作,就是跨过整个第二维的元素,如果进行间接访问,那么就是进入下一维的元素中,此时表示的是指向元素类型是第三维的数组。以此类推。
在这里插入图片描述
对于数组和指针的一些相同与区别,以及一些难点稍作总结,希望可以帮助你理解。欢迎留言讨论。

欢迎关注我,一起成长,一起玩编程,回复C语言获取我的C语言学习资料。
在这里插入图片描述

原创文章 31 获赞 8 访问量 2314

猜你喜欢

转载自blog.csdn.net/HIT_zhanmusi/article/details/105592647