指针进阶

主要内容:

1. 字符指针

2. 数组指针
3. 指针数组
4. 数组传参和指针传参
5. 函数指针
6. 函数指针数组
7. 指向函数指针数组的指针
8. 回调函数
9. 指针和数组面试题的解析
指针初阶回顾:
1.指针就是个变量,用来存放地址,地址唯一标识一块内存空间。
2.指针的大小是固定的4/8个字节(32位平台/64位平台)。
3.指针是有类型的,指针的类型决定了指针的+-整数的步长,指针解引用操作的时候的权限。
4.指针的运算。

1.字符指针

指针类型为字符指针char*;

一般使用:

int main()
{
char ch='w';
char *pc=&ch;
*pc='w';//给指针pc解引用
return 0;
}

还有一种使用方式如下;

int main()
{
const char* pstr="hello";//这里不是把hello整个字符串放到pstr指针变量里,而是把字符串的首字符的地址放到pstr中。
printf("%s\n",pstr);//拿到首字符的地址,就可以打印整个字符,直到遇到\0,打印停止。
return 0;
}

 例题分析:

#include <stdio.h>
int main()
{
    char str1[] = "hello bit.";
    char str2[] = "hello bit.";
    const char *str3 = "hello bit.";
    const char *str4 = "hello bit.";
    if(str1 ==str2)
 printf("str1 and str2 are same\n");
    else
 printf("str1 and str2 are not same\n");
       
    if(str3 ==str4)
 printf("str3 and str4 are same\n");
    else
 printf("str3 and str4 are not same\n");
       
    return 0;
}

打印结果:str1 and str2 are not same

                  str3 and str4 are same

因为str1与str2是两个数组的数组名,数组名表示地址,所以两个数组地址不一样,即str1与str2不一样;str3与str4是两个字符串指针,且str3与str4指向的是同一个常量字符串。c/c++会把常量字符串存储到单独的一个内存区域,当几个指针指向同一个字符串的时候,他们实际指向同一块内存。但是用相同的常量字符串去初始化不同的数组的时候会开辟出不同的内存块。所以,1与2不同,3与4相同。

2.指针数组

int* arr1 [ 10 ]; // 整形指针的数组
char * arr2 [ 4 ]; // 一级字符指针的数组
char ** arr3 [ 5 ]; // 二级字符指针的数组

3.数组指针

3.1 数组指针的定义:指向一个数组的指针

int * p1 [ 10 ];
int ( * p2 )[ 10 ];
//p1, p2 分别是什么?
p1是一个整形指针数组,数组里面存放了十个整形指针。
p2是一个数组指针,指向的是一个大小为10个整形的数组
// 这里要注意: [] 的优先级要高于 * 号的,所以必须加上()来保证 p 先和 * 结合。

3.2 &数组名VS数组名

&arr和arr的值是相同的,但是意义是不一样的。&arr表示数组地址,而不是数组数组首元素的地址。int arr[10] = { } ,&arr的类型是:int(*)[10],是一个数组指针类型,给数组的地址加一,就会跳过整个数组。数组名表示首元素的地址。

3.3 数组指针的使用

数组指针指向的是数组,那数组指针中存放的就应该是数组的地址。

补充-----eg:arr[3][5]    取地址数组名表示整个数组的地址。数组名arr,表示首元素地址;但是二维数组的首元素是二维数组的第一行,所以,调用函数的时候传参时,传二维数组的数组名,其实相当于第一行的地址,是一维数组的地址,可以用数组指针来接收。

所以,在接收的时候可以有两种方式 

1.void print_arr1(int arr[3][5], int row, int col)

2.void print_arr2 ( int ( * arr )[ 5 ], int row , int col )

补2:对于整形数组的打印不能一次性全部打印,因为没有结束标志,只有字符串数组才能得到首元素地址然后才能一次性全部打印。

补3:如果有int arrr[10], 将 int(*p)[10]=arr进行强制类型转换

int arrr[10] arrr的类型是int*; int(*p)[10]的p的类型是int(*)[10],所以强制类型转化的结果为int(*p)[10]=(int(*)[10])arr。 强转要放在括号里。

4.数组参数、指针参数

4.1一维数组传参

在写代码的时候难免要把【数组】或者【指针】传给函数,那函数的参数该如何设计呢?
#include <stdio.h>
void  test ( int   arr []) //可以,只说明类型,不说明大小也可以
{}
void  test ( int  arr [ 10 ]) //可以,大小类型均已说明。前两种是数组传参数组接收
{}
void  test ( int* arr ) //可以,传入的参数是地址可用指针接收。第三种是数组传参指针接收。
{}
void  test2 ( int* arr [ 20 ]) //可以,类型是int*[],可不写大小。
{}
void  test2 ( int** arr ) //可以,传入指针的地址可以用二级指针接收
{}
int  main ()
      int   arr [ 10 ] = { 0 };

     int*arr2[20] = {0};  //指针数组,数组的每个元素的类型都是int*

     test ( arr );
     test2 ( arr2 );     //传入数组名表示首元素地址,所以传入的是指针的地址
}

