C语言之数组参数,指针参数,函数指针,函数指针数组

简介: C语言之数组参数,指针参数,函数指针,函数指针数组

在进行程序设计的时候,难免会出现将数组或指针传给参数,那函数的参数该如何设计呢?

一维数组传参的方式:

举例:

#include<stdio.h>
 int main()
{
  int arr[10] = { 0 };
  int* arr2[20] = { 0 };
  test(arr);
  test2(arr2);
  return 0;
}

对整型数组:

根据数组名:

1:指明数组长度

void test(int arr[10])

2:不指明数组长度

void test(int arr[])
用指针指向的方式:
void test(int *arr)

对指针数组:

1:用数组进行传递

void test2(int *arr[20])

2:用指针指向进行传递

void test2(int **arr)

总结一下:一维数组在进行传参的时候,参数可以是指针也可以是数组,数组大小可以忽略。

二维数组传参的方式:

举例:

#include<stdio.h>
int main()
{
  int arr[3][5] = { 0 };
  test(arr);
  return 0;
}

用数组名的方式:

1:行列都指明

void test(int arr[3][5])

2:只指明列

void test(int arr[][5])

3:行列都不指明:这种方式是错误的

二维数组传参,函数参数的设计只能省略第一个[]的数字,因为对一个二维数组,可以不知道有多少行,但是必须知道一行有多少个元素

void test(int arr[][])

用指针的方式:

1:用普通一级指针的方式:这种方式是错误的!


数组名代表首地址,但因为arr是二维数组,首地址为第一行的地址,而int*arr只能用来存放整形的地址,传地址用指针来接收,指针只占四个字节,而一般一个二维数组的字节数都大于四个字节。

void test(int *arr)

2:这种方式也是错误的,int*arr[5]不是指针

void test(int *arr[5])

3:用数组指针的方式:

int (*arr)[5],为数组指针,该指针里面存放的是包含5个整形的一个数组

void test(int(*arr)[5])

4:用二级指针的方式:该方法是错误的

int arr是二级指针,只能用来存放一级指针变量的地址

void test(int **arr)

一级指针传参:

举例:

核心:指针传参,指针接收

#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]);
  print(p, sz);//指针传参
  return 0;
}
1
2
3
4
5
6
7
8
9
0

当函数的参数部分为一级指针的时候,函数能接收什么参数?

举例:

一级指针为整形指针的时候:

void test1(int*p)
{}
int main()
{
  int a = 10;
  int* p1 = &a;
  test1(&a);//传递地址
  test1(p1);//传递指针
  return 0;
}

一级指针为字符型指针的时候:

举例:

void test2(char*p)
{}
int main()
{
  char ch = 'w';
  char* pc = &ch;
  test2(&ch);//传递地址
  test2(pc);//传递指针
  return 0;
}

二级指针传参:

举例:

核心:可通过直接传递二级指针或者传递二级指针中存放的一级指针的地址

#include<stdio.h>
void test(int** ptr)
{
  printf("num=%d\n", **ptr);
}
int main()
{
  int n = 10;
  int* p = &n;
  int** pp = &p;//二级指针pp存放一级指针p的地址
  test(pp);//传递二级指针
  test(&p);//二级指针pp存放一级指针p的地址,等价于传递二级指针pp
  //int* arr[10];二级指针也可接受指针数组
  //test(arr);传递数组名
  return 0;
}
num=10
num=10

函数指针:指向函数的指针

如何得到函数的地址?
&函数名和函数名,都是函数的地址,与数组类似但有所差异

举例:

#include<stdio.h>
int add(int x, int y)
{
  int z = 0;
  z = x + y;
  return z;
}
int main()
{
  int a = 10;
  int b = 20;
  printf("%d\n", add(a, b));//对函数进行调用
  printf("%p\n", &add);
  printf("%p\n", add);
  return 0;
}

函数指针该怎么书写呢?

举例:

与数组指针的写法相类似

int (*pa)(int ,int)=add;//*pa一定要用括号括起来,比面pa和后面的括号结合变成函数
//可以类别数组指针的定义方法
//(int,int)代表函数的参数,参数名可写可不写

下面我们通过示例验证一下:

#include<stdio.h>
int add(int x, int y)
{
  int z = 0;
  z = x + y;
  return z;
}
int main()
{
  int a = 10;
  int b = 20;
  int (*pa)(int, int) = add;//定义指针变量pa指向函数add
  printf("%d\n", (*pa)(a, b));//对指针进行解引用操作
  return 0;
}
30

将某变量强制类型转化为函数指针类型:

举例:

该行代码的含义就是,将0强制类型转化为函数指针类型,此时0就代表了函数的地址,再对其进行解引用操作,相当于函数的调用。

函数的返回也是函数指针:

举例:

该行代码就是signal函数的参数有两个,第一个是int,第二个是函数指针,该函数指针指向的函数的参数是int,返回类型是void,signal函数的返回也是一个函数指针,该函数指针指向的函数的参数是int,返回类型是void,signal(int,void(*)(int))为函数的声明

怎么理解呢?我们可以用 之前学过的typedef来将其拆分;


多个*在函数调用中其实无实际意义:

举例:

#include<stdio.h>
int add(int x, int y)
{
  int z=0;
  z = x + y;
  return z;
}
int main()
{
  int a = 10;
  int b = 20;
  int (*pa)(int, int) = add;
  printf("%d\n", add(2, 3));//常规方法对函数进行调用
  printf("%d\n", (pa)(2, 3));//用指针的方法对函数进行调用
  //以下几种都是通过先对pa进行解引用在调用函数,*pa等价于add
  printf("%d\n", (*pa)(2, 3));
  printf("%d\n", (**pa)(2, 3));
  printf("%d\n", (***pa)(2, 3));
  return 0;
}
5
5
5
5
5

函数指针数组

数组是一个存放相同类型数据的存储空间,之前我们已经学习过了指针数组,比如:

int *arr[10];//数组的每个元素是int*

那么函数指针数组该如定义呢?

其实和数组指针的定义有相似之处:

如下图所示:pa指向的函数类型是int,函数参数类型也是int

函数数组只能该怎么使用呢?

举例:

#include<stdio.h>
int add(int x, int y)
{
  int z = 0; 
  z = x + y;
  return z;
}
int sub(int x, int y)
{
  int z = 0;
  z = x - y;
  return z;
}
int mul(int x, int y)
{
  int z = 0;
  z = x * y;
  return z;
}
int div(int x, int y)
{
  int z = 0;
  z = x / y;
  return z;
}
int main()
{
  //指针数组
  //int* arr[5];
  //需要一个数组,这个数组可以存放4个函数的地址----函数指针的数组
  //int(*pa)(int, int)=add;
  int(*parr[4])(int, int) = { add,sub,mul,div };//定义函数指针数组,使其指向add,sub,mul,div函数
  int i = 0;
  for (i = 0; i < 4; i++)//通过for循环,使其对对函数指针数组中的函数进行遍历
  {
    printf("%d\n",parr[i](2, 3));//当i=0,调用add函数,当i=1,调用sub函数,以此类推
  }
  return 0;
}
5
-1
6
0

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

下面我们通过示例感受一下:

该实例想实现的功能为模拟计算器进行数据四则运算

#include<stdio.h>
void menu()
{
  printf("********************************\n");
  printf("**    1.and        2.sub      **\n");
  printf("**    3.mul        4.div      **\n");
  printf("********************************\n");
}
//通过不同的函数,实现不同的功能
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 input = 0;
  int x = 0, y = 0;
  do
  {
    menu();
    printf("请选择:>");
    scanf_s("%d", &input);
    switch (input)
    {
    case 1:
      printf("请输入两个操作数:");
      scanf_s("%d%d", &x, &y);
      printf("%d\n",add(x, y));
      break;
    case 2:
      printf("请输入两个操作数:");
      scanf_s("%d%d", &x, &y);
      printf("%d\n", sub(x, y));
      break;
    case 3:
      printf("请输入两个操作数:");
      scanf_s("%d%d", &x, &y);
      printf("%d\n", mul(x, y));
      break;
    case 4:
      printf("请输入两个操作数:");
      scanf_s("%d%d", &x, &y);
      printf("%d\n", div(x, y));
      break;
    case 0://不需要输入操作数
      printf("退出\n");
    default ://不需要输入操作数
      printf("选择错误\n");
      break;
    }
  } while (input);
}


上述方法是通过调用不同的函数实现两数之间的不同运算,但数据的运算不仅是加减乘除,如果我们想实现位与,位或等等的其他运算呢?有人说,那就再编写功能函数啊,这样的方法确实没有错,但每增加一个功能函数,就需要增加一个case语句,这样大大增加了代码的量,执行效率也不高。


既然要实现的是两个数的四则运算,每个功能函数的参数个数相同,那么我们就可以结合之前学过的函数指针数组将函数,将其放在一个数组中,这样就不需要编写一个个case语句了,大大减少了代码量。


具体操作如下:

