1.字符指针
字符指针是一个指向字符串的指针,它存储着一个字符地址,指向一个常量字符串,并且字符串不可修改。
以下是定义:
const char* ch="abcd";//"abcd"称为常量字符串
因为ch指针指向的字符串不能被修改,而且被修改了程序会奔溃,因此可以加上const ch储存着常量字符串的首元素地址,也就是’a’的地址,"abcd"是常量字符串,不能被修改,被储存在只读数据区,当被引用时返回首字符地址。
这里我讲讲被常量字符串被调用返回的情况:
const char ch1 ="abcd";
const char ch2 ="abcd";
printf("%p\n",ch1);
printf("%p\n",ch2);
上面的结果是ch1和ch2的值是相同的,因为相同的常量字符串在只读数据区中储存的空间是相同的。
2.指针数组
指针数组是一个数组,只是用来存放指针的一个数组*
char* arr1[10];//存放一级字符指针的数组
int* arr1[10];//存放一级整形指针的数据
int** arr2[10];//存放二级整形指针的数据
3.数组指针
3.1 数组指针的定义
指针数组不是指针,只是一个存放指针的数组 而数组指针则是一个指向数组的指针
定义:
char arr1[10]; //指针数组
char (*arr2)[10];//数组指针
char *(*arr3)[10];//数组指针
’ [] ‘操作符的优先级比’ * ‘操作符的优先级高,所以arr1会先和’ [] '结合,形成数组,然后剩下的char就是数组存储数据的类型
arr2在括号的影响下先和’ * '结合,所以arr2代表的是一个数组指针,而剩下的char 变量名 [10] ;是代表指向数组的类型
同理:arr3也是一个数组指针,指向的是一个类型为char* [10] 的数组
3.2 &数组名VS数组名
我们在前面的学习也可以知道,数组名带表的是首元素地址,而&数组名又是什么呢?
int arr[10]={0};
printf("%p\n",arr);
printf("%p\n",&arr);
当我们得到上面的打印的结果可以发现,arr和&arr的值是一样的, 我们可以再尝试打一串代码
int arr[10]={0};
printf("%p\n",arr);
printf("%p\n",arr+1);
printf("%p\n",&arr+1);
当我们得到结果可以发现,arr+1的值相比arr大了4,而&arr+1比arr大了40,到这里应该就可以明白&arr和arr的不同了,&arr得到的地址代表整个数组,而arr只代表首元素地址。
3.3 数组指针的使用
由3.2可以知道,&数组名得到的是整个数组,所以数组指针指向的是整个数组。
int arr[10]={1,2,3,4,5,6,7,8,9,10};
int (*parr)[10]=&ch;
//使用
//先对pch解引用,在使用[]操作符进行访问空间
(*arr2)[0];
还有一种对数组指针的巧用,在定义数组指针的时候,arr(*pch)[10]中的[10]其实pch指针变量的一种权限
例如:
int arr[10]={1,2,3,4,5,6,7,8,9,10};
int (*parr)[5]=&ch;
//如果我们向打印7
printf("%d",*(*(parr+1)+1));
//或者
printf("%d",parr[1][1]);
从上面的例子可以看出,其实数组指针权限为几,就代表加一跳过几个元素(一个数组)。
4. 数组参数,指针参数
4.1 一维数组传参
一维数组传参大多数都是直接将数组名传过去,也可以传&数组名 例:
void test(int arr[],size_t size);
void test(int* arr,size_t size);
int main()
{
int arr[10] = { 0 };
test(arr);
return 0;
}
其中,两种函数的形参接受都一样的,int arr[]等于int* arr,都只是将数组的首元素地址传过去,因为数组的储存在空间中是连续的,所以得到了首元素地址,那么后面的元素也可以访问的到,size是数组元素个数
void test(int(*arr)[10]);
int main()
{
int arr[10] = { 0 };
test(&arr);
return 0;
}
第二种就是将整个数组传过去
4.2二维数组传参
二维数组的传参和一维类似
第一种传参方法:
void test(int** arr);
void test(int(*arr)[10]);
void test(int arr[][10]);
void test(int arr[10][10]);
int main()
{
int arr[10][10] = { 0 };
test(arr);
return 0;
}
或者将整个二维数组传过去:
void test(int(*arr)[10][10]);
int main()
{
int arr[10][10] = { 0 };
test(&arr);
return 0;
}
5. 函数指针
函数指针,跟其他指针类似,函数指针其实就是指向函数的指针,和数组名一样,函数名也代表函数指针,但是与数组名不同的是,&函数名跟函数名代表的意义是一样的。
定义:
void test1();
int test2(int a);
char test3(char a,int* b);
int main()
{
void(*p1)() = test1;
//void(*p1)() = &test1;
int (*p2)(int) = test2;
//int (*p2)(int) = &test2;
char(*p3)(char,int*)=test3;
//char(*p3)(char,int*)=&test3;
return 0;
}
在定义函数指针的时候,指针变量前的是返回类型,后面括号里的参数的类型和数量要保持和函数一致
6. 函数指针数组
函数指针数组就是一个数组,每个元素都是函数指针
定义:
int (*pfArr[4])(int,int);
[] 操作符的优先级比 * 高,pfArr先和 [] 结合,代表它是个数组,之后再和 * 结合,代表它的数组类型是函数指针
7. 函数指针数组指针
函数指针数组指针就是一个指向函数指针数组的指针
定义:
int (*(*pfArr)[4])(int, int);
,pfArr先和 * 结合,代表它是个指针,之后再和 [] 结合,代表它是个指向数组的指针,之后再和 * 结合,代表它指向的数组的元素类型是指针类型,剩下的代表它是个函数指针数组指针
8. 回调函数
回调函数是指 使用者自己定义一个函数,实现这个函数的程序内容,然后把这个函数(入口地址)作为参数传入别人(或系统)的函数中,由别人(或系统)的函数在运行时来调用的函数。函数是你实现的,但由别人(或系统)的函数在运行时通过参数传递的方式调用,这就是所谓的回调函数。简单来说,就是由别人的函数运行期间来回调你实现的函数。
使用:
void Print()
{
printf("Hello Wrold\n");
}
void test(void Print())
{
Print();
}
int main()
{
test(Print);
return 0;
}
具体的使用可以参考qsort函数的实现 链接:qsort函数的原理
9. 一些奇怪的例子
一:
(*(void(*)())0)();
上面的意思是将0强转为一个无返回类型无参数的函数指针并且调用
首先得到 (void( * )( )) 0,这里是将0强转为函数指针,然后( * ( void( * )( ) ) 0),加上是对指针=解引用,最后((void(*)())0)(),后面加上(),()是函数调用操作符。
二:
void( *signal( int, void(*)(int) ) ) (int);
首先,signal是一个函数,它的返回类型是void( * )(int),函数参数有int类型和void(*)(int) ;
signal先和()结合,代表它是一个函数,然后括号里面是函数的参数,剩余的就是signal的一个返回类型
三: typedef定义函数指针类型
typedef void(* pfun_t)(int);
|