2021年信息学部物联网工程学院学生科协第二次软件大培训
一、指针是什么
1、地址
-
定义:C语言地址,是指内存地址的概念。计算机内存中的各个存储单元都是有序的,按字节编码。此编码即为地址。
-
通俗解释:地址就是可以唯一标识某一点的一个编号,即一个数字。内存像尺子一样线性排布,为了计算机可以在众多内存当中找到,科学家引入了地址的概念,计算机则可以通过地址来寻找到需要的那一块内存。就像通过门牌号的指引来访问到真正的住户,在这里,门牌号代表地址,住户则代表真正的内存内容。
2、指针
-
定义:狭义的指针定义实际上就是刚刚所讲述的地址,但是我们习惯上将指针变量也叫做指针,而指针变量相当于是值类型为地址的变量。
-
通俗解释:指针是一种数据类型,就像int、float一样,int类型装载整型数据,float类型装载浮点型数据。而指针则是装地址型数据,仅此而已。习惯上我们也将“指针变量”简称为“指针”,但大家心里一定要明白这两个指针的区别。一个是真正的指针,它的本质是地址;而另一个是指针变量的简称
-
图片讲解指针含义:
二、指针的简单使用
1、两个运算符&和*
&a
的运算结果是一个指针,指针的类型是a 的类型加个*
,指针所指向的类型是a 的类型,指针所指向的地址就是a 的地址。*p
的运算结果就五花八门了。总之*p
的结果是p
所指向的东西。换句话说,一个指针指向一个变量,*
这个指针就是这个变量本身。另外,int *
当中的星号不是运算符,而是和int
看成一个整体,int *
是一种数据类型
2、实战演练
定义一个指针变量:
int *a; //定义整型指针变量a
char *b; //定义char类型指针变量
int **c; //定义(int *)类型指针变量,所谓的二级指针
char ***d; //定义(char **)类型的指针变量,所谓的三级指针
指针变量的赋值:
int x = 5;
int *p = &x;
printf("%d\n", *p);
//或者
int i, j, *p, *q;
p = &i;
q = &j;
i = 2;
j = 3;
3、指针的特殊赋值方式
指针变量的值中有一个非常特殊的值: NULL,它不指向系统中的任何变量或者函数。一般,我们使用它作为一个标志(返回指针的函数没有正确执行、到达链表末尾等等)访问NULL指针,实际上就是访问0x0地址,由于没有权限访问,因此系统会报错。
4、悬摆指针的危害
如果指针没有指向任何变量,即没有赋值或初始化,那么这个指针就是一个悬摆指针它可能指向内存中的任意一个位置,这就导致了后面给他赋值可能会篡改指针原来指向的那个值,随意访问或操作悬摆指针,轻则出现程序运行时错误,重则导致系统崩溃。
5、实例感受指针的加减
int a = 10, *pa = &a, *paa = &a;
double b = 99.9, *pb = &b;
char c = '@', *pc = &c;
printf("&a=%#X, &b=%#X, &c=%#X", &a, &b, &c);
printf("pa=%#X, pb=%#X, pc=%#X", pa, pb, pc);
pa++;pb++;pc++;
printf("pa=%#X, pb=%#X, pc=%#X", pa, pb, pc);
pa-=2;pb-=2;pc-=2;
printf("pa=%#X, pb=%#X, pc=%#X", pa, pb, pc);
if(pa == paa){
printf("%d\n", *paa);
}else{
printf("%d\n", *pa);
}
运行结果
&a=0X28FF44, &b=0X28FF30, &c=0X28FF2B
pa=0X28FF44, pb=0X28FF30, pc=0X28FF2B
pa=0X28FF48, pb=0X28FF38, pc=0X28FF2C
pa=0X28FF40, pb=0X28FF28, pc=0X28FF2A
2686784
6、指针的加减运算
从运算结果可以看出:pa、pb、pc 每次加 1,它们的地址分别增加 4、8、1,正好是 int、double、char 类型的长度。以pa为例子,实际上地址值加了sizeof(int) 这么多。说明白点,我们加减的数字是以指针指向的数据类型为量度的,p+1表示p在内存中前移1个int的距离,如果p不是int类型,而是其他类型道理也是一样。
7、图解指针运算
三、指针与数组
1、指针数组
-
定义:存储指针的数组我们把它称为指针数组。
-
案例演示:通过指针输出a,b,c
#include <stdio.h> int main() { int a = 16, b = 932, c = 10; int *arr[3] = {&a, &b, &c}; printf("%d,%d,%d", *arr[0], *arr[1], *arr[2]); return 0; }
-
图解指针数组的元素:
-
从优先级的角度理解指针数组:
从该图中可知:数组取下标的优先级为1,即数组名与数组取下标先行结合。
图示如下:
2、一维数组与指针
-
一维数组数组名本质上指向这个数组第一个元素的指针。
代码验证:
printf("%d\n",x); printf("%d\n",&x[0]);
运行一下你会发现输出的两行是一样的。
-
案例演示:使用指针遍历一维数组。
#include <stdio.h> int main() { int x[] = {99, 15, 100, 88, 252}; int *p = x; int i; for (i = 0; i < 5; i++) { printf("%d ", *(p + i)); } return 0; }
运行过程如下图所示:
3、二维数组与指针
-
行指针、列指针与数组名的定义
行指针:指向一整行的地址,不指向具体元素的地址的指针。
列指针:指向具体元素的地址的指针。
数组名:数组名指向该数组的第一个元素的首地址。 -
案例说明:
int a[3][4] = {{1, 3, 5, 7}, {9, 11, 13, 15}, {17, 19, 21, 23}};
演示代码中a为数组名,a[0]、a[1]、a[2]为行指针,具体元素的地址为列指针。
具体图示如下:
a[0]表示的是第0行第0个元素的地址
a[1]表示的是第1行第0个元素的地址
a[2]表示的是第2行第0个元素的地址
*a[0]表示的是第0行第0个元素的值
*a[1]表示的是第1行第0个元素的值
*a[2]表示的是第2行第0个元素的值
下面为等价的表达式,均表示第1行第0个元素的值
*a[1] *(*(a+1)) **(a+1)
-
以下为二维数组与指针的总结
4、数组指针
-
如果要将一个指针指向一个多维数组,我们就要用到数组指针。
-
案例演示:用一个数组指针来遍历二维数组
#include <stdio.h> int main() { int x[3][4] = {{1, 3, 5, 7}, {9, 11, 13, 15}, {17, 19, 21, 23}}; int (*p)[4] = x; int i, j; for (i = 0; i < 3; i++) { for (j = 0; j < 4; j++) { printf("%d\t", *(*(p + i) + j)); } printf("\n"); } return 0; }
-
从优先级角度理解指针数组
下面图示为p和*p的指向: