目录
本章主要进一步讲解指针有什么运算,随后通过这些运算来进一步讲解指针,和我们熟悉的数组和字符串两者之间的关系,最后,我们会通过一些练习来进一步加深理解.
一.指针的运算
在谈及指针运算前,我们必须先讨论一个问题.
就是既然指针都为地址,大小都为4个字节或8个字节,为什么我们不统一用一种类型数据去接受它呢?比如像指针上篇提到那样,全部用void*去接收,不是也很好吗?
所以,实际上,指针类型,是有着它本身的意义所在的.(在指针上篇我们也有提到.)
指针的类型,决定了指针移动步长和解引用时访问的空间.
因此,这样就自然而然引出指针运算的第一种模式——指针+-整数
1.指针+-整数
比如指针是int *类型的,对指针+1,代表的含义就是访问的地址跳过一个int类型(4个字节)
或者指针是double*类型的,对指针+1,代表的含义就是访问的地址跳过一个double类型(8个字节)
2.指针-指针
我们定义一个整型的数组,将第一个元素和第二个元素的地址,分别存到p1,p2中,然后两个指针进行相减,我们可以看到得到的答案是2,这恰好是数组下标相减得到的值.
实际上这并不是巧合,指针相减得到的并不是地址相减,不然上面程序的答案就是8了
指针相减,代表的是指针之间的距离(用数组的元素来进行度量)
换种角度来看,假设偏移量为k,p1+k = p2,那p2 - p1实际上就等于p1要经过多大的步长(当然步长大小,我们前面提过和指针类型相关),和p2相等.
像我们之前模拟实现strlen函数,实际上也是运用这个道理,char*指针相减,得到的恰好是元素的个数,也就是字符串的长度.
PS:在任何一个不指向数组的指针进行相减,是没有任何意义,而且也会导致未定义的行为.
只有两个指针同时指向同一个数组或者字符串,指针相减才有其对应的意义.
3.指针的关系运算
我们知道指针实际就是地址,地址肯定就分低地址和高地址.
同样,也只有两个指针同时指向同一个数组时,比较指针才有意义.比较的结果当然就依赖于元素在数组中相应的地址.
我们还需要注意:
标准规定
二.指针与数组之间的关系
当讲完指针的相关运算后,我们就可以谈谈指针和数组之间的关系是什么.
1.指针和一维数组
首先,我们必须记住一个关键的信息,在C语言中
数组名代表的就是首元素的地址
如何验证这件事呢?我们可以简单编写一格小程序进行验证.
可以发现,arr数组名和arr[0]的地址,其实就是一个东西.
那我们用一个指针指向arr数组的时候,其实存的就是首元素地址给这个指针(int* 类型)
当然有两个例外
1.sizeof(数组名)计算的是整个数组的大小
2.&数组名取出的是整个数组的地址
我们简单看下下面这段程序
数组有5个元素,每个元素是int类型,占4个字节,用sizeof计算,刚好是20个字节,也就是一个数组的大小,而不是首元素的大小.
同样,虽然&arr取出的地址和首元素地址相同,但当它加1后,所得到的地址跳过了20个字节,这也代表着,&arr代表的的确是整个数组的地址,此时如果我们要用一个指针去接收它,一定要是一个数组指针.(int (*) [5]类型)
进一步看这个问题
arr[1]代表的实际上是首元素的地址(指针)移动相应的步长1,然后再解引用,得到相应的元素.
类似arr[2],其实就是*(p + 2)= * (arr + 2)等等.
2.指针和二维数组
在C语言中,二维数组是按照行主序存储的.
图片来自《c语言现代程序设计》 .
所以,理论上,我们用一个变量p指向二维数组首元素的地址,一直对它加1操作,就可以访问二维数组的所有元素.
通过上面的例子,假设我们将二维数组的每个最小单元看作一个一维数组(一维数组每个最小单元就是一个元素)这样我们就可以得到
二维数组名[i] = 第i行的首元素地址
真实的情况是不是这样呢?我们可以验证一下.
由图片可知,确实如此,我们也可以简单证明一下
&arr[i] [0] = *(*(arr + i) + 0) = &*(arr[i]) = arr[i]
C语言把二维数组实际上是看作一个一维数组,而这个一维数组的每个元素又是一个一维数组
二维数组的数组名是指向第一行(一个一维数组)的地址.
arr[i] [j] = *(*(arr + i) + j)
三.指针与字符串
这里主要谈及一个常见的错误.
上面这段程序,在编译器里,是完全可以运行的,前者是一个字符数组,后者是一个字符指针.
但值得注意的是,两者的区别是非常大的.
第一,字符串的内容只有字符数组可以修改,而字符字面量是不能被修改的字符指针仅仅只能被使用为访问这个字符串.
第二,字符指针随时可以在程序执行期间指向其它变量,而字符数组则不可以.
四.具体练习详解
在了解相关知识后,我们就可以通过一系列类似的题目加深理解了.
ps:下面题目都在X86环境下运行,指针的大小统一为4个字节.
1.一维数组
2.字符数组
(1).初始化中不含\0
(2).初始化中含\0
3.二维数组