指针的定义
C语言里,变量存放在内存中,而内存其实就是一组有序字节组成的数组,每个字节有唯一的内存地址。CPU 通过内存寻址对存储在内存中的某个指定数据对象的地址进行定位。这里,数据对象是指存储在内存中的一个指定数据类型的数值或字符串,它们都有一个自己的地址,而指针便是保存这个地址的变量。也就是说:指针是一种保存变量地址的变量。
指针的大小只和操作系统有关,在86位操作系统下的指针大小位四个字节,在64位操作系统下为八个字节
例如我们想用函数封装的形式来实现一个两数交换的功能
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<math.h>
void swap(int a, int b) {
int temp = a;
a = b;
b = temp;
}
int main() {
int a = 1;
int b = 2;
swap(a, b);
printf("%5d%5d", a, b);
}
那么这个函数会打印出 a b的值为多少呢
我们运行这个程序,得到结果
可以看到,a为1,b为2,显然两者并没有发生交换,main函数内部没有问题,那么问题就出在了swap函数中,我们输入的参数是元素 a,b。他们的值分别为1,2,在函数内部完成交换后,swap函数的类型为void,因此没有返回值,也就是说在执行完这个交换功能之后,开辟的内存空间就消失了,那么交换后的值也没有保存到a,b当中,通俗的来说就是main函数和swap函数自己干了自己的事,两者用了两块地方,之间除了参数没有别的联系。
那么修改的办法就显而易见了,我们需要让main函数和swap函数共同使用一块内存空间,这样即使swap函数执行完之后,交换后的值已经保存到a,b当中,从而完成了a,b交换的功能。那么我们如何让这两个函数访问同一个内存空间呢,这里我们就要用到指针。
我们通常这样定义指针
int *p=&a
*代表了这是一个指针变量,其指向的是int类型变量a的地址
我们通过访问p就可以得到a的地址,从而就可以实现在同一个内存空间上完成对a的相关操作
在具体应用指针之前,我们还要了解一个重要的概念——解引用
"*"的作用是引用指针指向的变量值,引用其实就是引用该变量的地址,"解"就是把该地址对应的东西解开,解出来,就像打开一个包裹一样,那就是该变量的值了,所以称为"解引用"。也就是说,解引用是返回内存地址中对应的对象。
比如int a=10; int *p=&a; 前文说到,p指向了int类型变量a的地址,那么*p就指向了int类型变量a的值
也就是说 *p=10
接下来我们就可以使用指针完成上述操作了,那么联系这两个函数的是形式参数和实际参数,因此我们就要在这里就行修改
void swap(int* p, int* q) {
int temp;
temp = *p;
*p = *q;
*q = temp;
}
int main() {
int a = 10;
int b = 20;
swap(&a, &b);
printf("%5d%5d", a, b);
}
可以看到我们传入的实际参数为a,b的地址,然后用指针变量接收,再在函数内部解引用,再进行交换操作,从而完成了函数功能的实现
这里就正确实现了函数功能
接下来就涉及到了当指针指向的数据存入内存中时,它是一种怎么样的形式呢
我们这里就要了解一个新的知识点,Windows数据存储的小端模式
比如说十进制数20在内存中以上16进制存储的,同时是四个字节,那么他在内存中的应该是以
00000014存储的,但是实际上,它在内存中是以14000000存储的
这是为什么呢,首先main函数会开辟一个栈,然后从高地址往低地址
又因为存储方式是小端存储
数据的低位放在低地址空间,数据的高位放在高地址空间
简记:小端就是低位对应低地址,高位对应高地址
读取数据的时候是从低地址开始读取,所以在内存中显示出来的是14000000
最后要说的就是关于指针的运算
p + n ——>
p向高地址偏移 n*sizeof(元素类型)
int *p = arr; //&arr[0]
p+1 //&arr[1] <=> p+1
*(p+1) -> arr[1]
p+3 -> &arr[3]
*(p+3) -> arr[3]
加括号的原因是*与+的优先级问题
这里还涉及到了*号的各种用法