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");
}
相关文章
|
10天前
|
存储 C语言 C++
如何通过指针作为函数参数来实现函数的返回多个值
在C语言中,可以通过将指针作为函数参数来实现函数返回多个值。调用函数时,传递变量的地址,函数内部通过修改指针所指向的内存来改变原变量的值,从而实现多值返回。
|
10天前
|
C语言
【c语言】指针就该这么学(3)
本文介绍了C语言中的函数指针、typedef关键字及函数指针数组的概念与应用。首先讲解了函数指针的创建与使用,接着通过typedef简化复杂类型定义,最后探讨了函数指针数组及其在转移表中的应用,通过实例展示了如何利用这些特性实现更简洁高效的代码。
11 2
|
10天前
|
存储 搜索推荐 C语言
如何理解指针作为函数参数的输入和输出特性
指针作为函数参数时,可以实现输入和输出的双重功能。通过指针传递变量的地址,函数可以修改外部变量的值,实现输出;同时,指针本身也可以作为输入,传递初始值或状态。这种方式提高了函数的灵活性和效率。
|
10天前
|
C语言
如何避免 C 语言中的野指针问题?
在C语言中,野指针是指向未知内存地址的指针,可能引发程序崩溃或数据损坏。避免野指针的方法包括:初始化指针为NULL、使用完毕后将指针置为NULL、检查指针是否为空以及合理管理动态分配的内存。
|
10天前
|
C语言
C语言:哪些情况下会出现野指针
C语言中,野指针是指指向未知地址的指针,通常由以下情况产生:1) 指针被声明但未初始化;2) 指针指向的内存已被释放或重新分配;3) 指针指向局部变量,而该变量已超出作用域。使用野指针可能导致程序崩溃或不可预测的行为。
|
10天前
|
编译器 C语言
【c语言】指针就该这么学(2)
本文详细介绍了指针与数组的关系,包括指针访问数组、一维数组传参、二级指针、指针数组和数组指针等内容。通过具体代码示例,解释了数组名作为首元素地址的用法,以及如何使用指针数组模拟二维数组和传递二维数组。文章还强调了数组指针与指针数组的区别,并通过调试窗口展示了不同类型指针的差异。最后,总结了指针在数组操作中的重要性和应用场景。
12 0
|
算法 编译器 程序员
C语言学习笔记—P11(数组<2>+图解+题例+三子棋游戏<初级>)
C语言学习笔记(数组<2>+图解+题例+三子棋游戏<初级>)
130 0
C语言学习笔记—P11(数组<2>+图解+题例+三子棋游戏<初级>)
|
存储 C语言
C语言学习笔记—P10(数组<1>+图解+题例)
C语言学习笔记(数组<1>+图解+题例)
135 0
C语言学习笔记—P10(数组<1>+图解+题例)
|
C语言
C语言学习笔记——数组(二)
C语言学习笔记——数组
178 0
C语言学习笔记——数组(二)
|
C语言
C语言学习笔记——数组(一)
C语言学习笔记——数组
166 0
C语言学习笔记——数组(一)