指针进阶教程

1.字符指针

  1. 指针就是个变量,用来存放地址,地址是唯一的标识空间,内存单元有编号,编号=地址=指针

  2. 指针固定4/8字节(32位/64位)

  3. 指针有类型,类型觉得指针+1-1时的步长,决定了指针解引用时访问的大小

2.指针数组

指针数组是数组 字符数组--存放字符的数组 整型数组--存放整型的数组 指针数组--存放指针的数组,数组中的元素都是指针元素的 int* arr[5] char* ch[6]

3.数组指针

指针数组是数组,是存放指针的数组 数组指针是指针 字符指针--指向字符的指针 整型指针--指向整型的指针 浮点型指针-指向浮点型的指针 数组指针--指向数组的指针

数组名理解:数组名是数组首元素的地址,但是存在两个例外

  1. sizeof(数组名),这里表示整个数组,所以计算的是整个数组所占大小,单位为字节

  2. &数组名,这里数组名表示整个数组,取出的是数组的地址

int main(){
    int arr[10]={0};
    int (*p)[10]=&arr;
    char *arr2[5];
    char* (*pc)[5]=&arr2;
    char arr3[5];
    char(*p3)[5]=&arr3;
    return 0;
}

数组指针到底有什么用?

sizeof数组指针可以获得数组的全部长度,所以数组指针存放的是数组的地址,而不是数组首元素的地址。

一维数组传参

//数组作为形参时,长度可以不写
void prints(int arr[],int sz){
    for (int i = 0; i < sz; i++)
    {
      printf("%d ",arr[i]);
    }
    
}
//一维数组传参传递首元素的地址,本质是指针,可以用int指针接收
void prints1(int* arr,int sz){
    for (int i = 0; i < sz; i++)
    {
      printf("%d ",arr[i]);
    }
    
}
int main(){
  int arr[]={1,2,3,4,5};
  int sz=sizeof(arr)/sizeof(arr[0]);
  prints1(arr,sz);
  return 0;
}

二维数组传参

void print(int arr[][5],int r,int c){
  for (int i = 0; i < r; i++)
  {
    for (int j = 0; j < c; j++)
    {
      printf("%d ",arr[i][j]);
    }
    printf("\n");
  }
  
​
}
//传递第一行数组的地址,可以用数组指针接收
void print(int (*arr)[5],int r,int c){
  for (int i = 0; i < r; i++)
  {
    for (int j = 0; j < c; j++)
    {
      printf("%d ",*(*(arr+i)+j));
    }
    printf("\n");
  }
  
​
}
//二维数组传参 传递二维数组的数组名其实是传递二维数组的首元素地址,也就是第一行数组整体的地址,注意不是第一行数组第一个元素的地址
int main(){
  int arr[][5]={{1,2,3,4,5},{2,3,4,5,6},{3,4,5,6,7}};
  print(arr,3,5);
  return 0;
}

练习

int arr[5];//arr是能存储五个元素的数组
int *parr1[10];//parr1是一个指针数组,元素是整形指针类型,能存储十个元素
int (*parr2)[10];//parr2是数组指针,指向元素个数为10的整型数组的地址。
int (*parr3[10])[5];//parr3是数组,数组的元素是数组指针,每个元素指向长度为5的整型数组。parr可以存储十个数组指针

4.数组参数、指针参数

写代码的时候难免要把数组或指针传给函数,那函数参数该如何设计呢

一维数组传参

#include 
void test(int arr[])//ok 数组传参,形参可以写成数组形式
{}
void test(int arr[10])//ok形参数组的大小可以写错,因为传的本质是地址
{}
void test(int *arr)//ok 本质是首元素的地址,所以可以用指针接收
{}
void test2(int *arr[20])//ok可以用数组接收
{}
void test2(int **arr)//ok 传递的是指针数组首元素的地址,指针数组的首元素是指针类型,用二级指针接收 没问题
{}
int main(){
    int arr[10] = {0};
    int *arr2[20] = {0};
    test(arr);
    test2(arr2);
}

二维数组传参

