深挖C语言:深入理解指针(3)

目录

1. 数组名的理解

2.  使用指针访问数组

3.  一维数组传参的本质

4.  冒泡排序

5.  二级指针

6. 指针数组

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


正文:

1. 数组名的理解

    数组名实质是数组首元素的地址。验证代码如下:

#include<stdio.h>

int main()
{
int arr[10] = {0};
printf("%p\n",&arr[0]);
printf("%p\n",arr);//二者结果相同

return 0;
}

这里有一段代码,又会提出新的问题:

int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
printf("%d\n",sizeof(arr));
//输出结果为:40

数组名确实是数组的首元素地址,但是有两个例外的情况:

(1)sizeof(数组名),此时数组名表示整个数组,求的是整个数组的内存大小(单位字节)

(2)&数组名,这里的数组名表示整个数组,取出的是整个数组的地址(整个数组的地址与数组首元素的地址内涵上不同)

测试代码:

#include<stdio.h>

int main()
{
int arr[10] = {1,2,3,4,5,6,7,8,9,10};
printf("&arr[0]=%p\n",&arr[0]);
printf("arr    =%p\n",arr);
printf("&arr   =%p\n",&arr);
//三者输出结果完全相同

return 0;
}

那么 arr 与 &arr 之间的到底有什么区别呢?进行数组 + - 整数试试:

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

代码结果:

&arr[0] = 0077F820
arr     = 0077F820
&arr    = 0077F820
&arr[0] + 1 = 0077F824
arr + 1     = 0077F824
&arr + 1    = 0077F848

    结论:在指针+-整数时,arr + 1 跳过的是数组的一个元素,而 &arr + 1 跳过的是整个数组,这就很好的解释了 &数组名 代表的是整个数组。

2.  使用指针访问数组

#include<stdio.h>

int main()
{
int arr[10] = {0};
int* parr = arr;
//利用指针对数组进行输入
for(int i = 0;i < 10;i++)
scanf("%d",p + i);
//利用指针进行数组打印
for(int i = 0;i < 10;i++)
printf("%d",*(p+i));//等价于 printf("%d",arr[i]);

return 0;
}

    在上述代码中,不难看出:*(p + i) == p[ i ] ,又因为 arr 是数组首元素地址,则:

结论:arr[ i ] == *(arr + i) ;&arr[ i ] == arr + i

3.  一维数组传参的本质

    问题:我们之前都是在函数外部计算数组的元素个数,那可以把数组传给一个函数后,函数内部求数组元素的个数吗?(这里指字符串数组以外的数组)

#include<stdio.h>

void test(int arr[])
{
int sz2 = sizeof(arr) / sizeof(arr[0]);
printf("sz2 = %d\n",sz2);
}
int main ()
{
int arr[10] = {1,2,3,4,5,6,7,8,9,10};
int sz1 = sizeof(arr) / sizeof(arr[0]);
printf("sz1 = %d\n",sz1);
test(arr);
return 0;
}
// 结果:sz1 = 10   sz2 = 1

函数形参接收的是数组首元素地址,在函数内部写的 sizeof(arr) ,计算的是一个地址的大小,而不是一个数组的大小,所以不能利用函数求解数组元素个数。

 结论

1.  数组名是数组首元素的地址,在数组传参的时候,传递的是数组名,本质上数组传参传递的是数组首元素的地址。

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

4.  冒泡排序

    核心思想:两两相邻元素进行比较。

#include<stdio.h>

void bubble_sort(int arr[],int sz)
{
for(int i = 0;i < sz - 1;i++)
{
int flag = 1;
for(int j = 0;j < sz - 1 - i;j++)
{
if(arr[j] > arr[j + 1])
{
flag = 0;
int tmp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = tmp;
}
}
if(flag == 1)
break;
}
for(int i = 0;i < sz;i++)
printf("%d ",*(arr + i));
}

int main()
{
int arr[10] = {10,9,8,7,6,5,4,3,2,1};
int sz = sizeof(arr) / sizeof(arr[0]);
bubble_sort(arr,sz);

return 0;
}

5.  二级指针

    指针变量也是变量,是变量就会有地址,那么指针变量存放在哪里?

存放指针变量地址的变量,称为二级指针

二级指针变量的运算:

以上述代码为例:

*ppa == pa;**ppa == a;一个 * 解引用一次,两个 * 解引用两次。

6. 指针数组

    指针数组是存放指针的数组。

parr 是一个数组,数组有 3 个元素,每个元素的类型是 int *

指针数组的每个元素是个地址,又可以指向一块区域。

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

#include<stdio.h>

int main()
{
int arr1[] = {1,2,3,4,5};
int arr2[] = {2,3,4,5,6};
int arr3[] = {3,4,5,6,7};
//数组名是数组首元素的地址,类型是 int* ,就可以存放在 parr 数组中。
int* parr[3] = {arr1,arr2,arr3};
//外层循环控制每一个指针数组元素
for(int i = 0;i < 3;i++)
{
//内层循环对指针指向内容进行访问
for(int j = 0;j < 5;j++)
printf("%d ",parr[i][j]);
printf("\n");

return 0;
}

parr[ i ] 是访问 parr 数组的元素,parr[ i ]找到的数组元素指向了整型一维数组,parr[ i ][ j ]

就是整型一维数组中的元素。

二维数组每一行在内存空间上都是连续的,上述代码每行一维数组在内存上不连续,只是模拟出二维数组的效果,不完全等同于二维数组。

最后,感谢阅读,欢迎大家交流与指正。

源码链接: https://pan.quark.cn/s/a4b39357ea24 DMA(直接内存访问)是计算机系统中一种关键的数据传输机制,它使得特定的硬件子系统得以直接对系统内存进行读写操作,无需CPU的介入。这种机制对于提高I/O操作的效能具有极其重要的作用,特别是在网络设备、存储设备等驱动程序的编写过程中占据着核心地位。Cache(缓存)则是一种用于暂存频繁访问的数据和指令的存储结构,其目的是减少处理器对主存储器的访问次数,进而增强系统的整体性能。然而,DMA和Cache之间存在着一致性的挑战,特别是在部分嵌入式系统中,DMA操作可能绕过Cache机制,从而引发数据不一致的情况,这就需要采取一系列策略来维护Cache的一致性。 在DMA的运作模式中,主要存在两种Cache一致性问题:流式DMA(streaming DMA)与一致性DMA(coherent DMA)。流式DMA通常应用于需要大量数据传输的场景,它不关注Cache的一致性,因此传输速度较快,但要求软件开发者自行管理数据的一致性。而一致性DMA则保证了在DMA传输期间,数据在Cache与主内存之间保持同步,通常适用于对一致性要求较高的应用场景。 在Linux内核中,为了有效管理DMA操作,提供了一系列接口函数。其中,一致性DMA接口负责维护数据的一致性,而流式DMA接口则提供了更快的传输速度,但要求开发者自行解决数据一致性的问题。开发者在选用这些接口时,必须依据硬件平台的特点和性能需求,选择合适的DMA模式。 Cache一致性的解决方案通常取决于硬件平台的属性。在某些先进的处理器架构中,Cache对程序员而言是透明的,即处理器与Cache控制器之间的交互对程序员不可见,从而简化了编程的复...
评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值