4.2二维数组传参

void test ( int arr [ 3 ][ 5 ]) //可以
{}
void test ( int arr [][]) //不可以,行可以省但列不能省
{}
void test ( int arr [][ 5 ]) //可以     前三个都是二维数组传参数组接收
{}
// 总结:二维数组传参,函数形参的设计只能省略第一个 [] 的数字。
// 因为对一个二维数组,可以不知道有多少行,但是必须知道一行多少元素。
// 这样才方便运算。
void test ( int * arr ) //不可以 传入的是第一行的地址 不能用整形指针来接收,这种一般都是接收一维数组的以一个元素的地址
{}
void test ( int* arr [ 5 ]) //不可以   指针数组
{}
void test ( int ( * arr )[ 5 ]) //可以    数组指针
{}
void test ( int ** arr ) //不可以   二级指针是用来接收一级指针的地址的 但是此刻传入的是数组的地址所以 不可以 {}
int main ()
{
int arr [ 3 ][ 5 ] = { 0 };
test  ( arr ) ;  //数组名是首元素的地址,是二维数组第一行的地址
}
补:
int*arr[3][3]={1};
int p=**arr;
printf("%d",p);
//arr是二维数组的数组名,表示首元素地址,即第一行的地址,所以,*arr表示第一行为arr[0],arr[0]为第一行数组的数组名,表示首元素的地址,即第一个元素的地址,所以**arr为arr[0][0]为1.

4.3一级指针传参

指针传参指针接收

当一个函数的参数部分为一级指针的时候,函数能接收什么参数?
void test1 ( int * p )
{}
int a=10;
int *p=&a;
int arr[10];
test(arr);//地址
test(&a);//地址
test(p);//指针

4.4二级指针传参

二级指针传参二级指针接收

当一个函数的参数部分为一级指针的时候,函数能接收什么参数?
void test (int  ** p)
{}
int**ptr;
int*pp;
int*arr[10];
test(ptr);//二级指针
test(&pp);//一级指针的地址
test(arr);//指针数组的首元素的地址

5.函数指针

int add(int x,int y)
{
return x+y;
}
int main()
{
int(*pf)(int,int)=&add;
int ret=(*pf)(2,3);//函数调用
//pf是一个存放函数地址的指针变量--函数指针
//&函数名和函数名都是函数的地址

eg:

//0被当作函数的地址,再进行接应用,对函数进行调用。

  void (*   signal(int , void(*)(int) )    )  (int);-------其实可以写为void(*)(int) singal(int,void(*)(int))//该代码是一次函数的声明。声明的函数名字叫singal,此函数有两个参数,第一个是int类型,第二个是函数指针类型,该函数指针能够指向的那个函数的参数是int返回类型是void。singal函数的返回类型是一个函数指针,该函数指针能够指向的那个函数的参数是int,返回类型是void.

6.函数指针数组

数组是一个存放相同类型数据的存储空间,那我们已经学习了指针数组
int*arr[10];//数组每个元素都是int*
那么同理,函数指针数组就是把函数的地址存到一个数组中
int    ( * parr1 [ 10 ])  ();//parr1先与[]结合,说明parr1是数组,数组中每个元素的类型是int(*)()的函数指针。
函数指针的用途:转移表
eg:计算器
#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语句的优化,避免冗长。

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

指针指向一个 数组 ,数组的元素都是 函数指针
int main()
{
int arr[10];
int (*pa)[10]=&arr;//数组指针
int(*pf)(int,int);//函数指针
int (*pf[5])(int,int);//函数指针数组
int(*(*ppf)[5](int,int)=&pf;//指向函数指针数组的指针

8.回调函数(补充void*用法)

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

例如:使用qsort函数

void qsort(void base,//数组地址
           size_t num,//元素个数
           size_t width,//一个元素占的字节数
           int (*cmp)(const void* elem1,//需要比较的第一个元素的地址
                      const void*elme2)//第二个元素的地址
               );
//进行比较
int cmp_int(const void*e1,const void*e2)//参数和返回类型与规定的一致
{
//由于void*不能直接用,所以要先进行类型转换
return (*(int*)e1-*(int*)e2);
//返回值是 e1<e2 小于0 相等返回等于0 大于返回大于0
int main()
{
int arr[]={9,8,7,6,5,4,3,2,1,0};
int sz=sizeof(arr)/sizeof(arr[0]);
qsort(arr,sz,sizeof(arrr[0]),cmp_int);
int i=0;
for(i=0,i<sz,i++)
{
printf("%d",arr[i]);
}
return 0;
}

//补充:
//因为不知道会用qsort函数去比较什么类型的数据,void* 是无具体类型的指针,


int a=10;
float f=5.5f;
int* p=&a;//可以
//p=&f;//类型不兼容,p是int,f是float
void* pp=&f;//可以
pp=&a;//可以,任意谁的地址都可以放到pp中,是一个通用指针,无具体类型
void* 的指针是没办法直接解引用的,所以,在用的时候需要先进行类型转换
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值