指针与数组

[ ] 的优先级高于* ,所以在写数组指针的时候一定要加上( ),不然就是,指针数组的意思了

一、指针数组


1.指针数组的基本介绍


我们以前学过数值数组和字符数组

例如:

int arr[10];    存放整形的数组             char arr[5];   存放字符的数组

当数组元素的类型为指针类型的时候,成为指针数组

它的定义形式为               

类型表示符 *数组名[常量表达式];

常量表达式代表元素的个数

例如:

int * arr[10];   存放整型指针的数组   ,有10个元素   

char *ch[5];  存放字符指针的数组  ,有5个元素

2、指针数组的实例说明
 

int main()
{
	int a = 10;
	int b = 20;
	int c = 20;
	int* arr[3] = { &a,&b,&c };
	for (int i = 0; i < 3; i++)
	{
		printf("%d ", *(arr[i]));
	}
}
 
//结果为   10 20 20


这里都是定义了一个数组  arr  来存放a,b,c  的 地址,也就是指针

打印结果时也可以用for循环来实现

3、模拟二维数组  

int main()
{
	int i, j;
	int arr1[5] = { 1,2,3,4,5 };
	int arr2[5] = { 2,3,4,5,6 };
	int arr3[5] = { 3,4,5,6,7 };
	int* p[3] = {arr1,arr2,arr3};  //数组名就是地址,不要用&地址符号,指针也就是地址
	for (i = 0; i < 3; i++)
	{
		for (j = 0; j < 5; j++)
		{
			//printf("%d ", p[i][j]);
			//printf("%d ", *(p[i]+j));
			printf("%d ", *(*(p+i) + j));
		}
		printf("\n");
	}
	return 0;
}


printf("%d ", p[i][j]);
printf("%d ", *(p[i]+j)); 

 printf("%d ", *(*(p+i) + j));

这三种写法都是对的

因为  p[i][j]==*(p[i]+j)   p[i]==*(p+i)

二、数组指针


1.数组指针的基本介绍


我们以前学过

整形指针-- 指向整形的指针,存放整形变量地址的

int a=10;  int *p=&a;

字符指针---指向字符的指针,存放的是字符变量的地址

char ch='w' ;    int *p=&ch;

而现在我们要学习新的一种指针类型   

  数组指针----指向数组的指针

指向一行的数组指针变量,简称为行指针

定义形式  类型标识符(*指针变量名)[长度]  (这里的长度是二维数组的列数)

例如: int (*p) [4];

p是一个行数组指针变量,一行有4个元素

注意:*先和p结合,说明p是指针变量,再指向int [4],说明它是指向int [4]的数组指针

[ ]的优先级高于*,如果没有括号,就变成了int*  p[4];   它是指针数组

2、数组指针的实例说明


 

int main()
{
	int a[10] = { 0 };
	printf("%p\n", a);                 //0000000B724FF7B8
	printf("%p\n", a + 1);             //0000000B724FF7BC
 
		printf("%p\n", &a[0]);        //0000000B724FF7B8
		printf("%p\n", &a[0] + 1);    //0000000B724FF7BC
 
		printf("%p\n", &a);           // 0000000B724FF7B8
		printf("%p\n", &a + 1);       //0000000B724FF7E0
		return 0;
}


可以看出  数组名a 和 &a[0] 都能代表,数组的首元素地址。+1,就跳过了4个字节(因为sizeof(int)=4)
但&a(这里是取整个数组的地址)也是一样的结果,是因为数组的存储是从首地址开始的,

int (*p)[10]=&a(这里是取整个数组的地址)  把整个数组地址放到p指针里
&a+1 ,比 &a 跳过了40(4*10)个字节,这是跳过了int a[10] 这一整个数组
 

三、用数组指针存放一个数组的总地址

1,int arr[10];


