C语言——指针

目录

指针的基本概念

地址运算符:&

间接运算符:*

指针的声明

指针类型的意义

1.决定指针的±整数的步长

2.决定指针的权限

 对于void*指针的一些认识

const修饰变量

1.const放*前

2.const放*后

 指针作为函数参数的用法

利用指针来输出数组

野指针

断言(assert)

指针在数组中的使用

数组名的两个特例

数组传参

二级指针

指针数组

一个有关const的应用

数组指针变量

数组指针的用法 -- 二维数组传参

函数指针

函数指针简单介绍

typedef的相关内容

函数指针数组以及回调函数

回调函数

正常的选择语句

先使用函数指针的数组

使用回调函数的写法


指针的基本概念

指针是一个值为内存地址的变量(或数据对象)

地址运算符:&

当&后跟一个变量名是,&给出它的地址。

p = &q;

对于该代码而言,其解释是p“指向”q。这样p里面就是q的地址

p是变量(即可修改的左值),&q是常量(右值)。

间接运算符:*

在创建指针变量之前,需要先对指针变量的类型进行声明,这就需要用到间接运算符:*。

间接运算符*可以找出储存在p中的值

p = &q;
r = *p;

两行代码得到的就是下面的结果

r = q;

指针的声明

指针所需要指向变量的类型有多种,此外不同的变量类型占用了不同的储存空间。

我们也需要知道在指定地址上储存的数据类型是什么。

char * p;//p是指向char类型变量的指针
int * p1, *p2;//p1,p2是指向int类型变量的指针

char,int等类型说明符表示指针所指向对象的类型

*表示声明的变量(p,p1,p2)是个指针

char * p表示p是一个指针,*p是char类型

对于p本身,它的类型是“指向char类型的指针”

指针变量的大小取决于地址的大小

32位平台下地址是32个bit位(即4个字节)

64位平台下地址是64个bit位(即8个字节)


指针类型的意义

1.决定指针的±整数的步长

int main()
{
	int a = 10;
	int* pa = &a;
	char* pc = &a;

	printf("pa    = %p\n", pa);
	printf("pa+1  = %p\n", pa+1);

	printf("pc    = %p\n", pc);
	printf("pc+1  = %p\n", pc+1);
	//pa和pc地址相同pa与pa+1的地址相差4个字节,但是pc与pc+1的地址相差1个字节,因为一个是int*一个是char*
 
  // +1 跳过1*sizeof(int) 个字节
 
	return 0;
}

如:

int * p        p+1->4

char *p      p+1->1

double * p p+1->8

2.决定指针的权限

即决定指针进行解引用操作符的时候访问几个字节

int main()
{
	int a = 0x11223344;//十六进制0x
	int* pa = &a;
	*pa = 0;
	//这个会改变a的地址对应的值为00 00 00 00(设置为4列)因为是四个字节 11 22 33 44各一个
	return 0;
}

int main()
{
	int a = 0x11223344;
	char* pa = &a;//改成x86 char*和int都是四个字节
	*pa = 0;
	//这个会改变a的地址对应的值为00 33 22 11(设置为4列)
	//说明char* 会从这个位置向后访问一个位置
	//以此类推,short*会向后访问两个位置

	return 0;
}

 对于void*指针的一些认识

首先

char* 指向字符的指针

short* 指向短整型的指针

int* 指向整型的指针

float* 指向浮点型的指针

void* 无具体类型的指针(泛型指针)

//void* 不能直接进行+-整数的操作
//void* p = &a;
//*p = 20;这种不可以,因为不知道void*要访问多少个字节
//p = p + 1也不可以


int main()
{
	int a = 0;
	float f = 0.0f;
	//char* p = &a;//int*    编译器会报警告,两边内容不兼容
	void* p = &a;
	p = &f;
	//这两个都不会报错    如果当你不知道它是什么类型,就可以写void*
	return 0;
}
//void* 用来接收不同类型的地址

const修饰变量

通过改变const修饰的变量的地址来改变其值

我们知道,const修饰的,在本质上还是变量(但是在C++中就成为常量)

可以用数组arr[a]来验证(数组初始化需要常量),会报错

