世界上有两个设计软件的方法,一种方法是设计的尽量简单,以至于明显的没有什么缺陷,另外一种方式是使他尽量的复杂,以至于其缺陷不那么明显。
👩指针的相关概念
指针是C语言显著的特点之一,使用起来十分灵活高效,但如果使用不当很容易造成系统错误,这也是为什么指针一直是初学者最恐惧的知识点。
👨地址与指针
系统的内存就好比带有编号的小房间,就如同我们出去酒店住宿,我们要到房间去就需要得到房间编号。C语言也是这个道理,如果,假设有一个整型变量a,整型变量需要4个字节,所以编译器为变量a分配的编号为900~903的。
而什么是地址呢?地址就是内存区中对每个字节的编号,如下图所示的第一部分,900、901、901、902就是地址。为了进一步说明,我们看图的第二部分
如第二部分所示,900、904…就是内存单元的地址,而0、1、2…就是内存单元的内容。那么指针又是什么呢?在这里我们将指针看作是内存中的第一地址就可以了,多数情况下,这个地址是内存中另一个变量的位置,如图第三部分所示:我们给程序定义了一个变量,在进行编译时就会给该变量在内存中分配一个地址,通过访问这个地址就可以找到我们所需的变量,那么这个变量的地址我们就称为该变量的”指针“。
🧑变量与指针
变量的地址是变量和指针二者之间连接的纽带,如果一个变量包含了另一个变量的地址,则可以理解成第一个变量指向第二个变量。指针变量是指向一个变量的地址。所以将一个变量的地址值赋给这个指针变量后,这个指针变量就指向了该变量。
在代码程序中是通过变量名对内存单元进行存取操作的,但是经过编译之后,代码中的变量名已经被转换为该变量在内存中的存放地址,对变量值的存取都是通过地址进行的。
👧指针变量
1.指针变量的一般形式
如果有一个变量专门用来存放另一变量的地址,则我们将它称为指针变量,其一般形式如下:
类型说明 * 变量名
其中,“*”表示该变量是一个指针变量,变量名即为定义的指针变量名,类型说明表示本指针变量所指向的变量的数据类型。
2.指针变量的赋值
指针变量和普通变量并没有什么不同,使用之前也需要定义。不仅如此,还必须给他赋予具体的值。未经赋值的指针变量不能使用。给指针变量所赋的值与给其他变量所赋的值不同,给指针变量的赋值只要能赋予地址,而不能赋予给任何其他数据,否则编译器就会报错。C语言中提供了“&”地址运算符来表示变量的地址,其一般形式如下:
& 变量名;
也就是&a表示的就是a的地址,&b表示的就是b的地址。对于给一个指针变量赋值,我们一般有如下两种方法:
①.定义指针变量的同时进行赋值,例如:
int a;
int *p = &a;
②.先定义指针变量之后再赋值,例如:
int a;
int *p;
p = &a;
3.指针变量的引用
引用指针变量是对变量进行间接访问的一种形式,一般形式如下:
*指针变量
其含义就是引用指针变量所指向的值。
👦指针自加自减运算
指针的自加自减运算不同于普通变量的自加自减运算。这里如果讲解的话过于抽象,我们来看一个实例:
#include <stdio.h>
int main()
{
int a = 0;
int *p;
printf("请输入一个数字:\n");
scanf("%d", &a);
p = &a;
printf("结果是: %d\n", a);
p++; //地址加1,这里的1代表的不是一个字节
printf("结果是: %d\n", a);
return 0;
}
运行结果:
整型变量a在内存中占4个字节,指针p是指向变量a的地址,这里的p++不是简单的在地址上加上个1,而是指向下一个存放基本整型数的地址。所以当执行p++之后,p的值增加了4个字节。
🧒数组与指针
数组与指针是一个难点,也是一个混淆点,通过指针变量引用数组分类更细,包括了一维数组以及二维数组,我们下面会进行详细的介绍。
👶一维数组与指针
当我们定义一个一维数组的时候,系统会在内存中为该数组分配一个存储空间,其数组的名称就是数组在内存中的首地址。若再定义一个指针变量,并将数组的首地址传给指针变量,则该指针就指向了这个一维数组。例如:
int *p;
int arr[10];
p = arr;
这里的arr是数组名,也就是数组的首地址,将它赋给指针变量p,也就是将数组a的首地址赋给p,也可以写成如下形式:
int *p;
int arr[10];
p = &arr[0]
上面的语句是将数组arr中的首个元素的地址赋给变量p。由于arr[0]的地址就是首元素的地址,所以这两条赋值操作效果是完全相同的。
👴二维数组与指针
先来看一张图:
从图中我们可以看到几种表示二维数组中元素地址的方法,下面逐一进行介绍
①.&[0][0]可以看作数组0行0列的地址,也可看作数组元素的首地址&[m][n]就是第m行第n列元素的地址。
②.a[0]+n表示第0行地址第n个元素的地址。
③.&a[0]是第0行的首地址,当然&a[0]就是第n行的首地址。
④.a+n表示第n行的首地址。
⑤.* (*(a+n)+m)表示的是第n行第m列的元素
⑥.*(a[n]+m)表示的是第n行第m列的元素
👵字符串与指针
我们一般可以通过两种方法来访问一个字符串。第一种方式就是前面讲过的使用字符数组来存放一个字符串,从而实现对字符串的操作,另一个方式就是下面将要介绍的使用字符指针指向一个字符,此时可不用定义数组。来看一个例子:
#include <stdio.h>
int main()
{
char *p = "hello CSDN";
printf("%s", p);
return 0;
}
例子中定义了字符型指针变量p,用字符串常量”hello CSDN“为其赋初值。注意,我们这里并没有将所有字符都存放到p中,而是将该字符串中的第一个字符的地址赋给了指针变量p。
🧓字符串数组
前面我们讲过了字符数组,这里提到的字符串数组和字符数组是有所区别的。字符数组是一个一维数组,而字符串数组是以字符串作为数组元素的数组,可以将其看成一个二维字符数组,我们可以定义一个简单的字符串数组:
char baskerball player =
{
"Kobe";
"James";
"Irving";
"Durant";
"paul";
}
字符串数组变量basketball player被定义为含有五个字符串的数组,每个字符串的长度要小于20(这里要考虑字符串最后的’\0’)
在这里我们可以发现,我们再内存中给这些字符串分配了20字节的空间,但是只用了6个字节,这就造成的资源浪费。这时候我们就可以使用是指针数组,使每个指针只想所需要的字符常量,这种方法虽然需要再数组中保存字符指针,而且也占用空间,但也是远少于字符串数组所需要的空间。一维指针数组的定义形式如下:
类型名 数组名[数组长度]