指针的概念:
指针就是个变量,用来存放地址,地址唯一标识一块内存空间。
指针的大小是固定的4/8字节,取决于32位/64位平台。
指针是类型,指针的类型决定了指针+-整数的步长,指针解引用操作的时候的权限。
指针的运算。
参见:初识指针
字符指针
一般使用:
123456 | int main() { char ch = 'w'; char* pc = &ch; return 0;} |
123456789 | int main() { char arr[] = "abcdef"; char* pc = arr; printf("%s\n",arr); printf("%s\n",pc); return 0;} |
打印arr和打印pc的结果是一致的。
12345678 | int main() { char* p = "abcdef";//"abcdef"常量字符串,把a的地址赋给了p,即把首地址给了p printf("%c\n",*p);//打印的结果是a printf("%c\n",*p+2);//打印的结果是c printf("%s\n",p);//打印的结果是abcdef,是由于通过a的地址,打印出了这个字符串(%s是打印字符串) return 0;} |
1234567 | int main() { char* p = "abcdef";//常量字符串 *p = 'W'; printf("%s\n",p); return 0;} |
上面的写法是错误的。因为abcdef是常量字符串,不能修改,下面的代码是修正后的。
1234567 | int main() { const char* p = "abcdef"; //*p = 'W'; printf("%s\n",p); return 0;} |
12345678910111213141516 | int main() { char arr1[] = "abcdef"; char arr2[] = "abcdef"; char* p1 = "abcdef"; char* p2 = "abcdef"; if (arr1 == arr2) { printf("arr1 == arr2"); } else { printf("arr1 != arr2"); } return 0;} |
打印的结果是arr1 != arr2,因为if (arr1 == arr2)比较的不是数组的内容,而是数组的地址,而两个数组的地址不会相等。
123456789101112131415 | int main() { char arr1[] = "abcdef"; char arr2[] = "abcdef"; char* p1 = "abcdef"; char* p2 = "abcdef"; if (p1 == p2) { printf("p1 == p2"); } else { printf("p1 != p2"); } return 0;} |
打印的结果是p1 == p2,因为指向的都是同一个常量字符串,常量字符串不会被修改,为了节约内存空间,内存中不会存两份,地址是一致的,所以相等。char* p1和const char* p1是一致的。
指针数组
指针数组本质是数组,是用来存放指针的。int* parr[4]; 整型指针数组,用来存放整型指针的数组。char* pch[4];字符型指针数组,用来存放字符指针的数组。
12345678910111213 | int main() { int a = 10; int b = 20; int c = 30; int d = 40; int* arr[4] = {&a,&b,&c,&d}; int i = 0; for (i = 0; i<4; i++) { printf("%d ",*(arr[i])); } return 0;} |
打印的结果是10 20 30 40
12345678910111213141516 | int main() { int arr1[] = {1,2,3,4,5}; int arr2[] = {2,3,4,5,6}; int arr3[] = {3,4,5,6,7}; int* parr[] = {arr1,arr2,arr3}; int i = 0; for (i=0; i<3; i++) { int j = 0; for (j = 0; j<5; j++) { printf("%d ",*(parr[i]+j)); } printf("\n"); } return 0;} |
打印的结果是:
1 2 3 4 5
2 3 4 5 6
3 4 5 6 7
数组指针
数组指针本质是指针。int *p = NULL; p是整型指针 - 指向整型的指针 - 可以存放整型的地址char * pc = NULL; pc是字符指针 - 指向字符的指针 - 可以存放字符的地址
数组指针 就是指向数组的指针 - 可以存放数组的地址。arr - 首元素地址&arr[0] - 首元素地址&arr - 数组的地址
123456 | int main() { int arr[10] = {1,2,3,4,5,6,7,8,9,10}; int (*p)[10] = &arr;//数组指针 - 数组的地址存起来 - p就是数组指针,首先和*结合 return 0;} |
上面的p就是数组指针。就是指向数组的指针 - 可以存放数组的地址。
int (*p)[10]p先和星号*结合,在和[]结合。
先结合的就是本质,后结合的就是类型。
数组指针的使用:
12345678910 | int main() { int arr[10] = {1,2,3,4,5,6,7,8,9,10}; int (*p)[10] = &arr;//数组指针 - 数组的地址存起来 - p就是数组指针,首先和*结合 int i = 0; for (i=0; i<10; i++) { printf("%d ",(*p)[i]); } return 0;} |
一般而言,数组指针最好在二维数组中使用才更方便。
123456789101112131415161718192021222324252627282930313233 | void print1(int arr[3][5],int x,int y){ int i = 0; int j = 0; for (i = 0; i<x; i++) { for (j=0; j<y; j++) { printf("%d ",arr[i][j]); } printf("\n"); }} void print2(int (*p)[5],int x,int y){ int i = 0; for (i=0; i<x; i++) { int j = 0; for (j = 0; j < y; j++) { printf("%d ",*(*(p+i)+j));//也可以写成(*(p + i))[j] } printf("\n"); }} int main() { int arr[3][5] = { {1,2,3,4,5},{6,7,8,9,10},{11,12,13,14,15} }; print1(arr,3,5);//arr - 数组名 - 首元素地址 print2(arr,3,5); return 0;} |
int arr[5]; arr是一个5个元素的整型数组int *parr1[10];parr1是一个数组,数组有10个元素,每个元素的类型是int*,parr是指针数组int (*parr2)[10];parr2是一个指针,该指针指向一个数组,数组有10个元素,每个元素的类型是int,parr2是数组指针int (*parr3[10])[5];parr3是一个数组,该数组有10个元素,每个元素是一个数组指针,该数组指针指向的数组有5个元素,每个元素是int
判断是指针数组还是数组指针?
——根据[ ]的优先级比*的优先级高的原则判断。
数组参数、指针参数
12345678 | int main(){ int arr[10] = {0}; int* arr2[20] = {0}; test(arr); test2(arr2);} |
void test(int arr[]){...}、void test(int arr[10]){...}、void test(int *arr){...} 上面的三种传参方法都是可以的。可以写数组名,也可以使用数组的地址。void test2(int *arr[2])、void test2(int **arr)上面的两种写法都可以,可以是一级指针,也可以是二级指针。
二维数组传参
1234567891011 | void test(int arr[3][5]){}void test(int arr[][5]){} int main(){ int arr[3][5] = {0}; test(arr);} |
void test(int arr[3][5])和void test(int arr[][5])都是可以的,在传参是,二维数组的行可以省略。
但是,void test(int arr[3][])写法是错误的,二维数组的列不能省略。void test(int arr[][])也是错误的,二维数组不能省略列,也不能都省略。
123456789 | void test(int (*arr)[5]){} int main(){ int arr[3][5] = {0}; test(arr);} |
总结:二维数组传参,函数形参可以省略第一个[]中的数字。
因为对一个二维数组,可以不知道有多少行,但必须得知道一行有几个元素。
一级指针传参
123456789101112131415161718 | 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]); print(p, sz); return 0;} |
1234567891011 | void test1(int* p){} int main(){int a = 10;int* p1 = &a;test1(&a);test1(p1);} |
如果函数变量是指针形式,比如int *p,可以传递地址,比如&a或者直接传递指针(p1)
二级指针传参
123456789101112131415 | void test(int** ptr){ printf("%d\n",**ptr);} int main(){ int n = 10; int *p = &n; int **pp = &p; test(pp); test(&p); return 0;} |
传递的pp和&p都是二级指针,使用二级指针接收(int** ptr)
使用int** ptr函数的参数是二级指针可以接收二级指针和一级指针,还可以传指针数组。
指针数组相当于是二级指针
函数指针
函数指针就是指向函数的指针,存放函数地址的第一个指针。
&函数名和函数名都是函数的地址。
1234567891011121314151617 | int Add(int x,int y){ int z = 0; z = x + y; return z;} int main(){ int a = 20; int b = 10; printf("%p\n",&Add); printf("%p\n",Add);//&函数名和函数名都是函数的地址. int (*pa)(int,int) = Add;//函数指针,把函数的地址存到pa里 printf("%d\n",(*pa)(2,3));//打印的结果是5,证明pa中存的是函数的地址} |
1234567891011 | void Print(char* str){ printf("%s\n",str);} int main(){ void (*p)(char*) = Print; (*p)("hello");} |
123456789101112131415 | int Add(int x,int y){ int z = 0; z = x + y; return z;}int main(){ int (*pa)(int,int) = Add;//函数指针,把函数的地址存到pa里 printf("%d\n",(pa)(2,3)); printf("%d\n",(*pa)(2,3));//打印的结果是5,证明pa中存的是函数的地址 printf("%d\n",(**pa)(2,3)); printf("%d\n",(***pa)(2,3));} |
上面的四种调用方式都是可以的,打印的结果都是5,但不建议使用后面两种。
*pa是对pa的解引用,找到对应的函数。
对于函数,*pa前的*可以省略,对于结果无影响。
后两种虽然结果是正确的,但是这种写法是没有意义的。
函数指针数组
函数指针数组就是把一些函数的地址存在一个数组里。
12345678910111213141516171819202122232425262728 | //先定义加、减、乘、除四个函数int Add(int x,int y){ return x + y;}int Sub(int x,int y){ return x - y;}int Mul(int x,int y){ return x * y;}int Div(int x,int y){ return x / y;} int main(){ int (*parr[4])(int,int) = {Add,Sub,Mul,Div};//函数指针数组 int i = 0; for (i = 0; i<4; i++) { printf("%d\n",parr[i](2,3));//分别进行加、减、乘、除 }} |
打印的结果是5,-1,6,0
函数指针的用途:转移表(比如计算器)
char* my_strcpy(char* dest,const char* src);
1、写一个函数指针pf,指向my_strcpy。char* (*pf)(char*,const char*);
2、写一个函数指针数组pfArr,能够存放4个my_strcpy函数的地址。char* (*pfArr[4])(char*,const char*);
函数指针数组实现计算器
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556 | void menu(){ printf("*************************\n"); printf("*** 1.add 2.sub ***\n"); printf("*** 3.mul 4.div ***\n"); printf("*** 5.XOR 0.exit ***\n"); printf("*************************\n");} int Add(int x,int y){ return x + y;}int Sub(int x,int y){ return x - y;}int Mul(int x,int y){ return x * y;}int Div(int x,int y){ return x / y;}int XOR(int x,int y){ return x ^ y;} int main(){ int x = 0; int y = 0; int input = 0; //pfArr是函数指针数组 - 叫转移表 int (*pfArr[6])(int,int) = {0,Add,Sub,Mul,Div,XOR}; do { menu(); printf("请选择:->"); scanf("%d",&input); if (input >= 1 && input <= 5) { printf("请输入两个操作数:->"); scanf("%d%d",&x,&y); int ret = pfArr[input](x,y); printf("%d\n",ret); } else if(input == 0){ printf("退出\n"); }else { printf("选择错误\n"); } } while (input);} |
上例中,pfArr是函数指针数组 - 叫转移表
回调函数
回调函数就是一个通过函数指针调用的函数。
如果把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其他所指向的函数时,我们就说这是回调函数。
12345678910111213141516 | void print(char *str){ printf("hehe:%s",str);} void test(void (*p)(char*)){ printf("test\n"); p("abc");} int main(){ test(print); return 0;} |
使用回调函数实现计算器
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879 | void menu(){ printf("*************************\n"); printf("*** 1.add 2.sub ***\n"); printf("*** 3.mul 4.div ***\n"); printf("*** 5.XOR 0.exit ***\n"); printf("*************************\n");} int Add(int x,int y){ return x + y;}int Sub(int x,int y){ return x - y;}int Mul(int x,int y){ return x * y;}int Div(int x,int y){ return x / y;}int XOR(int x,int y){ return x ^ y;} void Calc(int (*pf)(int,int))//回调函数{ int x = 0; int y = 0; printf("请输入两个操作数:->"); scanf("%d%d",&x,&y); printf("%d\n",pf(x,y));} int main(){ int x = 0; int y = 0; int input = 0; do { menu(); printf("请选择:->"); scanf("%d",&input); switch (input) { case 1: printf("加法:"); Calc(Add); break; case 2: printf("减法:"); Calc(Sub); break; case 3: printf("乘法:"); Calc(Mul); break; case 4: printf("除法:"); Calc(Div); break; case 5: printf("XOR:"); Calc(XOR); break; case 0: printf("退出\n"); break; default: printf("输入非法\n"); break; } } while (input);} |
指向函数指针数组的指针
12345678910111213 | int main(){ int arr[10] = {0}; int (*p)[10] = &arr;//取出数组的地址 int (*pf)(int,int);//函数指针 int (*pfArr[4])(int,int);//pfArr是一个数组,函数指针的数组 //pfArr是一个指向[函数指针数组]的指针 int (*(*PPfArr)[4])(int,int) = &pfArr; //ppfArr是一个数组指针,指针指向的数组有4个元素 //指向数组的每个元素的类型是一个函数指针int(*)(int,int) return 0;} |