内存地址
地址相当于是门牌号,每家都有一个门牌号,类似每一个分配的内存空间都有独一无二的地址, 在c++中为8字节的16个16进制的数(可以表示2的64次方个内存地址): 例如:000000604CAFF9E4
首地址
如果计算机分配的一块内存是连续的,那么第一个元素的内存空间地址就称为首地址,
如数组、函数体,的存储方式为连续存储 例如:数组首地址加上一段地址的变化就能得到相应元素的地址,进而取得数组任意元素的值; 所以我们可以直接用首地址+地址增量来表示这一段内存的全部地址
例如:一栋楼一共有100户,我家在一单元一楼,门牌号为0x01001(假设从一开始); 你家在6楼,咱们中间一共有4个楼层(0x01002,0x01003,0x01004,0x01005), 那么你家的门牌号就为0x01006,依次类推第100楼的门牌号就为0x01100; 通过我家的门牌号跟楼层数就能得到所有楼层的门牌号;
指针的定义:
指针(Pointer)是编程语言中的一个对象,利用地址,它的值直接指向(Pointed to)存在电脑存储器中另一个地方的值。
简单的说:指针是一个变量,且这个变量是专门用来存放地址的。
举个栗子:你想给你朋友打电话,你朋友的电话号为12345678,(你记不住)你拿一个小本本记下来,这个小本本就是指针变量,里面记的东西就是电话号,也就是地址,以后你想找你朋友都可以通过小本本里的电话号联系到。
1、指针变量
普通用法 既然叫做变量里面存储的肯定是常量,这个常量就是-----地址; 指针变量的定义形式为:
int * p ;
其中p为一个指针变量,里面可以存int类型普通变量的地址;
int a = 10;
a为一个普通变量,值为10;
p = &a;
如果这时候输出p,输出的为a的16进制地址; 输出结果为:000000A39A6FF9F4
这时候想要取a的值就有两种情况: 第一种:直接输出变量;
cout << a << endl;
第二种:通过指针取值;
cout << *p << endl;
其中*号在此时作为取值符;
特殊用法 用指针定义数组:
int *d;
这是一个指针变量,里面可以存int类型普通变量的地址,也可以存int类型数组的首地址; 当指针变量中存的值为一块存放变量类型为int的连续内存的首地址时,指针变量可以当作数组; 类似为:
int *d;
int b[10];
d = b;
这时候d与b用法类似。 当不知道需要创建多大的数组的时候就可以使用指针来动态创建
int *Arr = (int*)malloc(sizeof(int));
直观一点,直接上代码:
#include <iostream>
using namespace std;
void main()
{
int *c;
int a = 10;
char m = 'a';
char* p;
p = &m;
c = &a;
int *d;
int b[10];
b[0] = 11;
b[1] = 12;
d = b;
int *Arr = (int*)malloc(sizeof(int));
Arr[0] = 30;
Arr[1] = 31;
cout << "p: " << static_cast<void *>(p) << endl;
cout << "c: " << c << endl;
cout << "*c: " << *c << endl;
cout << "a: " << a << endl;
cout << "b[0]: " << b[0] << endl;
cout << "&b[0]: " << &b[0] << endl;
cout << "d: " << d << endl;
cout << "d[1]: " << d[1] << endl;
cout << "Arr: " << Arr << endl;
cout << "Arr[1]: " << Arr[1] << endl;
}
输出结果为:
总结: 1、指针变量是一种变量(有内存也有地址),里面存的是地址。 2、地址指向的是空间,所以指针指向内存空间。(所以经常说某个指针指向某一块内存) 3、指针变量存的地址可以是连续内存里的首地址(可以用作数组)
2、 数组指针
这是一个文字理解,实际上可以理解为数组的指针(是指针,指向数组第一个元素的内存空间): 定义为:
int (*p)[10];
其中括号()的优先级最高,所以说这里的整个*p为一个指针变量的定义,p为指针,指向的是一个数组(匿名数组),数组里面存的是int类型的元素;(规定了数组的大小)
这里解释一下,上面定义的10个数组每个数组相当于一个指针。 (也就是说每个数组元素就是一个数组样子的指针); 第一个指针为p[0],第二个为p[1]。。。。,所以说元素p[0]实际上是一个地址,*p[0]就相当于是取p[0]存的地址的值(首地址),也就是取p[0][0]的元素。
总结:数组指针是数组样子的指针; (下面上代码) 例子:
#include <iostream>
using namespace std;
void main()
{
int arr[10];
arr[0] = 10;
arr[1] = 11;
arr[2] = 12;
int(*p)[10] = &arr;
p[0][0] = 0;
p[0][1] = 1;
p[0][2] = 2;
cout << "p[0][0]:" << p[0][0] << endl;
cout << "p[0][1]:" << p[0][1] << endl;
cout << "p[0][2]:" << p[0][2] << endl;
cout << "p[0]:" << p[0] << endl;
cout << "p[1]:" << p[1] << endl;
cout << "p[2]:" << p[2] << endl;
cout << "p[3]:" << p[3] << endl;
cout << "*p[0]:" << *p[0] << endl;
cout << "*p[1]:" << *p[1] << endl;
cout << "arr[0]:" << arr[0] << endl;
cout << "&arr[0]:" << &arr[0] << endl;
cout << "arr[1]:" << arr[1] << endl;
cout << "&arr[1]:" << &arr[1] << endl;
}
3、 指针数组
理解为指针的数组(是数组,里面存的是指针,跟二级指针类似): 定义为
int *p[10];
其中[]的优先级最高,表示它是一个数组。首先申请了一个空间为【10】的数组,然后数组里面存的是指向int类型的指针,p指向的是这个指针数组首地址的内存空间; 二级指针的定义:
int **p;
与二级指针的区别在于预先定义的指针数组的一维大小;
解释一下,p为数组的名字,申请了10个空间,每个空间里面存的是int *(指向存放int类型空间的指针),所以说p[0]里面存的是指针,与上面数组指针不同的是,数组指针是申请的空间本身就是指针(只不过名字看起来像数组),这里申请的是数组,但是里面存的是指针。
总结:指针数组实际上就是内存空间里面存的是指针的数组;
#include <iostream>
using namespace std;
void main()
{
int arr[10];
arr[0] = 10;
arr[1] = 11;
arr[2] = 12;
int*p[10] = {arr};
p[0][0] = 0;
p[0][1] = 1;
p[0][2] = 2;
cout << "p[0]:" << p[0] << endl;
cout << "p[1]:" << p[1] << endl;
cout << "&p[0]:" << &p[0] << endl;
cout << "&p[1]:" << &p[1] << endl;
cout << "*p[0]:" << *p[0] << endl;
cout << "p[0][0]:" << p[0][0] << endl;
cout << "p[0][1]:" << p[0][1] << endl;
cout << "&p[0][0]:" << &p[0][0] << endl;
cout << "&p[0][1]:" << &p[0][1] << endl;
cout << "arr[0]:" << arr[0] << endl;
cout << "&arr[0]:" << &arr[0] << endl;
cout << "arr[1]:" << arr[1] << endl;
cout << "&arr[1]:" << &arr[1] << endl;
}
4、函数指针
函数指针是一个指针,因为函数跟数组一样是一个连续的存储空间,所以函数指针可以指向函数的首地址从而调用; 函数指针定义为:
int (*fun)(int x,int y);
表示的是这个fun指针可以指向类型为返回值为int类型,参数为int x,int y的函数的那一段内存空间
例如两个函数声明为:
int func(int x, int y);
int fun0000(int x);
定义为:
int func(int x, int y)
{
return 0;
}
int fun0000(int x)
{
return 0;
}
函数指针的定义为:
int(*fun)(int x, int y);
调用类型为:
fun = &func;
如果类型不一样则:
5、指针函数
指针函数实际上就是函数,只不过返回值为指针; 看看下面这个函数声明:
int func();
这只是一个没有参数的,返回值为int类型数据的普通函数; 再看看指针函数的定义:
int* func();
仅仅多了一个*号,其返回值是一个 int 类型的指针,是一个地址值。 实际上指针函数和普通函数对比不过就是其返回了一个指针(即地址值)而已。
6、特殊指针
①空指针
定义为:
#ifndef NULL
# ifdef __cplusplus
# define NULL 0
# else
# define NULL ((void *)0)
# endif
#endif
short *pa=NULL;
short *pa = 0;
它指示指针实际上并不涉及任何有效的内存地址。 该特殊值称为空指针(null pointer),并在内部表示为值0,NULL 是一个标准规定的宏定义,用来表示空指针常量。
②Void类型指针
无类型指针,这个就厉害了,在定义上是无类型指针,但是实际上用起来却包罗万象; 通过强制类型转换可以变为所有类型的指针; 例如可以将上文函数指针赋值给它:
int function(int x, int y)
{
return 0;
}
int(*fun)(int x, int y);
fun = &function;
void * p_void;
p_void = fun;
可以将函数地址赋值给它:
int(*fun)(int x, int y);
fun = &func;
void * p_void;
p_void = &function;
甚至可以将函数指针的地址赋值给它:
int(*fun)(int x, int y);
fun = &func;
void * p_void;
p_void = &fun;
这就是无中生有,道法自然 😊😊😊😊😊
③nullptr指针
C语言:NULL C++03前:0 C++11:nullptr
nullptr是C++11 新标准引入的方法,在之前使用的是NULL,NULL是一个预处理变量,它的值为0。
int *p1 = nullptr;
int *p2 = 0;
int *p3 = NULL
④结构体指针(类指针)
结构体(类)类型的指针,也就是指向的是结构体(类)类型的空间的指针。 例如:
class Point
{
int x;
int y
};
struct Student
{
char* name;
char* sex;
int number;
int age;
};
定义结构体指针为:
Point *pt;
Student *stu;
使用为:
pt->x = 100;
pt->y = 100;
stu->name = "卡尔曼确实慢";
stu->sex = "未知";
stu->number = 1;
stu->age = 23;
⑤this指针
成员函数中都包含一个特殊的指针,这个指针的名字是固定的——this。它是当前类对象的指针。 1)对象o.成员函数x(…){…},则函数体中的this表示“&o”; 2)对象指针p.成员函数x(…){…},则函数体中的this表示“p”; 3)对象引用r.成员函数x(…){…},则函数体中的this表示“&r”;
引用
一句话,引用相当于是别名,指的是同一个东西。 引用常用于函数中的参数传递。
例子
int a = 1;
int b = 1;
int &x = a;
x = 20;
cout << a << endl;
结果为20;
与取地址容易混淆的地方: 等号左边或者无等号用&就是引用, 等号右边用&就是取地址。
|