进阶C-指针的进阶

指针之前有个初阶的总结,这次到了进阶。里面有更多的,更坑的地方。并且涉及到了各种指针。

字符指针

在指针的类型中我们知道有一种指针类型为字符指针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;
}

图一:

在这里插入图片描述

图二:

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/skrskr66/article/details/86527556