指针
一、指针是什么
在口语中,指针通常指的是指针变量,它用来存放地址
内存中,最小单元的编号称为地址,指针就是用来存放它们的。
#include <iostream>
using namespace std;
int main()
{
int a = 10;
int* p1 = &a;
return 0;
}
以上代码中,我们手动开辟了一个空间,这个空间的名字为a,里面存放着一个整型10,然后我们通过取地址操作符**&** 将a的地址取出来,存放到一个叫做p1的空间内(地址我瞎写的)。
那指针有多大呢?
答:在32为机器上,地址是由32个0或者1组成的二进制序列,那地址就得用4个字节的空间来存储,所以一个指针变量的大小就应该是4个字节。同理,在64位机器上,一个指针变量大小为8个字节。
二、指针类型
众所周知,变量有多种不同的类型,如整型,浮点型等。那指针也是变量,也就是说它也有类型。
只有对应类型的指针变量存储数据才能避免发生错误
#include <iostream>
using namespace std;
int main()
{
char ch = 'w';
char *p1 = &ch; //nullptr 为C++中空指针的关键字。
int *p2 = nullptr;
float *p3 = nullptr;
return 0;
}
解析:p1的类型是char*,它为了存放char类型变量的地址。
三、指针±整数
既然指针是个变量,那么它加减一个整数会产生什么效果呢?
#include <stdio.h>
int main()
{
int a = 10;
char* p1 = (char*)&a;
int* p2 = &a;
printf("%p\n", &a);
printf("%p\n", p1);
printf("%p\n", p1 + 1);
printf("%p\n", p2);
printf("%p\n", p2 + 1);
return 0;
}
由上图可以看出,不同类型的指针决定了指针移动一步的距离。
四、指针的解引用
知道了指针是存放地址的,那我们如何取到这个地址里面的东西呢?
答:我们可以通过解引用操作符解引用指针来拿到里面的数据
解引用操作符也为“*”。
#include <iostream>
using namespace std;
int main()
{
int a = 10;
int *p1 = &a;
cout << *p1 << endl;
*p1 = 20;
cout <<"a的值为: " <<a << endl;
cout << *p1 << endl;
return 0;
}
可以看到,我们通过解引用操作符对指针进行解引用后我们就可以拿到这个地址里面的东西了,并且可以对它进行修改。
五、指针-指针
指针与指针相减得到的是这两个指针之间类型的个数
#include <iostream>
using namespace std;
int my_strlen(const char* star)
{
if (star == nullptr)
return 0;
const char* end = star;
while (*end != '\0')
{
end++;
}
return end - star;
}
int main()
{
const char* c1 = "hello world";
int a = my_strlen(c1);
cout << a << endl;
return 0;
}
六、二级指针
什么是二级指针?
我们先来看一下二级指针的定义:
int** p2 = nullptr;
先对 int** p2 进行解析:靠近p2的这个* 与p2结合,我们首先可以知道p2是个指针变量。然后将*p2拿掉后剩下的就是p2里存储的数据类型,该数据类型为int *。
int *数据类型是什么呢?很显然,它也是个指针。从这里我们可以推出,p2指针里面存放的是一级指针的地址。结合以下代码更容易理解
#include <iostream>
using namespace std;
int main()
{
int a = 10;
int *p1 = &a;
int* *p2 = &p1;
}
我们同样可以通过二级指针解引用的方式取到a的值:
先对二级指针p2进行第一次解引用: (*p2) 得到一级指针p1存放的a的地址,然后再进行第二次解引用拿到a存储的值,即 *(*p2)。
#include <iostream>
using namespace std;
int main()
{
int a = 10;
int* p1 = &a;
int** p2 = &p1;
*(*p2) = 20;
cout << "a的值为: " << a << endl;
return 0;
}
七、指针数组
指针数组是什么?是指针?还是数组?
答:指针数组是一个数组 ,这个数组里面存放的是指针。
举个例子:
int[]里面存放的数据都是int类型,char[]里面存放的是char类型。
那么,指针数组的定义是怎么定义的呢?
type* arr[] = {nullptr};
arr是变量名称,[ ] 的优先级比 *
高,所以arr先和[ ]结合变成数组,这个数组里面存放的数据就是
int*
#include <iostream>
using namespace std;
void PrintArr(int* arr[], int length)
{
for (int i = 0; i < length; ++i)
{
cout << *arr[i] << " ";
}
cout << endl;
}
int main()
{
int a = 10;
int b = 20;
int c = 30;
int* arr[] = { &a, &b, &c };
int length = sizeof(arr) / sizeof(arr[0]);
PrintArr(arr, length);
for (int i = 0; i < length; ++i)
{
*(arr[i]) = 50;
}
PrintArr(arr, length);
return 0;
}
八、数组指针
顾名思义,数组指针是指针,它指向数组。
int (*p1)[10];
p1是指针,它指向一个数组,这个数组里有10个元素,这10个元素的数据类型是int型。
九、字符指针
指针类型即
char* ch = 'w';
它是用来存放字符类型的地址
上述是一种存放方式,还有下列存放方式
char* ptr = "hello world";
注意:这种存放方式不是将字符串存不存放到ptr中,因为ptr只有4个字节,而字符串有11个字符加上一个\0,所以这种使用方式是将字符串的首地址存放到字符型指针ptr中
因为字符串通常是不做修改的,所以通常使用以下写法
const char* ptr = "hello world";
十、函数指针
顾名思义,函数指针是指针,它是指向函数的。
先看一个例子
void (*p1)();//由于括号的优先级比*高
//如果是指针的话就要将指针变量名与*结合
那么函数指针需要怎么定义呢?
如上述例子,void是函数的返回值,p1是该指针的变量名,在指针变量名后跟着的括号里是该指向的函数的参数列表,该例子无参数即()里内容为空。如果有参数,只需要按照函数参数的数据类型写入即可。
十一、函数指针数组
这是一个数组,数组里存放的类型是函数指针。先来看以下几个例子
int ia[];
int* a[];
char* b[];
这是常规的数组定义方式,将数组名及括号去掉后,剩下的就是数组内存放的数据类型。
从九中可知函数指针的定义,根据指针数组的定义,在变量名后增加一个[]后就变成了函数指针数组。
void (*p1[5])();
p1为数组名,该数组中存放着5个函数指针,这五个函数指针指向的函数的返回值是void,函数参数为空。在对数组进行赋值时,可取函数的地址也可以直接传函数名,因为这两个得到的都是同一个地址。