C语言指针知识点总结
1.指针的使用和本质分析
(1)初学指针使用注意事项
1)指针一定要初始化,否则容易产生野指针(后面会详细说明);
2)指针只保存同类型变量的地址,不同类型指针也不要相互赋值;
3)只有当两个指针指向同一个数组中的元素时,才能进行指针间的运算和比较操作;
4)指针只能进行减法运算,结果为同一个数组中所指元素的下表差值。
(2)指针的本质分析
①指针是变量,指针*的意义:
1)在声明时,*号表示所声明的变量为指针。
例如:int n = 1; int* p = &n;
这里,变量p保存着n的地址,即p<—>&n,*p<—>n
2)在使用时,*号表示取指针所指向变量的地址值。
例如:int m = *p;
②如果一个函数需要改变实参的值,则需要使用指针作为函数参数(传址调用),如果函数的参数数据类型很复杂,可使用指针代替。
最常见的就是交换变量函数void swap(int* a, int* b)
③指针运算符*和操作运算符的优先级相同
例如:int m = *p++;
等价于:int m= *p; p++;
2.指针和数组
(1)指针、数组、数组名
如果存在一个数组 int m[3] = {1,2,3};
定义指针变量p,int *p = m(这里m的类型为int*,&a[0]==>int*)
这里,
其中,&m为数组的地址,m为数组0元素的地址,两者相等,但意义不同,例如:
m+1 = (unsigned int)m + sizeof(*m)
&m+1= (unsigned int)(&m) + sizeof(*&m)
= (unsigned int)(&m) + sizeof(m)
m+1表示数组的第1号元素,&m+1指向数组a的下一个地址,即数组元素“3”之后的地址。
等价操作:
m[i]←→*(m+i)←→*(i+m)←→i[m]←→*(p+i)←→p[i]
实例测试如下:
1 #include<stdio.h> 2 3 int main() 4 { 5 int m[3] = { 1,2,3 }; 6 int *p = m; 7 8 printf(" &m = %p\n", &m); 9 printf(" m = %p\n", m); 10 printf("\n"); 11 12 printf(" m+1 = %p\n", m + 1); 13 printf(" &m[2] = %p\n", &m[2]); 14 printf(" &m+1 = %p\n", &m + 1); 15 printf("\n"); 16 17 printf(" m[1] = %d\n", m[1]); 18 printf(" *(m+1) = %d\n", *(m + 1)); 19 printf(" *(1+m) = %d\n", *(1 + m)); 20 printf(" 1[m] = %d\n", 1[m]); 21 printf(" *(p+1) = %d\n", *(p + 1)); 22 printf(" p[1] = %d\n", p[1]); 23 24 return 0; 25 }
输出结果为:
(2)数组名注意事项
1)数组名跟数组长度无关;
2)数组名可以看作一个常量指针; 所以表达式中数组名只能作为右值使用;
3)在以下情况数组名不能看作常量指针:
- 数组名作为sizeof操作符的参数
- 数组名作为&运算符的参数
(3)指针和二维数组
一维数组的指针类型是 Type*,二维数组的类型的指针类型是Type*[n]
(4)数组指针和指针数组
①数组指针
1)数组指针是一个指针,用于指向一个对应类型的数组;
2)数组指针的定义方式如下所示:
int (*p)[3] = &m;
②指针数组
1)指针数组是一个数组,该数组里每一个元素为一个指针;
2)指针数组的定义方式如下所示:
int* p[5];
3.指针和函数
(1)函数指针
函数的本质是一段内存中的代码,函数的类型有返回类型和参数列表,函数名就是函数代码的起始地址(函数入口地址),通过函数名调用函数,本质为指定具体地址的跳转执行,因此,可定义指针,保存函数入口地址,如下所示:
int funcname(int a, int b);
int(*p)(int a, int b) = funcname;
上式中,函数指针p只能指向类型为int(int,int)的函数
(2)函数指针参数
对于函数int funcname(int a, int b);
普通函数调用 int funcname(int, int),只能调用函数int func(int, int)
函数指针调用 intname(*func)(int,int),可以调用任意int(int,int)类型的函数,从而利用相同代码实现不同功能,
实例测试如下,假设有两个相同类型的函数func1和func2:
1 int func1(int a, int b, int c) 2 { 3 return a + b + c; 4 } 5 6 int func2(int a, int b, int c) 7 { 8 return a - b - c; 9 }
普通函数调用和函数指针调用方式及结果如下所示
1 printf("普通函数调用\n"); 2 printf("func1 = %d\n", func1(100, 10, 1)); 3 printf("func2 = %d\n", func2(100, 10, 1)); 4 printf("\n"); 5 6 printf("函数指针调用\n"); 7 int(*p)(int, int, int) = NULL; 8 p = func1; 9 printf("p = %d\n", p(100, 10, 1)); 10 p = func2; 11 printf("p = %d\n", p(100, 10, 1)); 12 printf("\n");
需要注意的是,数组作为函数参数的时候,会变为函数指针参数,即:
int funcname( int m[] )<——>int funcname ( int* m );
调用函数时,传递的是数组名,即
funcname(m);
(3)回调函数
利用函数指针,可以实现一种特殊的调用机制——回调函数。回调函数是指把需要调用的函数指针作为参数传递给另一个函数,这个指针会在适当的时机被用来调用其所指向的函数,该函数指针指向的函数可能有多个,这种机制具有非常大的灵活性。
所谓的回调指的是,上层调用底层,底层又回来调用上层,回调函数的机制:
1)调用者不知道需要调用的具体函数
2)被调用的函数不知道何时会被调用
3)在满足特定条件时,调用者通过函数指针调用被调用的函数
一个简单的回调函数的例子如下所示:
1 #include<stdio.h> 2 3 int func1(int a, int b) 4 { 5 int num = 2 * a * b; 6 printf("func1的num = %d\n", num); 7 return num; 8 } 9 10 int func2(int a, int b) 11 { 12 int num = 4 * a * b; 13 printf("func2的num = %d\n", num); 14 return num; 15 } 16 17 //回调函数 18 int callback(int(*p)(int, int)) 19 { 20 int a = 3, b = 2; 21 return p(a, b); 22 } 23 24 int main() 25 { 26 callback(func1); 27 callback(func2); 28 }
运行结果如图所示:
可以看出,如果不使用函数指针,而使用普通函数调用,则无法使用callback函数来调用func1和func2两个函数。
4.指针和常量
int const* p ; (指针p本身是变量,p指向的数据为常量)
int* const p;(指针p本身是常量,p指向的数据为变量)
const int* p;(指针p本身是变量,p指向的数据为常量)
const int* const p;(指针p和p指向的数据都是常量)