C语言指针进阶(最后模拟实现快速排序)

指针的概念:
1. 指针就是个变量,用来存放地址,地址唯一标识一块内存空间。
2. 指针的大小是固定的 4/8 个字节( 32 位平台 /64 位平台)。
3. 指针是有类型,指针的类型决定了指针的 +- 整数的步长,指针解引用操作的时候的权限。
4. 指针的运算。

字符指针

在指针的类型中我们知道有一种指针类型为字符指针 char* ;
一般使用方法:
还有一种使用方法:
这段代码的意思是:定义一个指针pstr,然后将字符串的首元素h的放进去。同理我们可以联想到,当我们定义一个数组的时候,数组名则是数组首元素的地址。
接下来我们来思考下面的题目:
该代码的输出如下:
这里 str3 str4 指向的是一个同一个常量字符串。 C/C++ 会把常量字符串存储到单独的一个内存区域,
当几个指针。指向同一个字符串的时候,他们实际会指向同一块内存。但是用相同的常量字符串去初始
化不同的数组的时候就会开辟出不同的内存块。所以 str1 str2 不同, str3 str4 不同。

指针数组

int* arr1 [ 10 ]; // 整形指针的数组
char * arr2 [ 4 ]; // 一级字符指针的数组
char ** arr3 [ 5 ]; // 二级字符指针的数组
指针数组的意思就是存放指针的数组,该数组中的每个元素都是指针
我们来实现一个案例,使用指针数组来模拟实现二维数组

数组指针

数组指针顾名思义就是指向数组的指针

int ( * p )[ 10 ];
// 解释: p 先和 * 结合,说明 p 是一个指针变量,然后指着指向的是一个大小为 10 个整型的数组。所以 p 是一个 指针,指向一个数组,叫数组指针。
// 这里要注意: [] 的优先级要高于 * 号的,所以必须加上()来保证 p 先和 * 结合
当我们定义一个数组的时候是这样的:int  arr[10]={0};
我们都知道arr是指向数组首元素的地址,那么&arr表示的就是整个数组的地址,而不是首元素的地址,数组的地址+1跳过的就是整个数组的大小。
我们一般这样来定义数组指针。
我们接下来用一个案例来使用数组指针。
我们可以把该二维数组来看作是有着三个一维数组元素的数组,我们都知道数组名是指向数组首元素的地址,所以二维数组的数组名即是指向二维数组中的第一个一维数组的指针,因此我们在传参的时候定义一个数组指针  int  (* arr) [5]  来接收这个指针。
在函数中 arr[i][j] 的意思是 *(*(arr+i) + j)  。  arr+i 就是将指针指向该数组的第 i  行,*(arr + i) 就是将指针指向第 i 行的第一个元素,*(arr + i)+ j  就是将指针指向第 i 行的第 j 个元素, *(*(arr+i) + j) 就是第 i 行的第 j 个元素的内容。
int arr [ 5 ];   // int 类型的数组
int * parr1 [ 10 ]; // int * 类型的数组
int ( * parr2 )[ 10 ]; // 指向数组的指针
int ( * parr3 [ 10 ])[ 5 ];  // 存放数组指针的数组
一维数组的传参方式
二维数组的传参方式

函数指针

我们来看一段代码

输出为:

因此我们可以得知函数的名字就是指向该函数的指针,这两个地址是 test 函数的地址。 那我们的函数的地址要想保存起来,怎么保存?

下面我们看代码:
我们可以用void (* p) (int ,int) 这个指针来接收函数的地址 ,因此void (* p) (int ,int) 就是函数指针
接下来我们来介绍两个有趣的代码
// 代码 1
( * ( void ( * )()) 0 )();
// 代码 2
void ( * signal ( int , void ( * )( int )))( int );
代码 1 :我们先将0强制转换成void (*)()函数指针类型 ,然后再使用(* 0)()这个函数指针
代码 2:这个代码有些麻烦,我们将其简化,把void(*)(int)这个函数指针类型换个名字
typedef void ( * pfun_t )( int ); 这时 我们就把void(*)(int )该类型的名字换成了 pfun_t
最后将代码 2 简化成 pfun_t signal ( int , pfun_t );

函数指针数组

int ( * parr1 [ 10 ]) ()  函数指针数组顾名思义就是存放 int (*)()函数指针的数组
函数指针数组的用途: 转移表
例子(计算器)
#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 ()
{
int x , y ;
int input = 1 ;
    int ret = 0 ;
    do
  {
        printf ( "*************************\n" );
        printf ( " 1:add           2:sub \n" );
        printf ( " 3:mul           4:div \n" );
        printf ( "*************************\n" );
        printf ( " 请选择: " );
        scanf ( "%d" , & input );
        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" );
breark ;
        default :
              printf ( " 选择错误 \n" );
              break ;
      }
} while ( input );
   
    return 0 ;
}
这是我们用常见的方法来实现计算器,当我们看到这个代码的时候,是不是有一种感觉,就是显得特别冗余,这时我们就引进函数指针数组来解决
#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 ()
{
    int x , y ;
    int input = 1 ;
    int ret = 0 ;
    int ( * p [ 5 ])( int x , int y ) = { 0 , add , sub , mul , div }; // 转移表
    while ( input )
    {
          printf ( "*************************\n" );
          printf ( " 1:add           2:sub \n" );
          printf ( " 3:mul           4:div \n" );
          printf ( "*************************\n" );
          printf ( " 请选择: " );
      scanf ( "%d" , & input );
          if (( input <= 4 && input >= 1 ))
        {
          printf ( " 输入操作数: " );
              scanf ( "%d %d" , & x , & y );
              ret = ( * p [ input ])( x , y );
        }
          else
              printf ( " 输入有误 \n" );
          printf ( "ret = %d\n" , ret );
    }
      return 0 ;
}
这时我们就解决了一个switch 语句中很多case语句,让代码变得简洁些了

指向函数指针数组的指针

指向函数指针数组的指针是一个 指针 指针指向一个 数组 ,数组的元素都是 函数指针 ;
void test ( const char* str )
{
printf ( "%s\n" , str );
}
int main ()
{
// 函数指针 pfun
void ( * pfun )( const char* ) = test ;
// 函数指针的数组pfunArr
void ( * pfunArr [ 5 ])( const char* str );
pfunArr [ 0 ] = test ;
// 指向函数指针数组 pfunArr 的指针 ppfunArr
void ( * ( * ppfunArr )[ 10 ])( const char* ) = & pfunArr ;
return 0 ;
}

回调函数

回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一
个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。回调函数不是由该
函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或
条件进行响应。
首先演示一下 qsort 函数的使用:
int int_cmp ( const void * p1 , const void * p2 )
{
  return ( * ( int * ) p1 - * ( int * ) p2 );
}
int main ()
{
    int arr [] = { 1 , 3 , 5 , 7 , 9 , 2 , 4 , 6 , 8 , 0 };
    int i = 0 ;
   
    qsort ( arr , sizeof ( arr ) / sizeof ( arr [ 0 ]), sizeof ( int ), int_cmp );
    for ( i = 0 ; i < sizeof ( arr ) / sizeof ( arr [ 0 ]); i ++ )
  {
      printf ( "%d " , arr [ i ]);
  }
    printf ( "\n" );
    return 0 ;
}
该函数能够将任意类型的数组进行排序。该函数我们需要引进四个参数 (数组名,数组元素的个数,数组每个元素所占字节大小,判断前后两个元素大小的函数)。在这个函数中我们要引进一个判断是否交换顺序的函数,如果这个函数的返回值大于0则交换,小于或等于0则不交换。
我们使用回调函数的方法模拟实现qsort 函数(使用冒泡排序)
#include <stdio.h>
int int_cmp ( const void * p1 , const void * p2 )
{
  return ( * ( int * ) p1 - * ( int * ) p2 );
}
void _swap ( void * p1 , void * p2 , int size )
{
    int i = 0 ;
    for ( i = 0 ; i < size ; i ++ )
  {
        char tmp = * (( char * ) p1 + i );
* (( char * ) p1 + i ) = * (( char * ) p2 + i );
      * (( char * ) p2 + i ) = tmp ;
  }
}
void bubble ( void * base , int count , int size , int ( * cmp )( void * , void * ))
{
    int i = 0 ;
    int j = 0 ;
    for ( i = 0 ; i < count - 1 ; i ++ )
  {
      for ( j = 0 ; j < count - i - 1 ; j ++ )
      {
            if ( cmp (( char * ) base + j * size , ( char * ) base + ( j + 1 ) * size ) > 0 )
          {
              _swap (( char * ) base + j * size , ( char * ) base + ( j + 1 ) * size , size );
          }
      }
  }
}
int main ()
{
    int arr [] = { 1 , 3 , 5 , 7 , 9 , 2 , 4 , 6 , 8 , 0 };
    //char *arr[] = {"aaaa","dddd","cccc","bbbb"};
    int i = 0 ;
    bubble ( arr , sizeof ( arr ) / sizeof ( arr [ 0 ]), sizeof ( int ), int_cmp );
    for ( i = 0 ; i < sizeof ( arr ) / sizeof ( arr [ 0 ]); i ++ )
  {
      printf ( "%d " , arr [ i ]);
  }
    printf ( "\n" );
    return 0 ;
}
这里面我们引进了void * 指针,该指针可以接收任意类型的指针,我们在使用的时候再将他强制转换成我们想用的类型
  • 41
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值