一步带你走进 数组名与 *** 作符sizeof,函数strlen之间并不复杂的“ 复杂关系”

一步带你走进 数组名与 *** 作符sizeof,函数strlen之间并不复杂的“ 复杂关系”,第1张

一步带你走进 数组名与 *** 作符sizeof,函数strlen之间并不复杂的“ 复杂关系”

目录

一,基础知识

1.1数组名的两种表现形式

1.2二维数组的数组名

二,一维数组

三,字符数组

3.1用单个字符表示的字符数组

3.1.1sizeof *** 作符

3.1.2strlen函数 

3.2字符串数组

 3.3常量字符串

 四,二维数组


一,基础知识 1.1数组名的两种表现形式

数组名一般情况下,指的都是数组首元素地址,但是有两种额外的情况:

首先,当数组名用在 *** 作符sizeof中时,数组名表示的整个数组。

其次,&数组名,这里的数组名也表示整个数组。

1.2二维数组的数组名

一般情况下,数组名,不管是几维数组,表示的是“首元素”的地址,那么这里的首元素在二维数组中其实是首行元素的地址。而在数组通用的两种额外情况下,数组首元素表示也是整个数组。

二,一维数组

首先,我们以一维数组为例了解 *** 作符sizeof与数组名之间的关系。

如下代码所示,需要注意,我们这里用的是整型数组:

	int a[] = { 1,2,3,4 };
	printf("%dn", sizeof(a));
	//16  数组名单独放在sizeof中代表整个数组
	printf("%dn", sizeof(a + 0));
	// 4/8  数组名没有单独放在sizeof中,则数组名表示数组首元素的地址,加0还是表示数组首元素的地址
	printf("%dn", sizeof(*a));
	// 4 这里a表示数组首元素,解引用找到的是首元素,因为不同平台一个元素的大小不同,故为一个字节大小。

第一条打印代码中,因为单独放在sizeof *** 作符内部,所以表示的是整个数组的大小。而一个整型数据为4字节,所以值为4*4=16。

第二条代码sizeof内部是数组名加0,这里需要注意的是,当数组名不是单独放在该 *** 作符括号中时,数组名就是一般的情况,即表示数组首元素的地址,那么加零之后,即表示地址加0,还是表示数组首元素的地址,同上面那种情况不一样的是,这里表示的地址的大小,那我们知道,在不同的机器环境下,地址的大小为4字节或者8字节。

第三种情况下,因为数组名也不是单独放在该 *** 作符括号中,所以也是一般的数组名,解引用之后找到的是便是首元素了,那么一个元素的话,大小即为4字节。 

那么我们接着看:

	printf("%dn", sizeof(&a));
    //4/8  &a表示整个数组的地址,既然是地址,则为4/8
	printf("%dn", sizeof(*&a));
	//16 &a找到整个数组的地址,然后解引用找到所有元素,所以是整个数组的大小
	printf("%dn", sizeof(&a[0]));
	//4/8 取地址首元素,还是地址,即为4/8

这里的三条语句均与 &(取地址) *** 作符有关。首先,第一条语句,&a即表示那两种额外情况中的一种,表示取出整个数组的地址。那么既然是地址的话,其大小为4或者8字节大小。

第二条语句,取地址找到该数组所有元素的地址之后,再解引用,即表示访问所有元素,所以这里求出的即为整个数组的大小16。

第三条语句,a[0]即表示数组首元素,对其取地址之后,则表示取到首元素的地址,因为是地址,所以也为4或者8字节。

三,字符数组

好的,了解完一维数组之后,我们通过字符数组进行分析:

3.1用单个字符表示的字符数组 3.1.1sizeof *** 作符

如下代码所示,arr数组一共有6个字符。

	char arr[] = { 'a','b','c','d','e','f' };
	printf("%dn", sizeof(arr));//6  
	printf("%dn", sizeof(arr + 0));//4/8  首元素的地址
	printf("%dn", sizeof(*arr));//1  没有单独存放,即表示首元素地址,解引用找到首元素
	printf("%dn", sizeof(arr[1]));//1  首元素大小
	printf("%dn", sizeof(&arr));//4/8  整个数组的地址
	printf("%dn", sizeof(&arr + 1));// 4/8 跳过一整个数组后面那个地址
	printf("%dn", sizeof(&arr[0] + 1));//4/8  下标为1的元素的地址

