指针之前有个初阶的总结,这次到了进阶。里面有更多的,更坑的地方。并且涉及到了各种指针。
字符指针
在指针的类型中我们知道有一种指针类型为字符指针char*
一般使用:
int main(){
char ch = 'w';
char *pc = &ch;
*pc = 'w';
return 0;
}
对于某一指针p,指向的是单个字符。并且指向的不是字符串,就不能使用strlen。
强行使用strlen就会内存访问越界,此时就是未定义行为了。
指针数组
指针数组是一个存放指针的数组。数组的每一个元素是一个指针
例如:int* arr[5]
,这就是个指针数组
数组指针
数组指针就是一个指针。指向的元素是整个数组
例如:int (*arr)[5]
,这就是个数组指针,这个指针的类型 int(*)[5]
&数组名VS数组名
arr是数组名,数组名表示数组首元素的地址。int*类型的变量
&arr是数组指针。
‘房间号’是相同的,但是类型不同
函数指针(第二重要)
int Func(){
printf("hehe\n");
return 10;
}
//()函数调用操作符
int main(){
//Func 也就是函数指针了
printf("%p\n",Func);
printf("%d\n",Func());
return 0;
}
Func
打印出来的就是当前函数存放的地址
在指针中,void*不能解引用。但是函数指针可以。
对于函数指针来说,最重要的操作是调用()
int (*p)() = Func;
,此时定义了一个指针变量p,p的类型 int(*)()
函数指针数组
把函数的地址存到一个数组中,那这个数组就叫函数指针数组
int (*parr1[10]])();
int *parr2[10]();
int (*)() parr3[10];
只有第一种写法算比较科学,但是还是推荐用typedef来描述
#include <stdio.h>
int add(int a, int b)
{
return a + b;
}
int sub(int a, int b)
{
return a - b;
}
int mul(int a, int b)
{
return a*b;
}
int div(int a, int b)
{
return a / b;
}
int main(){
typedef int(*T)(int,int);
T arr[] = {
Add,
Sub,
Mul,
Div
};
arr[choice - 1](10,20);
return 0;
}
这是利用函数指针数组来实现
回调函数
回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应
void Swap(int* x,int* y){
int tmp = *x;
*x = *y;
*y = tmp;
}
//is_desc => 1降序排序 0升序排序
通过调用一个开关进行排序
void BubbleSort(int arr[],int size,int is_desc){
//[0,bound)已排序区间
//[bound,size)待排序区间
int bound = 0;
for(bound = 0; bound < size; ++bound){
//反着循环是为了每次找到一个最小的元素并且放> 到合适的位置上
int cur = 0;
for(cur = size - 1;cur > bound; --cur){
if(is_desc == 1){
if(arr[cur - 1] > arr[cur]){
//这个条件其实就是在描述排序规则
//这两个相邻元素不符合排序规则,需要交换
Swap(&arr[cur - 1],&arr[cur]);
}else if(is_desc == 0){
if(arr[cur - 1] > arr[cur]){
Swap(&arr[cur - 1],&arr[cur]);
}
}
}
}
}
}
通过回调函数来进行冒泡排序
typedef int(*T)(int x,int y);
//参数中引入一个函数指针,这个函数指针描述了排序> 规则
//排序规则可能会非常复杂,根据实际情况可能要考虑> 很多很多方面
int Less(int x,int y){
return x < y ? 1 : 0;
}
int Greater(int x,int y){
return x > y ? 1 : 0;
}
int AbsLess(int x,int y){
return fabs(x) < fabs(y) ? 1 : 0;
}
void BubbleSort2(int arr[],int size,T cmp){
//[0,bound)已排序区间
//[bound,size)待排序区间
int bound = 0;
for(bound = 0; bound < size; ++bound){
//反着循环是为了每次找到一个最小的元素并且放> 到合适的位置上
int cur = 0;
for(cur = size - 1;cur > bound; --cur){
if(cmp(arr[cur - 1] ,arr[cur]) == 0){
//这个条件其实就是在描述排序规则
//这两个相邻元素不符合排序规则,需要交换
Swap(&arr[cur - 1],&arr[cur]);
}
}
}
}
int main(){
int arr[5] = {0};
//升序
BubbleSort2(arr,5,Less);
//降序
BubbleSort2(arr,5,Greater);
//元素的绝对值升序排序
BubbleSort2(arr,5,AbsLess);
//回调函数函数调用时机不是由调用者来决定,而是> 由操作系统或者由代码框架来决定。
return 0;
}
接着就是之前在指针初阶的总结中一部分超坑的打印题
附上链接,拉到底部即可
接着还有一些超坑的指针代码块!
int main()
{
int a[5] = { 1, 2, 3, 4, 5 };
int *ptr = (int *)(&a + 1);
printf( "%d,%d", *(a + 1), *(ptr - 1));
//结果为2,5
int a[4] = { 1, 2, 3, 4 };
int *ptr1 = (int *)(&a + 1);
int *ptr2 = (int *)((int)a + 1);//需修改
printf( "%x,%x", ptr1[-1], *ptr2);
//ptr1是指针数组+1,跳过了这个数组,那么-1就是向前取一位,也就是4
//对于ptr2,假设a对应地址 0x ff ff ff ff 00 00 00 01 64位地址
//0x 00 00 00 01 强转成int 占4个字节
//0x 00 00 00 02 再+1
//0x 00 00 00 00 00 00 00 02 再转为int*,所以再64位机子上,int的高位被截断,转没了,所以改为long,能正常显示
int a[3][2] = { (0, 1), (2, 3), (4, 5) };
int *p;
p = a[0];
printf( "%d", p[0]);
//这里涉及到了逗号表达式,整个表达式的值为最后一个表达式的值。
//所以数组的初始化应为{1,3,5,0,0,0}
int a[5][5];
int(*p)[4];
p = a;
printf( "a_ptr=%#p,p_ptr=%#p\n" , &a[4][2], &p[4][2]);
printf( "%p,%d\n", &p[4][2] - &a[4][2], &p[4][2] - &a[4][2]);
//内存分布见图一
//a与p的内存之间从对其,到逐渐有了差值
int aa[2][5] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
int *ptr1 = (int *)(&aa + 1);
int *ptr2 = (int *)(*(aa + 1));//相当于aa[1]强转成int*,就是指向6的指针
printf( "%d,%d", *(ptr1 - 1), *(ptr2 - 1));
char *a[] = {"work","at","alibaba"};//数组指针
char** p = a;//将a数组中的首元素地址放在p指针中
p++;//根据数据类型,+4个字节,这时候指针刚好指向了第二个元素
printf("%s\n", *p);//再解引用,此时得到就是 “at”
char *c[] = {"ENTER","NEW","POINT","FIRST"};
char**cp[] = {c+3,c+2,c+1,c};
char***cpp = cp;
printf("%s\n", **++cpp);//打印point
printf("%s\n", *--*++cpp+3);
printf("%s\n", *cpp[-2]+3);
printf("%s\n", cpp[-1][-1]+1);
//内存见图二
//第一个打印
++ => cpp &&p3
* => &p3
* => p3
//第二个打印
//运算顺序是 ++ * -- * +
++ => &&p2
* => &p2 此时相当于重新定义了一个指针指向p2元素的地址
-- =>p2移动,位&p1
* => p1
+3 => "ENTER",指向了第三个字符之后的
//第三个打印
//运算顺序 [] * +
[-2] => 先减2在解引用,cpp指针由之前此时指向&p2,-2得到的是&p4
* => p4
+3 => ‘ST’
//第四个打印
[-1] => &p3,相当于有另个指针指向了p3
[-1] => p3-1,就相当于p2
+1 => 'EW'
return 0;
}
图一:
图二: