目录
闲话:
引言:
1、数组名的理解
2、指针访问数组
3、一维数组传参的本质
4、二级指针
5、指针数组
6、指针数组模拟二维数组
结语:
闲话:
指针这个模块更新的比较慢,主要是小编还得学习指针的知识点,虽然说是学习笔记,但是也是需要小编自己学会后才能够更好的将知识点介绍给大家,所以还请见谅一下哈!!!
引言:
本篇文章将带来数组与指针之间的关系介绍,希望能对大家有所帮助!
1、数组名的理解
int arr[10] = {0};
从概念上讲:
数组名代表数组在内存中的起始地址。可以将数组名视为一个指向数组首个元素的指针。
例如,定义一个整型数组 int arr[10] , arr 就代表了这个数组的起始地址。
来看一组对比:
#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 语言中,数组名具有特殊的含义和性质。因此,数组名肯定不能只有这么单一的用法喽!
来看一组代码:
#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]这三个打印地址的结果
#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、指针访问数组
前面说了数组名就是首元素地址,也可以看作是一个指向首元素的指针。既然如此,我们就可以很方便的使用指针来访问数组了。
怎么理解指针访问数组呢?
当我们使用指针来访问数组时,实际上是通过指针指向数组的起始地址,然后根据指针的移动和运算来访问数组中的各个元素。
先来看一下不使用指针访问一个一维数组的代码展示,也就是直接使用下标来访问数组
#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)
代码展示:
#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、一维数组传参的本质
数组传参是指在函数调用时将数组作为参数传递给函数。
int main() {
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
test(arr);
return 0;}
数组传参只需要写数组名就可以了。注意:数组名是arr,而不是arr[10]
数组传参形参该怎么写呢?
void test(int arr[])//元素个数写不写无所谓
等下会说为什么写不写都不影响
现在我们来分别在(test)函数外部与函数内部计算数组元素的个数、
来,展示!
可以看到在函数内部sz2结果为1,而函数外部sz1结果为10;这是为什么呢?
关于sz1 = 10;的结果我们都清楚,sizeof(arr)求得数组的总大小,sizeof(arr[0])求得数组首元素的大小,然后得出元素个数,但是为什么在test函数内部求得的元素个数结果变为1了呢?
我们来逆推一下,首先sizeof(arr[0])表示的是数组首元素的大小是不变的,因此sizeof(arr[0])等于4
sz2 = sizeof(arr) / 4 = 1;因此sizeof(arr)也等于4,那么在什么情况下能得到aizeof(arr) = 4 呢?
在数组传参的时候 test(arr);我们传递的是整个数组吗?还记得前面关于数组名的理解吗?这里arr既不是在sizeof中,前面也没有&符号,所以,test(arr)中的arr指的就是数组首元素的大小,因此我们传参过去的是首元素的地址,这便是一维数组传参的本质,既如此,我们便可以明白aizeof(arr) = 4是怎么得到的了,地址在32位机器上占4个字节,在64位机器上占8个字节 ,小编是在32位上操作的,所以最终得到izeof(arr) = 4 的结果。
void test(int arr[])//元素个数写不写无所谓
{
int sz2 = sizeof(arr) / sizeof(arr[0]);
printf("sz2 = %d\n", sz2);
}
#include <stdio.h>
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;}
综上:数组传参传递的就是首元素地址
1.我们传递的不是整个数组,函数形参的部分是不会真实创建数组的,所以就不需要数组大小,也就是形参部分元素大小写不写都无所谓,没有什么影响
2.数组传过去的是数组首元素地址,地址应该拿指针来接收,所以函数形参部分应该使用指针变量来接收,而我们写成int arr[])是为了更加方便我们的理解。
void test(int arr[])可以写为void test(int* arr)
注意:
一维数组传参的时候,形参可以写成数组的方式,主要是为了方便理解,形参也可以写成指针变量的方式
如果我们想要在函数内部获取数组元素的个数,该怎么写呢?
void test(int arr[10],int sz)
{
//遍历数组
int i = 0;
for (i = 0; i < sz; i++)
{
printf(" %d ", arr[i]);
}
}
#include <stdio.h>
int main()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
int sz = sizeof(arr) / sizeof(arr[0]);
test(arr,sz);
return 0;
}
4、二级指针
我们以前所接触的指针都叫一级指针,在介绍二级指针前,我们先谈一谈一级指针,一级指针的出现是为了什么呢?也就是一级指针的作用是什么?
#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*类型的
5、指针数组
只谈及指针数组是不是感觉比较陌生?但是我们可以类比一下整型数组,字符数组。
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]));
}
}
指针数组的每个元素都是用来存放地址(指针)的。
6、指针数组模拟二维数组
#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中的全部元素
我们怎么通过首元素地址打印全部元素呢?
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;
}
结果展示:
结语:
本篇文章到这里就结束了,希望本篇文章能够带你了解数组和指针之间的关系。下篇文章再见!