C语言基础 -- 指针(中),指针运算及其妙用

上一个节,我们讲解了指针的基本概念,用一句话总结起来就是:指针是用来存储另一个变量地址的变量。这一节,我们我们就介绍一下与指针相关的运算。

变量类型 相关运算
int 整数 +、-、*、/、%
float 浮点数 +、-、*、/  (没有模运算)
指针 地址 *、+、-、

在上表中,我们对比以前学习过的知识。int型的变量,存储的值是整数,与int型变量的相关运算包括但不限于 +、-、*、/、%,这些运算其实就是整数所能参与的运算;float型变量,存储的额值是浮点数(单精度),与float相关的运算包括但不限于 +、-、*、/(注意模运算%不是),这些运算其实就是浮点数所能参与的运算。指针类型的变量(这里是一个总的概念,包括但不限于char *,int  *,float *,double *等类型声明的指针),存储的值是一个地址,同理地址能够参与的运算,就是与指针相关的运算。下面我们就具体讲一下有哪些运算是与指针和地址相关的。

一、间接引用运算 *

这个我们在上一节讲过,我们要引用到一个变量,有两种途径:直接引用和间接引用。

(1)直接引用:就是直接在源代码中敲上一个变量的变量名。根据变量前后的运算符,我们用这种办法既可以取到变量的值,也可以改变变量的值。

(2 )间接引用:就是用间接引用运算符*,后接一个变量的地址或者后接一个存储着该变量地址的指针的方法,引用该变量。同理,我们用这个办法既可以取到该变量的值,也可以改变该变量的值。

代码1:

#include <stdio.h>
int main () {
	int a = 1, *b = &a;
	printf("%d ", a);
	a += 2;
	printf("%d ", a);
	*b += 2;
	printf("%d\n", a);
	return 0;
}

输出1:

1 3 5

在代码1中,每一个printf语句中出现的变量名a,其实就是对a的直接引用,属于取到a的值。a += 2,中的出现的变量名,也是对a的直接引用,属于改变了a的值。*b += 2,中的用法就是间接引用,用*后接指针b,指针b存储着变量a的地址,我们可以用这样的间接引用来改变变量a的值,这一点从输出1也可以看出来。

注意:你也可以这样理解*b的含义,我们把它分成两层来理解,第一层只写一个b,就是直接引用,属于取到了b的值,也就是&a;第二层在&a前面加了一个间接引用运算符*,就是对原变量a的间接引用了,后面跟的是+=2,这里的间接引用属于改变a的值。大多数人把*b理解为直接对指针b的间接引用,引用到的是指针b指向的变量a。

二、指针/地址的加减、递增递减、赋值运算

除了间接引用运算,我们对指针和地址用的最多的运算就是加减、递增递减、赋值运算。

我们以加1为例,看下面的代码2:

#include <stdio.h>
int main () {
	char a = 0, *pa = &a;
	int b = 0, *pb = &b;
	float c = 0, *pc = &c;
	double d = 0, *pd = &d;
	printf("%d %d %d %d\n", &a, pa, &a+1, pa+1);
	printf("%d %d %d %d\n", &b, pb, &b+1, pb+1);
	printf("%d %d %d %d\n", &c, pc, &c+1, pc+1);
	printf("%d %d %d %d\n", &d, pd, &d+1, pd+1);
	return 0;
}

输出2:

6422015 6422015 6422016 6422016
6422008 6422008 6422012 6422012
6422004 6422004 6422008 6422008
6421992 6421992 6422000 6422000

我们先任意选一行(例如第一行),观察前两列,可以验证出:指针的值,就是地址的值;观察后两列,可以得出结论:对指针加1,就是地址加1。这里插一句:正是因为指针和它指向的变量的地址,不管在数值上,还是在运算上,都有相同的结果,所以不少人就把指针和地址混为一谈。这里再次强调,指针是一个变量,地址是一个常值,指针里面可以存储一个地址。

我们在对比这四行的第一列和第三列、或者第二列和第四列。会发现,最然每一行都是对地址、指针的加1运算,但是得到的值却不相同,分别是原来地址编码加上1、4、4、8的值。

先从底层原理解释:以pb这个指针为例,pb的值是6422008,这个值就是变量a的地址。而变量b所占内存为4个字节,6422008是编码最小的那个自己,完整的四个字节的编码应该是:6422008、6422009、6422010、6422011;这四个字节都归b所有。于是pb+1所得到的一个新的内存地址的编码就不能侵犯到属于b的内存,所以规定pb+1就是pb的值加上4,即sizeof (int)。其他类型指针同理。

在从语法上解释:因为pb是用int *声明的,所以pb只能存储一个int型变量的地址,要改变pb的值,也只能也以sizeof(int)的整数倍去改变。其他类型的指针同理。

以上我们以加1运算为例,讲解了指针、地址的运算,对于++,--,-,+=,-=等运算道理是一样,可以自行测试。

注意两点:(1)指针、地址没有*、/、%等运算。(2)指针、地址之间也可以进行减法,但只限于同类型指针、地址。

三、指针运算的妙用

下来讲一下利用指针的这些运算性质我们可以做哪些事情。

我们常常利用指针的间接引用运算和加整数的运算,来引用数组元素。例如:

代码3:

#include <stdio.h>
int main () {
	int a[5] = {1,2,3,4,5}, *pa = &a[0];
	for (int i = 0; i < 5; i++) {
		printf("%d ", *(pa+i));
	} 
	return 0;
}

输出3:

1 2 3 4 5

代码4:

#include <stdio.h>
int main () {
	int a[2][3] = {
   
   {1,2,3},{4,5,6}}, *pa = &a[0][0];
	for (int i = 0; i < 2; i++) {
		for (int j = 0; j < 3; j++) {
			printf("%d ", *(pa+3*i+j));
		}
		printf("\n");
	} 
	return 0;
}

输出4:

1 2 3
4 5 6

对于一维数组,当把数组首元素的地址赋给指针pa,就可以用*(pa+i)来引用数组的下标为i的元素。对于二维数组(设列数为k),当把首行的首个元素的地址赋值给指针pa,就可以用*(pa+k*i+j)来引用数组第i行,第j列元素,(i,j从0开始)。

猜你喜欢

转载自blog.csdn.net/morn_l/article/details/133914573