int main()
{
	const int a = 10;
	//a=20;//err
	int* p = &a;
	*p = 0;
	printf("a = %d\n", a);//这样可以修改
	//const相当于把门锁上    const只是从语法层面限制不让修改a
	//int* p翻墙    实际编程上不让这样    因为使用const就是想不让修改a
	return 0;
}

1.const放*前

不可以修改*p

int main()
{
	int a = 10;//&a--0x0012ff40
	int b = 10;//&b--0x0012ff44
	int const* p = &a;

	//p = &b;//ok
	//*p = 100;//不行

	return 0;
}

2.const放*后

不可以修改p

const 限制的是指针变量本身,指针变量不能再指向其它变量

但是可以通过指针变量,修改指针变量指向的内容

int main()
{
	int a = 10;//&a--0x0012ff40
	int b = 10;//&b--0x0012ff44

	int* const p = &a;
	//p = &b;//这样不行    const限制了p
	
	//但是可以改 *p
	
	return 0;
}

 指针作为函数参数的用法

#include<stdio.h>
void swap(int * x, int * y);
int main()
{
	int x = 10, y = 20;
	printf("before swap  x:%d, y:%d\n", x, y);
	swap(&x, &y);
	printf("after swap   x:%d, y:%d\n", x, y);
	return 0;
}
void swap(int* x, int* y)
{
	int tmp = *x;
	*x = *y;
	*y = tmp;
}

利用指针来输出数组

原理:产生每个元素的地址,然后输出

*p + i也输出数组是因为第一个元素0与之后的都相差i!!!

