内存单元
- 内存由很多内存单元组成,这些内存单元用于存放各种类型的数据
- 计算机对内存的每个内存单元都进行了编号,这个编号就称为内存地址,地址决定了内存单元在内存中的位置
- 地址操作记忆不方便,所以c++编译器提供按名字来访问这些内存位置
指针基础概念
指针定义的基本形式:指针本身就是一个变量,其符合变量定义的基本形式,它存储的是值的地址。对类型T,T* 是 到T的指针类型,一个类型为T*的变量能保存一个类型T的对象地址。
- 号靠近变量和靠近类型是没有区别的
int a = 112; float c = 3.14;
int* d = &a; float* e = &c;
通过一个指针访问它所指向地址的过程称为间接访问或引用指针;用于执行间接访问的操作服是单目操作服(*)
cout << (*d) << endl;
cout << (*e) << endl;
变量:
-
三个重要的信息 1.1 变量的地址位置 1.2 变量所存的信息 1.2 变量的类型
-
指针变量是一个专门用来记录变量的地址的变量;通过指针变量可以间接的操作另一个变量的值;
数组与指针
int main() {
char strHelloWorld[] = {"hello"};
char* pStrHelloWorld = {"hello"};
// 指针变量的值允许改变
pStrHelloWorld = strHelloWorld;
// 数组变量的值不允许改变 报错
// strHelloWorld = pStrHelloWorld;
// strHelloWorld 不可变,strHelloWorld[index]的值可变
// pStrHelloWorld可变,pStrHelloWorld[index]的值可变取觉于所值区间的存储区域是否可变
return 0;
}
指针的数据 T* t[],数组中每一个元素都是一个指针 数组的指针 T(*t) [],指向一个数组的指针
int main() {
int c[4] = {15,25,35,45};
// 定义一个指针数组,每个元素都是一个指针
int* a[4];
// 定义一个指针,这个指针指向类型为int[4]的一个数组
int(*b)[4];
b = &c;
for (unsigned i=0; i<4;i++){
a[i] = &(c[i]);
}
cout << *(a[0]) << endl; // 15
cout << (*b)[3] << endl; // 45
return 0;
}
当需要使用指针来描述一个数组时,直接使用 数组类型 * 变量名来定义,如int类型则为 int * p。 同时指针变量p直接可看作数组c变量的别名进行操作。
int main() {
int c[4] = {15,25,35,45};
int * p;
int * q;
p = c;
q = &(c[2]);
cout << "&c = " << &c << endl;// 0x7ff7b3a378b0
cout << "p = " << p << endl; // 0x7ff7b3a378b0,指针变量名为对应数组第一个元素的地址
cout << "*p = " << *p << endl;// 指向数组第一个元素 15
cout << "p[1] = " << p[1] << endl; // 25,直接类似数组操作[index]
cout << "p+1 = " << p+1 << endl; // 地址 0x7ff7b3a378b4
cout << "*(p+1) = " << *(p+1) << endl; // 25 加 1 则等同于数组操作[index+1],取后一个数组位置的值
cout << "q = " << q << endl;// 多了两个int元素,则 加 8,地址为0x7ff7b3a378b8
cout << "*q = " << *q << endl;// 35
cout << "*(q+1) = " << *(q+1) << endl;// 45
return 0;
}
定义:由四个指向int的指针组成的数组
int sum(int *ar2[4],int size);
定义:指向一个由4个int组成的数组的指针
int sum(int (*ar2)[4],int size);
const pointer 与 pointer to const
int main() {
// const修饰看左边,左边没有再看右边
char strHelloWorld[] = {"helloworld strHelloWorld"};
// const 修改char,原数据不允许修改,但是指针的值可以修改
char const* pStr1 = "hellowrold pStr1"; // const char
// const 修改指针,指针的值可以修改
char* const pStr2 = strHelloWorld;
// 原始值和指针值都不允许修改
char const* const pStr3 = "helloworld pStr3"; // const char* const
// 不可改变
// pStr2 = strHelloWorld;
// 不可改变
// pStr3 = strHelloWorld;
for (unsigned index=0;index < strlen(pStr2); index++){
// 不允许改变
// pStr3[index] += 1;
// 不允许改变
// pStr1[index] += 1;
pStr2[index] += 1;
}
cout << pStr2 << endl;
cout << pStr1 << endl;
cout << pStr3 << endl;
cout << strHelloWorld << endl;
return 0;
}
指向指针的指针
二级指针
int main() {
int a = 123;
int* b = &a;
int** c = &b;
// *操作符具有从右到左的结合性
// **这个表达式相当于*(*c),必须从里向外层求值
// *c得到的是c指向的位置,即b;b里面存储的是a的地址
// **c相当于*b,得到变量a的值
return 0;
}
野指针
指向垃圾内存的指针,if等判断对它们不起作用,因为没有设置为NULL
一般有三种情况
- 指针变量没有初始化
- 已经释放不用的指针没有置为NULL,如delete和free之后的指针
- 指针操作超越了变量的作用范围
int *a;
*a = 12;
指针a未初始化,会随机指向一个地址。若此时赋值
- 可能定位到一个非法地址,程序报错,从而终止
- 定位到一个可以访问的地址,无意中修改了值,引发不可预料的错误
所以,在指针进行访问之前,需要确保其已初始化,并被恰当的赋值
NULL指针一个特殊的指针变量,表示不指向任何东西。
int *a = NULL:
对于一个指针,若初始化需赋值为某一地址,否则需要设置为NULL.
对某一指针进行引用之前,需判断其值是否为NULL.
int *pA = NULL:
// 引用前判断NULL
if (pA != NULL){
xxx
}
// 不用时设置为NULL
pA = NULL:
指针的操作
& :取地址符号
- :取该地址对应的值
将变量名称看成地址
#include <iostream>
using namespace std;
int main() {
int a = 1;
// 将指针a1的值,设置为变量a的地址
int *a1 = &a;
// 查看a的值 1
cout << "a : "<< a << endl;
// 查看a的地址 0x7ff7b5d0f8c8
cout << "&a : "<< &a << endl;
// a1的值,指向内存地址值 0x7ff7b5d0f8c8
cout << "a1 : "<< a1 << endl;
// 查看指针a1指向内存的值 1
cout << "*a1 : "<< *a1 << endl;
// 查看指针a1的值,指针的地址值 0x7ff7b5d0f8c0
cout << "&a1 : "<< &a1 << endl;
// 报错 a1 是指针的值,只能赋值为地址
// a1 = 2;
// 修改值
int b = 2;
// 将指针a1的值,设置为修改为b的地址
a1 = &b;
// 查看a的值 不变 1
cout << "a : "<< a << endl;
// 查看a的地址 不变 0x7ff7b5d0f8c8
cout << "&a : "<< &a << endl;
// a1的值 变为b的地址 0x7ff7b5d0f8bc
cout << "a1 : "<< a1 << endl;
// 查看指针a1指向内存的值 变为2
cout << "*a1 : "<< *a1 << endl;
// 查看指针a1的地址 不变 0x7ff7b5d0f8c0
cout << "&a1 : "<< &a1 << endl;
// 将指针a1的值,指向的真实的内存的值修改为字符 'c'
*a1 = 3;
// 查看a的值 不变 1
cout << "a : "<< a << endl;
// 查看a的地址 不变 0x7ff7b5d0f8c8
cout << "&a : "<< &a << endl;
// a1的值还是b的地址 0x7ff7b5d0f8bc
cout << "a1 : "<< a1 << endl;
// 查看指针a1指向内存的值 3
cout << "*a1 : "<< *a1 << endl;
// 查看指针a1的地址 0x7ff7b5d0f8c0
cout << "&a1 : "<< &a1 << endl;
// b的值 变为 3
cout << "b : "<< b << endl;
// b的地址值 不变 0x7ff7b5d0f8bc
cout << "&b : "<< &b << endl;
return 0;
}
++ 和 – 操作符
++ 操作
// 先进行加1 再执行运算,cp2是cp加1后的结果
char *cp2 = ++cp;
// 先进行运算,再进行加1,cp3是cp的值
char *cp3 = cp++;
– 操作和 ++ 操作类似。
++++,—-等运算符
编译器程序分解成符号的方法是:一个字符一个字符的读入,如果该字符可能 组成一个符号,那么读入下一个字符,一直到读入的字符不再能组成一个有意义的符号。
int a=1,b=2,c,d;
c = a+++b; // 相当于 a++ + b
d = a++++b; // 报错