#define _CRT_SECURE_NO_WARNINGS  
#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
#include<string.h>
//一级指针接收        地址或者指针
void print(int*ptr, int sz)//一级指针接收
{
  int i = 0;
  for (i = 0; i < sz; i++)
  {
  printf("%d ", *(ptr + i));
  }
}
void test(char*p)
{
}
int main()
{
  int arr[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
  int *p = arr;
  int sz = sizeof(arr) / sizeof(arr[0]);
  print(p, sz);//p是一级指针
  char ch = 'w';
  char*p1 = &ch;
  test(&ch);//char*
  test(p1);
  return 0;
}
//二级指针传参
void test(int**p2)
{
  **p2 = 20;
}
int main()
{
  int a = 0;
  int*pa = &a;//pa是一级指针
  int**ppa = &pa;//ppa是二级指针
  test(ppa);//把   1. 二级指针       进行传参
  test(&pa);//传  2.一级指针的地址                两者几乎等价
  
  int b = 0;
  int*p3 = &b;
  int*arr[10] = {p3};// 不能是空指针
  test(arr);//传  3.存放一级指针的数组的数组名!!!!!!!!
  printf("%d\n", a);//?
  printf("%d", b);
  return 0;
}
//【函数指针定义】 Add 前 加不加&都行 ,调用时 *pf() pf() Add() 3种
int Add(int x, int y)
{
  return x + y;
}
int main()
{
  int a = 0;
  int *pa = &a;
  char ch = 'w';
  char*pc = &ch;
  int arr[10] = { 0 };
  int (*parr)[10]= &arr; // 取出数组的地址
  //parr是指向数组的指针 --存放的是数组的地址
    
  //函数指针--存放函数地址的指针
  //&函数名--取到的就是函数的地址
  //pf就是一个  函数 指针变量
  int(*pf)(int,int) = &Add;
  //int(*pf)(int, int) = Add;
  //返回类型+ 指针+函数参数
  /*printf("%p ",&Add);
  printf("%p ", Add);*/
  //     数组名!= &数组名
  //但是 函数名 ==  &函数名,只有函数是例外
  return 0;
}
void test(char*str)
{
}
int main()
{
  void(*pt)(char*)=&test;//viod省去了会报错
  return 0;
}
int Add(int x, int y)
{
  return x + y;
}
int main()
{
  //pf就是一个函数指针变量
  //int(*pf)(int, int) = &Add;
  int(*pf)(int, int) = Add;      //同上一行
  //int ret = (*pf)(3, 5);                                                                  1
                              // 这个不行:  int ret = *pf(3, 5);           pf 成了函数
  //int ret = pf(3, 5);          // Add===pf      注:说明*是个摆设,用来理解的             2
  int ret=Add(3,5);             //                                                         3
  printf("%d\n", ret);  
  return 0;
}
//【调用0地址处的函数】用来理解,不可模仿 哈哈,0地址不可用。
int main()
{
  (*(void(*)())0)();
  //调用0地址处的函数
  //该函数无参,返回类型是void
  //1.void(*)()   -  函数指针类型
  //2.( void(*)() )0  -  对0进行强制类型转换,被解释为一个函数的地址(指针==地址)
  //3. *((void(*)())0  -  对0地址进行了解引用操作
  //4.(*((void(*)())0)()  -   调用0地址处的函数  
  //                           ------《C陷阱和缺陷》
  // 错误写法:把常量的值 作为地址:(*((void(*)())0x0012ff40)(),尽管编译器不会报错
  return 0;
}
//void(*signal(int,void(*)(int)))(int);
void(*signal(int, void(*)(int)))(int);
//简化 写法如下
//tepedef-对类型进行重定义
typedef void(*pfun_t)(int);//对void(*)(int)函数指针类型 重命名为 pfun_t;
pfun_t signal(int, pfun_t);
//单独放,就是本身。 变量在右,等价于变量在中。
//1.signal 先和()先结合,说明signal是函数名;
//2.signal函数第一个参数的类型是int,第二个参数的类型是函数指针
//该函数指针,指向一个 参数为int,返回类型为void的函数
//3.signal函数的返回类型也是一个函数指针,因为signal的位置就在“函数指针的指针处”
//该函数指针,指向一个 参数为int,返回类型为void的函数
//signal是一个函数的声明
//【函数指针数组】
//整形指针     int*
//整形指针数组 int*arr[5]
//计算器 计算整型变量 加减乘除  (但是 case 1 2 3 4 重复多了,需要用函数指针数组来实现简便)
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;
}
void menu()
{
  printf("xxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n");
  printf("xxxxx   1.add  2.sub   xxxxxx\n");
  printf("xxxxx   3.mul  4.div   xxxxxx\n");
  printf("xxxxx     0.exit       xxxxxx\n");
  printf("xxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n");
  printf("xxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n");
}
int main()
{
  int input = 0;
  do{
      menu();
  int(*parr[5])(int, int) = { NULL, Add, Sub, Mul, Div };
  int ret = 0;
  int x = 0;
  int y = 0;
     printf("请选择:>");
      scanf("%d", &input);
  if (input >= 1 && input <= 4)
  {
    printf("请输入两个操作数:>");
    scanf("%d %d", &x, &y);
    ret = (parr[input](x, y));
    printf("ret=%d\n", ret);
  }
  else if (input == 0)
  {
    printf("退出程序\n");
  }
  else
  {
    printf("选择错误\n");
  }
  
  
//     switch (input)
//     {
//
// case 1:
//        printf("请输入两个操作数:>");
//        scanf("%d %d", &x, &y);
//     ret = Add(x, y);
//     printf("ret=%d\n", ret);
//     break;
//
// case 2:
//        printf("请输入两个操作数:>");
//        scanf("%d %d", &x, &y);
//     ret = Sub(x, y);
//     printf("ret=%d\n", ret);
//     break;
//
//  case 3:
//        printf("请输入两个操作数:>");
//        scanf("%d %d", &x, &y);
//     ret = Mul(x, y);
//     printf("ret=%d\n", ret);
//     break;
//
// case 4:
//        printf("请输入两个操作数:>");          //打算计算 才提示 输入操作数
//        scanf("%d %d", &x, &y);
//     ret = Div(x, y);
//     printf("ret=%d\n", ret);         //有结果才打印
//     break;
//
// case 0:
//  printf("退出程序\n");
//  break;
//default:
//  printf("选择错误,请重新输入");
//  break;
//      }
  
  } while (input);  
  
  //int(*pf1)(int,int)=Add;//pf是指向函数的指针
  //int(*pf2)(int, int) = Sub;
  //int(*pfarr[2])(int, int) = { Add, Sub };//函数指针数组 存放同类型的函数指针 多个
  return 0;
}
//【回调函数】 函数指针数组最方便,此方法也行,适合封装
//计算器  回调函数版本(一个函数的地址 是另一个函数的参数)
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;
}
void menu()
{
  printf("xxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n");
  printf("xxxxx   1.add  2.sub   xxxxxx\n");
  printf("xxxxx   3.mul  4.div   xxxxxx\n");
  printf("xxxxx     0.exit       xxxxxx\n");
  printf("xxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n");
  printf("xxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n");
}
int Calc(int(*pf)(int, int))//回调函数( 函数指针需要和调用的函数类型吻合)(Calc执行重复部分和变化部分)
{
  int x = 0;
  int y = 0;
  int ret = 0;
  printf("请输入两个操作数:>");
  scanf("%d %d", &x, &y);
  ret = pf(x, y);//可以用这个指针 调用传过来地址 对应的函数
  printf("ret=%d\n", ret);
}
int main()
{
  int input = 0;
  do{
      menu();
     printf("请选择:>");
        scanf("%d", &input);
     switch (input)
     {
     case 1:
        Calc(Add);
     break;
     case 2:
        Calc(Sub);
     break;
     case 3:
        Calc(Mul);
     break;
     case 4:
       Calc(Div);
     break;
     case 0:
  printf("退出程序\n");
  break;
     default:
  printf("选择错误,请重新输入");
  break;
      }
  } while (input);  
  return 0;
}
//【冒泡排序】(只能排序整形数据)
void bubble_sort(int arr[],int sz)
{
  int i = 0;
  //冒泡排序趟数:9趟 0-8     9 10不要
  for (i = 0; i < sz - 1; i++)
  {
  //一趟冒泡排序:9 趟 9 8 7......
  int j = 0;
  for (j = 0; j < sz - 1 - i; j++)
  {
    //交换
    if (arr[j]>arr[j + 1])
    {
    int tmp = 0;
    tmp = arr[j];
    arr[j] = arr[j + 1];
    arr[j + 1] = tmp;
    }
  }
  }
}
void print_arr(int arr[], int sz)
{
  int i = 0;
  for (i = 0; i < sz; i++)
  {
  printf("%d ", arr[i]);
  }
  printf("\n");
}
int main()
{
  int arr[] = { 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 };
  int sz = sizeof(arr) / sizeof(arr[0]);
  print_arr(arr, sz);
  bubble_sort(arr, sz);
  print_arr(arr, sz);
  return 0;
}
//【qsort学习】
  void qsort(void*base,//base中存放的是 待排序数据中第一个对象的地址     1
  size_t num,//排序数据中的 元素个数                                       2
  size_t size,//排序数据中一个元素的大小,单位是字节                             3
  int(*compare)(const void*,const void*)//用来比较待排序数据中 两个元素 的 函数( 返回数> == < 0来比较)  4
                                        [交给了使用者来实现]
  )
  int(*compare)(const void* e1,const void* e2) ; //e1 e2没什么意义,因为指针接收
