自学内容网 自学内容网

指针详解(2)

指针详解(2)

对数组名的理解

在C语言里数组名还表示着数组首元素地址。

int arr[5] = {1, 2, 3, 4, 5};
int* p = &arr[0];
int* p = arr;

以上这两种,对指针p进行赋值的操作均是等价的,都将数组首元素的地址赋给指针p。

不妨,我们可以测试一下

int main()
{
int arr[5] = {1, 2, 3, 4, 5};
printf(" &arr[0] = %p\n", &arr[0]);
printf(" arr     = %p\n", arr);
return 0;
}

在这里插入图片描述

更具运行结果能够证明,arr 与 &arr[0] 是等价的,都表示着数组首元素的地址


但也有两个例外,

  • sizeof(数组名),数组名表示整个数组,计算的是整个数组的大小,单位字节
  • &数组名,数组名表示整个数组,取出的是整个数组的地址。(整个数组的地址与首元素地址有区别的)

​ 除此之外,数组名表示首元素的地址

int main()
{
int arr[5] = { 1, 2, 3, 4, 5 };
printf("sizeof(arr) = %d\n", sizeof(arr));
return 0;
}

在这里插入图片描述

int main()
{
int arr[5] = { 1, 2, 3, 4, 5 };
int* p = &arr;
printf("&arr  = %p\n", &arr);
printf("arr   = %p\n", arr);
return 0;
}

在这里插入图片描述

这里会不会看着有点懵,&arr,与arr地址是相同的。不妨看看这串代码。

int main()
{
int arr[5] = { 1, 2, 3, 4, 5 };
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);
printf("&arr       = %p\n", &arr);
printf("&arr + 1   = %p\n", &arr + 1);
return 0;
}

在这里插入图片描述

根据运行结果不难看出,

  • &arr[0] 和 arr,表示着数组名首元素的地址

  • &arr[0] + 1 和 arr + 1,表示第二的数组元素的地址,地址大小加4

  • &arr,表示整个整个数组元素,打印的地址是首元素的地址

  • &arr + 1,由00AFF934 - 00AFF920得出地址向后偏移了20个字节,意味着&arr + 1跳出了数组元素的空间,指向了一块没有使用的空间

int arr[5] = { 1, 2, 3, 4, 5 };
int* p = arr + 1;
printf("p = %n\n", p);

可以运行这窜代码,将会报错,指针变量p 现在是一个野指针,使用printf函数解引用会报错。

一维数组传参本质

有了上述对数组名的理解,看看一下代码试着猜测运行结果。

void test(int arr[])
{
printf("sizeof(arr) = %d\n", sizeof(arr));
}
int main()
{
int arr[5] = { 1, 2, 3, 4, 5 };
printf("sizeof(arr) = %d\n", sizeof(arr));
test(arr);//等价test(&arr[0]);
return 0;
}

运行结果:
在这里插入图片描述

很明显在函数里计算数组元素的大小为4,主函数里为20。

根据代码运行结果发现,一维数组在进行传参时,因为数组名是首元素地址,在本质上传递的实际上是数组首元素的地址。在在理论上参数因该使用指针变量来接收数组元素的指针。

函数里使用sizeof(arr)计算数组大小时实际上计算的是指针变量的大小而不是数组的大小。
在这里插入图片描述

这里将编译器改变为64位,重新运行代码后的结果,在64位下计算指针的大小,为8个字节。


由于传递的是地址,在函数里可以使用指针变量来接收

