深入了解C语言指针(2)

本文详细探讨了指针的特性,包括数组名作为首元素地址的特殊情况,数组大小的计算,使用指针访问数组的方法,一维数组传参的本质,冒泡排序示例以及二级指针和指针数组的应用。
摘要由CSDN通过智能技术生成

深入了解指针(2)

1.数组名的理解

先前我们说过数组名其实就是数组的首元素地址

我们现在就来验证一下 这个说法到底有多少可信度

int main()
{
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
	int* p = &arr[0];
	printf("%p\n", arr);
	printf("%p\n", p);
	printf("%p\n", &arr[0]);
	// 运行后 我们发现地址都是一样的
	return 0;
}
从这个结果看 数组名其实就是数字的首元素地址的说法是正确的

那我们再来看一种情况

	printf("%d\n",sizeof(arr)); // 40

如果数组名其实就是数字的首元素地址的话 这里应该是4或者8 但是是40说明了这个说法还是有一点问题的

还要第二种情况

&数组名 这里的地址是整个数组的地址

	printf("%p\n", arr);
	printf("%p\n", &arr);
	printf("%p\n", &arr[0]);

这三个打印的其实都是数组首元素的地址

	// 在这种情况下 区别就出来了
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
	printf("arr + 1 = %p\n", &arr + 1);
	printf("&arr[0] + 1 = %p\n", &arr[0] + 1);
	printf("arr + 1 = %p\n", arr + 1);

&arr + 1 这个实际上会跳过40个字节

来一张图片更加清晰直观

image-20240322000207970

总结:(数组名的两个例外)

只有两种情况下的数组名不代表数组的首元素地址

  1. sizeof(数组名) 这里放的数组名代表的是整个数组,计算的是整个数字的大小,单位是字节
  2. &数组名 这里的数组名也是代表整个数组,取出的是 整个数组的地址

注意!

我们之前说过 每个指针都有类型 代表着它们的步长 也就是一次+1跳过多少个字节

&数组名 + 1 一次跳过了40个字节 那这是什么类型呢

这其实是一个特殊类型———— 数组指针类型

2.使用指针访问数组

在之前的学习中,我们已经知道了指针访问数组的方法

现在再来重温加深一下

int main()
{
	// 通过指针来对数组进行输入和输出
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
	int sz = sizeof(arr) / sizeof(arr[1]);
	int* p = arr;  // 指针类型要和数组类型是一个类型 
	 输入
	//for (int i = 0; i < sz; i++)
	//{
	//	scanf("%d", p + i); // p + i 等价于 &arr[i]
	//}
	 输出
	//for (int i = 0; i < sz; i++)
	//{
	//	printf("%d ", *p);
	//	p++;  // 要注意这里的p执行完之后已经越界访问了
	//}
	// 还有一种写法
	for (int i = 0; i < sz; i++)
	{
		/*printf("%d ", *(p+i));*/ // 这里的p执行完后还是指向首元素地址
		// 我们可以思考一下  arr就是首元素地址  也就是说p代表的地址和arr是一样的
		// 那么*(p+i)和 *(arr+i) 其实就是等价的  
		// 而*(arr+i) 和  arr[i] 又是等价的
		// 那么有没有一种写法是 p[i]呢
		printf("%d ", p[i]);  // 我们发现是可以的
	}

	return 0;
}

**实际上在编译器当中 (p+i)和 (arr+i) 和 arr[i] 和 p[i]的写法是完全等价的

*编译器会将arr[i] 转化成这种方式 (arr+i) 去计算

其实还有更加奇葩的写法

*(arr + i) == *(i + arr) == i[arr] // 这种写法并不建议

其实就是说 [] ----- 操作符

x[y]的x和y对于这个[]操作符来说其实就是两个操作数 顺序调换一下并不会有什么影响

3.一维数组传参的本质

