C语言重点突破之指针进阶

之前我们在初识C语言中介绍了指针,今天我们来深入学习指针,也就是指针进阶。

本章重点

1. 字符指针

2. 数组指针

3. 指针数组

4. 数组传参和指针传参

5. 函数指针

6. 函数指针数组

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

8. 回调函数

9. 指针和数组面试题的解析

字符指针

int main()
{
    char ch = 'w';
    char *pc = &ch;
    *pc = 'w';
    return 0;
}

在这里,*pc就是字符指针,指向ch代表的‘w’

cahr *pa="abcdej";

在这一行代码里边,这一串表达式代表的是首元素的地址。

是把字符串的首字符放在p中。

字符指针,不仅可以指向单独的字符,也可以指向字符串。

小练习
#include <stdio.h>
int main()
{
    char str1[] = "hello lala.";
    char str2[] = "hello lala.";
    const char *str3 = "hello lala.";
    const char *str4 = "hello lala.";
    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;
}

这个会怎么打印?

3

2

1

答案揭晓:

为什么会是这样的结果呢

第一个if,首先我们要知道数组名单独使用代表的是首元素的地址,两个独立数组首元素的地址,怎么可能一样呢?当然会打印not same了

第二个if,是两个字符串的比较,常量字符串的值是不能通过指针修改的,在我们的内存中,两个完全相同的常量字符串的地址也一样,因为反正也不能修改,就不用再费力的开辟第二个空间来存储一样的字符串了。所以是are same了

指针数组

顾名思义就是存放指针的数组

int *arr[3];//整形指针数组
char *arr[3];//字符指针数组
char *arr[]={"abcdr","hello lala","Nba2kol"}

这里创建了一个指针数组,分别存储了三个字符串的首地址。

{a,h,n}

数组参数、指针参数
一维数组传参
#include <stdio.h>
void test(int arr[])//ok?
{}
void test(int arr[10])//ok?
{}
void test(int *arr)//ok?
{}
void test2(int *arr[20])//ok?
{}
void test2(int **arr)//ok?
{}
int main()
{
 int arr[10] = {0};
int *arr2[20] = {0};
 test(arr);
 test2(arr2);
}

请大家判断以上的传参方式正确与否:

第二行与第四行,数组传参形参是数组,当然是可以的

第六行,形参是一个一级指针,可不可以接收?我们在主函数部分实参传递的是数组名字,代表首元素的地址,所以可以用指针接收地址

第八行和第十行与上面是一个道理,只是数组变成了指针数组,其他的传递方式都相同,关于指针数组的特点上文我们都介绍了。

总结:一维数组传参的时候形参可以是数组也可以是指针,当形参是指针的时候,要注意类型。

二维数组传参
void test(int arr[3][5])//ok?
{}
void test(int arr[][])//ok?
{}
void test(int arr[][5])//ok?
{}
//总结:二维数组传参,函数形参的设计只能省略第一个[]的数字。
//因为对一个二维数组,可以不知道有多少行,但是必须知道一行多少元素。
//这样才方便运算。
void test(int *arr)//ok?
{}
void test(int* arr[5])//ok?
{}
void test(int (*arr)[5])//ok?
{}
void test(int **arr)//ok?
{}
int main()
{
 int arr[3][5] = {0};
 test(arr);
}

来看看这串代码中二维数组中的传参方式哪些是正确的吧。

第一行,我们传递的是三行五列的二维数组,他也这样接收,没有一点毛病。

第三行,这种传参方式肯定是不可以的,二维数组传参,可以省略行,但是不能省略列,因为访问的时候要知道这个数组一行有多少个数,并不关心有几行,所以第五行的传参方式是正确的。

第十行的形参用了一个指针传参,也是不可以的,因为我们实参用了数组名字,代表着二维数组的首元素地址,二维数组的首元素是整个的第一行,不能只用一个指针接受,像第14行那,用一个数组指针来接收,,所以第十四行是正确的。

第十六行,二级指针是用来接收一级指针的,实参传递的也不是一级指针,所以不正确。

