C:指针和数组之间的关系-学习笔记

JonlyMay 2024-08-24 17:05:02 阅读 99

目录

闲话:

引言:

1、数组名的理解

2、指针访问数组

3、一维数组传参的本质

4、二级指针

5、指针数组

6、指针数组模拟二维数组

结语:


闲话:

指针这个模块更新的比较慢,主要是小编还得学习指针的知识点,虽然说是学习笔记,但是也是需要小编自己学会后才能够更好的将知识点介绍给大家,所以还请见谅一下哈!!!

引言:

本篇文章将带来数组与指针之间的关系介绍,希望能对大家有所帮助!


1、数组名的理解

int arr[10] = {0};

从概念上讲:

数组名代表数组在内存中的起始地址。可以将数组名视为一个指向数组首个元素的指针。

例如,定义一个整型数组 int arr[10]  , arr  就代表了这个数组的起始地址。

来看一组对比:

<code>#include <stdio.h>

int main()

{

int arr[10] = { 0 };

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

printf(" arr = %p\n", arr);

return 0;

 结果:(x86环境下展示的地址)

我们发现使用数组名打印地址和取首元素地址打印的结果相同。

 因此可以更加确定数组名就是数组首元素的地址。

但是!在 C 语言中,数组名具有特殊的含义和性质。因此,数组名肯定不能只有这么单一的用法喽!

来看一组代码:

<code>#include <stdio.h>

int main()

{

int arr[10] = { 0 };

printf(" %zd\n",sizeof(arr));

return 0;

}

这个结果会是多少呢?4还是8,或者都不是?验证一下结果

嗯,都不是,结果是40。其实对于这个结果也不是很出乎意料,毕竟我们前面还使用表达式sizeof (arr) / sizeof(arr[0])来计算数组元素个数

不过到这里我们就应该明白了,在这种情况下的数组名就不是首元素地址了。

并不是说数组名是首元素地址是错误的,只是有凡是都有例外嘛!数组名也是如此,数组名有两个例外:

sizeof(数组名),这里的数组名表示的是整个数组,计算的是整个数组的大小,单位是字节;&数组名,这里的数组名表示整个数组,取出的是整个数组的地址(整个数组的地址和数组首元素的地址是有区别的,待会介绍)

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

我们来比较一下&arr,arr,&arr[0]这三个打印地址的结果

<code>#include <stdio.h>

int main()

{

int arr[10] = { 0 };

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

printf("arr =%p\n",arr);

printf("&arr =%p\n",&arr);

return 0;

}

结果:

从上面的结果来看,&arr,arr,&arr[0]似乎没有什么区别,打印的结果也都是一样的,我们现在分别给它们+1,,再来看一下结果:

&arr[0]和arr在+1后的结果是相同的,都是跳过4个字节,而&arr则不同

&arr+1后跳过了40个字节,怎么算的呢?

十六进制计算

A8-80=28

A是10,10-8 = 2,8-0 = 8,

所以是28,十六进制的28对应的十进制数字是2 * 16 + 8 * 1= 40 

理解:关于&arr[0]和arr在+1后跳了4个字节,我们将首元素地址看作是一个整体,那么当我们+1的时候就会跳过首元素地址,来到第二个元素的地址;而&arr 取的是整个数组的地址,因此我们将整个数组看作一个整体,那么&arr+1后就应该跳过整个数组,由于该数组有十个元素,所以就是跳过40个字节。

问: 数组+1 后跳过几个字节是取决与什么?

答:指针类型决定了指针+-整数时候跳过多少字节。

&arr[0] 的指针类型是 int*

arr 的指针类型是 int*

总结:数组名就是首元素的地址,除了sizeof(数组名)和&数组名以外。

2、指针访问数组

前面说了数组名就是首元素地址,也可以看作是一个指向首元素的指针。既然如此,我们就可以很方便的使用指针来访问数组了。

怎么理解指针访问数组呢?

当我们使用指针来访问数组时,实际上是通过指针指向数组的起始地址,然后根据指针的移动和运算来访问数组中的各个元素。

先来看一下不使用指针访问一个一维数组的代码展示,也就是直接使用下标来访问数组

<code>#include <stdio.h>

int main()

{

int arr[10] = { 0 };

int i = 0;

int sz = sizeof(arr) / sizeof(arr[0]);//计算数组元素个数

//输入

for (i = 0; i < sz; i++)

{

scanf("%d",&arr[i]);

}

//输出

for (i = 0; i < sz; i++)

{

printf("%d ", arr[i]);

}

return 0;

}

我们都知道数组内的元素地址是连续的,如果我们能得到首元素地址,就可以访问整个数组,关于整型数组,我们希望一个整数一个整数的访问,因此我们使用整型指针最为合适

&arr[i]这个就是取数组中下标为 i 的元素的地址

现在我们来使用指针访问数组

与上代码变化:添加int* p = arr ,将输入for循环里的&arr[i]改写为p+i,将输出for循环里的&arr[i]改写为*(p+i)

代码展示:

<code>#include <stdio.h>

int main()

{

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

int i = 0;

int sz = sizeof(arr) / sizeof(arr[0]);//计算数组元素个数

int* p = arr;

//输入

for (i = 0; i < sz; i++)

{

scanf("%d",p+i);//p+i就是下标为i元素的地址

}

//输出

for (i = 0; i < sz; i++)

{

printf("%d ",*(p+i));

}

return 0;

}

int* p = arr这个表达式中我们将arr赋值给p。也就是说p其实是等价于arr的。p = arr ,arr是首元素地址,我们将arr赋给p的时候,p里面存放的也就是首元素地址。

既如此,我们是否可以将*(p+i)中的p换做arr ? 也就是*(arr+i),我们来检验一下

#include <stdio.h>

int main()

{

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

    int i = 0;

    int sz = sizeof(arr) / sizeof(arr[0]);//计算数组元素个数

   //输入

   for (i = 0; i < sz; i++)

   {

      scanf("%d",p+i); 

   }

    int* p = arr;

   //输出

    for (i = 0; i < sz; i++)

    {

        printf("%d ", *(arr + i));

    }

    return 0;

}

 结果展示:

程序还是可以运行的,现在是不是更能够理解p等价于arr了?

 还有一些就不一 一举例说明了,直接总结一下吧!(不一定全,希望能够有大佬帮忙补充一下)

关于 p 等价于 arr

在输入中:

p + i   =  arr + i

在输出中:

arr[i] --- *(arr+i)   完全等价

arr[i]这种写法编译器在执行的时候也会转换成*(arr+i)来执行,也就是说数组底层运算逻辑也是通过指针的方式来执行的。

*(p+i) ---  p + i  完全等价

在整个流程中:

arr[i] = p[i] 

 关于arr[i]还有一个更加抽象的写法,arr[i] = i[arr] ,这里就是说明一下[]就是一个操作符而已,不推荐这种写法。

3、二级指针

我们以前所接触的指针都叫一级指针,在介绍二级指针前,我们先谈一谈一级指针,一级指针的出现是为了什么呢?也就是一级指针的作用是什么?

<code>#include <stdio.h>

int main()

{

int a = 10;

int* pa = &a;//pa是指针变量,一级指针变量

return 0;

}

一级指针变量也是变量,既然如此,在32位平台下我们要申请4个字节空间,在64位平台下我们要申请8个字节空间,因为该指针变量所存放是地址。所以一级指针变量的作用是存储了一个变量的地址

a是一个变量,a的类型是int ,通过&a(0x0012ff40)存放到指针变量pa中;

pa也是一个变量,pa的类型是int*,我们也可以通过&pa(取出pa的地址:0x0012ff48)存放起来,而存放pa的地址的变量该怎么写呢? int** ppa = &pa; 这里的ppa就是二级指针变量。

二级指针变量就是用来存放一级指针变量的地址的

再来理解一下

int * 中*说明pa是个指针变量,int是说明pa指向的变量是int类型的

int* * 中第二个*是说明ppa是指针变量,而前面的int* 是说明ppa指向的变量是int*类型的

4、指针数组

只谈及指针数组是不是感觉比较陌生?但是我们可以类比一下整型数组,字符数组。

<code>int main()

{

int arr1[] = { 1,2,3 };//整型数组

char arr2[] = { 'a' };//字符数组

}

整型数组就是用来存放整型的数组,字符数组就是用来存放字符的数组,所以指针数组就是用来存放指针的数组

整数类型int,字符类型char,但是指针类型有很多,int*,char*,因此指针数组该怎么写呢?

int main()

{

int* arr1[5] = { 0 };//整型指针数组

char* arr2[10] = { 0 };//字符指针数组

……

}

#include <stdio.h>

int main()

{

int a = 1;

int b = 2;

int c = 3;

int* arr[3] = { &a,&b,&c };

int i = 0;//下标

for (i = 0; i < 3; i++)

{

printf("%d ", *(arr[i]));

}

}

指针数组的每个元素都是用来存放地址(指针)的。

5、指针数组模拟二维数组

<code>#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* int* int*

int* arr[] = { arr1,arr2,arr3 };//arr是指针数组

return 0;

}

理解指针数组arr,通过找到arr1,arr2,arr3首元素地址进而找到arr1,arr2,arr3中的全部元素 

我们怎么通过首元素地址打印全部元素呢?

<code>int i = 0;

for (i = 0; i < 3; i++)

{

int j = 0;

for (j = 0; j < 5; j++)

{

printf("%d ", arr[i][j]);

}

}

上面 i 是arr中的下标,j 则是arr1,arr2,arr3中数组的下标

我们先通过下标 i 找到arr1,arr2,arr3的首元素,然后再通过下标 j 分别找到arr1 ,arr2 ,arr3 中的元素。

总代码:

#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* int* int*

int* arr[] = { arr1,arr2,arr3 };//arr是指针数组

int i = 0;

for (i = 0; i < 3; i++)

{

int j = 0;

for (j = 0; j < 5; j++)

{

printf("%d ", arr[i][j]);

}

printf("\n");

}

return 0;

}

结果展示:


结语:

本篇文章到这里就结束了,希望本篇文章能够带你了解数组和指针之间的关系。下篇文章再见!



声明

本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。