目录
一,基础知识
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("%d\n", sizeof(a));
//16 数组名单独放在sizeof中代表整个数组
printf("%d\n", sizeof(a + 0));
// 4/8 数组名没有单独放在sizeof中,则数组名表示数组首元素的地址,加0还是表示数组首元素的地址
printf("%d\n", sizeof(*a));
// 4 这里a表示数组首元素,解引用找到的是首元素,因为不同平台一个元素的大小不同,故为一个字节大小。
第一条打印代码中,因为单独放在sizeof操作符内部,所以表示的是整个数组的大小。而一个整型数据为4字节,所以值为4*4=16。
第二条代码sizeof内部是数组名加0,这里需要注意的是,当数组名不是单独放在该操作符括号中时,数组名就是一般的情况,即表示数组首元素的地址,那么加零之后,即表示地址加0,还是表示数组首元素的地址,同上面那种情况不一样的是,这里表示的地址的大小,那我们知道,在不同的机器环境下,地址的大小为4字节或者8字节。
第三种情况下,因为数组名也不是单独放在该操作符括号中,所以也是一般的数组名,解引用之后找到的是便是首元素了,那么一个元素的话,大小即为4字节。?
那么我们接着看:
printf("%d\n", sizeof(&a));
//4/8 &a表示整个数组的地址,既然是地址,则为4/8
printf("%d\n", sizeof(*&a));
//16 &a找到整个数组的地址,然后解引用找到所有元素,所以是整个数组的大小
printf("%d\n", 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("%d\n", sizeof(arr));//6
printf("%d\n", sizeof(arr + 0));//4/8 首元素的地址
printf("%d\n", sizeof(*arr));//1 没有单独存放,即表示首元素地址,解引用找到首元素
printf("%d\n", sizeof(arr[1]));//1 首元素大小
printf("%d\n", sizeof(&arr));//4/8 整个数组的地址
printf("%d\n", sizeof(&arr + 1));// 4/8 跳过一整个数组后面那个地址
printf("%d\n", 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("%d\n", strlen(arr));
//随机值 strlen函数需要找到\0,之前的数个数即为长度,给了首元素地址之后,
//会一直找直到直到\0,但是这里没有\0,所以这个值为随机值
printf("%d\n", strlen(arr + 0));//随机值 从首元素地址开始找
printf("%d\n", strlen(*arr));//err
//解引用找到的是首元素,
//strlen会以为字符‘a’的ASCII码值 - 97 为地址
//那么访问 96 地址时出现越界访问,所以说这是一条错误的语句。
printf("%d\n", strlen(arr[1]));//err 同上条语句
printf("%d\n", strlen(&arr));//随机值
//但由于传递的都是地址,所以会发生强制转换,
//所以还是有结果的,不过还是随机值
printf("%d\n", strlen(&arr + 1));//随机值,同上分析,不过比上条语句相差 6
首先第一条输出语句,strlen函数的参数为数组名arr,即数组首元素的地址,那么该函数将会从改地址出往后一直找,直到发现 \0,但是我们发现这个数组是由一个一个的字符组成的,所以这里长度将会是一个随机值,因为我们不知道字符串结束标志在哪里。
然后第三条语句,函数的参数为 *arr ,即对数组名表示的首元素地址进行解引用,找到首元素,即为字符 'a',那么strlen函数便将该字符的ASCII码值 97?作为起始地址,进行顺序查找 \0,而我们并不知道97这个地址在哪里,运行将会发生越界访问警告。所以这是一条错误的语句。
而第四条语句与第三局如出一辙。
?接下来我们分析一下第五条和第六条语句:
首先第五条。&arr传递的是整个数组的地址,虽然是首元素的地址,但是它的数据类型为 char(*)[ ](数组指针),因为地址本质上就是指针,而这个指针指向了一个数组,这个数组每个元素又是char类型。所以&arr反悔了一个数组指针,但同时,strlen函数返回类型为 ?char *,所以两者类型不匹配,但是又因为两者都是指针(地址),所以会发生强制类型转换。但是转换完事之后,我们也不知道其实地址会在哪里,但是会有一个随机值结果。
其次第六条语句,跟第五条语句有区别的地方就是没强制转换之前,跳过了一个arr数组的长度,即6个字节。
3.2字符串数组
前面我们用字符数组了解了sizeof操作符和strlen函数用在数组名上关系。接下来我们通过字符串来分析:
char arr[] = "abcdef";
printf("%d\n", sizeof(arr));//7
//注意\0也算做是字符串的一个元素,所以是7个元素
//因为这里是数组,所以字符串结尾的\0是算作字符串里面的
//printf("%d\n", sizeof(arr + 0));//4/8
//printf("%d\n", sizeof(*arr));//1
//printf("%d\n", sizeof(arr[1]));//1
//printf("%d\n", sizeof(&arr));//4/8
//printf("%d\n", sizeof(&arr + 1));//4/8
//printf("%d\n", sizeof(&arr[0] + 1));//4/8
printf("%d\n", strlen(arr));//6
printf("%d\n", strlen(arr + 0));//6
//printf("%d\n", strlen(*arr));//err 首元素传递过去之后,字符a的ASCII码值为地址,发生越界中断
//printf("%d\n", strlen(arr[1]));//err
printf("%d\n", strlen(&arr));//6 类型不匹配,但是依然可以接受
printf("%d\n", strlen(&arr + 1));//随机值
对于这个数组,首先,第一条语句。因为这个字符串放在了数组中,所以该数组最后一个元素为\0,所以在这里求元素个数的时候就会数为7,而一个char类型又为一个字节,所以这里的值为7。
然后中间的部分代码,由于与前面的基本没有区别,故不做过多赘述,大家感兴趣可以看一下。
然后下面的第二条语句,这里单个的arr表示数组首元素的地址,所以直到 \0 ,之前字符串的实际长度为6,所以此时值为6 。第三条语句大差不差。
然后,最后的三条语句。首先第一条,为什么这里的值为6呢?而上面单个字符的数组是随机值呢?
其实不难理解,这里的数组存放的是字符串,是有结束标志的,所以尽管这里类型不匹配,但是强制类型转换完之后,同上面字符数组一样,依然是可以接收的,但恰恰因为这里有\0,所以长度是6。
而这条语句后面的,则因为加1跳过了整个数组,所以值为随机值。
?3.3常量字符串
在这里,因为很多内容我们都了解了,所以重点要了解的如下所示:
printf("%d\n", strlen(p));//6
//这里\0不算做一个元素,是因为这里是用指针变量实现的,
//也即指针找到最后一个数\0时,认为字符串结束,所以这里是6
printf("%d\n", strlen(p + 1));//5
printf("%d\n", strlen(&p));//随机值
//指针变量p和字符串的地址时分开的,这里找的是p的地址,
//p地址往后不知道存放的是什么,所以是随机值
首先第一条语句,与字符串数组不同的是,这里字符串存不存放在数组中,而是用一个指针变量来访问,所以结果为6。
然后最后一条语句,如下图所示:因为指针变量存放的位置和字符串存放的位置一定不一样,是相对分离的,所以当取地址p时,该指针变量的地址后面什么时候出现 \0?是未知的,所以这里是随机值。
?四,二维数组
在了解了上述类型数组以及字符串之后,将其运用到二维数组中。如下代码所示:
int a[3][4] = { 0 };
printf("%d\n", sizeof(a));//4*12=48
printf("%d\n", sizeof(a[0][0]));//4
printf("%d\n", sizeof(a[0]));//16
//a[0] 单独放在sizeof中,表示整行元素
printf("%d\n", sizeof(a[0] + 1));//4
//a[0]没有单独放,也没有取地址,加 1 表示该行第二个元素
printf("%d\n", sizeof(*(a[0] + 1)));//16
printf("%d\n", sizeof(a + 1));//4/8
//数组名单独放,表示数组首行元素的地址,加 1 则表示下一行元素的地址
printf("%d\n", sizeof(*(a + 1)));//16
printf("%d\n", sizeof(&a[0] + 1));//4/8
//取地址之后,地址加1,则表示下一行地址。
printf("%d\n", sizeof(*(&a[0] + 1)));//16
printf("%d\n", sizeof(*a));//16
printf("%d\n", sizeof(a[3]));//16
此时基本理解都是一样的,不过对于二维数组,我们需要补充一些知识:
首先,就是,数组名一般表示数组首元素的地址,但是二维数组中的首元素代表的是首行一整行元素的的地址,所以 a[0]?表示的是首行元素,而对于其加1,则表示首行下一个元素,而当数组名a加一之后,表示下一行元素。然后其他概念和前面一模一样。
好了,本文到此就结束了,?如有错误,还请各位指正哦!!!
?
?
|