//这是一个指针数组,有10个元素,每个元素的类型是  int
//这时想用一个指针来存放,这一整个指针数组的地址
//应写成
int(*p)[10] = &arr;       //他就是一个  数组指针!!!,用来存放数组的全部地址
//括号里的* 说明了 p是一个,指针变量
//把(*p)去掉,剩下他的类型——int [10]  表明了 p这个指针指向的是  int  [10] 这整个数组,这个数组有10个元素,元素类型为 int(整形)

2、int* arr[10];


//这是一个指针数组,有10个元素,每个元素的类型是  int*
//这时想用一个指针来存放,这一整个指针数组的地址
//应该要写成    
int* (*p)[10] = &arr;     //他就是一个  数组指针!!!,用来存放数组的全部地址
//括号里的* 说明了 p是一个,指针
// 把(*p)去掉,剩下的就是 他的类型—— int* [10]   说明,它是一个指针数组,它有10个元素,元素类型为  int* (也是指针)
 

四、数组指针数组(存放数组指针的数组)

int(*prr[10])[5];


// [ ]的优先级高于* ,所以prr先和,[10] 结合,它是一个数组,有10个元素
//通常我们都是去掉数组名来判断他的类型,
//去掉prr[10]  剩下它的类型——int (*) [5] ,
//说明了他的类型是一个,数组指针
//所以 可以发现,prr是一个存放数组指针的一个数组,它有10个元素,每个元素类型为 数组指针


五、一维数组传参(传的是首元素地址)

主函数如下

int main()
{
	int arr1[10] = { 0 };
	int* arr2[10] = { 0 };
	test1(arr1);
	test2(arr2);
	return 0;
}

1.形参写成数组形式

void test1(int arr1[])  //[] 里面的元素个数课不写(写大了也不要紧)
{

}

void test2(int* arr[]) //[] 里面的元素个数课不写(写大了也不要紧)
{

}

2.形参写成指针形式

void test(int* p)  //int 是arr1 数组一样的类型,
{                     //*说明p是指针,p用来接收arr1数组的首元素的地址

}

void test(int** p) //int* 是arr2数组同一类型, 
{                   //第二个*说明,p是指针变量,p用来接收arr2数组的首元素地址

}

六、二维数组传参 (传的是首元素地址)

主函数如下

int main()
{
	int arr[3][5];
	test(arr);
	return 0;
}

1.形参写成数组形式

void test(int arr[3][5])   //行数可以省略,列数不行
{

}

2.形参写成指针形式

void  test(int(*p)[5])  //  * 说明p是指针变量,他指向int [5] 这一数组 5个元素,
{                             //类型为int(与原来的类型一致)
					          // [5] 这就是原来二维数组的列数,
}	                         //这就是行指针,指向一行的一维数组

同理 char *[3][5]  传参时写成行指针 ——char*(*p)[5]

七、二维数组传参 (传的是整个二维数组地址)

void test1(int(*p)[5])
{

}

void test2(int(*p)[3][5]) //明确标明3行5列
{

}

int main()
{
	int arr[3][5];
	test1(arr);         //传递的是首元素的地址
	test2(&arr);     //传递的是整个二维数组的地址时
	return 0;
}

八、二级指针变量

void test(char** pp)   //二级指针变量
{

}

int main()
{
	char a = 'w';
	char* p = &a;
	char** pp = &p;       //pp是一个二级指针
	test(pp);
	return 0;
}

九、函数指针

1.函数指针的写法

int Add(int x, int y)
{
	return x + y;
}
int main()
{
	int (*p) (int, int) = Add;
}
 
 
 
 
int test(char* str)
{
 
}
int main()
{
	int (*p)(char*) = test;
}

对比可知,函数指针的写法

返回值类型,参数的类型都和函数定义函数时相同,一一对应, 但不要写出变量,只写类型标识符

2、函数指针的地址

可以看出,访问函数指针的地址加不加&都一样      函数地址也是他本身

3.函数指针的使用

可以看出*号个数有无,多少没什么区别,

但把Add看做地址的话,(*pt) 表示把它解引用,更好理解

调用函数指针的时候就像调用自定义函数一样 

Add(函数名)相当于pt

十、函数指针数组(存放函数指针的数组)

