欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 健康 > 养生 > 深入理解指针(3)

深入理解指针(3)

2025/4/25 11:26:00 来源:https://blog.csdn.net/2301_79479763/article/details/147468802  浏览:    关键词:深入理解指针(3)

1.指针的使⽤和传址调⽤

1.strlen的模拟实现

库函数strlen的功能是求字符串⻓度,统计的是字符串中\0 之前的字符的个数。函数原型如下:

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

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
int my_strlen(const char* str)
{int count = 0;while (*str != '\0'){count++;str++;}return count;
}
int main()
{char arr[] = "abcdrf";int len = my_strlen(NULL);printf("%d\n", len);return 0;
}

2.传值调⽤和传址调⽤

学习指针的⽬的是使⽤指针解决问题,那什么问题,⾮指针不可呢?

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

我们可以发现,此时并没有完成交换。

为什么呢?

进行调试观察:

x86:

继续调试

由此可见:

当实参传递给形参的时候,形参是实参的一份临时拷贝

对形参的修改不会影响实参的

我们发现在main函数内部,创建了a和b,a的地址是0x00cffdd0,b的地址是0x00cffdc4,在调⽤ Swap1函数时,将a和b传递给了Swap1函数,在Swap1函数内部创建了形参x和y接收a和b的值,但是 x的地址是0x00cffcec,y的地址是0x00cffcf0,x和y确实接收到了a和b的值,不过x的地址和a的地址不 ⼀样,y的地址和b的地址不⼀样,相当于x和y是独⽴的空间,那么在Swap1函数内部交换x和y的值, ⾃然不会影响a和b,当Swap1函数调⽤结束后回到main函数,a和b的没法交换。Swap1函数在使⽤ 的时候,是把变量本⾝直接传递给了函数,这种调⽤函数的⽅式我们之前在函数的时候就知道了,这 种叫传值调⽤。

那怎么办呢?

我们现在要解决的就是当调⽤Swap函数的时候,Swap函数内部操作的就是main函数中的a和b,直接 将a和b的值交换了。那么就可以使⽤指针了,在main函数中将a和b的地址传递给Swap函数,Swap 函数⾥边通过地址间接的操作main函数中的a和b,并达到交换的效果就好了。

对代码进行如下改写:

就可以啦!

我们可以看到实现成Swap2的⽅式,顺利完成了任务,这⾥调⽤Swap2函数的时候是将变量的地址传 递给了函数,这种函数调⽤⽅式叫:传址调⽤。

传址调⽤,可以让函数和主调函数之间建⽴真正的联系,在函数内部可以修改主调函数中的变量;所 以未来函数中只是需要主调函数中的变量值来实现计算,就可以采⽤传值调⽤。如果函数内部要修改 主调函数中的变量的值,就需要传址调⽤。

2.数组名的理解

之前在使⽤指针访问数组的内容时,有这样的代码:

int arr[10] = {1,2,3,4,5,6,7,8,9,10};

int *p = &arr[0];

这⾥我们使⽤ &arr[0] 的⽅式拿到了数组第⼀个元素的地址,但是其实数组名本来就是地址,⽽且 是数组⾸元素的地址,我们来做个测试。

发现数组名和数组⾸元素的地址打印出的结果⼀模⼀样,数组名就是数组⾸元素(第⼀个元素)的地 址。

那么下面一段代码怎么理解呢?

x86:

输出的结果是:40,如果arr是数组⾸元素的地址,那输出应该的应该是4/8才对。

其实数组名就是数组⾸元素(第⼀个元素)的地址是对的,但是有两个例外:

• sizeof(数组名),sizeof中单独放数组名,这⾥的数组名表⽰整个数组,计算的是整个数组的⼤⼩, 单位是字节

• &数组名,这⾥的数组名表⽰整个数组,取出的是整个数组的地址(整个数组的地址和数组⾸元素 的地址是有区别的) 除此之外,任何地⽅使⽤数组名,数组名都表⽰⾸元素的地址。

数组名就是首元素的地址,类型是int*,+1就是跳过一个整型(4个)

这⾥我们发现&arr[0]和&arr[0]+1相差4个字节,arr和arr+1相差4个字节,是因为&arr[0]和arr都是 ⾸元素的地址,+1就是跳过⼀个元素。

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

3.使⽤指针访问数组

