在前面的两节内容中,我们学习了数组
的相关概念,并对数组使用过程中的部分注意事项进行了强调,今天,我们接着讲解一些关于数组的特别内容
,然后介绍实参和形参的区别
,
话不多说,开整!!!
数组的扩展知识
数组名的默认含义
到目前为止,我们已经学会了数组
的定义方式,如下所示:
int arr[] = {1,2,3};
我们即定义了一个整型数组,数组的名称是arr
,这个大家都知道,那么如果打印arr,我们会得到什么结果呢,我们不妨来试一试,代码如下:
#include<stdio.h>
int main()
{
int arr[] = {1,2,3};
printf("%d\n",arr);
return 0;
}
此时编译器会提示我们%d期待的打印对象为整型,但此处的arr为int*
,如下所示:
前面我们讲到过,x*代表的是指针类型
,那么我们不妨听从编译器的建议,就打印指针类型,大家还没有忘记吧,打印指针类型的变量符号
为%p
,修改代码,然后保存编译,可以得到以下结果:
我们得到了一个十六进制数字
,仔细一看,这怎么和我们之前打印的地址
长得这么像呢,那么他到底是不是地址呢,如果是地址,又是什么地址呢?
在这里先说明结论,然后进行验证:
一般情况下,数组名代表着数组中首个元素的地址(两种情况除外)
所以我们在打印数组时,打印出来的就是数组首元素的地址,既然是地址,那自然就是指针变量了,因为只有指针变量才能存放地址
,这也就证明了前述中编译器的建议是正确的。
现在我们进行验证,我们再对数组中的首元素地址
进行打印,查看二者的地址是否一样,代码如下:
#include<stdio.h>
int main()
{
int arr[] = {1,2,3};
printf("%p\n",arr);
printf("%p\n",&arr[0]);
return 0;
}
编译查看结果:
可以发现确实二者的值一样,确实是一个地址,因此上述的说法成立。
数组名默认含义的两种特殊情况
上述说到,数组名默认为数组中首个元素的地址(两种情况除外)
,下面就来介绍这两种特殊情况是什么。
sizeof函数内
我们在之前的内容中就学习了这个函数的作用是什么,其作用是计算数据的占用空间
,因此,当使用该函数时,其中的数组名arr不是代表数组中首元素的地址,而是整个数组
,上述代码后添加以下:
printf("%d\n",sizeof(arr));
编译运行,得到如图所示结果:
表示12个字节
,因为我们定义的数组为整型数组,每个整型为4字节
,数组内有3个元素,因此3*4=12字节,所以:
在sizeof函数内,数组名不代表数组中的首元素地址,而是整个数组
&数组名
数组名默认含义的第二种失效环境
就是类似如下形式:
&arr
此时其也不代表着默认的含义,而是代表着整个数组的地址
,注意:是整个数组的地址,示例代码如下:
#include<stdio.h>
int main()
{
int arr[] = {1,2,3};
printf("%p\n",arr);
printf("%p\n",&arr[0]);
printf("%d\n",sizeof(arr));
printf("%p\n",&arr);
return 0;
}
保存编译运行,结果如下:
看到这里,你可能会疑惑,这不和数组首元素的地址一样吗,为什么我还要着重强调它代表着的是整个数组的地址而不是首元素地址
呢,下面我们验证。
我们对代表首元素地址的arr
和代表整个数组的地址&arr
分别加1,可能你不明白为什么地址还能加减,在此处简单理解:地址存放的我们称为指针变量,既然是一个变量,那么自然可以进行加减法运算,后面在指针内容时还会详细讲解,示例代码如下:
#include<stdio.h>
int main()
{
int arr[] = {1,2,3};
printf("%p\n",arr+1);
printf("%p\n",&arr[0]+1);
printf("%d\n",sizeof(arr));
printf("%p\n",&arr+1);
return 0;
}
编译查看结果:
我们将两个结果进行对比,如下所示:
从这张对比图中,我们可以更加清楚的了解为什么要强调此处的&arr代表的是整个数组的地址
,我们对于首元素地址加1,其只增加了4个字节,而&arr进行加1却增加了一个数组的长度即12个字节,运算过程如下:
所以说:
&arr 代表整个数组的地址
除了上述两种例外之外,其余时刻,arr均代表的是数组中首元素的地址
。
实参与形参
实参与形参,又称为实际参数与形式参数
,从马克思主义的基本观点来看,二者可以说是:你中有我,我中有你,辩证统一。
至于二者的区分呢,也非常容易区分,在你调用函数时,括号内的参数即为实参,定义函数时,括号内的参数即为形参
。如下所示代码:
#include<stdio.h>
int test(int a)
{
int sum = a + 1;
}
int main()
{
int a = 1;
int sum = test(a);
return 0;
}
如下图所示的标记:
联系
二者是的命名是可以相同的,二者之间的联系是:
形参是实参的一份临时拷贝,使用完之后就会自动销毁
什么意思呢,也就是说:
当调用函数test
时,会将a =1这个值临时拷贝
一份,放入test函数中使用,再调用函数结束
之后,该值自动从内存中删除
。
区别
既然有联系,自然就有区别,不然怎么能叫辩证统一呢。其区别就在于:
修改形参不会改变实参的值
也就是我改变了形参值,外部的实参值,是不会改变的,比如我想要实现两个数字互换位置的功能,a=5,b=2调用函数后
变为a=2,b=5;那我编写了一个代码如下:
#include<stdio.h>
void exchange(int a,int b)
{
int temp = a;//创建临时变量存储a
a = b;
b = temp;
}
int main()
{
int a = 5;
int b = 2
exchange(a,b);
printf("a= %d b= %d ",a,b);
return 0;
}
那么它能实现这个功能吗,编译查看结果:
我们可以看到并没有像我们想象的一样
,交换二者的值,这正是因为型参与实参的区别,我们现在进行调试,可以看到监视窗口此时的a、b以及其对应地址如下:
在进入自定义函数后,再查看监视窗口
可以看到此处,二者确定已经交换了值,但为什么打印时,二者的值没有出现交换呢,细心的朋友可能已经发现了,在前后两次a、b的地址是不一样
的,如下:
也就是我在另外的地址交换了,那和我原来的地址毛关系没有,
那么如何解决这种问题呢?既然二者的地址不一样,那我传地址
是不是就可以了,下面进行测试,此时传入a、b的地址
,由于传入的是地址,所以需要接受为指针变量,代码如下:
#include<stdio.h>
void exchange(int* a,int* b)
{
int temp = *a;//创建临时变量存储地址a中的数值
*a = *b;
//*解引用操作符:也就是把地址b中的内容取出来赋值给a所在的地址
*b = temp;
}
int main()
{
int a = 5;
int b = 2;
exchange(&a,&b);
printf("a= %d b =%d ",a,b);
return 0;
}
编译运行,可以得到以下结果:
此时,我们看到二者的内容进行了交换,那么到这里我们不得不疑问了:到底什么时候传值,什么时候传地址呢,大家只需记住以下的话即可:
想要改变外部的值则传址,无需改变则传值
上述内容交换即使前者,所以需要进行传地址才可以。
上述内容即使今天的全部内容了,感谢大家的观看。
如果方便,辛苦大家点个赞和关注哦!
您的点赞或评论或关注是对我最大的肯定,谢谢大家!!!