指针中容易混淆的概念以及常见笔试题

C语言指针

1.字符串指针

注:注意代码中的注释

  • : void* 类型的指针可以接收任意类型的指针
    - void 类型的指针不能进行解引用的操作,也不能进行加减整数的操作*
int main()
{
    char arr1[]="abcdef";
    char arr2[]="abcdef";
    char* p1="abcdef"; //常量字符串 不能修改
    char* p2="abcdef";
    if(arr1==arr2)  //arr相当于数组首地址 所以不相等
    {
        printf("相等\n");
    }else
    {
        printf("不相等\n");
    }
    
     if(p1==p2)  //指针里面放的常量字符串 ,所以相等
    {
        printf("相等\n");
    }else
    {
        printf("不相等\n");
    }
}

2.指针数组

指针数组是数组,存放指针的数组

int main()
{
    int arr1[]={1,2,3,4,5};
    int arr2[]={2,3,4,5,6};
    int arr3[]={3,4,5,6,7};
    int* parr[]={arr1,arr2,arr3};
    int i=0,j=0;
    for(i=0;i<3;i++)
    {
        for(j=0;j<5;j++)
        {
            printf("%d ",*(parr[i]+j))
        }
        printf("\n");
    }
    return 0;
}
打印结果为:
1 2 3 4 5
2 3 4 5 6
3 4 5 6 7    

3.数组指针

数组指针—指向数组的指针—存放数组的地址

int* p1[10]; 指针数组

int (*p2)[10] 数组指针
在这里插入图片描述
arr表示的是数组首元素的地址

&arr表示的是数组的地址,而不是数组首元素的地址

int mian()
{
    char* arr[5];
    char* (*p)[5]=&arr;  //&arr取出的是一个数组的地址
      
    return 0;
}
int main()
{
    int arr[5]={1,2,3,4,5};
    int (*p)[5]=&arr;
    int i=0;
   // for(i=0;i<5;i++)
    //{
      //  printf("%d ",*(*p+i)); //*p=arr 
   // }
    //或者下面这种写法
    for(i=0;i<5;i++)
    {
        printf("%d ",(*p)[i]); //打印出1,2,3,4,5
    }
    return 0;
}
#include <stdio.h>
void print1(int arr[3][5],int x,int y)
{
    int i,j;
    for(i=0;i<x;i++)
    {
        for(j=0;j<y;j++)
        {
            printf("%d ",arr[i][j]);
        }
        printf("\n");
    }
    
}

void print2(int (*p)[5],int x ,int y)
{
    int i,j;
    for(i=0;i<x;i++)
    {
        for(j=0;j<y;j++)
        {
            printf("%d ",*(*(p+i)+j));  //*(p+i)代表每行首元素的地址
        }
        printf("\n");
    }
}
int main()
{
    int arr[3][5]={{1,2,3,4,5}, {2,3,4,5,6}, {9,3,4,5,6}};
    print1(arr,3,5);
    print2(arr,3,5); //传的是一维数组的地址
    //数组名arr代表首原素的地址
    //但是二维数组的首元素是二维数组的第一行,所以这里传递arr,其实相当于第一行的地址,是一维数组的地址,可以用数组指针来接收
    
    return 0  
}

下面看看几种常见的写法:

int arr[5]; //arr是一个5个元素的整形数组
int* parr1[10]; //parr是一个数组,数组有10个元素,每个元素的类型是int*,parr1是指针数组
int (*parr2)[10]; //parr2是一个数组,数组有10个元素,每个元素的类型是int, parr2是数组指针
int (*parr3[10])[5]; //parr3是一个数组,该数组有10个元素,每个元素是一个数组指针,该数组指针指向的数组有5个元素,每个元素是int.

4.数组参数,指针参数

二维数组传参可以省略行,但不能省略列.

二级指针传参可以传哪些参数: 如下代码中的三种都行

void test(char **p)
{
 
}
int main()
{
 char c = 'b';
 char*pc = &c;
 char**ppc = &pc;
 char* arr[10];
 test(&pc);
 test(ppc);
 test(arr);//Ok?
 return 0;
}

5.函数指针 - 指向函数的指针

函数指针 - 指向函数的指针----存放函数地址的指针

int Add(int x ,int y)
{
    int z = x+y;
    return z;
    
}
int main()
{
    int a = 10 ;
    int b = 20 ;
    //&函数名和函数名都是函数的地址
    int (*p)(int, int)=Add;// 把add的地址传给了p
    printf("%d\n", (*p)(2,3));//5
    printf("%d\n",p(2,3)); //5
    //*可以加也可以不加
    return 0;
}
void (* signal(int ,void(*)(int)))(int) (1)
   
typedef void(*pfun)(int);  (2)
pfun signal(int , pfun)    (3)
    (2)(3)等效为(1)