1、函数指针数组介绍

pf先与[4]结合,[ ]比  *  优先级更高,说明    pf【4】是个数组,有4个元素

去掉pf[4]   剩下int(*)(int ,int ) 就是它的类型,函数指针,

pf【4】这个数组,有4个元素  类型都是函数指针

2、函数指针数组使用

以(计算器)为例

int (*p[5])(int, int) = { 0,Add,Sub,Mul,Div }; 

这里创建了这样一个函数指针数组,0相当于NULL (空指针)

这样的优点是很方便,不要用多个case 语句,

但是 前提是 函数指针的返回值类型,参数类型都是一致的

int  Add(int i, int j)
{
	return i + j;
}
int Sub(int i, int j)
{
	return i - j;
}
int Mul(int i, int j)
{
	return i * j;
}
int Div(int i, int j)
{
	return i / j;
}
 
void meau()
{
	printf("********************\n");
	printf("***1.Add    2.Sub***\n");
	printf("***3.Mul    4.Div***\n");
	printf("***    0.Exit    ***\n");
	printf("********************\n");
}
 
int main()
{
	int (*p[5])(int, int) = { 0,Add,Sub,Mul,Div }; //函数数组的使用,简单,
                                                 //但要求了类型都一 样才能实现
    int input = 1;                                               
	int a, b;
	while (input)
	{
		meau();
		printf("请选择:->");
		scanf("%d", &input);   
		if (input >= 1 && input <= 4)
		{
			printf("enter two numbers\n");
			scanf("%d%d", &a, &b);
			int ret = p[input](a, b);       //int ret = (*p[input])(a, b);  
                                             //都行,函数地 址相当函数本身
                                                              
			printf("结果为%d\n", ret);
		}
		else if(input ==0)
		{
			printf("程序退出\n");
			break;
		}
		else 
			printf("请重新输入\n");
	
}
	return 0;
}

十一、指向函数指针数组的指针(函数指针数组指针)

1、介绍

(1)、类比:

int a[10];
          int(*p)[10] = &a;

(2)、推理


 

int (*p1[4])(int, int);  


//函数指针数组,它有4个元素,每个元素类型为 (去掉p[4]看类型) ----- (int (*)(int ,int )
//这时又要有一个指针指向这个数组(函数指针数组) ,来存放这个数组的地址,
//应该写成

int (*(*p2)[4])(int, int)=&p1;    

 //去掉(*p2)[4]看类型,为int(*)(int ,int)
    //p2是一个指向函数指针数组的指针

详细如图:

2、使用 

int  Add(int i, int j)
{
	return i + j;
}
int Sub(int i, int j)
{
	return i - j;
}
int Mul(int i, int j)
{
	return i * j;
}
int Div(int i, int j)
{
	return i / j;
}

int main()
{
	int (*pfarr[4])(int, int) = { Add,Sub,Mul,Div };
	int (*(*p)[4])(int, int) = &pfarr;
	for ( int i = 0; i < 4; i++)
	{
		int ret = (*p)[i](6, 2);
		printf("%d\n", ret);
	}
	return 0;
}

int  Add(int i, int j)
{
	return i + j;
}
int Sub(int i, int j)
{
	return i - j;
}
int Mul(int i, int j)
{
	return i * j;
}
int Div(int i, int j)
{
	return i / j;
}

int main()
{
	int (*pfarr[4])(int, int) = { Add,Sub,Mul,Div };
	int (*(*p)[4])(int, int) = &pfarr;         //函数指针数组指针
	for ( int i = 0; i < 4; i++)
	{
		int ret = (*p)[i](6, 2);//解引用p到pfarr 用i依次使用加,减,乘,除
		printf("%d\n", ret);
		
	}
	printf("\n");
	for (int i = 0; i < 4; i++)
	{
		int ret = pfarr[i](6, 2);         //直接使用函数指针数组
		printf("%d\n", ret);
	}
	return 0;
}

 直接使用函数指针数组和解引用函数指针数组指针效果一样

  • 16
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值