void test(int arr[]) // 本质上接受的是指针
{
	int sz2 = sizeof(arr) / sizeof(arr[0]); // 2
	printf("sz2 = %d\n", sz2);
}
int main()
{
	int arr[10] = { 0 };
	int sz1 = sizeof(arr) / sizeof(arr[0]); // 10
	printf("sz1 = %d\n", sz1);
	test(arr);
	return 0;
}

由于 传过去的是arr 本质上是首元素的地址 在64位的环境下地址的大小是8个字节 因此8/4 = 2

而sz1 的arr代表的就是整个数组的大小 就是40 / 4 = 10

4.冒泡排序

排序算法其实很多

  1. 冒泡排序
  2. 选择排序
  3. 出入排序
  4. 希尔排序
  5. 快速排序

这里我们学习一个冒泡排序

// 冒泡排序
void bubble_sort(int arr[],int sz)
{
	int flag = 0;
	for (int i = 0; i < sz - 1; i++)
	{
		for (int j = 0; j < sz - 1; j++)
		{
			//if (arr[j] > arr[j + 1])
			//{
			//	int tmp = arr[j];
			//	arr[j] = arr[j + 1];
			//	arr[j + 1] = tmp;
			//	flag = 1;
			//}
			// 也可以改成指针的形式
			if (*(arr + j) > *(arr + j + 1))
			{
				int tmp = *(arr+j);
				*(arr + j) = *(arr + j + 1);
				*(arr + j + 1) = tmp;
				flag = 1;
			}
		}
		if (flag == 0)
			break;
	}
}
void Print(int* p, int sz)
{
	for (int i = 0; i < sz; i++)
	{
		printf("%d ", *(p + i));
		
	}
}
int main()
{
	int arr[] = { 1,3,2,4,6,7,5,8,9,10 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	bubble_sort(arr, sz);
	Print(arr, sz);

	return 0;
}

5.二级指针

我们都知道指针可以指向一个地址 并把这个地址存起来 但是指针变量存下一个地址的同时也需要开辟自己的内存空间 ,那么这个指针变量也有自己的地址 那么当另一个指针指向指针变量的地址时这个指针就叫二级指针

image-20240322124122110

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

pp就是2级指针 放着p这个指针变量的地址

int * p 的第一个 * 代表p是指针变量 int 代表指针指向的是一个int类型的变量 代表着p这个指针变量的步长

int** pp 最右边的 * 代表了pp是一个指针变量 int*代表了 指向的对象是一个int *类型的

来看一个实际应用

int main()
{
	int a = 10;
	int* p = &a;
	int** pp = &p;
	**pp = 100;
	printf("%d\n", a); // 100
	return 0;
}

*pp代表解引用pp这个指针变量 找到的是p 在解引用一次 就找到了a

6.指针数组

我们知道数组有各种类型

  1. 有整形数组 int arr[10] 存放整形
  2. 有字符数组 char arr 存放字符

那么指针数组就是专门用来存放指针的数组

int* arr[5]

arr 是数组名称 [5]是数组元素个数 int * 表示数组内部存放的是指向整形的指针的类型

7.指针数组模拟二维数组

我们直接来看一个案例

//指针数组模拟二维数组

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[3] = { arr1,arr2,arr3 };
	for (int i = 0; i < 3; i++)
	{
		for (int j = 0; j < 5; j++)
		{
			printf("%d", arr[i][j]);
		}
		printf("\n");
	}
	return 0;
}

因为每个数组名都是各自首元素的地址 因此我们就可以实现上面这个代码

,3,4,5,6 };
int arr3[] = { 3,4,5,6,7 };
// 通过指针数组来储存一维数组 模拟实现二维数组
int* arr[3] = { arr1,arr2,arr3 };
for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 5; j++)
{
printf(“%d”, arr[i][j]);
}
printf(“\n”);
}
return 0;
}


因为每个数组名都是各自首元素的地址  因此我们就可以实现上面这个代码

  • 19
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值