一、const修饰指针变量
- const如果放在*的左边,修饰的是指针指向的内容,保证指针指向的内容不能通过指针来改变。但是指针变量本身的内容可变;
- const如果放在*的右边,修饰的是指针变量本身,保证了指针变量的内容不能修改,但是指针指向的内容,可以通过指针改变。
void test1() { int n = 10; int m = 20; int* p = &n; *p = 20; p = &m; } void test2() { //代码2 int n = 10; int m = 20; const int* p = &n; *p = 20;//报错 p = &m; } void test3() { int n = 10; int m = 20; int* const p = &n; *p = 20; p = &m; //报错 } int main() { //测试无cosnt的 test1(); //测试const放在*的左边 test2(); //测试const放在*的右边 test3(); return 0; }
二、字符指针
int main() { //本质是把"hello"这个字符串的首字符的地址存储在了ps中 char* ps = "hello"; printf("%c\n", *ps); return 0; }
int main() { char str1[] = "hello bit."; char str2[] = "hello bit."; const char* str3 = "hello bit."; const char* str4 = "hello bit."; if (str1 == str2) printf("str1 and str2 are same\n"); else printf("str1 and str2 are not same\n"); if (str3 == str4) printf("str3 and str4 are same\n"); else printf("str3 and str4 are not same\n"); return 0; }
三、 指针数组
指针数组本质是数组,数组中存放的是指针。
int main() { //存放整型指针的数组 //int* arr[3]; int a[5] = { 1,2,3,4,5 }; int b[] = { 2,3,4,5,6 }; int c[] = { 3,4,5,6,7 }; //模拟了一个二维数组,但并不是二维数组,二维数组是连续存放的。 int* arr[3] = { a, b, c }; int i = 0; for (int i = 0; i < 3; i++) { int j = 0; for(j = 0;j < 5; j++){ //printf("%d ", *(arr[i] + j)); printf("%d ", arr[i][j]); } printf("\n"); } return 0; }
四、数组指针
指向数组的指针,注意区分和数组指针的区别。
int (*p)[10];
解释:p先和 * 结合,说明p是一个指针变量,然后指着指向的是一个大小为10个整型的数组。所以p是一个指针,指向一个数组,叫数组指针。
注意:[]的优先级是高于 * 的,所以必须加上()来保证p先和 * 结合。
int main() { int arr[10] = { 1,2,3,4,5 }; int (*parr)[10] = &arr;//取出的是数组的地址 //parr 就是一个数组指针 - 其中存放的是数组的地址 double* d[5]; double* (*pd)[5] = &d;//pd就是一个数组指针 return 0; }
我们基本不会使用数组指针来存放一维数组的地址
五、数组传参、指针传参
- 一维数组传参
//数组传参,数组接受 void test(int arr[]) {} //数组传参,数组接受,这里的10是没有意义的 void test(int arr[10]) {} //数组传参实际传的是首元素地址,用用指针接收没有问题 void test(int* arr) {} //arr2本身就是一个指针数组,可以用指针数组接收 void test2(int* arr[20]) {} //数组名表示首元素地址,也就是一个int*的地址 //int*是一个一级指针, //这里arr2相当于取了一个一级指针地址 //那么arr2就相当于一个二级指针,可以用二级指针接收 void test2(int** arr) {} int main() { int arr[10] = { 0 }; int* arr2[20] = { 0 };//整型指针数组 test(arr); test2(arr2); return 0; }
- 二维数组传参
void test(int arr[3][5]) {} //二维数组传参,函数形参的设计只能省略第一个[]的数字 //因为对一个二维数组,可以不知道有多少行,但是必须知道一行多少元素 void test(int arr[][])//报错 {} void test(int arr[][5]) {} //二维数组的数组名也是表示的是地址 //但它表示的是第一行元素的地址,而不是第一个元素的地址 // 不能用一级指针来接收 void test(int* arr) {} //int* arr[5]是一个指针数组,不能接收二维数组 void test(int* arr[5]) {} //数组指针能够指向一个含5个int类型元素的一维数组 //二维数组的数组名表示的是第一行元素的地址 //正好是一个含5个int类型元素的一维数组 void test(int (*arr)[5]) {} //传的是一个一维数组的地址,也不能用二级指针来接收 void test(int** arr) {} int main() { int arr[3][5] = { 0 }; test(arr); }
- 一级指针传参
void print(int* p, int sz) { int i = 0; for (i = 0; i < sz; i++) { printf("%d\n", *(p + i)); } } int main() { int arr[10] = { 1,2,3,4,5,6,7,8,9 }; int* p = arr; int sz = sizeof(arr) / sizeof(arr[0]); //一级指针p,传给函数 print(p, sz); return 0; }
- 二级指针传参
void test(int** ptr) { printf("num = %d\n", **ptr); } int main() { int n = 10; int* p = &n; int** pp = &p; test(pp); //可以一级指针的地址 //二级指针本身就是用来存放以及指针地址的 test(&p); int* arr[10] = { 0 }; //可以传一个指针数组 //指针数组内存放的是指针也就是地址 //有点类似传过去了一个一级指针地址 test(arr); return 0; }
六、函数指针
函数指针就是存放函数地址的指针。
int Add(int x, int y){ return x + y; } int main() { //pf就是一个函数指针 int (*pf)(int, int) = &Add; //&Add == Add //&Add、Add都表示函数的地址 printf("%p\n", &Add); printf("%p\n", Add); printf("%p\n", pf); //实际上这里*是没有实意的 //它的作用是让我们知道这是一个指针 int ret1 = (*pf)(3, 5); printf("%d\n", ret1); //int (*pf)(int, int) = Add; //pf === Add //函数调用可以写成Add(3, 5) //所以可以写成pf(3, 5) int ret2 = pf(3, 5); printf("%d\n", ret2); return 0; }
七、函数指针数组
函数指针数组用来存放同类型的函数指针。
int Add(int x, int y) { return x + y; } int Sub(int x, int y) { return x - y; } int main() { int (*pf1)(int, int) = Add; int (*pf2)(int, int) = Sub; //pfArr函数指针数组,存放同类型的函数指针 int (*pfArr[2])(int, int) = { Add, Sub }; return 0; }
八、指向函数指针数组的指针
指向函数指针数组的指针是一个指针,指针指向一个数组,数组的元素都是函数指针。
void test(const char* str) { printf("%s\n", str); } int main() { //函数指针pfun void (*pfun)(const char*) = test; //函数指针的数组pfunArr void (*pfunArr[5])(const char* str); pfunArr[0] = test; //指向函数指针数组pfunArr的指针ppfunArr void (*(*ppfunArr)[5])(const char*) = &pfunArr; return 0; }
九、回调函数
回调函数就是一个通过函数指针调用的函数。把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。