首先,这里数据类型为字符型,即在内存空间大小为 1 字节的数据类型。

因为这里大多数语句与上面一维数组差不多,所以大多数我们不做过多介绍,如有小伙伴不明白,可以根据代码后面的注释学习。

这里我们主要分析最后两条语句,首先倒数第二条语句。因为是用了取地址 *** 作符,所以这里数组名表示的是整个数组,故对数组名取地址之后得到的是整个数组的地址。那么加一即表示跳过一整个这样的数组,因为仍旧表示一个地址,所以其值为4或者8。

接下来倒数第一条语句,因为取地址 *** 作符后面的是arr [0] 表示数组首元素,所以取地址是取到了首元素的地址,再对其加1,即指向了下标为1的元素的地址,所以仍旧为4或者8。 

3.1.2strlen函数 

接下来我们通过strlen函数来了解有关数组名的内容。代码如下:

    char arr[] = { 'a','b','c','d','e','f' };
    printf("%dn", strlen(arr));
	//随机值  strlen函数需要找到,之前的数个数即为长度,给了首元素地址之后,
	//会一直找直到直到,但是这里没有,所以这个值为随机值
	printf("%dn", strlen(arr + 0));//随机值 从首元素地址开始找
	printf("%dn", strlen(*arr));//err
	//解引用找到的是首元素,
	//strlen会以为字符‘a’的ASCII码值 - 97 为地址
	//那么访问 96 地址时出现越界访问,所以说这是一条错误的语句。
	printf("%dn", strlen(arr[1]));//err 同上条语句

	printf("%dn", strlen(&arr));//随机值
	//但由于传递的都是地址,所以会发生强制转换,
    //所以还是有结果的,不过还是随机值
	printf("%dn", strlen(&arr + 1));//随机值,同上分析,不过比上条语句相差 6

首先第一条输出语句,strlen函数的参数为数组名arr,即数组首元素的地址,那么该函数将会从改地址出往后一直找,直到发现 ,但是我们发现这个数组是由一个一个的字符组成的,所以这里长度将会是一个随机值,因为我们不知道字符串结束标志在哪里。

然后第三条语句,函数的参数为 *arr ,即对数组名表示的首元素地址进行解引用,找到首元素,即为字符 'a',那么strlen函数便将该字符的ASCII码值 97 作为起始地址,进行顺序查找 ,而我们并不知道97这个地址在哪里,运行将会发生越界访问警告。所以这是一条错误的语句。

而第四条语句与第三局如出一辙。

 接下来我们分析一下第五条和第六条语句:

首先第五条。&arr传递的是整个数组的地址,虽然是首元素的地址,但是它的数据类型为 char(*)[ ](数组指针),因为地址本质上就是指针,而这个指针指向了一个数组,这个数组每个元素又是char类型。所以&arr反悔了一个数组指针,但同时,strlen函数返回类型为  char *,所以两者类型不匹配,但是又因为两者都是指针(地址),所以会发生强制类型转换。但是转换完事之后,我们也不知道其实地址会在哪里,但是会有一个随机值结果。

其次第六条语句,跟第五条语句有区别的地方就是没强制转换之前,跳过了一个arr数组的长度,即6个字节。

3.2字符串数组

前面我们用字符数组了解了sizeof *** 作符和strlen函数用在数组名上关系。接下来我们通过字符串来分析:

    char arr[] = "abcdef";
	printf("%dn", sizeof(arr));//7  
	//注意也算做是字符串的一个元素,所以是7个元素
	//因为这里是数组,所以字符串结尾的是算作字符串里面的

	//printf("%dn", sizeof(arr + 0));//4/8
	//printf("%dn", sizeof(*arr));//1
	//printf("%dn", sizeof(arr[1]));//1
	//printf("%dn", sizeof(&arr));//4/8
	//printf("%dn", sizeof(&arr + 1));//4/8
	//printf("%dn", sizeof(&arr[0] + 1));//4/8

	printf("%dn", strlen(arr));//6
	printf("%dn", strlen(arr + 0));//6
	//printf("%dn", strlen(*arr));//err 首元素传递过去之后,字符a的ASCII码值为地址,发生越界中断
	//printf("%dn", strlen(arr[1]));//err 
	printf("%dn", strlen(&arr));//6  类型不匹配,但是依然可以接受
	printf("%dn", strlen(&arr + 1));//随机值