6.函数指针数组

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 i = 0;
    //下面这个函数指针数组可以存放4个函数的地址
    int (*parr[4])(int , int ) = {Add , Sub , Mul , div}
   for(i=0;i<4;i++)
   {
       printf("%d\n",parr[i](2,3)); //5 -1 6 0
   }
    return 0;
}
//char* my_strcpy(char* dest , const char* src)
//1.写一个函数指针pf,能够指向my_strcpy
char* (*pf)(char* , const char*)
//2.写一个函数指针数组,能够存放4个my_strcpy函数的地址
char* (*fp[4])(char* , const char*)

函数指针数组的用途 : 转移表

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 meun()
{
    printf("**********************\n");
    printf("**** 1.Add  2.Sub ****\n");
    printf("**** 3.Mul  4.Div ****\n");
    printf("****** 0.exit ********\n");
    printf("**********************\n");
}
int main()
{
    int x =0 ,y=0;
    int input = 0;
    meun();
    printf("请选择哪种算法\n:");
    scanf("%d",&input);
    printf("请输入两个数:> ");
    scanf("%d%d",&x , &y);
    int (*parr[5])(int ,int )={0,Add,Sub,Mul,Div};
    do
    {
        if(input>=1&&input<=4)
        {
            printf("%d\n",parr[inpnt](x,y));
        }else if(input==0)
        {
            printf("退出\n");
        }else 
        {
            printf("选择错误\n");
        }
    }while(input)
        return 0;
}
//注: 这题也可以用swich case ,但代码长度较长

小结

int Add(int x , int y)
{
    return x+y;
}
int main()
{
    //指针数组
    int* parr[4];
    //数组指针
    int* (*P)[4]=&parr;
   // 函数指针
    int (*p)(int ,int )=Add;
    //函数指针的数组
    int (*parr[4])(int , int)={Add,Sub,Mul,Div};
    //指向函数指针的数组的指针
      int (*(*parr)[4])(int , int);  
}
  • 回调函数: 回调函数就是一个通过函数指针调用的函数.如果你把函数的指针作为参数传递给另一个函数,当这个指针被用来调用其他所指向的函数时,我们就是说这是一个回调函数.回调函数不是由该函数的现实方直接调用,而是在特定的事件或条件发生时有另一方调用的,由于对该事件或条件进行响应. 如 :

    void cola (int (*fp)(int ,int))

    cola(Add);
    //cola 函数能Add函数的地址

    void print(char* str)
    {
      printf("%s",str)  ;
        
    }
    
    void test(void(*p)(char *))
    {
        printf("TEST ");
        p("hello world");
    }
    int main()
    {
        test(print); //输出:TEST hello world
        return 0 ;
    }
        
    

7.库函数qsort的用法: qsort(void* p,int num1 , int num2 , int (cmp)(counst void e1, counst void* e2) )

第一个参数 : 待排序数组首元素的首地址

第二个参数 : 待排序数组元素的个数

第三个参数 : 待排序数组每个数组元素的大小—单位为字节

第四个参数 : 函数指针–比较两个元素函数的地址,这个函数要自己实现

//自己实现一个qsort排序
struct stu
{
  char name[10];
  int age;
};
void swap(char *p1 ,char *p2, int witdh)
{
    int i= 0;
    for(i=0;i<witdh;i++)
    {
     char temp=*p1;
        *p1=*p2;
        *p2=temp;
        p1++;
        p2++;
    }
    
}

void bubble_sort(void* base, int sz ,int width , int (*cmp)(void *e1, void *e2))
{
    int i=0;
    //趟数
    for(i=0;i<sz-1;i++)
    {
        int j=0 ;
        //每一趟比较的对数
        for(j=0;j<sz-1-i;j++)
        {
            //两个元素的比较 得到两个元素的首地址
            if( cmp((char*)base+j*width , (char*)base+(j+1)*width ) >0 )
            {
                //交换
                swap((char*)base+j*width , (char*)base+(j+1)*width ,width);
            }
                
        }
        
    }
    
}
 
int cmp_init(void* p1,void* p2)
{
    return *(int*)p1-*(int*)p2;
}

void test1()
{
    int i=0;
     int arr[]={9,8,7,6,5,4,3,2,1,0};
    int sz=sizeof(arr)/sizeof(arr[0]);
    bubble_sort(arr,sz,sizeof(arr[0]),cmp_init);
    for(i=0;i<sz;i++)
    {
        printf("%d",arr[i]);
    }
}
int cmp_age(void* p1, void* p2)
{
   return (struct stu*)p1->age - (struct stu*)p2->age;
}
void test2()
{
    struct stu s[3]={{"zhangsan",20},{"lisi",30},{"wangwu",25}};
    int sz=sizeof(s)/sizeof(s[0]);
    bubble_sort(s,sz,sizeof(s[0]),cmp_age);
}
int main()
{
 test1();
 test2();   
    return 0;
}

8.指针和数组练习题

数组名为首元素地址, 除以下两种情况外:

1.sizeof(数组名) --数组名表示整个数组

2.&数组名 --数组名表示整个数组

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OMBIGiRk-1624359006910)(C:\Users\cqf\AppData\Roaming\Typora\typora-user-images\image-20210516153516088.png)]
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
注: strlen里面的参数为某个数组的地址
在这里插入图片描述

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

不倒翁*

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值