void test(int* arr)
{
//printf("arr[2] = %d", arr[2]);
printf("*(arr + 1)= %d", *(arr + 1);//两者等价
}

这里,通过指针的写法来访问数组元素,*(数组名 + 下标),这种写法与 arr[1]是等价的,数组名表示收元素地址,对地址+ 1或+ 其它数字,地址就会跳转到该数字对应下标的数组元素。

int main()
{
int arr[5] = { 1, 2, 3, 4, 5 };
printf("*(arr + 4) = %d\n", *(arr + 4));
return 0;
}

注意:数组下标最大是4,在使用指针的写法时,*(arr + 5)这样是错误的写法,将会对野指针解引用,程序报错。

二级指针

指针变量用来存放变量地址,那指针变量的地址呢?变量的地址存放在二级指针里

int main()
{
int a = 0;
int* pa = &a;
int** ppa = &pa;
**ppa = 6;
printf("%d\n", a);
return 0;
}

在这里插入图片描述

变量地址
a=*pa=**ppa0X00FFA40
pa=*ppa0X00FFA80X00FFA4
ppa0X00FFAC0X00FFA8

对一级指针pa进行解引用 *pa,找到了变量a,a的值为 0,对二级指针ppa进行解引用找到了一级指针pa,pa的值为0X00FFA4,是变量a的地址,在进行解引用就找到了变量a,a的值为0。


案例:对二级指针 ppa解引用一次

int main()
{
int a = 0;
int b = 20;
int* pa = &a;
int** ppa = &pa;
*ppa = &b;
printf("%d\n", **ppa);
return 0;
}

这里对二级指针进行解引用(*ppa),实际上就是对一级指针进行操作,将b的地址赋给一级指针pa。它们之间的关系变为:

变量地址
b=*pa=**ppa0X00FFA020
pa =*ppa0X00FFA80X00FFA0
ppa0X00FFAC0X00FFA8

对ppa解引用两次就是b的值,打印的结果为20

在这里插入图片描述

指针数组

指针数组是什么?是指针,还是数组?更具之前学习过的,整形数组(int arr[]),字符数组(char arr[]),整形数组使用来存放整形的数组,字符数组是用来存放字符的数组,指针数组使用来存放指针的数组吗?是滴没错!

在这里插入图片描述

指针数组的写法,它可以是整形,也可以是字符类型,int* arr[5]; char* arr[5];

数字5代表着这个指针数组可以存放5个指针变量。指针数组与整形数组字符数组的理解相比并不难,它只是用来存放指针的数组。

初始化:

int a = 10;
int b = 6;
int* parr[2] = {&a, &b};

int* p[5] = { 0 };

使用指针数组,它与常见的整形数组用法区别不大

printf("%d\n", *parr[0]);

由于数组里存放的是指针,parr[0]是一个指针,对指针进行解引用来获取它指向的数据。

使用指针数组模拟二维数组

首先,二维数组在空间上是连续存放的,int arr[3][5];,arr数组里,每行有5个元素,一共有三行,我们说可以近似的将这个三行的二维数组看作三个一维数组的拼接,诶~事实上是可以这样操作的。是以指针数组就可以完成这样的作法。

#include <stdio.h>
int main()
{
int i, j;
int arr1[5] = { 0 };
int arr2[5] = { 0 };
int arr3[5] = { 0 };
int* parr[5] = {arr1, arr2, arr3};
for (i = 0; i < 3; i++)
{
for (j = 0; j < 5; j++)
{
parr[i][j] = i + j;
}
}
for (i = 0; i < 3; i++)
{
for (j = 0; j < 5; j++)
{
printf("%d ", parr[i][j]);
}
printf("\n");
}
return 0;
}

在这里插入图片描述

​ 这里呢,使用指针数组模拟了二维数组的赋值和打印,为什么可以这样 parr[i][j]; ,数组下标引用操作符,本质上可以理解为 *(arr + 1),对数组首元素加有效的数字,就获得数组下标对应元素的地址,然后进行解引用,就获取了地址对应的值,parr[i][j]; 同样是这样理解,第一个下标引用操作符找到这个指针数组的元素,

parr[0][j] 等价 arr1[j] ,在对arr1使用数组下标引用操作符就可以找到 i,j对应的位置,和元素。

​ 同理,也可以使用二级指针模拟二维数组。首先对二级指针开辟空间一块整形指针类型的空间,然后在循环里

对每一个整形指针开辟相同大小的空间,就完成了对二维数组结构建立,之后就可以正常使用下标引用操作符,对二级指针赋值,打印之类的操作。

有效的数字,就获得数组下标对应元素的地址,然后进行解引用,就获取了地址对应的值,parr[i][j]; 同样是这样理解,第一个下标引用操作符找到这个指针数组的元素,

parr[0][j] 等价 arr1[j] ,在对arr1使用数组下标引用操作符就可以找到 i,j对应的位置,和元素。

​ 同理,也可以使用二级指针模拟二维数组。首先对二级指针开辟空间一块整形指针类型的空间,然后在循环里

对每一个整形指针开辟相同大小的空间,就完成了对二维数组结构建立,之后就可以正常使用下标引用操作符,对二级指针赋值,打印之类的操作。


原文地址:https://blog.csdn.net/sparrowfart/article/details/140303345

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!