1、为什么要有指针,指针的两个要素
要知道内存的地址,还要知道内存的大小(在声明指针时的数据类型就规定了要声明的内存的大小)
计算机内存很大,一个变量很小,在使用内存的时候不可能挨个去寻找内存,要给变量一个内存地址来访问内存
内存以字节为单位编址
2、指针的声明方式
数据类型* 变量名称
int* pStudentId;
两种声明方法的比对
int a,*b;
int*b, a; //a是int类型的整数,不是指针,容易产生歧义
建议的声明方式
int a;
int* b;
3、读取指针得到内存地址
局部变量的内存地址会发生变化
全局变量的内存地址不会发生变化
4、使用指针的小区别
直接使用 变量a 和使用 *a 来操作内存空间之间有区别
直接使用是操作系统发挥了作用,而*a是直接操作内存空间
5、注意运算符优先级
运算符优先级问题,要加括号
6、数组的内存地址连续
一维数组、二维数组的内存地址都是连续的
7、指针的大小
32位指令集架构(x86)下指针的大小是4个字节
64位指令集架构(x64)下指针的大小是8个字节
8、深入理解数据类型的匹配(结合第9点的示例理解)
一个变量在内存中就是32个二进制位,但在使用变量时不可能直接使用二进制
这就需要根据不同的数据类型对这些二进制位进行不同的解释
指针同样是这个道理,不同数据类型的指针对数据的解释也不同
eg:-1用int来届时就是-1,-1用unsign int来解释就会非常大(它把首位用于表示正负的符号位也解释为了数值位,所以值就会很大)
9、理解指针的重点示例
unsigned a{9999}; //声明一个无符号数
int* ptr{(int*)&a}; //将a的地址赋给指针ptr
*ptr = -1; //通过操作指针将a的值修改为负数-1
//此时内存中a的值为 0xFF FF FF FF
char* cptr = (char*)ptr; //通过指针的强制类型转换将ptr的第一个字节的地址赋值给指针cptr
*cptr ='A'; //通过操作指针cptr修改a的内存的值
//由于cptr指向的内存大小是一个字节,这个修改的本质就是将a的第一个字节的内容修改为A对应的ASCII码值
//修改后的a内存的值为 0xFF FF FF 41('A'65对应的十六进制为41)
std::cout<<a; //最终输出的结果如图所示
输出的数就是a内存中的十六进制数对应的十进制数
由此可见指针类型的重要性,指针类型表示了通过该指针可以访问的内存地址大小
10、注意自增运算符以及运算符优先级
*ptr++注意最后是让ptr指针+1,而不是让*ptr+1
而std::cout<<(*ptr)++<<std::endl 是先打印出*ptr的值,然后再让*ptr+1
要注意上面两种情况的区别
指针+1加的是一个指针所对应的变量类型的大小,如此例中+1则加了int类型的4个字节大小
11、指针的指针
指针是一个变量,所以就会有指针的指针
12、常量指针和指针常量
1、常量指针: 可以理解为 (const int)*ptr{}
常量:const int a{1000};
const int b{2500};
指向常量的指针:const int* ptr{&a};
特性:
①*ptr = 999;会运行错误,因为*ptr是一个常量,不可以修改
②但是ptr本身是一个变量,ptr = &b; 是可以的,可以改变ptr的指向
③(重点)如下图,虽然c不是常量,但是如果用ptr指向c,*ptr的值仍然无法修改
2、指针常量: 可以理解为 int* (const ptr){}
ptr为一个常量,不可以修改ptr的指向
但是可以修改*ptr的值
3、指向常量的常量指针
const int * const ptr {}
13、容易忽略的知识点
1、知识点:常量折叠
2、知识点:const关键字和volatile关键字
注意c++编译器在编译阶段会对代码进行优化,所以可能造成不易察觉的错误