#include<stdio.h>
void menu()
{
  printf("********************************\n");
  printf("**    1.and        2.sub      **\n");
  printf("**    3.mul        4.div      **\n");
  printf("********************************\n");
}
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 input = 0;
  int x = 0, y = 0;
  int(*pfarr[])(int, int) = { 0,add,sub,mul,div };//pfarr是函数指针数组,用过下标转移到对应的函数
  do
  {
    menu();
    printf("请选择:>");
    scanf_s("%d", &input);
    if (input >= 1 && input <= 4)
    {
      printf("请输入操作数:>");
      scanf_s("%d%d", &x, &y);
      int ret = pfarr[input](x, y);//ret作为接收转移的函数的返回值
      printf("%d\n", ret);
    }
    else if (input == 0)
    {
      printf("退出");
    }
    else
    {
      printf("选择错误\n");
    }
  } while (input);
}

此后如果还想增加位与,位或的操作,则只需要在该行代码中增加其函数名,并编写相对相应的函数

int(*pfarr[])(int, int) = { 0,add,sub,mul,div };

再在可选菜单中加入该选项即可。

void menu()
{
  printf("********************************\n");
  printf("**    1.and        2.sub      **\n");
  printf("**    3.mul        4.div      **\n");
  printf("********************************\n");
}
相关文章
|
存储 NoSQL 编译器
【C语言】指针的神秘探险:从入门到精通的奇幻之旅 !
指针是一个变量,它存储另一个变量的内存地址。换句话说,指针“指向”存储在内存中的某个数据。
482 7
【C语言】指针的神秘探险:从入门到精通的奇幻之旅 !
|
12月前
|
存储 人工智能 Java
一文轻松拿捏C语言的指针的基础使用
本文介绍了C语言中的指针概念,包括直接访问和间接访问内存的方式、指针变量的定义与使用、取址运算符`&`和取值运算符`*`的应用,帮助读者深入理解指针这一C语言的核心概念。君志所向,一往无前!
274 0
|
存储 编译器 C语言
【C语言】指针大小知多少 ?一场探寻C语言深处的冒险 !
在C语言中,指针的大小(即指针变量占用的内存大小)是由计算机的体系结构(例如32位还是64位)和编译器决定的。
1537 9
|
安全 程序员 C语言
【C语言】指针的爱恨纠葛:常量指针vs指向常量的指针
在C语言中,“常量指针”和“指向常量的指针”是两个重要的指针概念。它们在控制指针的行为和数据的可修改性方面发挥着关键作用。理解这两个概念有助于编写更安全、有效的代码。本文将深入探讨这两个概念,包括定义、语法、实际应用、复杂示例、最佳实践以及常见问题。
472 7
|
机器学习/深度学习 算法 C语言
技术原理:C语言中函数指针数组浅析
发现问题 今天,在阅读Linux内核中关于socket的源代码时,遇到了下面一段代码: struct proto_ops { int family; struct module *owner; i...
1190 0
|
机器学习/深度学习 算法 C语言
|
5月前
|
存储 C语言
`scanf`是C语言中用于按格式读取标准输入的函数
`scanf`是C语言中用于按格式读取标准输入的函数,通过格式字符串解析输入并存入指定变量。需注意输入格式严格匹配,并建议检查返回值以确保读取成功,提升程序健壮性。
1155 0
|
存储 算法 C语言
【C语言程序设计——函数】素数判定(头歌实践教学平台习题)【合集】
本内容介绍了编写一个判断素数的子函数的任务,涵盖循环控制与跳转语句、算术运算符(%)、以及素数的概念。任务要求在主函数中输入整数并输出是否为素数的信息。相关知识包括 `for` 和 `while` 循环、`break` 和 `continue` 语句、取余运算符 `%` 的使用及素数定义、分布规律和应用场景。编程要求根据提示补充代码,测试说明提供了输入输出示例,最后给出通关代码和测试结果。 任务核心:编写判断素数的子函数并在主函数中调用,涉及循环结构和条件判断。
777 23
|
7月前
|
安全 C语言
C语言中的字符、字符串及内存操作函数详细讲解
通过这些函数的正确使用,可以有效管理字符串和内存操作,它们是C语言编程中不可或缺的工具。
375 15
|
12月前
|
人工智能 Java 程序员
一文彻底搞清楚C语言的函数
本文介绍C语言函数:函数是程序模块化的工具,由函数头和函数体组成,涵盖定义、调用、参数传递及声明等内容。值传递确保实参不受影响,函数声明增强代码可读性。君志所向,一往无前!
518 1
一文彻底搞清楚C语言的函数