int main()
{
	int arr[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
	int* p = &arr[0];
	int i = 0;
	int sz = sizeof(arr) / sizeof(arr[0]);
	    
	//第一个元素地址 p 第二个元素地址 p+1以此类推
	for (i = 0; i < sz; i++)
	{
		printf("%d ", *(p + i));
        //上面得出,每个元素地址位p+i
		
	}
}

归根结底是因为    数组是连续存储的空间

指针之间的比较

指针 + 整数 == 指针 (指针 + 整数 是跳过n个元素)
推得  指针 - 指针 = 整数 (绝对值    得到的是两个指针之间元素的个数)
地址 + 地址    没什么意义
int main()
{
	int arr[10] = { 0 };
	printf("%zd\n", &arr[9] - &arr[0]);
	printf("%zd\n", &arr[0] - &arr[9]);
}

野指针

野指针就是指针指向的位置是不可知的(随机的、不正确的、没有明确限制的)

1.指针所指向的空间被释放
//空间释放了    出了test(),a的地址就被释放
int* test()
{
	int a = 10;
	return &a;
}
int main()
{
	int* p = test();
	printf("%d\n", *p);

	return 0;
}

2.指针越界访问
#include <stdio.h>
int main()
{
	int ch[10]={0];
	int * p = ch;
	for(int i = 0; i < 12; i++)
	{
		*p++=i;
	}
	return 0;
}

3.指针未定义
#include <stdio.h>
int main()
{
	int* p;//p未给初始值,默认是随机值
	*p=10;
	return 0;
}

NULL是一个标识符常量,它的值为0,0也是个地址,但这个地址无法使用,读写会报错
明确知道指针应该指向哪里,就给初始化一个明确的地址
如果现在还不知道应该指向哪里,那就初始化NULL

断言(assert)

断言(assertion)是一种在编程中用来检查程序在运行时是否满足特定条件的方法。断言通常用于在代码中插入一些检查点,以确保程序的正确性。如果断言的条件不满足,程序将抛出一个异常或错误消息。

在使用前需要引入头文件 assert.h

#define NEDBUG  //使用了NEDBUG则会全局取消断言assert的使用
#include<assert.h>
//const是为了防止修改arr数组
//size_t是为了返回无符号的整型
size_t my_strlen(const char* str)
{
	//使用 *str依次指数组中每个元素    count统计
	size_t count = 0;
	assert(str != NULL);
	while (*str != '\0')
	{
		count++;
		str++;
	}
	return count;
}
int main()
{
	//strlen - 求字符串长度,统计的是字符串中、0之前的字符个数
	char arr[] = "a bcdef";
	//[a b c d e f \0]

	int len = my_strlen(arr);//数组名arr是数组首元素地址    arr == &arr[0]
	printf("%d\n", len);

	return 0;
}

指针在数组中的使用

数组名的两个特例

1.sizeof(数组名)这里表示的不是首元素地址,而是整个数组,计算的是整个数组的大小,单位是字节

2.&数组名 这里数组名表示整个数组,取出的是整个数组的地址

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
int main()
{
	int arr[11] = { 0 };
	printf("arr      = %p\n", arr);
	printf("arr + 1  = %p\n", arr + 1);
	printf("&arr[0]  = %p\n", &arr[0]);
	printf("&arr     = %p\n", &arr);
	printf("&arr + 1 = %p\n", &arr + 1);
	return 0;
}
//由此可以得出 arr = &arr[0]

//数组的地址和首元素的地址,从值的角度上看两者相等,但是区别在于一个是数组的地址,一个是数组首元素地址
//如何要看区别可以各给+1
//首元素指针+1跟类型有关 int*加4个字节
//数组+1与原来差44,因为数组有11个元素为int*类型    11 * 4 = 44 验证的话可以把数组元素个数改为10等

为什么访问数组可以使用指针
1.数组在内存中是连续存放的
2.指针的+-整数运算方便我们获得每一个元素的地址

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
int main()
{
	int arr[10] = { 0 };
	//使用指针来访问数组
	//输入十个数
	int sz = sizeof(arr) / sizeof(arr[0]);
	int* p = arr;
	int i = 0;
	for (i = 0; i < sz; i++) 
	{
		//输入1个值
		scanf("%d", p + i);//p+i == &arr[i] == arr + i
		//gets是用于输入字符串的
	}
	for (i = 0; i < sz; i++)
	{
		//输入1个值
		printf("%d ", *(p + i));//arr[i] == *(arr + i)    *(p + i) == p[i]
		//gets是用于输入字符串的
	}
	return 0;
}

这样我们就可以得到

数组就是数组,是一块连续的空间(数组的大小与数组元素个数和元素类型都有关系)
指针(变量)就是指针(变量),是一个变量(4/8个字节
数组名是地址,是首元素的地址
可以用指针来访问数组

数组传参

以下程序印证了
 1.数组传参的本质是传递了数组首元素的地址    形参访问的数组和实参访问的数组是一个
 2.形参的数组是不会单独再创建数组空间的,所以形参的数组是可以省略数组大小的。
如void Print(int arr[10])可以写成void Print(int arr[])

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
//数组传参的时候,形参是可以写成数组的形式的
//但本质上还是指针变量
void Print(int arr[10])//这里的int arr[10]相当于int* arr  本质上还是一个指针变量 
{
	int sz = sizeof(arr) / sizeof(arr[0]);//得不到元素个数
	//这里的sizeof(arr)就是求一个指针变量的大小(int* )    4
	//4 / 4 == 1 故只循环1次
	int i = 0;
	for (i = 0; i < sz; i++)
	{
		printf("%d ", arr[i]);
	}
}
int main()
{
	int arr[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
	Print(arr);//这里不是两个例外中的,arr是首元素的地址
	//应该用指针接受    int* p    而不是用    int arr[10]
	//sizeof(arr) / sizeof(arr[0])
	return 0;
}
//该程序执行得到的结果为 1 2

二级指针

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
int main()
{
	int a = 10;
	int* p = &a;//p是一级指针    指针变量也是变量  p也有地址(&p)
	int** pp = &p;//pp就是二级指针
	//还是分开理解
	//int* *  后面的*说明pp是指针变量  int*说明pp指向的p的类型是int*
	//int* *  是pp的类型
	//p+1    因为p指向int类型的a  跳过4个字节
	//pp+1   因为pp指向int*类型的p  跳过4/8个字节(int*等指针变量的大小与  电脑32位/64位有关)
	return 0;
}

指针数组

指针数组模拟二维数组的形式
是储存指针的数组

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
int main()
{
	int arr1[] = { 1, 2, 3, 4, 5 };
	int arr2[] = { 2, 3, 4, 5, 6 };
	int arr3[] = { 3, 4, 5, 6, 7 };

	int* arr[] = { arr1, arr2, arr3 };

	int i = 0;
	for (i = 0; i < 3; i++)
	{
		int j = 0;
		for (j = 0; j < 5; j++)
		{
			printf("%d ", arr[i][j]);
			//这个虽然是二维数组的形式
			//arr[i][j]  == *(*(arr+i)+j)    这俩等价
			//*(arr1 + j) == arr1[j]
			//    int (*a)[10]也是二维指针
		}
		printf("\n");
	}
}

一个有关const的应用

相同的常量字符串,没必要保存2份,因为常量字符串不能被修改,所以共用一份,节省空间

arr1  arr2  是两个独立的空间

调试中 arr3 arr4地址相同

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
int main()
{
	char arr1[] = "hello ffy.";
	char arr2[] = "hello ffy.";

	const char* arr3 = "hello ffy.";//一开始写成const char arr3[]  是错误的写法
	const char* arr4 = "hello ffy.";

	if (arr1 == arr2)
		printf("arr1 and arr2 are same\n");
	else
		printf("arr1 and arr2 are not same\n");

	if (arr3 == arr4)//两个的首元素地址
		printf("arr3 and arr4 are same\n");
	else
		printf("arr3 and arr4 are not same\n");


	printf("%p\n", &arr1);
	printf("%p\n", &arr2);
	printf("%p\n", arr3);
	printf("%p\n", arr4);


	return 0;
}

数组指针变量

存放的是数组的地址

例1
int main()
{
	int a = 10;
	int* pa = &a;

	char ch = 'w';
	char* pc = &ch;

	int arr[10] = { 0 };
	int (*p)[10] = &arr;//取出的是数组的地址
	//arr -- 数组首元素的地址

	int* p1[10];//p1是指针数组(重点是数组)  指针存放在数组里面
	//*p1[10]  p1首先与 [ 结合,就成为了数组    方块的优先级比*高
	int(*p2)[10];//旁边放了*,p2是指针变量,指向十个类型为int的数组
}
例2
int main()
{
	int arr[10] = { 0 };

	//指针类型决定+1地址+?

	int* p1 = arr;
	//  int* == int* 
	printf("%p\n", p1);
	printf("%p\n", p1 + 1);//p1+1  地址+4

	int(*p2)[10] = &arr;//[10]不能省略
 
	//int(*)[10] == int(*)[10]    数组指针类型
	printf("%p\n", p2);
	printf("%p\n", p2 + 1);//p2+1  地址+40 == 4*10

	return 0;
}

数组指针的用法 -- 二维数组传参

//一般的写法
//形参写成数组
void print(int arr[3][5], int r, int c)
{
	int i = 0;
	for (i = 0; i < r; i++)
	{
		int j = 0;
		for (j = 0; j < c; j++)
		{
			printf("%d ", arr[i][j]);
		}
		printf("\n");
	}
}
int main()
{
	int arr[3][5] = { {1, 2, 3, 4, 5},{2, 3, 4, 5, 6}, {3, 4, 5, 6, 7} };
	//一维数组,数组名是首元素的地址
	//二维数组,也是
	//两个表示整个数组的例外同样
	//1.&数组名
	//2.sizeof(数组名)
	//说明在函数中,也可以传递地址!!!
	print(arr, 3, 5);
	return 0;
}
//形参写成指针的形式
//可以把二维数组一行  理解为一维数组
//首元素的地址(arr == &arr[0])  就是第一行的地址
//第一行的地址就是一维数组的地址  类型是数组指针  (把他看成一维数组  一维数组是二维数组里的元素  是为了理解首元素到底是谁)
void print(int (*arr)[5], int r, int c);
//int (*arr)[5]  这个就是指向一行的指针    int(*)[5]就是  二维数组中的一维数组的类型
//不能用int ** arr
//二级指针变量是用来存放一级指针变量的地址
{
	int i = 0;
	for (i = 0; i < r; i++)
	{
		int j = 0;
		for (j = 0; j < c; j++)
		{
			//*(*(arr + i)+j) == arr[i][j]  因为指针的运算也会转换成数组的运算
			//arr+i == &arr[i]  *(arr + i) == arr[i] 对arr+i解引用  但它还是个地址
			//arr[i]+j  找到第i行第j个元素的地址  &arr[i][j]
			printf("%d ", *(*(arr + i) + j));
		}
		//printf("%p ", *(arr + i));
		//printf("%p ", (arr + i));
		printf("\n");
	}
}
int main()
{
	int arr[3][5] = { {1, 2, 3, 4, 5},{2, 3, 4, 5, 6}, {3, 4, 5, 6, 7} };
	//二维数组,地址实际上是连续存放
	//第一行是arr[0]  第二行arr[1]
	//第一行是int [5]  []说明是数组  五个整形元素的数组
	print(arr, 3, 5);
	return 0;
}

二维数组传参的本质 -- 传递的是第一行这个一维数组的地址

函数指针

函数指针简单介绍

int (*pf3)(int x, int y)
int是pf3指向函数的返回类型
*pf3是函数指针变量名
int x, int y是pf3指向函数的参数类型和个数交代


//函数的地址
//函数名
//&函数名
//都是函数的地址,没有区别!!!
//对于函数名来说,前面的&和*都会被忽略,所以 函数名 前面加不加取地址都没区别。
int Add(int x, int y)
{
	return x + y;
}
int main()
{
	int (*pf)(int, int) = Add;//pf是专门用来存放函数地址的,pf就是函数指针变量  (*pf)说明是指针
	printf("%p\n", &Add);
	printf("%p\n", Add);
	//去掉名字剩下类型int (*)(int ,int)
	printf("%p\n", *pf);
	printf("%p\n", pf);

	int c = Add(2, 3);//函数名调用
	printf("%d\n", c);

	int d = (*pf)(3, 4);//函数指针调用
	printf("%d\n", d);//pf存放函数的地址

	int e = pf(4, 5);//*其实可以不写
	printf("%d\n", e);
	if (*pf == &Add)//pf == *pf == Add ==&Add!!!
		printf("dui");
}

例:声明一个指向含有10个元素的数组的指针,其中每个元素是一个函数指针,该函数的返回值是int,参数是int*

  指向函数指针数组的指针
  int (*(*p)[10])(int *)
  (*p) 是 指针
  (*p)[10] 是 指向含有10个元素的数组的指针
  int (* )(int *) 是 函数指针
  int 是 返回值
  int* 是 参数

typedef的相关内容


typedef unsigned int u_int;
//指针类型的定义
typedef int* pint_t;
//数组指针定义
typedef int(*)[5] pf_t;//这样不行
typedef int(*parr_t)[5] ;//得这样写  parr_t == int(*)[5]
//对函数指针的定义
typedef void (*)(int) pf_t;//跟上面类似
typedef void (*pf_t)(int);//  pf_t == void (*)(int)
//上面的那个就可以写成  pf_t signal(int, pf_t)


typedef int* ptr_t;
#define PTR_T int*;

ptr_t p1, p2;//p1 p2都是指针变量
PTR_T p3, p4;//p3是指针变量 p4是整型变量
//对于define 编译器是替换  相当于int* p3,p4

函数指针数组以及回调函数

函数指针数组

int (* a[10])(int *)

一个有10个指针的数组:*a[10];

该指针指向一个函数:(*a[10])();

该函数有一个整形参数:(*a[10])(int);

并返回一个整型数: int (*a[10])(int)

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 (*pf1)(int, int) = Add;//pf1是函数指针变量
	int (*pfarr[4])(int, int) = { Add, Sub, Mul, Div};//pfarr是函数指针数组
	int i = 0;
	for (; i < 4; i++)
	{
		//不用解引用,153行
		int r = pfarr[i](8, 4);
		printf("%d\n", r);
	}
	return 0;
}

回调函数

回调函数是指将一个函数作为参数传递给另一个函数,并在特定事件发生时被调用。

正常的选择语句

//第一种方法 -- 正常的switch
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 menu()
{
	printf("***************************\n");
	printf("*****  1.add   2.sub  *****\n");
	printf("*****  3.mul   4.div  *****\n");
	printf("*****  0.exit         *****\n");
	printf("***************************\n");
}
int main()
{
	int input = 0;
	int x = 0;
	int y = 0;
	int z = 0;
	do
	{
		menu();
		printf("请选择:");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			printf("请输入两个操作数:");
			scanf("%d %d", &x, &y);
			z = Add(x, y);
			printf("%d\n", z);
			break;
		case 2:
			printf("请输入两个操作数:");
			scanf("%d %d", &x, &y);
			z = Sub(x, y);
			printf("%d\n", z);
			break;
		case 3:
			printf("请输入两个操作数:");
			scanf("%d %d", &x, &y);
			z = Mul(x, y);
			printf("%d\n", z);
			break;
		case 4:
			printf("请输入两个操作数:");
			scanf("%d %d", &x, &y);
			z = Div(x, y);
			printf("%d\n", z);
			break;
		case 0:
			printf("退出计算机\b");
			break;
		default:
			printf("输入有误,请重新输入\n");
			break;
		}
	} while (input);
}

其中我们会发现,case1,2,3,4中的内容都大差不大,

为此我们可以简化

先使用函数指针的数组

(注意需要手动添加数组arr[0]以便队名菜单)

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 menu()
{
	printf("***************************\n");
	printf("*****  1.add   2.sub  *****\n");
	printf("*****  3.mul   4.div  *****\n");
	printf("*****  0.exit         *****\n");
	printf("***************************\n");
}
int main()
{
	int input = 0;
	int x = 0;
	int y = 0;
	int z = 0;
	//函数指针的数组创建
	//先创建函数指针  (*pfArr[])
	//添加返回类型  和  希望指向的参数  int(*pfArr[4])(int x, int y)
	//int(*pfArr[4])(int x, int y) = { Add, Sub, Mul, Div };
	                                0    1    2    3    不匹配
	int(*pfArr[5])(int x, int y) = { 0, Add, Sub, Mul, Div };
	//                               0   1    2    3    4 

	do
	{
		menu();
		printf("请选择:");
		scanf("%d", &input);

		if (input >= 1 && input <= 4)
		{
			printf("请输入两个操作数:");
			scanf("%d %d", &x, &y);
			z = pfArr[input](x, y);
			printf("%d\n", z);
		}
		else if (input == 0)
		{
			printf("退出计算器\n");
		}
		else
			printf("输入错误,请重新输入\n");
	} while (input);
}

使用回调函数的写法

将Add, Sub, Mul, Div这些回调函数通过主函数中的选择语句来传递给*pf

int(*pf)(int, int)
{
	int x = 0;
	int y = 0;
	int z = 0;
	
	printf("请输入两个操作数:");
	scanf("%d %d", &x, &y);
	z = pf(x, y);//pf == Add 等传递过来的
	printf("%d\n", z);
}

具体代码如下

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 calc(int(*pf)(int, int))  
 //把选择后的Add等传递给 *pf   这里写pf也没问题,两者等价
 //int(*pf)(int, int)
{
	int x = 0;
	int y = 0;
	int z = 0;
	
	printf("请输入两个操作数:");
	scanf("%d %d", &x, &y);
	z = pf(x, y);//pf == Add 等传递过来的
	printf("%d\n", z);
}
void menu()
{
	printf("***************************\n");
	printf("*****  1.add   2.sub  *****\n");
	printf("*****  3.mul   4.div  *****\n");
	printf("*****  0.exit         *****\n");
	printf("***************************\n");
}
//主调函数
int main()
{
	int input = 0;
	do
	{
		menu();
		printf("请选择:");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			calc(Add);
			break;
		case 2:
			calc(Sub);
			break;
		case 3:
			calc(Mul);
			break;
		case 4:
			calc(Div);
			break;
		case 0:
			break;
		default:
			printf("输入有误,请重新输入\n");
			break;
		}
	} while (input);
}


// 1.数组传参的本质是传递了数组首元素的地址    形参访问的数组和实参访问的数组是一个
// 2.形参的数组是不会单独再创建数组空间的,所以形参的数组是可以省略数组大小的。
// 如void Print(int arr[10])可以写成void Print(int arr[])
//印证了
// 1.数组传参的本质是传递了数组首元素的地址    形参访问的数组和实参访问的数组是一个
// 2.形参的数组是不会单独再创建数组空间的,所以形参的数组是可以省略数组大小的。
// 如void Print(int arr[10])可以写成void Print(int arr[])

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值