什么是指针?
指针其实也是一个变量,指针变量跟普通变量的区别就是,普通变量存储的是一个具体的值,而指针变量存储的是一个变量在内存中的地址(也可以说是这个指针指向某个变量在内存中的内存空间的首地址).
指针的声明及使用
指针声明的格式:
指针类型 *指针变量名称 = 指针指向变量的地址;
// 声明一个int类型的变量并赋值为10
int a = 10;
// 声明一个int类型的指针变量,指向int类型的变量a的地址
int *ptr1 = &a;
printf("%d\n",*ptr1); // 输出的结果为10 跟 变量a的值一致
printf("%p\n",ptr1); // 输出的是指针的存储值(也就是变量a的地址) 类似0x7fff5fbff7b4
printf("%p\n",&ptr1); // 输出的是指针变量本身的地址(每个变量都有一个地址,包括指针变量) 类似0x7fff5fbff7a8
// 可以先声明指针变量,再赋值
int *ptr2; // 默认值为NULL
ptr2 = &a;
printf("%d\n",* ptr2); // 输出的结果为10
// 直接操作内存地址存储的值(游戏外挂就会用到)
int a = 10; // a在内存中的地址 0x7fff5fbff7bc
int *p = 0x7fff5fbff7bc; // 直接把a的地址赋值给指针p
*p = 100; // 修改p指向地址的值
printf("a = %d\n", a); // 100;
空指针
值为NULL的指针变量就叫空指针
int *ptr = NULL;
printf("%p\n",ptr); // 结果0x0
printf("%d\n",*ptr); // 会发生运行时错误
printf("%p\n",&ptr); // 指针变量就算是空指针但是它本身是有地址的 0x7fff5fbff7b0
野指针
指向的是"垃圾"内存(不可用内存)的指针变量叫做野指针
char *ptr = NULL;
printf("ptr:%d\n", ptr); // ptr:0
printf("ptr地址:%p\n", &ptr); // ptr地址:0x7fff5fbff7a8
if (ptr == NULL) {
printf("ptr == NULL \n");
} else {
printf("ptr != NULL \n");
}
// 输出为"ptr == NULL"
// 为ptr在堆区分配空间
ptr = (char *)malloc(100);
if (ptr == NULL) { // 若为空直接return出程序
return;
}
printf("ptr:%d\n", ptr); // ptr:2106560
printf("ptr地址:%p\n", &ptr); // ptr地址:0x7fff5fbff7a8
if (ptr != NULL) { // 目的:释放ptr
free(ptr); // 只释放了ptr指向的堆区空间 并没有将指针ptr置为空
}
if (ptr == NULL) {
printf("ptr == NULL \n");
} else {
printf("ptr != NULL \n");
}
// 输出为"ptr != NULL"
printf("ptr:%d\n", ptr); // ptr:2106560
printf("ptr地址:%p\n", &ptr); // ptr地址:0x7fff5fbff7a8
野指针解决办法
char *ptr = NULL;
printf("ptr:%d\n", ptr); // ptr:0
printf("ptr地址:%p\n", &ptr); // ptr地址:0x7fff5fbff7a8
if (ptr == NULL) {
printf("ptr == NULL \n");
} else {
printf("ptr != NULL \n");
}
// 输出为"ptr == NULL"
// 为ptr在堆区分配空间
ptr = (char *)malloc(100);
if (ptr == NULL) { // 若为空直接return出程序
return;
}
printf("ptr:%d\n", ptr); // ptr:2106560
printf("ptr地址:%p\n", &ptr); // ptr地址:0x7fff5fbff7a8
if (ptr != NULL) { // 目的:释放ptr
free(ptr); // 只释放了ptr指向的堆区空间 并没有将指针ptr置为空
ptr = NULL; // 重置为NULL
}
if (ptr == NULL) {
printf("ptr == NULL \n");
} else {
printf("ptr != NULL \n");
}
// 输出为"ptr == NULL"
printf("ptr:%d\n", ptr); // ptr:0
printf("ptr地址:%p\n", &ptr); // ptr地址:0x7fff5fbff7a8
如果防止野指针
- 养成良好习惯,定义指针的同时赋值初始值为NULL
- 在指针指向的内存空间释放的同时把指针重置为NULL
多级指针(指向指针的指针)
指向指针的指针就叫做多级指针,指针的级数并没有限制,可以无限的指向下去,只是指针的*要与指针的级数一致.
int a = 10;
// 1级指针
int *p = &a;
printf("%d\n", *p); // 指针p 指向的变量a的值 10
printf("%p\n", p); // 指针p 指向变量的地址 0x7fff5fbff7bc
printf("%p\n", &p); // 指针p 本身的地址 0x7fff5fbff7b0
// 2级指针
int **p1 = &p;
printf("%d\n", **p1); // 指针p1 指向的指针变量p所指向的变量的值 10 p1->p->a 几级指向就用几个*符来取值
printf("%p\n", p1); // 指针p1 指向指针变量p的地址 0x7fff5fbff7b0
printf("%p\n", &p1); // 指针p1 本身的地址 0x7fff5fbff7a8
// 3级指针
int ***p2 = &p1;
printf("%d\n", ***p2); // 指针p2 指向指针变量p1所指向的指针变量p所指向的变量的值 10 p2->p1->p->a 几级指向就用几个*符来取值
printf("%p\n", p2); // 指针p2 指向指针变量p1的地址 0x7fff5fbff7a8
printf("%p\n", &p2); // 指针p2 本身的地址 0x7fff5fbff7a0
指针运算
指针指向的地址是用一个十六进制的数来表示的,因此指针是可以进行运算的.
当对指针变量进行++ -- + - 的时候其实是在对指针指向的地址进行偏移,偏移的量是取决于变量的类型与跟指针变量发生运算的数据相关,在数组中特别明显.
如:一个指向int类型变量的一个指针 + 1, 得到的结果并不是在地址上偏移一位,而是偏移了sizeof(int) * 1 位,减法向低位偏移.
int a = 10;
int *p = &a;
printf("%d\n", *p); // 输出指针所指向变量的值 10
printf("%p\n", p); // 输出指针所指向的地址 0x7fff5fbff7bc
printf("%d\n", *p + 1); // 在指针p所指向变量的值上+1 11
printf("%p\n", p + 1); // 在指针p所指向变量的地址上+1(指针向高位偏移了指针类型所占字节 * 1 个位置) 0x7fff5fbff7c0
指针与const修饰符
指针能否修改取决于 const 所加位置以及所指向的变量的类型
int a = 10;
int b = 20;
// *前const
int const*p = &a; // const int *p 一样
p = &b; // p指向的地址可以修改
*p = b; // p指向地址的值不可以修改 会报错
// *后const
int * const p1 = &a;
p1 = &b; // p1指向的地址不可以修改 会报错
*p1 = b; // p1指向的地址的值可以修改
// *前*后const
const int * const p2 = &a;
p2 = &b; // 会报错
*p2 = b; // 会报错
// 指向const修饰的变量
const int a = 10;
int *p3 = &a;
*p3 = 100;
printf("%d\n",*p3); // 100
printf("%d\n",a); // 10
printf("p3 = %p\n",p3); // p3 = 0x7fff5fbff7bc
printf("&a = %p\n",&a); // &a = 0x7fff5fbff7bc
指向数组的指针
指向字符串的指针
指向函数的指针
函数指针就是一个指向函数首地址的指针
void exchangeVar(int *a, int *b) {
int temp = *a;
*a = *b;
*b = temp;
}
int a = 10;
int b = 20;
// 定义一个函数指针,指向exchangeVar函数的首地址
void(*pfun)() = &exchangeVar;
(*pfun)(&a,&b);
printf("a = %d b = %d", a,b); // a = 20 b = 10
高阶函数 - (函数中传入函数)
void print_num(num) {
printf("%d\n", num);
}
void test(void(*pfunc)(), int n) {
printf("前面的观众!\n");
(*pfunc)(n);
printf("后面的观众!\n");
}
test(&print_num, 1233);
/* 输出结果
前面的观众!
1233
后面的观众!
*/
函数指针数组
void fun1() {
printf("fun1()\n");
}
void fun2() {
printf("fun2()\n");
}
void fun3() {
printf("fun3()\n");
}
void(*pFunc1)() = &fun1;
void(*pFunc2)() = &fun2;
void(*pFunc3)() = &fun3;
//接下来就是定义一个数组把他们三个装进去。
void(*pFunArr[3])();
//在这里呢看着个指针数组,他就是一个具有3个以函数指针为内容的元素的函数指针数组。
pFunArr[0] = pFunc1;
pFunArr[1] = pFunc2;
pFunArr[2] = pFunc3;
pFunArr[0](); // 调用结果 fun1()
pFunArr[1](); // 调用结果 fun2()
pFunArr[2](); // 调用结果 fun3()
指向函数指针数组的指针
void (*(*pFunarr2)[3])() = &pFunArr;
(*pFunarr2)[0]();
(*pFunarr2)[1]();
(*pFunarr2)[2]();
指针在函数中的使用
在函数的参数处使用指针可以修改传入实参本身的值.
// 交换两个变量的值
void exchangeVar(int *a, int *b) {
int temp = *a;
*a = *b;
*b = temp;
}
int main(int argc, const char * argv[]) {
int a = 10;
int b = 20;
printf("a:%d\n", a); // 10
printf("b:%d\n", b); // 20
// 调用函数进行交换
exchangeVar(&a, &b);
printf("a:%d\n", a); // 20
printf("b:%d\n", b); // 10
return 0;
}
网友评论