有了前⾯知识的⽀持,再结合数组的特点,我们就可以很⽅便的使⽤指针访问数组了。

前提需要掌握以下两个要点:

1.数组在内存中是连续存在的

2.指针的+-整数运算,方便我们获得每一个元素的地址

如果我们再分析⼀下,数组名arr是数组⾸元素的地址,可以赋值 给p,其实数组名arr和p在这⾥是等价的。那我们可以使⽤arr[i]可以访问数组的元素,那p[i]是否也可 以访问数组呢?

1.数组就是数组,是一块连续的空间(数组的大小和数组元素个数和元素类型都有关系)

2.指针(变量)就是指针(变量),是一个变量(4/8个字节)

3.数组名是地址,是首元素的地址

4.可以使用指针来访问数组

arr[i] ====> *(arr+i)  

[]是下标引用操作符

所以:

arr[i] ====> *(arr+i)  ====>*(i+arr) ====>i[arr]

arr[i]    ==   i[arr]

  |                  |

*(arr+i)      *(i+arr)

相当于:2+1 ==== 1+2

这种方法是可行的在运行代码的时候不会报错,但是,不推荐这种方法!

4.⼀维数组传参的本质

数组是可以传递给函数的,这个⼩节我们讨论⼀下数组传参的本质。 ⾸先从⼀个问题开始,我们之前都是在函数外部计算数组的元素个数,那我们可以把函数传给⼀个函 数后,函数内部求数组的元素个数吗?

我们对代码进行以下修改:

发现输出只有1,之后对这段代码进行调试:

发现循环只循环了一次:

解析如下:

1.数组传参的本质是传递了数组首元素的地址,所以形参访问的数组和实参的数组是同一个数组的。

2.形参的数组是不会单独再创建数组空间的,所以形参的数组是可以省略掉数组大小的。

修改如下:

所以:

⼀维数组传参,形参的部分可以写成数组的形式,也可以写成指针的形式。

5.冒泡排序

排序:

很多数据,要排序

排序的算法有很多:

冒泡

插入

选择

快速

希尔

堆排序

……

冒泡排序的核⼼思想就是:两两相邻的元素进⾏⽐较。

10个数字9趟

n个数字n-1趟

如果数组为:9,0,1,2,3,4,5,6,7,8   进行一趟就有序了,怎么样才可以提高代码运行效率呢?

如下所示:对这一块的代码进行改写