总结:二维数组传参参数可以是数组,也可以是数组,如果是数组,行可以省略,但是列不能省略,如果

是指针,传过去的是第一行的地址,形参就应该是数组指针。

一级指针传参
#include <stdio.h>
void print(int *p, int sz)
{
 int i = 0;
 for(i=0; i<sz; i++)
 {
 printf("%d\n", *(p+i));
 }
}
int main()
{
 int arr[10] = {1,2,3,4,5,6,7,8,9};
 int *p = arr;
 int sz = sizeof(arr)/sizeof(arr[0]);
 //一级指针p,传给函数
 print(p, sz);
 return 0;
}

先仔细观察这段代码,创立了一个指针变量p,存放了数组首地址,传递到函数中,通过函数就能访问到数组,这样的传参是成功的,思考一波:当一个函数的参数部分为一级指针的时候,函数能接收什么参数?

整型变量,整形指针,整型数组,都可以传。

二级指针传参
#include <stdio.h>
void test(int** ptr)
{
 printf("num = %d\n", **ptr); 
}
int main()
{
 int n = 10;
 int*p = &n;
 int **pp = &p;
 test(pp);
 test(&p);
 return 0;
}

通过前面的学习,我们很容易想明白,一级指针传参的时候,传递过去的是一级指针或者地址,那么二级指针传参也就是传递二级指针,或者一级指针的地址。

同样思考:当函数的参数为二级指针的时候,可以接收什么参数?

还可以传递一维指针数组,因为数组传递的是首元素的地址,指针数组的每个变量都是指针,传递过去的就是一级指针的地址。

函数指针

整形指针 - 指向整形的指针 int*

字符指针 - 指向字符的指针 char*

数组指针 - 指向数组的指针 int arr[10] int (p)[10]=&arr;

函数指针 - 指向函数的指针 ????(怎么表示)

数组指针中存放的是数组的地址

函数指针中存放的应该是函数的地址,那么问题来了,

函数有地址吗?

那就用代码说话呗

#include<stdio.h>
int add(int x, int y)
{
    return x + y;
}
int main()
{
    printf("%p", &add);


    return 0;
}

看打印:

确实打印出来了函数的地址,得出结论,函数确实有地址。

其实,有一个知识点,就是函数名和&函数名,都能被表示函数的地址。

#include<stdio.h>
int add(int x, int y)
{
    return x + y;
}
int main()
{
    printf("%p", add);


    return 0;
}

这串代码我们将&符号去掉了,看打印结果

依旧是函数的地址。

问题来了,函数的指针形式如何用代码写出来呢?

看代码

#include<stdio.h>
int add(int x, int y)
{
    return x + y;
}
int main()
{
    /*printf("%p", add);*/
    int(*pf)(int, int) = add;//函数指针变量

    return 0;
}

第九行,pf就是函数指针变量,这样的命名方式和数组指针很相似,所以很好记。

而下来就是学习如何调用这个函数,还是看代码,一看便知:

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
int add(int x, int y)
{
    return x + y;
}
int main()
{
    /*printf("%p", add);*/
    int(*pf)(int, int) = add;//函数指针变量
    int ret = (*pf)(3, 5);
    printf("%d", ret);
    return 0;
}

像第11行那样,结果应该是8

函数指针数组

函数指针数组的每个元素都是函数指针

#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(*p[4])(int, int) = { add,sub,mul,div };
    int i = 0;
    for (i = 0; i < 4; i++)
    {
        int ret = p[i](8, 4);
        printf("%d\n", ret);
    }
}

函数指针数组的命名方式就在代码这样。

那么函数指针有什么用呢?

下面请看C语言--计算机+-*/实现

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;
}

关注我,会带来更多有意思的讲解

欢迎大家来我的gitee提取更多有趣代码

https://gitee.com/two-moon-rock/first-bloodf.git

  • 15
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 7
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

贰月磐石

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

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

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

打赏作者

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

抵扣说明:

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

余额充值