• C语言-指针的进阶



    前言

    本文主要讲解了c语言中指针的进阶内容,在初阶的基础上进行延伸.

    内存会划分为一个个的内存单元

    每个内存单元都有一个独立的编号,-编号也称为地址

    地址在C语言中也被称为指针

    指针(地址)需要存储起来-存储到变量中,这个变量也就被称为指针变量

    指针(地址)的大小固定说4/8个字节(32位平台/64位平台)

    地址是物理的电线上产生

    32为机器-32根地址线 -1/0

    32个0/1组成的二进制序列,把这个二进制序列作为地址,32bit位才能存储这个地址

    也就是需要4个字节才能存储,所以指针变量的大小是4个字节

    同理64位机器上,地址的大小是64个0/1组成的二进制序列,需要64bit位存储,也就是8个字节。所以指针变量的大小是8个字节。


    一、字符指针 

    在指针的类型中我们知道有一种指针类型为字符指针char*

    1. //在指针的类型中我们知道有一种指针类型为字符指针char*
    2. int main()
    3. {
    4. char ch = 'w';
    5. char* pc = &ch; //pc就是字符指针
    6. *pc = 'a';
    7. char arr[] = "abcdef"; //[a b c d e f \0]
    8. const char*p = "abcdef"; //常量字符串
    9. printf("%s\n",p); //abcdef
    10. printf("%c\n",*p); //a
    11. return 0;
    12. }

     经典面试题小试牛刀

    1. int main()
    2. {
    3. char str1[] = "hello xiaofan.";
    4. char str2[] = "hello xiaofan.";
    5. const char* str3 = "hello xiaofan.";
    6. const char* str4 = "hello xiaofan.";
    7. if (str1 == str2)
    8. {
    9. printf("str1 and str2 are same\n"); //数组名是数组首元素地址
    10. }
    11. else
    12. {
    13. printf("str1 and str2 are not same\n");
    14. }
    15. if(str3 == str4)
    16. {
    17. printf("str3 and str4 are same\n");
    18. }
    19. else
    20. {
    21. printf("str3 and str4 are not same\n");
    22. }
    23. return 0;
    24. }

    二、数组指针

    数组指针是数组还是指针 ----是指针哦

    数组指针类比:

    整型指针-指向整型变量的指针,存放整型变量的地址的指针变量

    字符指针-指向字符变量的指针,存放字符变量的地址的指针变量

    数组指针-指向数组的指针,存放的是数组的地址的指针变量

    1. //数组指针
    2. //数组名的理解
    3. //数组名是数组首元素的地址
    4. //2个例外:1.sizeof(数组名),这里的数组名不是数组首元素的地址,数组名表示整个数组,sizeof(数组名)计算的是整个数组的大小,单位是字节
    5. //2.&数组名,这里的数组名表示整个数组,&数组名取出的是整个数组的地址
    6. //除此之外,所有的地方的数组名都是数组首元素的地址
    7. // int main()
    8. // {
    9. // int arr[10] = {0};
    10. // printf("%p\n",arr); // int*
    11. // printf("%p\n",arr+1);
    12. // printf("%p\n",&arr[0]); //int*
    13. // printf("%p\n",&arr[0]+1);
    14. // printf("%p\n",&arr); //int(*)[10]
    15. // printf("%p\n",&arr+1);
    16. // return 0;
    17. // }
    18. //对于数组名来说
    19. //&数组名得到的是数组的地址
    20. // int main()
    21. // {
    22. // int arr[10] = {1,2,3,4,5,6,7,8,9,10};
    23. // //数组的地址,存储到数组指针变量
    24. // //int[10] *p = &arr; //err
    25. // int(*p)[10] = &arr;
    26. // }
    27. // int main()
    28. // {
    29. // int arr[10] = {1,2,3,4,5,6,7,8,9,10};
    30. // int* p = arr;
    31. // int i = 0;
    32. // for (i = 0;i<10;i++)
    33. // {
    34. // printf("%d ",*(p+i));
    35. // }
    36. // }
    37. // int main()
    38. // {
    39. // int arr[10] = {1,2,3,4,5,6,7,8,9,10};
    40. // int (*p)[10] = &arr; //*&arr -> arr
    41. // int i = 0;
    42. // for (i = 0;i<10;i++)
    43. // {
    44. // printf("%d ",*((*p)+i));
    45. // printf("%d ",(*p)[i]);
    46. // }
    47. // }
    48. //数组指针怎么使用呢?一般在二维数组上才方便
    49. //二维数组传参,形参是二维数组的形式
    50. // void Printf(int arr[3][5],int r,int c)
    51. // {
    52. // int i = 0;
    53. // for (i = 0; i<3;i++)
    54. // {
    55. // int j = 0;
    56. // for(j = 0;j < 5;j++)
    57. // {
    58. // printf("%d ",arr[i][j]);
    59. // }
    60. // printf("\n");
    61. // }
    62. // }
    63. // int main()
    64. // {
    65. // int arr[3][5] = {1,2,3,4,5, 2,3,4,5,6, 3,4,5,6,7};
    66. // Printf(arr,3,5);
    67. // return 0;
    68. // }
    69. //二维数组传参,形参是指针的形式
    70. // void Printf(int (*p)[5],int r,int c)
    71. // {
    72. // int i = 0;
    73. // for(i = 0; i < r; i++)
    74. // {
    75. // int j = 0;
    76. // for (j = 0; j < c; j++)
    77. // {
    78. // printf("%d ",*(*(p+i)+j));
    79. // }
    80. // }
    81. // }
    82. // //二维数组的每一行可以理解为二维数组的一个元素
    83. // //每一行又是一个一维数组
    84. // //二维数组其实是一维数组的数组
    85. // //二维数组的数组名,也是数组名,数组名就是数组首元素地址
    86. // //arr - 首元素地址 - 第一行的地址 -一维数组的地址 -数组的地址
    87. // int main()
    88. // {
    89. // int arr[3][5] = {1,2,3,4,5, 2,3,4,5,6, 3,4,5,6,7};
    90. // //arr数组名是首元素地址
    91. // Printf(arr,3,5);
    92. // return 0;
    93. // }
    94. //一维数组传参,形参的部分可以是数组,也可以是指针
    95. // void test1(int arr[5],int sz)
    96. // {
    97. // }
    98. // void test2(int* p,int sz)
    99. // {
    100. // }
    101. // int main()
    102. // {
    103. // int arr[5] = {0};
    104. // test1(arr,5);
    105. // test2(arr,5);
    106. // return 0;
    107. // }
    108. //二维数组传参,形参的部分可以数组,也可以是指针
    109. void test3(char arr[3][5],int r,int c)
    110. {
    111. }
    112. void test4(char(*p)[5],int r,int c)
    113. {
    114. }
    115. int main()
    116. {
    117. char arr[3][5] = {0};
    118. test3(arr,4,5);
    119. test4(arr,3,5);
    120. return 0;
    121. }

    三、指针数组

    指针数组

    整型数组-存放整型的数组

    字符数组-存放字符的数组

    指针数组-存放指针的数组

    1. // int main()
    2. // {
    3. // int* arr[10];//存放整型指针的数组
    4. // char* arr2[10]; //一级字符指针的数组
    5. // char **arr3[10];//二级字符指针的数组
    6. // return 0;
    7. // }
    8. //使用指针数组模拟实现二维数组
    9. int main()
    10. {
    11. int arr1[] = {1,2,3,4,5};
    12. int arr2[] = {2,3,4,5,6};
    13. int arr3[] = {3,4,5,6,7};
    14. //指针数组
    15. int* arr4[3] = {arr1,arr2,arr3};
    16. printf("%d\n",arr4[1][1]);
    17. int i = 0;
    18. for ( i = 0; i < 3; i++)
    19. {
    20. /* code */
    21. int j = 0;
    22. for(j=0;j<5;j++)
    23. {
    24. printf("%d ",arr4[i][j]);
    25. //printf("%d ",*(arr4+i));
    26. }
    27. printf("\n");
    28. }
    29. return 0;
    30. }

    四、数组传参和指针传参

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

    一维数组传参

    二维数组传参

    一级指针传参

    二级指针传参

    1. //二维数组传参,形参是二维数组的形式
    2. // void Printf(int arr[3][5],int r,int c)
    3. // {
    4. // int i = 0;
    5. // for (i = 0; i<3;i++)
    6. // {
    7. // int j = 0;
    8. // for(j = 0;j < 5;j++)
    9. // {
    10. // printf("%d ",arr[i][j]);
    11. // }
    12. // printf("\n");
    13. // }
    14. // }
    15. // int main()
    16. // {
    17. // int arr[3][5] = {1,2,3,4,5, 2,3,4,5,6, 3,4,5,6,7};
    18. // Printf(arr,3,5);
    19. // return 0;
    20. // }
    21. //二维数组传参,形参是指针的形式
    22. // void Printf(int (*p)[5],int r,int c)
    23. // {
    24. // int i = 0;
    25. // for(i = 0; i < r; i++)
    26. // {
    27. // int j = 0;
    28. // for (j = 0; j < c; j++)
    29. // {
    30. // printf("%d ",*(*(p+i)+j));
    31. // }
    32. // }
    33. // }
    34. // //二维数组的每一行可以理解为二维数组的一个元素
    35. // //每一行又是一个一维数组
    36. // //二维数组其实是一维数组的数组
    37. // //二维数组的数组名,也是数组名,数组名就是数组首元素地址
    38. // //arr - 首元素地址 - 第一行的地址 -一维数组的地址 -数组的地址
    39. // int main()
    40. // {
    41. // int arr[3][5] = {1,2,3,4,5, 2,3,4,5,6, 3,4,5,6,7};
    42. // //arr数组名是首元素地址
    43. // Printf(arr,3,5);
    44. // return 0;
    45. // }
    46. //一维数组传参,形参的部分可以是数组,也可以是指针
    47. // void test1(int arr[5],int sz)
    48. // {
    49. // }
    50. // void test2(int* p,int sz)
    51. // {
    52. // }
    53. // int main()
    54. // {
    55. // int arr[5] = {0};
    56. // test1(arr,5);
    57. // test2(arr,5);
    58. // return 0;
    59. // }
    60. //二维数组传参,形参的部分可以数组,也可以是指针
    61. // void test3(char arr[3][5],int r,int c)
    62. // {
    63. // }
    64. // void test4(char(*p)[5],int r,int c)
    65. // {
    66. // }
    67. // int main()
    68. // {
    69. // char arr[3][5] = {0};
    70. // test3(arr,4,5);
    71. // test4(arr,3,5);
    72. // return 0;
    73. // }
    74. //一维数组传参
    75. // void test(int arr[]) //true
    76. // {}
    77. // void test(int arr[10]) //true
    78. // {}
    79. // void test(int* arr) //true
    80. // {}
    81. // void test2(int* arr[20]) //true
    82. // {}
    83. // void test2(int** arr)
    84. // {}
    85. // int main()
    86. // {
    87. // int arr[10] = {0}; //存放整型
    88. // int *arr2[20] = {0}; //存放地址
    89. // test(arr);
    90. // test2(arr2);
    91. // }
    92. //一级指针传参
    93. // void print(int* p,int sz)
    94. // {
    95. // int i = 0;
    96. // for(i = 0;i
    97. // {
    98. // printf("%d\n",*(p+i));
    99. // }
    100. // }
    101. // int main()
    102. // {
    103. // int arr[10] = {1,2,3,4,5,6,7,8,9};
    104. // int *p = arr;
    105. // int sz = sizeof(arr)/sizeof(arr[0]);
    106. // //一级指针p,传给函数
    107. // print(p,sz);
    108. // return 0;
    109. // }
    110. //二级指针传参
    111. void test(int** ptr)
    112. {
    113. printf("num = %d\n",**ptr);
    114. }
    115. int main()
    116. {
    117. int n = 10;
    118. int* p = &n;
    119. int** pp = &p;
    120. test(pp); //10
    121. test(&p); //10
    122. return 0;
    123. }

    五、函数指针

    函数指针-指向函数的指针

    数组指针-指向函数的指针

    1. //函数指针
    2. // int Add(int x,int y)
    3. // {
    4. // return x+y;
    5. // }
    6. // int main()
    7. // {
    8. // int arr[10] = {0};
    9. // int (*pa)[10] = &arr;
    10. // // //&arr;
    11. // // printf("%p\n",&Add);
    12. // // printf("%p\n",Add);
    13. // //函数名是函数的地址
    14. // //&函数名也是函数的地址
    15. // int (*pf)(int,int) =&Add; //pf是函数指针变量 int(*)(int int)是函数指针类型
    16. // }
    17. // void test(char* pc,int arr[10])
    18. // {}
    19. // int main()
    20. // {
    21. // void (*pf)(char*,int[10]) = &test;
    22. // return 0;
    23. // }
    24. int Add(int x,int y)
    25. {
    26. return x+y;
    27. }
    28. int main()
    29. {
    30. //int (*pf)(int,int) = &Add;
    31. int (*pf)(int,int) = Add;
    32. int r = Add(3,5);
    33. printf("%d\n",r);
    34. int m = (*pf)(4,5);
    35. printf("%d\n",m);
    36. return 0;
    37. }
    38. //void (*p)() -p是函数指针
    39. //void (*)()是函数指针类型
    40. // int main()
    41. // {
    42. // //调用0地址处的函数
    43. // //1.将0强制类型转换为void(*)() 类型的函数指针
    44. // //2.调用0地址处的这个函数
    45. // ( *( void (*)())0)();
    46. // }
    47. // typedef unsigned int uint;
    48. // typedef int* ptr_t;
    49. // //typedef int(*)[10] parr_t; //err
    50. // typedef int(*parr_t)[10] ; //true
    51. // typedef int (*pf_t)(int,int);
    52. // int main()
    53. // {
    54. // uint u1;
    55. // ptr_t p1;
    56. // int* p2;
    57. // //parr_t[10];
    58. // //pf_t(2,3);
    59. // return 0;
    60. // }
    61. int main()
    62. {
    63. //signal是一个函数声明
    64. //signal函数有2个参数,第一个参数是int,第二个参数类型是void(*)(int) 函数指针类型
    65. //该函数指针指向的函数有一个int类型的参数,返回类型是void
    66. //signal 函数的返回类型也是 void(*)(int) ,该函数指针指向的函数有一个int类型的参数,返回类型是void
    67. typedef void(*pf_t)(int);
    68. pf_t signal(int ,pf_t);
    69. void (* signal(int,void(*)(int)))(int);
    70. return 0;
    71. }

    六、函数指针数组

    整型指针放在一个数组中

    int* arr[5];  //整型指针数组

    char* arr2[5];//字符指针数组

    函数指针数组:数组的每个元素是函数指针类型

    1. //函数指针数组
    2. int Add(int x,int y)
    3. {
    4. return x + y;
    5. }
    6. int Sub(int x,int y)
    7. {
    8. return x - y;
    9. }
    10. int Mul(int x,int y)
    11. {
    12. return x * y;
    13. }
    14. int Div(int x,int y)
    15. {
    16. return x / y;
    17. }
    18. // int main()
    19. // {
    20. // int (*pf1)(int , int) = Add;
    21. // int (*pf2)(int , int) = Sub;
    22. // int (*pf3)(int , int) = Mul;
    23. // int (*pf4)(int , int) = Div;
    24. // //函数指针数组
    25. // int (*pfArr[4])(int , int) = {Add,Sub,Mul,Div};
    26. // return 0;
    27. // }
    28. void menu()
    29. {
    30. printf("**********************************\n");
    31. printf("******* 1.add 2.sub ************\n");
    32. printf("******* 3.mul 4.div ************\n");
    33. printf("******* 0.exit ************\n");
    34. printf("**********************************\n");
    35. }
    36. // int main()
    37. // {
    38. // int input = 0;
    39. // int x = 0;
    40. // int y = 0;
    41. // int ret = 0;
    42. // do
    43. // {
    44. // menu();
    45. // printf("请选择:>");
    46. // scanf("%d",&input);
    47. // switch (input)
    48. // {
    49. // case 1:
    50. // printf("请输入两个操作数:");
    51. // scanf("%d %d",&x,&y);
    52. // ret = Add(x,y);
    53. // printf("ret = %d\n",ret);
    54. // break;
    55. // case 2:
    56. // printf("请输入两个操作数:");
    57. // scanf("%d %d",&x,&y);
    58. // ret = Sub(x,y);
    59. // printf("ret = %d\n",ret);
    60. // break;
    61. // case 3:
    62. // printf("请输入两个操作数:");
    63. // scanf("%d %d",&x,&y);
    64. // ret = Mul(x,y);
    65. // printf("ret = %d\n",ret);
    66. // break;
    67. // case 4:
    68. // printf("请输入两个操作数:");
    69. // scanf("%d %d",&x,&y);
    70. // ret = Div(x,y);
    71. // printf("ret = %d\n",ret);
    72. // break;
    73. // case 0:
    74. // printf("退出计算器\n");
    75. // break;
    76. // default:
    77. // printf("选择错误重新选择");
    78. // break;
    79. // }
    80. // } while(input);
    81. // return 0;
    82. // }
    83. //函数指针的方式
    84. int main()
    85. {
    86. int input = 0;
    87. int x = 0;
    88. int y = 0;
    89. int ret = 0;
    90. //函数指针数组的使用 - 转移表
    91. int (*pfArr[5])(int,int) = {NULL,Add,Sub,Mul,Div};
    92. do
    93. {
    94. menu();
    95. printf("请选择:>");
    96. scanf("%d",&input);
    97. if(input >=1 && input <=4)
    98. {
    99. printf("请输入两个操作数:");
    100. scanf("%d %d",&x,&y);
    101. ret = pfArr[input](x,y);
    102. printf("ret = %d\n",ret);
    103. }
    104. else if(input == 0)
    105. {
    106. printf("退出计算器\n");
    107. }
    108. else
    109. {
    110. printf("选择错误,重新选择\n");
    111. }
    112. } while(input);
    113. return 0;
    114. }

    七、指向函数指针数组的指针

    int(*pf)(int,int);//函数指针

    int(*pfArr[4])(int,int);//函数指针数组

    int(*(*p)[4](int,int) = &pfArr);//函数指针数组的地址

    p就是指向函数指针数组的指针

    1. void test(const char* str)
    2. {
    3. printf("%\n",str);
    4. }
    5. int main()
    6. {
    7. //函数指针pfun
    8. void (*pfun)(const char*) = test;
    9. //函数指针的数组pfunArr
    10. void (*pfunArr[5])(const char* str);
    11. pfunArr[0] = test;
    12. //指向函数指针数组pfunArr的指针ppfunArr
    13. void (*(*ppfunArr)[5])(const char*) = &pfunArr;
    14. return 0;
    15. }

    八、回调函数

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

    1. //回调函数
    2. // int Add(int x,int y)
    3. // {
    4. // return x + y;
    5. // }
    6. // int Sub(int x,int y)
    7. // {
    8. // return x - y;
    9. // }
    10. // int Mul(int x,int y)
    11. // {
    12. // return x * y;
    13. // }
    14. // int Div(int x,int y)
    15. // {
    16. // return x / y;
    17. // }
    18. // void menu()
    19. // {
    20. // printf("**********************************\n");
    21. // printf("******* 1.add 2.sub ************\n");
    22. // printf("******* 3.mul 4.div ************\n");
    23. // printf("******* 0.exit ************\n");
    24. // printf("**********************************\n");
    25. // }
    26. // void Calc(int (*pf)(int x,int y))
    27. // {
    28. // int x = 0;
    29. // int y = 0;
    30. // int ret = 0;
    31. // printf("请输入两个操作数:\n");
    32. // scanf("%d %d",&x,&y);
    33. // ret = pf(x,y);
    34. // printf("ret = %d\n",ret);
    35. // }
    36. // int main()
    37. // {
    38. // int input = 0;
    39. // int x = 0;
    40. // int y = 0;
    41. // int ret = 0;
    42. // do
    43. // {
    44. // menu();
    45. // printf("请选择:>");
    46. // scanf("%d",&input);
    47. // switch (input)
    48. // {
    49. // case 1:
    50. // Calc(Add);
    51. // break;
    52. // case 2:
    53. // Calc(Sub);
    54. // break;
    55. // case 3:
    56. // Calc(Mul);
    57. // break;
    58. // case 4:
    59. // Calc(Div);
    60. // break;
    61. // case 0:
    62. // printf("退出计算器\n");
    63. // break;
    64. // default:
    65. // printf("选择错误重新选择");
    66. // break;
    67. // }
    68. // } while(input);
    69. // return 0;
    70. // }
    71. //冒泡排序         
    72. //有一组整数,需要排序为升序
    73. //1.两两相邻的元素比较
    74. //2.如果不满足顺序就交换
    75. // int main()
    76. // {
    77. // int arr[10] = {9,8,7,6,5,4,3,2,1,0};
    78. // int sz = sizeof(arr)/sizeof(arr[0]);
    79. // int i = 0;
    80. // //int a = qsort(arr[10]);
    81. // //bubble_sort(arr,sz);
    82. // //趟数
    83. // for(i = 0;i < sz-1; i++)
    84. // {
    85. // //一趟比较
    86. // //两两元素比较
    87. // int j = 0;
    88. // for (j = 0;j
    89. // {
    90. // if(arr[j] > arr[j+1])
    91. // {
    92. // int tmp = arr[j];
    93. // arr[j] = arr[j+1];
    94. // arr[j+1] = tmp;
    95. // }
    96. // }
    97. // }
    98. // for (i = 0; i
    99. // {
    100. // printf("%d ",arr[i]);
    101. // }
    102. // return 0;
    103. // }
    104. //qsort 函数的特点
    105. //1.快速排序的方法quick
    106. //2.适用于任何类型数据的排序
    107. // void qsort(void *__base, //指向了需要排序的数组的第一个元素
    108. // size_t __nel, //排序的元素个数
    109. // size_t __width,//一个元素的大小,单位是字节
    110. // int (* _Nonnull __compar)(const void *, const void *));//函数指针类型-这个函数指针指向的函数,能够比较base指向数组中的两个元素
    111. int cmp_int(const void*p1,const void*p2)
    112. {
    113. return (*(int*)p1 - *(int*)p2);
    114. }
    115. void print(int arr[],int sz)
    116. {
    117. int i = 0;
    118. for(i = 0;i
    119. {
    120. printf("%d ",arr[i]);
    121. }
    122. }
    123. //测试qsort排序整型数据
    124. void test1()
    125. {
    126. int arr[10] = {9,8,7,6,5,4,3,2,1};
    127. int sz = sizeof(arr)/sizeof(arr[0]);
    128. //默认是升序的
    129. qsort(arr,sz,sizeof(int),cmp_int);
    130. print(arr,sz);
    131. }
    132. //测试qsort排序结构体数据
    133. struct Stu
    134. {
    135. char name[20];
    136. int age;
    137. };
    138. int cmp_stu_by_age(const void* p1,const void* p2)
    139. {
    140. return ((struct Stu*)p1)->age - ((struct Stu*)p2)->age;
    141. }
    142. int cmp_stu_by_name(const void* p1,const void* p2)
    143. {
    144. return strcmp(((struct Stu*)p1)->name,((struct Stu*)p2)->name);
    145. }
    146. void test2()
    147. {
    148. struct Stu arr[] = { {"张三",18}, {"李四,20"},{"王五,16"}};
    149. int sz = sizeof(arr)/sizeof(arr[0]);
    150. qsort(arr,sz,sizeof(arr[0]),cmp_stu_by_age);
    151. }
    152. void test3()
    153. {
    154. struct Stu arr[] = { {"张三",18}, {"李四,20"},{"王五,16"}};
    155. int sz = sizeof(arr)/sizeof(arr[0]);
    156. qsort(arr,sz,sizeof(arr[0]),cmp_stu_by_name);
    157. }
    158. int main()
    159. {
    160. //test1();
    161. //test2();
    162. test3();
    163. return 0;
    164. }
    165. //void* 指针
    166. //void*类型的指针可以接受任意类型的地址
    167. //这种类型的指针是不能直接解引用操作的
    168. //也不能直接进行指针运算的
    169. // int main()
    170. // {
    171. // int a = 10;
    172. // float f = 3.14f;
    173. // int* pa = &a;
    174. // char* pc = &a;
    175. // void* pv = &a;
    176. // }

    //使用冒泡排序的思想,实现一个功能类似qsort函数 bubble_sort()

    //1.使用冒泡排序的思想

    //2.适用于任意类型数据的排序

    1. //使用冒泡排序的思想,实现一个功能类似qsort函数 bubble_sort()
    2. //1.使用冒泡排序的思想
    3. //2.适用于任意类型数据的排序
    4. void Swap(char* buf1,char* buf2,int size)//交换arr[j],arr[j+1]这两个元素
    5. {
    6. int i = 0;
    7. int tmp = 0;
    8. for(i = 0;i < size;i++)
    9. {
    10. tmp = *buf1;
    11. *buf1 = *buf2;
    12. *buf2 = tmp;
    13. //char*指针一次只能交换一个字节,但是char有四个字节
    14. buf1++;
    15. buf2++;
    16. }
    17. }
    18. void bubble_sort(void* base,int num,int size,int (*cmp)(const void*,const void*))
    19. {
    20. int i = 0;
    21. //趟数
    22. for (i = 0; i < num - i; i++)
    23. {
    24. int j = 0;
    25. //一趟内部比较的对数
    26. for(j = 0; j < num - 1; j++)
    27. {
    28. //假设需要升序,cmp返回>0,交换
    29. if(cmp((char*)base+j*size,(char*)base+(j+1)*size) > 0) //两个元素比较,需要将arr[j],arr[j+1]的地址要传给cmp
    30. {
    31. //交换
    32. Swap((char*)base + j * size,(char*)base + (j+1) * size,size);
    33. }
    34. }
    35. }
    36. }
    37. int cmp_int(const void*p1,const void*p2)
    38. {
    39. return (*(int*)p1 - *(int*)p2);
    40. }
    41. //测试bubble_sort()排序整型数据
    42. void test1()
    43. {
    44. int arr[10] = {9,8,7,6,5,4,3,2,1};
    45. int sz = sizeof(arr)/sizeof(arr[0]);
    46. bubble_sort(arr,sz,sizeof(arr[0]),cmp_int);
    47. }
    48. //测试bubble_sort()排序结构体数据
    49. struct Stu
    50. {
    51. char name[20];
    52. int age;
    53. };
    54. int cmp_stu_by_age(const void* p1,const void* p2)
    55. {
    56. return ((struct Stu*)p1)->age - ((struct Stu*)p2)->age;
    57. }
    58. int cmp_stu_by_name(const void* p1,const void* p2)
    59. {
    60. return strcmp(((struct Stu*)p1)->name,((struct Stu*)p2)->name);
    61. }
    62. void test2()
    63. {
    64. struct Stu arr[] = { {"张三",18}, {"李四,20"},{"王五,16"}};
    65. int sz = sizeof(arr)/sizeof(arr[0]);
    66. bubble_sort(arr,sz,sizeof(arr[0]),cmp_stu_by_age);
    67. }
    68. void test3()
    69. {
    70. struct Stu arr[] = { {"zhangsan",18}, {"lisi,20"},{"wangwu,16"}};
    71. int sz = sizeof(arr)/sizeof(arr[0]);
    72. bubble_sort(arr,sz,sizeof(arr[0]),cmp_stu_by_name);
    73. }
    74. int main()
    75. {
    76. //test1();
    77. //test2();
    78. test3();
    79. return 0;
    80. }

    九、指针和数组面试题的解析

    // 数组名的理解

    // 数组名是数组首元素的地址

    // 但是有2个例外:

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

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

    // int main()

    // {

    // //一维数组

    // int a[] = {1, 2, 3, 4};

    // printf("%d\n", sizeof(a)); // 4*4=16

    // printf("%d\n", sizeof(a + 0)); //数组名a是数组首元素的地址,a+0还是首元素地址,地址的大小4/8

    // printf("%d\n", sizeof(*a));//数组名a是数组首元素的地址,*a就是首元素,int类型大小4个字节

    // printf("%d\n", sizeof(a + 1));//数组名a是数组首元素的地址,a+1是第二个元素的地址,地址的大小是4/8

    // printf("%d\n", sizeof(a[1]));//4

    // printf("%d\n", sizeof(&a));//&a是数组的地址,数组的地址也是地址,是4/8个字节

    // printf("%d\n", sizeof(*&a));// --> sizeof(a) ,*&a,取地址解引用a就是a 16

    // printf("%d\n", sizeof(&a + 1)); //8

    // printf("%d\n", sizeof(&a[0]));// 首元素地址 4/8字节

    // printf("%d\n", sizeof(&a[0] + 1));//&a[0]是首元素的地址,&a[0]+1就是第二个元素的地址,地址就是4/8个字节

    // }

    int main()

    {

    // 字符数组

    char arr[] = {'a', 'b', 'c', 'd', 'e', 'f'};

    // printf("%d\n", sizeof(arr)); //计算的是整个数组的大小,单位是字节,总共6个字节

    // printf("%d\n", sizeof(arr + 0)); //arr表示数组首元素地址,arr+0还是数组首元素地址,地址就是4/8字节

    // printf("%d\n", sizeof(*arr));//arr是首元素地址,*arr是首元素,大小1个字节

    // printf("%d\n", sizeof(arr[1]));//1

    // printf("%d\n", sizeof(&arr));//&arr是数组的地址 地址就是4/8

    // printf("%d\n", sizeof(&arr + 1)); //地址就是4/8

    // printf("%d\n", sizeof(&arr[0] + 1)); //地址是4/8

    printf("%d\n", strlen(arr)); //因为字符数组arr中没有\0,所以求字符串长度的时候,会一直往后找,产生的结构就是随机值

    printf("%d\n", strlen(arr + 0));// arr+0 是首元素的地址,和第一个一样,也是随机值

    //printf("%d\n", strlen(*arr));//arr是数组首元素的地址,*arr就是数组首元素 就是'a'-97

    //strlen函数参数的部分需要传一个地址,当我们传递的是'a'时,‘a’的ASCII码值就是97,那就是将97作为地址传参

    //strlen就会从97这个地址开始统计字符串长度,这就非法访问内存了

    //printf("%d\n", strlen(arr[1])); //err

    printf("%d\n", strlen(&arr));//&arr是数组的地址,数组的地址和数组首元素的地址,值是一样的,传递给strlen函数后,依然是从数组的第一个元素的位置,还是往后统计

    printf("%d\n", strlen(&arr + 1));//跳过这个数组 ,随机值

    printf("%d\n", strlen(&arr[0] + 1));//&arr[0] + 1是第二个元素的地址,随机值-1

    }

    1. #include
    2. #include
    3. // 数组名的理解
    4. // 数组名是数组首元素的地址
    5. // 但是有2个例外:
    6. // 1.sizeof(数组名),这里的数组名表示整个数组,计算的是整个数组的大小,单位是字节
    7. // 2.&数组名,这里的数组名表示整个数组,取出的是整个数组的地址
    8. // int main()
    9. // {
    10. // //一维数组
    11. // int a[] = {1, 2, 3, 4};
    12. // printf("%d\n", sizeof(a)); // 4*4=16
    13. // printf("%d\n", sizeof(a + 0)); //数组名a是数组首元素的地址,a+0还是首元素地址,地址的大小4/8
    14. // printf("%d\n", sizeof(*a));//数组名a是数组首元素的地址,*a就是首元素,int类型大小4个字节
    15. // printf("%d\n", sizeof(a + 1));//数组名a是数组首元素的地址,a+1是第二个元素的地址,地址的大小是4/8
    16. // printf("%d\n", sizeof(a[1]));//4
    17. // printf("%d\n", sizeof(&a));//&a是数组的地址,数组的地址也是地址,是4/8个字节
    18. // printf("%d\n", sizeof(*&a));// --> sizeof(a) ,*&a,取地址解引用a就是a 16
    19. // printf("%d\n", sizeof(&a + 1)); //8
    20. // printf("%d\n", sizeof(&a[0]));// 首元素地址 4/8字节
    21. // printf("%d\n", sizeof(&a[0] + 1));//&a[0]是首元素的地址,&a[0]+1就是第二个元素的地址,地址就是4/8个字节
    22. // }
    23. // int main()
    24. // {
    25. // // 字符数组
    26. // char arr[] = {'a', 'b', 'c', 'd', 'e', 'f'};
    27. // // printf("%d\n", sizeof(arr)); //计算的是整个数组的大小,单位是字节,总共6个字节
    28. // // printf("%d\n", sizeof(arr + 0)); //arr表示数组首元素地址,arr+0还是数组首元素地址,地址就是4/8字节
    29. // // printf("%d\n", sizeof(*arr));//arr是首元素地址,*arr是首元素,大小1个字节
    30. // // printf("%d\n", sizeof(arr[1]));//1
    31. // // printf("%d\n", sizeof(&arr));//&arr是数组的地址 地址就是4/8
    32. // // printf("%d\n", sizeof(&arr + 1)); //地址就是4/8
    33. // // printf("%d\n", sizeof(&arr[0] + 1)); //地址是4/8
    34. // printf("%d\n", strlen(arr)); //因为字符数组arr中没有\0,所以求字符串长度的时候,会一直往后找,产生的结构就是随机值
    35. // printf("%d\n", strlen(arr + 0));// arr+0 是首元素的地址,和第一个一样,也是随机值
    36. // //printf("%d\n", strlen(*arr));//arr是数组首元素的地址,*arr就是数组首元素 就是'a'-97
    37. // //strlen函数参数的部分需要传一个地址,当我们传递的是'a'时,‘a’的ASCII码值就是97,那就是将97作为地址传参
    38. // //strlen就会从97这个地址开始统计字符串长度,这就非法访问内存了
    39. // //printf("%d\n", strlen(arr[1])); //err
    40. // printf("%d\n", strlen(&arr));//&arr是数组的地址,数组的地址和数组首元素的地址,值是一样的,传递给strlen函数后,依然是从数组的第一个元素的位置,还是往后统计
    41. // printf("%d\n", strlen(&arr + 1));//跳过这个数组 ,随机值
    42. // printf("%d\n", strlen(&arr[0] + 1));//&arr[0] + 1是第二个元素的地址,随机值-1
    43. // return 0;
    44. // }
    45. // int main()
    46. // {
    47. // char arr[] = "abcdef"; //[a b c d e f \0]
    48. // // printf("%d\n", sizeof(arr)); //7
    49. // // printf("%d\n", sizeof(arr + 0)); //arr+0是首元素地址 4/8
    50. // // printf("%d\n", sizeof(*arr)); // *arr首元素 char类型是1
    51. // // printf("%d\n", sizeof(arr[1]));//下标1的元素b 1
    52. // // printf("%d\n", sizeof(&arr)); //地址就是4/8
    53. // // printf("%d\n", sizeof(&arr + 1));//地址 4/8
    54. // // printf("%d\n", sizeof(&arr[0] + 1));//地址4/8
    55. // printf("%d\n", strlen(arr));//6
    56. // printf("%d\n", strlen(arr + 0)); //6
    57. // //printf("%d\n", strlen(*arr)); //err
    58. // //printf("%d\n", strlen(arr[1])); //err
    59. // printf("%d\n", strlen(&arr)); //6
    60. // printf("%d\n", strlen(&arr + 1));//随机值
    61. // printf("%d\n", strlen(&arr[0] + 1));//5
    62. // return 0;
    63. // }
    64. // int main()
    65. // {
    66. // char *p = "abcdef"; //[a b c d e f \0]
    67. // // printf("%d\n", sizeof(p)); //p是一个指针变量,大小就要4/8个字节
    68. // // printf("%d\n", sizeof(p + 1)); //p+1是b的地址,是地址大小就是4/8个字节
    69. // // printf("%d\n", sizeof(*p)); //*p首元素,字符a 1个字节
    70. // // printf("%d\n", sizeof(p[0]));//p[0] = *(p+0) = *p 1
    71. // // printf("%d\n", sizeof(&p)); // 4/8 p = char* &p = char**
    72. // // printf("%d\n", sizeof(&p + 1));// 4/8
    73. // // printf("%d\n", sizeof(&p[0] + 1));// 4/8
    74. // printf("%d\n", strlen(p)); //6
    75. // printf("%d\n", strlen(p + 1));//5
    76. // //printf("%d\n", strlen(*p));// err *p = a
    77. // //printf("%d\n", strlen(p[0]));// p[0] = *(p+0) = *p err
    78. // printf("%d\n", strlen(&p));//随机值
    79. // printf("%d\n", strlen(&p + 1));//随机值
    80. // printf("%d\n", strlen(&p[0] + 1));//&p[0]+1 是b 5
    81. //return 0;
    82. // }
    83. int main()
    84. {
    85. // 二维数组
    86. int a[3][4] = {0};
    87. printf("%d\n", sizeof(a)); //3*4*4
    88. printf("%d\n", sizeof(a[0][0]));//4
    89. printf("%d\n", sizeof(a[0])); //4*4
    90. printf("%d\n", sizeof(a[0] + 1)); // 4/8
    91. //a[0]作为第一行的数组名,没有单独放在sizeof内部,没有&
    92. //a[0]表示数组首元素的地址,也就是a[0][0]的地址
    93. //a[0]+1是第一行第二个元素的地址
    94. //地址就是4/8
    95. printf("%d\n", sizeof(*(a[0] + 1)));//计算的就是第一行第2个元素的大小 4个字节
    96. printf("%d\n", sizeof(a + 1)); // 4/8
    97. //a是数组首元素的地址,是第一行的地址
    98. //a+1就是第二行的地址
    99. printf("%d\n", sizeof(*(a + 1))); //4*4
    100. //*(a+1)--> a[1] -> sizeof(a[1]),计算第二行的大小
    101. //a+1就是第二行的地址,*(a+1)计算第二行的大小
    102. printf("%d\n", sizeof(&a[0] + 1));// 4/8
    103. //&a[0]第一行的地址
    104. //&[0]+1第二行的地址
    105. printf("%d\n", sizeof(*(&a[0] + 1))); //第二行的大小 4*4
    106. printf("%d\n", sizeof(*a)); //4*4
    107. //a是数组首元素地址,就是第一行地址
    108. //*a就是第一行
    109. //*a --> *(a+0) --> a[0]
    110. printf("%d\n", sizeof(a[3])); //4*4
    111. //a[3] --> int[4]
    112. return 0;
    113. }

    //总结:数组名的意义

    //1sizeof(数组名),这里的数组名表示整个数组,计算的是整个数组的大小

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

    //3.除此之外所有的数组名都表示首元素的地址

    1. // 笔试题1
    2. // int main()
    3. // {
    4. // int a[5] = { 1, 2, 3, 4, 5 };
    5. // int *ptr = (int *)(&a + 1);
    6. // printf( "%d,%d", *(a + 1), *(ptr - 1)); //2 5
    7. // return 0;
    8. // }
    9. // 笔试题2
    10. // 由于还没学习结构体,这里告知结构体的大小是20个字节
    11. // struct Test
    12. // {
    13. // int Num;
    14. // char *pcName;
    15. // short sDate;
    16. // char cha[2];
    17. // short sBa[4];
    18. // } *p = (struct Test*)0x100000;
    19. // // 假设p 的值为0x100000。 如下表表达式的值分别为多少?
    20. // int main()
    21. // {
    22. // printf("%p\n", p + 0x1); //0x100020 +1跳过一个结构体就是20个字节
    23. // printf("%p\n", (unsigned long)p + 0x1);//0x100001
    24. // printf("%p\n", (unsigned int *)p + 0x1); //0x100004
    25. // return 0;
    26. // }
    27. //笔试题3
    28. // int main()
    29. // {
    30. // int a[4] = {1, 2, 3, 4};
    31. // int *ptr1 = (int *)(&a + 1);
    32. // int *ptr2 = (int *)((int)a + 1);
    33. // printf("%x\n,%x\n", ptr1[-1], *ptr2);
    34. // return 0;
    35. // }
    36. //笔试题4
    37. // #include
    38. // int main()
    39. // {
    40. // int a[3][2] = { (0, 1), (2, 3), (4, 5) };
    41. // int *p;
    42. // p = a[0]; //a[0] --> &a[0][0]
    43. // printf( "%d", p[0]);//p[0] --> *(p+0) -->*p 1
    44. // return 0;
    45. // }
    46. //笔试题5
    47. // int main()
    48. // {
    49. // int a[5][5];
    50. // int(*p)[4];
    51. // //p --> int(*)4 a -->int(*)5
    52. // p = a;
    53. // printf( "%p,%d\n",
    54. // &p[4][2] - &a[4][2],
    55. // &p[4][2] - &a[4][2]);
    56. // return 0;
    57. // }
    58. //笔试题6
    59. // int main()
    60. // {
    61. // int aa[2][5] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
    62. // int *ptr1 = (int *)(&aa + 1);
    63. // int *ptr2 = (int *)(*(aa + 1));
    64. // printf( "%d,%d", *(ptr1 - 1), *(ptr2 - 1));//10 5
    65. // return 0;
    66. // }
    67. //笔试题7
    68. // int main()
    69. // {
    70. // char *a[] = {"work","at","alibaba"};//char* work char* at char* alibaba
    71. // char**pa = a;
    72. // pa++; //0 work 1 at 2 alibaba
    73. // printf("%s\n", *pa); //at
    74. // return 0;
    75. // }
    76. //笔试题8
    77. int main()
    78. {
    79. char *c[] = {"ENTER","NEW","POINT","FIRST"};
    80. char**cp[] = {c+3,c+2,c+1,c};
    81. char***cpp = cp;
    82. printf("%s\n", **++cpp);
    83. printf("%s\n", *--*++cpp+3);
    84. printf("%s\n", *cpp[-2]+3);
    85. printf("%s\n", cpp[-1][-1]+1);
    86. return 0;
    87. }

    笔试题3

    笔试题5

     

     笔试题6

    总结:本章主要对指针在初阶的基础上更深一步的进行了研究,对指针地址和解引用都有详细的讲解,如需初阶可以观看C语言基础-指针_小凡喜编程的博客-CSDN博客

  • 相关阅读:
    计算机网络复习——第四章网络层
    【Azure Redis 缓存】 Python连接Azure Redis, 使用redis.ConnectionPool 出现 "ConnectionResetError: [Errno 104] Connection reset by peer"
    JavaWeb——IDEA操作:Project最终新建module
    【图像处理与机器视觉】灰度变化与空间滤波
    spring boot 整合 itextpdf 导出 PDF,写入大文本,写入HTML代码,分析当下导出PDF的几个工具
    SpringMVC入门宝典(六)SpringMVC文件上传(上)
    爱上开源之DockerUI-xterm.js实现Web控制台
    有限域的Fast Multiplication和Modular Reduction算法实现
    HTTPS双向认证和如何生成证书
    c#中单例模式详解
  • 原文地址:https://blog.csdn.net/qq_61658398/article/details/133294546