本文中所涉及到的代码如下所示(运行时,取消“//”注释即可):

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
//int my_strlen(const char* str)
//{
//	int count = 0;
//	while (*str != '\0')
//	{
//		count++;
//		str++;
//	}
//	return count;
//}
//int main()
//{
//	char arr[] = "abcdrf";
//	int len = my_strlen(NULL);
//	printf("%d\n", len);
//	return 0;
//}//写⼀个函数,交换两个整型变量的值
//void Swapl(int x, int y)
//{
//	int tmp = x;
//	x = y;
//	y = tmp;
//}
//int main()
//{
//	int a = 10;
//	int b = 20;
//	printf("交换前 a= %d b= %d\n", a, b);
//	Swapl(a,b);
//	printf("交换后 a= %d b= %d\n", a, b);
//	return 0;
//}//void Swap2(int* pa, int* pb)
//{
//	int tmp = 0;
//	tmp = *pa;//tmp = a
//	*pa = *pb;//a = b
//	*pb = tmp;//b = tmp
//}
//int main()
//{
//	int a = 10;
//	int b = 20;
//	printf("交换前 a= %d b= %d\n", a, b);
//	Swap2(&a, &b);
//	printf("交换后 a= %d b= %d\n", a, b);
//	return 0;
//}//int main()
//{
//	int arr[10] = { 0 };
//	int* p = &arr[0];
//	int* p = arr;//数组名是数组首元素的地址
//	return 0;
//}//int main()
//{
//	int arr[10] = { 0 };
//	printf("  arr   = %p\n", arr);
//	printf("&arr[0] = %p\n", &arr[0]);
//	return 0;
//}//int main()
//{
//	int arr[10] = { 0 };
//	printf("%zd\n", sizeof(arr));
//	return 0;
//}//int main()
//{
//	int arr[10] = { 0 };
//	printf("  arr    = %p\n", arr);
//	printf("  arr+1  = %p\n", arr+1);
//	printf("&arr[0]  = %p\n", &arr[0]);
//	printf("&arr[0]+1= %p\n", &arr[0]+1);
//	printf(" &arr   = %p\n", &arr);
//	printf(" &arr+1 = %p\n", &arr + 1);
//	return 0;
//}//int main()
//{
//	int arr[10] = { 0 };
//	//使用指针来访问数组
//	int sz = sizeof(arr) / sizeof(arr[0]);
//	//输入10个值
//	int* p = arr;
//	int i = 0;
//	for (i = 0; i < sz; i++)
//	{
//		//输入一个值
//		scanf("%d", p+i);//&arr[i]
//	}
//	//输出10个值
//	for (i = 0; i < sz; i++)
//	{
//		printf("%d ", *(p + i));
//	}
//	return 0;
//}//int main()
//{
//	int arr[10] = { 0 };
//	//使用指针来访问数组
//	int sz = sizeof(arr) / sizeof(arr[0]);
//	//输入10个值
//	int* p = arr;
//	int i = 0;
//	for (i = 0; i < sz; i++)
//	{
//		//输入一个值
//		scanf("%d", p + i);//&arr[i]
//	}
//	//输出10个值
//	for (i = 0; i < sz; i++)
//	{
//		printf("%d ", p[i]);//*(p+i) //arr[i] //*(arr+i)
//	}
//	return 0;
//}//void Print(int arr[10],int sz)
//{
//	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 };
//	int sz = sizeof(arr) / sizeof(arr[0]);
//	Print(arr, sz);
//	return 0;
//}//void Print(int arr[10])
//{
//	int sz = sizeof(arr) / sizeof(arr[0]);
//	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);
//	return 0;
//}数组传参的时候,形参是可以写成数组的形式的
但是本质上还是指针变量
//void Print(int arr[10])//int* arr
//{
//	int sz = sizeof(arr) / sizeof(arr[0]);
//	//得不到元素个数的
//	int i = 0;
//	for (i = 0; i < sz; i++)
//	{
//		printf("%d ", arr[i]);
//	}
//}//void Print(int* p, int sz)//应该是指针
//{
//	int i = 0;
//	for (i = 0; i < sz; i++)
//	{
//		printf("%d ", *(p + i));
//	}
//}
//int main()
//{
//	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
//	int sz = sizeof(arr) / sizeof(arr[0]);
//	Print(arr, sz);//arr数组名就是数组首元素的地址
//	//sizrof(arr)
//	//&arr
//	return 0;
//}冒泡升序
//void bubble_sort(int arr[],int sz)
//{
//	//确定趟数
//	int i = 0;
//	for (i = 0; i < sz - 1; i++)
//	{
//		//一趟排序的过程
//		int j = 0;
//		for (j = 0; j < sz-1-i ; j++)
//		{
//			if (arr[j] > arr[j + 1])
//			{
//				int tmp = arr[j];
//				arr[j] = arr[j + 1];
//				arr[j + 1] = tmp;
//			}
//		}
//	}
//}
//print_arr(int arr[], int sz)
//{
//	int i = 0;
//	for (i = 0; i < sz; i++)
//	{
//		printf("%d ", arr[i]);
//	}
//}
//int main()
//{
//	int arr[] = { 9,8,7,6,5,4,3,2,1,0 };
//	//排序
//	int sz = sizeof(arr) / sizeof(arr[0]);
//	bubble_sort(arr,sz);
//	//打印
//	print_arr(arr, sz);
//	return 0;
//}//冒泡升序
void bubble_sort(int arr[], int sz)
{//确定趟数int i = 0;for (i = 0; i < sz - 1; i++){//假设已经有序了int flag = 1;//一趟排序的过程int j = 0;for (j = 0; j < sz - 1 - i; j++){if (arr[j] > arr[j + 1]){int tmp = arr[j];arr[j] = arr[j + 1];arr[j + 1] = tmp;flag = 0;}}if (flag == 1){break;}}
}
print_arr(int arr[], int sz)
{int i = 0;for (i = 0; i < sz; i++){printf("%d ", arr[i]);}
}
int main()
{int arr[] = { 9,8,7,6,5,4,3,2,1,0 };//排序int sz = sizeof(arr) / sizeof(arr[0]);bubble_sort(arr, sz);//打印print_arr(arr, sz);return 0;
}

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词