void test(int arr[3][5])//ok?二维数组作为参数,使用二维数组进行接收
{}
void test(int arr[][])//ok?//二维数组作为参数,必须指定列长度
{}
void test(int arr[][5])//ok?二维数组作为参数,行可以省略
{}
//总结:二维数组传参,函数形参的设计只能省略第一个[]的数字。
//因为对一个二维数组,可以不知道有多少行,但是必须知道一行多少元素。
//这样才方便运算。
void test(int *arr)//ok?传递arr数组名,实际传递的是arr的首元素地址也就是第一行的数组地址,不能使用整型指针接收
{}
void test(int* arr[5])//ok?传递arr数组名,实际传递的是arr的首元素地址也就是第一行的数组地址,可以使用指针数组接收
{}
void test(int (*arr)[5])//ok?传递arr数组名,实际传递的是arr的首元素地址也就是第一行的数组地址,可以使用数组指针接收
{}
void test(int **arr)//ok?二级指针是用来接收一级指针的地址,所以不行 
{}
int main()
{
int arr[3][5] = {0};
test(arr);
}

一级指针传参

void print(int *p, int sz)//一级指针传参,使用一级指针接收就可以
{
int i = 0;
for(i=0; i

二级指针传参

void test(int** ptr)
{
printf("num = %d\n", **ptr);
}
int main()
{
int n = 10;
int*p = &n;
int **pp = &p;
test(pp);//传递二级指针
test(&p);//传递一级指针的地址
return 0;
}

当一个函数以二级指针为参数,函数能接收什么函数

void test(int **pp);
int a = 10;
int *p = &a;
int **pp=&p;
test(pp);//可以接收二级指针
test(&p)//可以接收一级指针的地址
int* arr[10];
test(arr)//可以接收指针数组的首元素地址。因为指针数组的元素为指针类型,指针类型的地址就是二级指针。

5.函数指针

数组指针---指向数组的指针---存放的是数组的地址---&数组名得到 函数指针---指向函数的指针---存放的是函数的地址---函数名也是函数的地址

int add(int x,int y){
    return x+y;
}
int main(){
   int (*pf1)(int,int) = add;
   int (*pf2)(int,int) = &add;
//    printf("%d",pf1==pf2);
printf("%d",(pf1)(3,2));
    return 0;
}

6.函数指针数组

int (* p[10])(int),本质上是一个数组 指针数组---char * arr[]存放的是字符指针 整型指针数组---int * arr2[]存放的是整型指针 函数指针数组是数组,数组的元素是函数指针,也就是函数的地址

用函数指针数组完成计算机

#include
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 input = 1,ret = 0,A1 = 0,A2 = 0;
    int (*cal[5])(int,int)={NULL,Add,Sub,Mul,Div};
    do
    {
        printf("********************\n");
        printf("***1.Add****2.Sub***\n");
        printf("***3.Mul****4.Div***\n");
        printf("***0.Exit***********\n");
        printf("********************\n");
        scanf("%d",&input);
        if (input==0)
        {
           printf("退出系统");
        }else if (input>0&&input<5)
        {    printf("请输入两个数字\n");
             scanf("%d %d",&A1,&A2);
             ret = cal[input](A1,A2);
             printf("ret = %d\n",ret);
            /* code */
        }else{
            printf("输入错误\n");
        }
        
        
    } while (input);
    
    return 0;
}

7.指向函数指针数组的指针

int(* (*p)[10])(int)

int main(){
    int a = 10;
    int b = 20;
    int c = 30;
    int *arr[] = {&a,&b,&c};//整型指针数组
    int (*p)[3] = &arr;//指向整型数组的指针
    //函数指针数组 - 数组 - 存放的是函数的地址
    int (*pfArr[5])(int,int)={NULL,Add,Sub,Mul,Div}; 
    //指向函数指针数组的指针
    int(*(*pF)[3])(int,int) = &pfArr;
​
}

8.回调函数

依赖函数指针,有函数指针才能实现回调函数 回调函数是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向函数时,我们说这就是回调函数。回调函数不应该由函数的实现方直接调用,而是在特定的事件或条件发生时由另一方调用,对于该事件或条件进行相应。

简单来说就是不可以直接使用函数名+()的形式调用,需要将回调函数的地址传给函数指针,通过函数指针来调用。

通过函数指针调用回调函数

void cals(int (*p)(int,int)){
     int a1,a2;
     printf("请输入两个数字\n");
     scanf("%d %d",&a1,&a2);
     int ret = p(a1,a2);
     printf("ret = %d\n",ret);
}
int main(){
    int input = 1,ret = 0,A1 = 0,A2 = 0;
    // int (*cal[5])(int,int)={NULL,Add,Sub,Mul,Div};
    do
    {
        printf("********************\n");
        printf("***1.Add****2.Sub***\n");
        printf("***3.Mul****4.Div***\n");
        printf("***0.Exit***********\n");
        printf("********************\n");
        scanf("%d",&input);
       switch (input)
       {
       case 1:
        cals(Add);
        break;
        case 2:
        cals(Sub);
        break;
         case 3:
        cals(Mul);
        break;
         case 4:
        cals(Div);
        break;
      case 0:
      printf("退出系统\n");
      break;
      default:
      printf("输入错误,重新输入\n");
      break;
       }
        
        
    } while (input);
    
    return 0;
}

qsort的使用

qsort是头文件stdlib.h中的函数。此函数有四个参数 void qsort(void *base, size_t nitems, size_t size, int (*compar)(const void *, const void*)) 第一个base代表要排序数组的第一个元素的地址一般传入数组名,如果想部分排序,需要获得其实排序元素的地址。 第二个代表要排序的元素的个数 第三个代表要排序数组中的每个元素的大小,以字节为单位 第四个需要传入一个compare函数,compare函数参数必须为void类型的指针。因为qsort()为了对所有类型排序。void类型指针可以接收任何类型的指针。compare的返回值为int。 类型为void的指针补充:类型为void类型的指针不具备访问内存空间的能力。但是此类型指针的特点是可以存储任何类型的指针,后续使用时可以把void类型转换为当时转换前的指针类型。

// int main(){
//     int a = 10;
//     int b = 20;
//     int c = 30;
//     int *arr[] = {&a,&b,&c};//整型指针数组
//     int (*p)[3] = &arr;//指向整型数组的指针
//     //函数指针数组 - 数组 - 存放的是函数的地址
//     int (*pfArr[5])(int,int)={NULL,Add,Sub,Mul,Div}; 
//     //指向函数指针数组的指针
//     int(*(*pF)[3])(int,int) = &pfArr;
​
// }
struct stu
{
    char name[20];
    int age;
};
​
// void print_arr(int* arr,int len){
//     for (int i = 0; i < len; i++)
//     {
//         printf("%d ",arr[i]);
//     }
//     printf("\n");
    
// }
// int compare(const void* p1,const void* p2){
//  return *(int*)p1-*(int*)p2;
// }
// int main(){
//     int arr[]={9,8,7,6,5,4,3,2,1};
//     int sz=sizeof(arr)/sizeof(arr[0]);
//     print_arr(arr,sz);
//     qsort(arr,sz,sizeof(arr[0]),compare);
//     print_arr(arr,sz);
//     return 0;
// }
// void print_struct(struct stu *stu,int sz){
//     for (int i = 0; i < sz; i++)
//     {
//         printf("name = %s , age = %d\n",(stu+i)->name,(stu+i)->age);
//     }
    
// }
// int compare(void *p1,void *p2){
//     return ((struct stu*)p1)->age-((struct stu*)p2)->age;
// }
// int compare_str(void *p1,void *p2){
//      return strcmp(((struct stu*)p1)->name,((struct stu*)p2)->name);
// }
// int main(){
//     struct stu Stus[3]={{"zvjing",61},{"xanjiam",20},{"yhangfei",43}};
//     print_struct(Stus,3);
//     // qsort(Stus,3,sizeof(Stus[0]),compare);
//      qsort(Stus,3,sizeof(Stus[0]),compare_str);
//     print_struct(Stus,3);
//     return 0;
// }

你可能感兴趣的:(排序算法,c语言,学习)