C语言数组与地址、数组名到底是什么详解

 更新时间:2023年06月06日 15:23:29   作者:Jambo!  
在写代码的时候,我们经常用到数组,那么有没有想过数组名是什么呢?这篇文章主要给大家介绍了关于C语言数组与地址、数组名到底是什么的相关资料,文中通过实例代码介绍的非常详细,需要的朋友可以参考下

1.问题引出

案例:设计一个函数,可以将整形数组的次序调换
例如:arr[5] = {1,2,3,4,5},输出形式为:arr[5] = {5,4,3,2,1}.

案例代码:

//能否可以正常排序?
#include <stdio.h>
void reverse(int* arr)
{
	int len = sizeof(arr) / sizeof(arr[0]);
	int top = 0;
	int tail = len - 1;
	while (top < tail)
	{
		int tmp = 0;
		tmp = arr[top];
		arr[top] = arr[tail];
		arr[tail] = tmp;
		top++;
		tail--;
	}
}
int main()
{
	int arr[] = { 1,2,3,4,5 };
	int len = sizeof(arr) / sizeof(arr[0]);
	reverse(arr);
	for (int i = 0; i < len; i++)
	{
		printf("%d ", arr[i]);
	}
	return 0;
}

我们来运行一下:

我们发现结果并不是和我们想象的一样输出 5 4 3 2 1 ,而是对于原数组来说没有变化,这是为什么呢?

2.分析错误代码

首先,按F10,点击调试——>窗口——>监视

在监视窗口中输入arr,这时是运行在主函数中,所以监控的是主函数中的arr

这里能够看到,arr中储存着5值

然后按F11进入函数内部,这时的监视窗口是这样:

这里有个技巧:需要在监视中输入arr,5才能正常监视到:

代码再往下走,我们发现len的值为1

但是int len = sizeof(arr) / sizeof(arr[0])len的值应该为arr数组的长度啊,应该是5,为什么是1呢,这就需要了解数组名到底是什么

3.数组名是什么?

先探究数组名与数组元素首元素地址:

int main()
{
	int arr[10] = { 0 };
	printf("%p\n", arr);    //数组名
	printf("%p\n", &arr[0]);//数组首元素地址
	return 0;
}

结果:

所以得出结论:数组名是数组首元素的地址

但是有两个例外:

1.sizeof(数组名),这里的数组名表示整个数组,计算的是整个数组的大小,单位是字节

2.&数组名,这里的数组名表示整个数组,&数组名取出的是数组的地址

验证一下sizeof(数组名):

int main()
{
	int arr[10] = { 0 };
	printf("%d", sizeof(arr));
	return 0;
}

结果:

数组长度为10,int类型一个元素4个字节,所以输出40,这就说明:

sizeof(数组名),数组名表示整个数组,计算的是整个数组的大小

验证 &数组名:

int main()
{
	int arr[10] = { 0 };
	printf("%p\n", arr);    //数组名
	printf("%p\n", &arr[0]);//数组首元素地址
	printf("%p\n", &arr);   //数组名取地址
	return 0;
}

结果:

发现这三个输出的地址都相同,它们有什么区别呢?我们再来探究一下

arr与·&arr[0]一样,都是指向数组首元素地址,他们的类型都是int*
&arr是整个数组的地址,&arr的类型为:int(*)[10]
由于这个数组的地址由数组首元素地址开头,所以这三个地址值从表面上看起来一样,但可以从他们的指针类型去区别他们

再对比一下:

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

结果:

分析:

arr&arr[0]都跳四个字节,也就是数组下一个元素地址
&arr跳过了40个字节,它跳过了整个数组
从这里的计算,也可以体会出arr&arr[0]的类型是int*,而&arr的类型是int(*)[10]

4.错误分析与修改

错误分析:

前面的内容我们知道了数组名是数组首元素地址,本质上是个指针
所以在函数中,就需要用int*去接收这个指针变量

void reverse(int* arr)
{
	int len = sizeof(arr) / sizeof(arr[0]);
	int top = 0;
	int tail = len - 1;
	while (top < tail)
	{
		int tmp = 0;
		tmp = arr[top];
		arr[top] = arr[tail];
		arr[tail] = tmp;
		top++;
		tail--;
	}
}