qsort-库函数-的作者提供的,cmp交给了使用者          e1 e1换位置可以逻辑相反 实现降序
整形比较函数
int cmp_int(const void*p1, const void*p2)
{
  return *(int*)p1 - *(int*)p2;//  return >0, qsort就执行换位置的功能
}
struct Stu
{
  char name[20];
  int age;
};
//结构体Stu年龄比较函数
int sort_by_age(const void * e1, const void * e2)
{
  return ((struct Stu*)e1)->age - ((struct Stu*)e2)->age;
}
void test1()
{
  //整形排序的数据
  int arr[] = { 9, 8, 7, 6, 5, 4, 33, 3, 2, 1, 0 };
  int sz = sizeof(arr) / sizeof(arr[0]);
  //排序
  qsort(arr, sz, sizeof(arr[0]), cmp_int);//首元素地址 元素个数 元素大小
  //打印
  print_arr(arr, sz);
}
int sort_by_name(const void * e1, const void * e2)
{
  return strcmp(    ((struct Stu*)e1)->name ,   ((struct Stu*)e2)->name  );//失效了
  //strcmp 返回值, 和我们要的效果一样(大于 就>0; 小于就<0;  等于就==0)
}
void test2()
{
  //使用qsort排序 结构体数据
  struct Stu s[3] = { { "la", 20 }, { "lc", 90 }, { "lb", 15 } };
    int sz = sizeof(s) / sizeof(s[0]);
  ////按照年龄来排序
  //qsort(s, sz, sizeof(s[0]), sort_by_age);
  //按照名字来排序
  qsort( s, sz, sizeof(s[0]), sort_by_name);
  printf("%s ", s[0].name);
  printf("%s ", s[1].name);
  printf("%s ", s[2].name);
}
//【模仿qsort实现一个冒泡排序函的通用算法】
void Swap(char*buf1, char*buf2, int width)//交换程序
{
  int i = 0;
  for (i = 0; i <width; i++)
  {
  char tmp = *buf1;
  *buf1 = *buf2;
  *buf2 = tmp;
  buf1++;
  buf2++;
  }
}
void bubble_sort(void* base,
  int sz,
  int width,
  int(*cmp)(const void *e1, const void* e2)//指向的元素类型不确定,加const只是比较 不会改变里面的值
                                         //cmp是接收 比较函数  的指针
  )
{
  int i = 0;
  for (i = 0; i < sz - 1; i++)//9趟
  {
  int j = 0;
  for (j = 0; j < sz - 1 - i; j++)//9趟
  {
    //两个 元素比较
    //原来 是 arr[j] 与 arr[j+1]比较               9 8 7 6 5 4 3 2 1 0
    if ( cmp(   (char*)base+j*width ,   (char*)base+(j+1)*width ) > 0)
    {
    //交换
    Swap(    (char*)base + j*width,  (char*)base + (j + 1)*width  ,width  );
    }
  }
  }
  
}
                
void test3()
{
  //整形排序的数据
  int arr[] = { 9, 8, 7, 1000,6, 5, 4, 33, 3,100, 2, 1, 0 };
  int sz = sizeof(arr) / sizeof(arr[0]);
  //排序
  bubble_sort(arr, sz, sizeof(arr[0]),cmp_int);//首元素地址 元素个数 元素大小
                                        //比较函数 cmp_int   sort_by_name    sort_by_age 三选一
  //打印
  print_arr(arr, sz);
}
int main()
 {
  //test1();
    //test2();
  test3();
  return 0;
}