对于这个数组,首先,第一条语句。因为这个字符串放在了数组中,所以该数组最后一个元素为,所以在这里求元素个数的时候就会数为7,而一个char类型又为一个字节,所以这里的值为7。

然后中间的部分代码,由于与前面的基本没有区别,故不做过多赘述,大家感兴趣可以看一下。

然后下面的第二条语句,这里单个的arr表示数组首元素的地址,所以直到 ,之前字符串的实际长度为6,所以此时值为6 。第三条语句大差不差。

然后,最后的三条语句。首先第一条,为什么这里的值为6呢?而上面单个字符的数组是随机值呢?

其实不难理解,这里的数组存放的是字符串,是有结束标志的,所以尽管这里类型不匹配,但是强制类型转换完之后,同上面字符数组一样,依然是可以接收的,但恰恰因为这里有,所以长度是6。

而这条语句后面的,则因为加1跳过了整个数组,所以值为随机值。

 3.3常量字符串

在这里,因为很多内容我们都了解了,所以重点要了解的如下所示:

	printf("%dn", strlen(p));//6
	//这里不算做一个元素,是因为这里是用指针变量实现的,
	//也即指针找到最后一个数时,认为字符串结束,所以这里是6
	printf("%dn", strlen(p + 1));//5
	printf("%dn", strlen(&p));//随机值  
	//指针变量p和字符串的地址时分开的,这里找的是p的地址,
	//p地址往后不知道存放的是什么,所以是随机值

首先第一条语句,与字符串数组不同的是,这里字符串存不存放在数组中,而是用一个指针变量来访问,所以结果为6。

然后最后一条语句,如下图所示:因为指针变量存放的位置和字符串存放的位置一定不一样,是相对分离的,所以当取地址p时,该指针变量的地址后面什么时候出现  是未知的,所以这里是随机值。

 四,二维数组

在了解了上述类型数组以及字符串之后,将其运用到二维数组中。如下代码所示:

	int a[3][4] = { 0 };
	printf("%dn", sizeof(a));//4*12=48
	printf("%dn", sizeof(a[0][0]));//4
	printf("%dn", sizeof(a[0]));//16
	//a[0] 单独放在sizeof中,表示整行元素
	printf("%dn", sizeof(a[0] + 1));//4
	//a[0]没有单独放,也没有取地址,加 1 表示该行第二个元素
	printf("%dn", sizeof(*(a[0] + 1)));//16
	printf("%dn", sizeof(a + 1));//4/8
	//数组名单独放,表示数组首行元素的地址,加 1 则表示下一行元素的地址
	printf("%dn", sizeof(*(a + 1)));//16
	printf("%dn", sizeof(&a[0] + 1));//4/8
	//取地址之后,地址加1,则表示下一行地址。
	printf("%dn", sizeof(*(&a[0] + 1)));//16
	printf("%dn", sizeof(*a));//16
	printf("%dn", sizeof(a[3]));//16

此时基本理解都是一样的,不过对于二维数组,我们需要补充一些知识:

首先,就是,数组名一般表示数组首元素的地址,但是二维数组中的首元素代表的是首行一整行元素的的地址,所以 a[0] 表示的是首行元素,而对于其加1,则表示首行下一个元素,而当数组名a加一之后,表示下一行元素。然后其他概念和前面一模一样。

好了,本文到此就结束了, 如有错误,还请各位指正哦!!!

 

 

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/zaji/5714263.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-17
下一篇 2022-12-17

发表评论

登录后才能评论

评论列表(0条)

保存