而在reverse内部,sizeof(arr)中的arr被判定成了指针,而不是一个数组,所以sizeof(arr)的值为4,sizeof(arr[0])也为4,所以len的值为1,而top=0tail = len-1 = 0top = tail,根本进不去下面的循环,所以数组才不会改变

修改:

因为数组作为参数,它的长度不可以在函数内部被计算出来,所以就要在主函数中把数组的长度计算出来,将数组长度作为参数传到函数中

#include <stdio.h>
void reverse(int* arr,int len)
{
	int top = 0;
	int tail = len - 1;
	while (top < tail)
	{
		int tmp = 0;
		tmp = arr[top];
		arr[top] = arr[tail];
		arr[tail] = tmp;
		top++;
		tail--;
	}
}
int main()
{
	int arr[] = { 1,2,3,4,5 };
	int len = sizeof(arr) / sizeof(arr[0]);
	reverse(arr,len);    //两个参数,一个是数组首地址,一个是数组长度
	for (int i = 0; i < len; i++)
	{
		printf("%d ", arr[i]);
	}
	return 0;
}

结果正确:

因为我们知道数组名其实就是一个指针,所以在函数的参数列表中,可以使用int arr[]去接受,也可以用int* arr去接受收

//这两种形式都可以
void reverse(int* arr,int len);
void reverse(int arr[],int len);

5.总结

1.一般情况下,数组名就是数组首元素的地址

2.有两个特殊情况:

  • sizeof(数组名),这里的数组名表示整个数组,计算的是整个数组的大小。
  • &数组名,这里的数组名表示整个数组,&数组名取出的是数组的地址。

3.有数组作为参数传入的函数中,想要计算这个数组的长度,在函数内部做不到,需要在主函数中将数组长度计算出来,将数组长度作为参数传入函数中,这才可以在函数中使用

到此这篇关于C语言数组与地址、数组名到底是什么的文章就介绍到这了,更多相关C语言数组与地址、数组名内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • C++ 获取dll当前路径下所有文件

    C++ 获取dll当前路径下所有文件

    本文主要介绍了C++ 获取dll当前路径下所有文件,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-09-09
  • C语言实现简易三子棋游戏

    C语言实现简易三子棋游戏

    这篇文章主要为大家详细介绍了C语言实现简易三子棋游戏,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-04-04
  • C++ 将一个文件读入数组再读出数组的方法

    C++ 将一个文件读入数组再读出数组的方法

    今天小编就为大家分享一篇C++ 将一个文件读入数组再读出数组的方法,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2018-07-07
  • Qt自定义控件实现圆盘进度条

    Qt自定义控件实现圆盘进度条

    这篇文章主要为大家详细介绍了Qt自定义控件实现圆盘进度条,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2019-12-12
  • 使用VC6.0对C语言程序进行调试的基本手段分享

    使用VC6.0对C语言程序进行调试的基本手段分享

    这篇文章主要介绍了用VC6.0开发c语言程序的时候调试代码的一些小技巧,需要的朋友可以参考下
    2013-07-07
  • C++使用链表实现图书管理系统

    C++使用链表实现图书管理系统

    这篇文章主要介绍了C++使用链表实现图书管理系统,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-03-03
  • C++基于文件流与armadillo读取mnist示例详解

    C++基于文件流与armadillo读取mnist示例详解

    这篇文章主要给大家介绍了关于C++基于文件流与armadillo读取mnist的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-05-05
  • 解析C++类内存分布

    解析C++类内存分布

    本篇文章介绍了C++类内存分布结构,我们来看看编译器是怎么处理类成员内存分布的,特别是在继承、虚函数存在的情况下
    2021-06-06
  • C++实现拼图游戏代码(graphics图形库)

    C++实现拼图游戏代码(graphics图形库)

    这篇文章主要为大家详细介绍了C++实现拼图游戏代码,带有graphics图形库,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2018-05-05
  • Qt操作SQLite数据库的教程详解

    Qt操作SQLite数据库的教程详解

    SQLite是一款开源、轻量级、跨平台的数据库,无需server,无需安装和管理配置。它的设计目标是嵌入式的,所以很适合小型应用,也是Qt应用开发种常用的一种数据库。本文为大家介绍了Qt操作SQLite数据库的示例,希望对大家有所帮助
    2022-12-12

最新评论