初识指针3

目录

1指针的使用和传值调用

1.1strlen的模拟实现

1.2传值调用和传址调用

2数组名的理解

3使用指针访问数组

4一维数组传参的本质 


1指针的使用和传值调用

1.1strlen的模拟实现

strlen的功能是求字符串长度,统计的是字符串中\0之前字符的个数

函数原型如下:

size_t strlen(const char*str);

参数str接收一个字符串的起始地址,然后开始统计字符串中\0之前的字符个数,最终返回长度。如果要模拟实现只要开始从起始地址开始向后逐个字符的遍历,只要不是\0字符,计数器就加1,这样直到\0就停止。

#include<stdio.h>
int my_strlen(const char* str)
{
	int count = 0;
	while (*str) 
	{
		count++;
		str++;
	}
	return count;
}
int main()
{
	    char arr[] = "hello world";
		int a = my_strlen(arr);
		printf("%d",a);
}

1.2传值调用和传址调用

学习指针的目的是使用指针解决问题,那么什么问题,一定要用指针来解决呢?

例如:写一个函数,交换两个整形变量的值

如果不用地址我们会写出这样的代码:

#include<stdio.h>
void  exchange(int x,int y) 
{
	int step = x;
	x = y;
	y = step;
}

int main()
{	 
	int a = 1;
	int b = 2;
	printf("交换前:a=%d   b=%d", a, b);
	exchange(a, b);
	printf("交换后:a=%d   b=%d", a, b);
}

这里可以看到,并没有实现两个数字的交换 。原因是:在exchange函数里用形参接受了a和b的值,进行交换的也是形参x和y。并不会改变实参a和b的值。

这时候就要用到地址了。代码如下:

#include<stdio.h>
void  exchange(int* x,int *y) 
{
	int step = *x;
	*x =* y;
	*y = step;
}

int main()
{	 
	int a = 1;
	int b = 2;
	printf("交换前:a=%d   b=%d", a, b);
	exchange(&a, &b);
	printf("交换后:a=%d   b=%d", a, b);
}

所以,当我们未来用通过函数来修改主函数中的变量,就可以使用传值调用。

2数组名的理解

在上一个章节我们在使用指针访问数组的内容时,有这样的代码:

int data[10] = { 0,1,2,3,4,5,6,7,8,9 };
int* p = &data[0];

这里我们使用&data【0】的方式拿到了数组第一个元素的地址,但其实数组名本来就是地址,而且是数组首元素的地址,我们可以做个测试:

可以看到结果,确实是相等的,但有的同学会疑问,下面这段代码如何理解呢 ?

int main() 
{
	int data[10] = { 0,1,2,3,4,5,6,7,8,9 };
	printf("%zd\n", sizeof(data));
	return 0;
}

结果是:40,如果data是数组首元素的地址,那输出的应该是4\8, 

其实数组名就是首元素的地址,但是有两个意外:

  • sizeof(数组名),sizeof中单独放数组名,这里的数组名就表示整个数组,计算的是整个数组的大小,单位是字节
  • &数组名,这里的数组名表示整个数组,取出的是整个数组的地址(整个数组的地址和数组首元素的地址是有区别的)

除此之外,任何地方使用数组名,数组名都表示首元素的地址.

接下来看看这段代码:

int main() 
{
	int data[10] = { 0,1,2,3,4,5,6,7,8,9 };
	printf("&data[0]   =%p\n", &data[0]);
	printf("&data[0]+1 =%p\n", &data[0]+1);
	printf("data       =%p\n", data);
	printf("data+1     =%p\n", data+1);
	printf("&data      =%p\n", &data);
	printf("&data+1    =%p\n", &data+1);
	return 0;
}

结果如下:

&data[0]   =010FF8B8
&data[0]+1 =010FF8BC
data       =010FF8B8
data+1     =010FF8BC
&data      =010FF8B8
&data+1    =010FF8E0

 可以看到&data[0]和&data[0]+1相差4个字节,data和data+1相差4个字节,&data[0]和data都是数组的首元素地址,+1就是跳过4个字节.

但是&data和&data+1相差40个字节,这就是因为&data是数组的地址,+1就是跳过整个数组.

3使用指针访问数组

有了前面知识的支持,再结合数组的特点,我们就可以很方便的使用指针访问数组了. 

int main()
{
	int arr[10] = { 0 };
	int i = 0;
	int se = sizeof(arr) / sizeof(arr[0]);
	int* p = arr;
	//输入
	for (i = 0; i < se; i++)
	{
		scanf("%d", p+i);
	}
	//输出
	for(i=0;i<se;i++)
	{
		printf("%d", p[i]);
	}
	return 0;
}

这是不使用地址的写法,那是不是也可以用地址来实现呢?代码如下:

int main()
{
	int arr[10] = { 0 };
	int i = 0;
	int se = sizeof(arr) / sizeof(arr[0]);
	int* p = arr;
	//输入
	for (i = 0; i < se; i++)
	{
		scanf("%d", p+i);
	}
	//输出
	for(i=0;i<se;i++)
	{
		printf("%d", *(p+i));
	}
	return 0;
}

4一维数组传参的本质 

数组我们学过了,之前也讲过了,数组是可以传给函数的,那我们来讨论一下数组传参的本质.

首先从一个问题出发,我们之前都是在主函数中那个计算数组的元素个数,那我们可以把素组传给一个函数后,函数内部求数组的元素个数吗?

void test(int arr[])
{
	printf("%zd\n", sizeof(arr) / sizeof(arr[0]));

}
int main()
{
	int arr[10] = { 0,1,2,3,4,5,6,7,8,9 };
	printf("%zd\n", sizeof(arr) / sizeof(arr[0]));
	test(arr);

}

 结果如下:

10
1

我们发现函数内部没有正确获得数组的元素个数.

这个时候我们就要学习数组传参的本质了,上一小节我们学习了:数组名就是数组首元素的地址;那么在数组传参的时候,传递的就是数组名,也就是说本质上数组传参传递的就是数组首元素的地址.

所以函数形参的部分理论上应该使用指针变量来接受首元素的地址.那么在函数内部我们写sizeof(arr)计算的是一个地址的大小(单位字节)而不是数组的大小(单位字节).正是因为函数的参数部分的本质是指针,所以在函数内部是没办法求数组的元素个数的.

void test(int arr[])// 参数写成数组形式,本质上还是指针
{
	printf("%zd\n", sizeof(arr) / sizeof(arr[0]));

}
void test1(int *arr)
{
	printf("%zd\n", sizeof(arr) / sizeof(arr[0]));

}
int main()
{
	int arr[10] = { 0,1,2,3,4,5,6,7,8,9 };
	printf("%zd\n", sizeof(arr) / sizeof(arr[0]));
	test(arr);
	test1(arr);

}

总结:一维函数传参,形参的部分可以写成数组的形式,也可以写成指针的形式. 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值