指针进阶(指针数组,数组指针等记录)

    关于指针,大家都知道指针就是用来存储对应类型变量的地址的变量,这一篇文章就来记录我最近学习关于指针的一些新的知识

 1  字符指针

    字符指针有一些特别,比如下面这条语句

char *ch = "abcdef"; 

可能会有小伙伴认为,此处是将abcdef的地址放到了ch之中,但实际上,ch中只存储了a的地址,具体证明可以用下列的代码进行证明:

#include<stdio.h>
int main()
{
	const char* p1 = "abcdef";
	const char* p2 = "abcdef";
	char p3[] = "abcdef";
	char p4[] = "abcdef";

	if (p1 == p2)
		printf("p1==p2\n");
	else
		printf("p1!=p2\n");
	if (p3 == p4)
		printf("p3==p4\n");
	else
		printf("p3!=p4\n");
	return 0;
}

 

为什么会输出p1和p2相等而p3和p4不相等呢?

是因为编译器中,abcdef是一个常量字符串,是存放在一个单独的区域(堆区),因此char*类型的p1和p2指向的都是同一个地址,这也证明了像

char * p  = "abcdef";

这样写的代码实际上是将字符串的首地址给了p指针,而不是将整个字符串放在了p里面。

 

 

 2  指针数组

指针数组顾名思义,实际上就是指一种数组,其中的元素全部都是指针变量,如

int * a[20];--->包含20个int* 类型变量的数组

char *b[20];--->包含20个char * 类型变量的数组

利用指针数组可以做到模拟二维数组的目的,虽然不常用,不过在这里依旧给出示范

#include<stdio.h>
int main()
{
	int a[5] = { 1,2,3,4,5 };
	int b[5] = { 2,3,4,5,6 };
	int c[5] = { 3,4,5,6,7 };
	int* p[3] = { a,b,c };
	for (int i = 0; i < 3; i++)
	{
		for (int j = 0; j < 5; j++)
		{
			printf("%d ", *(*(p + i)+j));
		}
		printf("\n");
	}
	return 0;
}

 

 当然这个和二维数组依旧是有区别的,因为二维数组的内存地址的一片连续的区域,而这种模拟数组实际上内存并不一定连续的。

3  数组指针

 数组指针实际上就是指向数组的指针,

格式为  (指向的数组类型) (*  指针名)[指向数组的大小];

讲到数组指针就有一个绕不过去的问题————那就是关于数组名的使用。

我们都知道,数组名实际上就是数组首元素的地址,但是有两个例外

1:sizeof(数组名)    这里实际上计算的是整个数组的大小

2: &数组名  这里实际上也是取的整个地址的大小。

证明如下:

#include<stdio.h>
int main()
{
	int a[10] = { 1,2,3,4,5,6,7,8,9,10 };
	int* p = (&a + 1);

	printf("%d ", sizeof(a));
	printf("%d ", *(p-1));
	return 0;
}

 我们可以看到,p指向的是(&a+1)的位置,*(p-1)却是10,因此证明&a是取了整个数组的地址,因此它的加减就是加减整个数组大小的地址,而sizeof(a)也是计算了整个数组的字节大小,由此明白这两个例外。

而数组指针使用方式实际上和&(数组名)脱离不开关系

示例如下:

int a[10] = { 1,2,3,4,5,6,7,8,9,10};
int (*pa)[10] = &a;

这里实际上是将整个数组的地址都给了pa,而*pa则是首元素的地址,

要输出应该这样写:

for(int i = 0;i<10;i++)
printf("%d ",*(*p+i));

但是这种写法其实并不常用,一般都是在传参的时候使用数组指针,比如将二维数组传参时可以用数组指针,示例如下:

#include<stdio.h>

void print(int(*pa)[5], int r, int c)
{
	for (int i = 0; i < r; i++)
	{
		for (int j = 0; j < c; j++)
		{
			//printf("%d ", *(*(pa + i) + j));
			printf("%d ", pa[i][j]);
		}
		printf("\n");
	}
}

int main()
{
	int a[3][5] = { 1,2,3,4,5,2,3,4,5,6,3,4,5,6,7 };
	print(a, 3, 5);
	return 0;
}

两种打印方式都能够打印出来,并且比刚才在main函数中的数组指针更好理解。

4  数组传参,指针形参

1 一维数组传参

当我们将一维数组传参传到函数里面的时候,接收的形参应该是如下几种方式

#include<stdio.h>

void test(int a[10]);
void test(int* a);
void tese(int a[]);

int main()
{
	int a[10] = { 1,2,3,4,5,6,7,8,9,10 };
	test(a);

	return 0;
}

 一维数组传参比较简单,因此就不详细介绍了。

 2 二维数组传参

二维数组传参时,应该有如下几种接受方式

 


#include<stdio.h>

void test(int a[3][5]);
void test(int a[][5]);
void test(int(*a)[5]);

int main()
{
	int a[3][5] = { 1,2,3,4,5,2,3,4,5,6,3,4,5,6,7 };
	test(a);
}

首先是非指针类型的形参,由于a是一个二维数组,编译器将其作为参数传参过去的时候,编译器可以不知道一共有几行,但是一定要知道一共有几列,这样编译器才能成功的将其分成几组,因此

int a[][5]中的5不可省略。

而虽然传过去的是数组名,但是依旧不可使用int * 来接收,因为这里闯过去的实际上是第一行的地址,因此用数组指针接收。

3 形参是一级指针

当形参是一级指针的时候,我们可以传参的有

1:对应变量的地址

2:一维数组的数组名

3:相同类型的一级指针

4 形参是二级指针

当形参是二级指针的时候,我们可以传参的有

1:一级指针的地址

2:相同类型的二级指针

5  函数指针 

这里应该就是各位的知识盲区了,函数指针又是个什么东西;

其实如果有研究过函数栈帧的内容的话,可以知道,函数也是有地址的,因此函数也是可以用指针变量指向的。

#include<stdio.h>

int test(int a)
{

}

int main()
{
	int(*pf)(int) = &test;
    //int(*pf)(int) = test;
	return 0;
}

 大概示例就是这个,&函数名和函数名实际上都是函数的地址,而函数指针的类型和函数相同,而里面的括号则是与函数的形参类型一致,不用写出形参的名字。

函数指针的用途之一是解决代码冗余的情况,接下来我来介绍一下函数指针的一个操作:回调函数————>利用函数指针在A函数内部调用BCD等函数。

示例如下:

#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(*arr[4])(int, int) = { Add,Sub,Mul,Div };
	for (int i = 0; i < 4; i++)
	{
		printf("%d ", arr[i](2, 2));
	}
	return 0;
}

大概调用如下,利用了函数指针数组来计算2+2,2-2,2*2,2/2,结果也是4,0,4,1;

以上就是指针进阶的内容了。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值