数组笔试解释
我们前面学习过了数组,也了解数组名的大概意思
整形数组
#include<stdio.h> int main() { //一维数组 int a[] = { 1,2,3,4 }; printf("%d\n", sizeof(a)); printf("%d\n", sizeof(a + 0)); printf("%d\n", sizeof(*a)); printf("%d\n", sizeof(a + 1)); printf("%d\n", sizeof(a[1])); printf("%d\n", sizeof(&a)); printf("%d\n", sizeof(*&a)); printf("%d\n", sizeof(&a + 1)); printf("%d\n", sizeof(&a[0])); printf("%d\n", sizeof(&a[0] + 1)); return 0; }
上面代码中我定义了一个整形数组,大小为4,要做好这些题我们就要深刻理解数组名的含义
数组名的理解
- sizeof(arr)单独传入数组名,这里的数组名代表的是整个整数,计算的是整个数组的元素大小,不是元素个数,单位为字节
2.&arr代表的是整个数组,取出的是数组的地址
3.除上面两个例外,数组名代表都是数组首元素的地址
解释1
这里的sizeof(a)数组名是单独传入进去的,所以代表的是整个数组,计算的是整个数组的大小,结果为16
解释2
数组名不是单独传入的,所以a代表数组首元素的地址,地址的大小是由环境决定的,大小为4或者8
解释3
数组名a不是单独的放入到sizeof中,所以数组名代表数组首元素的地址,*为解引用操作符,找出数组首元素,数组首元素是一个int类型的数,大小为4个字节,其中下面就可以解释了
解释4
这里a+1是数组第二个元素的地址,大小是4个字节
解释5
a[1] ==*(a + 1),所以大小是4个字节
解释6
这里可能有小可爱就不明白了,为啥这里不和sizeof(a)的结果一样?前面我已经讲得很清楚了,只要数组名单独放入sizeof才是代表整个数组,计算的是整个数组的大小,&a虽然代表的是整个数组,但是取的却是数组的地址,所以&a就是数组的地址,既然是地址,就是一个数,这个数的类型是int(*)[4] ,地址的大小是4或者8个字节,记住,指针的类型的大小是4或者8个字节,地址就是一个数,用十六进制表示而已,没有啥高大尚的,如果这个&a 加1就会跳过16个字节,主要是这里很容易搞混淆,指针的大小 4或者8 字节,不同的指针类型加1跳过的字节会不相同,所以上面的结果是4或者8字节,
数组元素的地址和数组的地址的区别:是类型的区别,不是大小的区别,因为地址的大小都是4或者8个字节,
解释7
这里我们可以拆分一下,*(&a),对数组的地址解引用,得出的是整个数组,计算整个数组的大小,或者我们可以这样理解为 *&会抵消掉,结果为16
解释8
&a的类型为int(*)[4],数组地址加1 跳过整个数组,
所以跳过16字节,但总归还是地址,是地址那就是4/8字节
解释9
这里是取出元素a[0]的地址,是地址,就是4/8个字节
最终的结果如下:
字符数组
#include<stdio.h> int main() { //字符数组 char arr[] = { 'a','b','c','d','e','f' }; printf("%d\n", sizeof(arr)); printf("%d\n", sizeof(arr + 0)); printf("%d\n", sizeof(*arr)); printf("%d\n", sizeof(arr[1])); printf("%d\n", sizeof(&arr)); printf("%d\n", sizeof(&arr + 1)); printf("%d\n", sizeof(&arr[0] + 1)); printf("%d\n", strlen(arr)); printf("%d\n", strlen(arr + 0)); printf("%d\n", strlen(*arr)); printf("%d\n", strlen(arr[1])); printf("%d\n", strlen(&arr)); printf("%d\n", strlen(&arr + 1)); printf("%d\n", strlen(&arr[0] + 1)); return 0; }
这里我定义了一个字符数组,我们来看看
解释1
这里数组名是单独的放的,代表整个数组,所以是计算数组的大小,单位为字节,结果是6
解释2
这里是地址,结果是4/8.不要认为是一个字节啊,指针变量的大小和类型无关,不管是啥类型的指针变量,大小都是4/8个字节
解释3
arr没有单独传入,所以代表的是数组首元素的地址,加上*解引用操作,所以计算的是是首元素的大小
解释4
这里和上面的类似,虽然&arr是代表整个数组,但是&arr取出的是数组的地址,是地址,那大小就是4或者8个字节,类型是char (*)[6]
解释5
这里和上面还是一样的,数组的地址加1,跳过整个数组,但还是地址,地址的大小是4/8个字节
解释5
strlen函数,计算字符串的长度,计算的是‘\0’前的字符个数
参数是一个字符指针
这里的arr是没有‘\0’的,所以是随机值
解释6
这里传入的是首元素的地址,因为strlen函数计算的是‘\0’前的字符个数,传入的只是从哪里开始计算的地址,所以大小为随机值
解释7
因为strlen函数的参数是一个字符指针,所以传入的任何数据都会被当成地址,而这里传入的是数组首元素,会转换成对应的ASCII值,然后转换成对应的十六进制,访问对应的内存,但是访问的内存不知道有没有,非法访问,所以会报错
解释8
这里传入的是数组的地址,虽然数组首元素的地址和数组的地址是一样的,但是类型不一样,数组的地址的类型为char (*)[6],而strlen的参数类型为 const char *,这里就会发生类型转换,编译器会报警告
解释9
这里会跳过整个数组,结果是随机值
另一种写法
#include<stdio.h> int main() { //字符数组 char arr[] = "abcdef"; printf("%d\n", sizeof(arr)); printf("%d\n", sizeof(arr + 0)); printf("%d\n", sizeof(*arr)); printf("%d\n", sizeof(arr[1])); printf("%d\n", sizeof(&arr)); printf("%d\n", sizeof(&arr + 1)); printf("%d\n", sizeof(&arr[0] + 1)); printf("%d\n", strlen(arr)); printf("%d\n", strlen(arr + 0)); printf("%d\n", strlen(*arr)); printf("%d\n", strlen(arr[1])); printf("%d\n", strlen(&arr)); printf("%d\n", strlen(&arr + 1)); printf("%d\n", strlen(&arr[0] + 1)); return 0; }
这里的情况和上面的类似,这里就不讲解了
另一种写法
#include<stdio.h> int main() { //字符数组 char* p = "abcdef"; printf("%d\n", sizeof(p)); printf("%d\n", sizeof(p + 1)); printf("%d\n", sizeof(*p)); printf("%d\n", sizeof(p[0])); printf("%d\n", sizeof(&p)); printf("%d\n", sizeof(&p + 1)); printf("%d\n", sizeof(&p[0] + 1)); printf("%d\n", strlen(p)); printf("%d\n", strlen(p + 1)); printf("%d\n", strlen(*p)); printf("%d\n", strlen(p[0])); printf("%d\n", strlen(&p)); printf("%d\n", strlen(&p + 1)); printf("%d\n", strlen(&p[0] + 1)); return 0; }
在解释之前要好好看看以下的图
可以看出p存放的是字符a的地址,而不是整个字符串
解释1
p里面存放的是字符a的地址,是地址,大小就是4/8个字节
解释2
p + 1 相当于是字符a的地址加1,偏移1个字节,地址指向的是第二个元素的地址,大小为4/8个字节
解释3
*p取出字符a,大小为1个字节
解释4
前面我们知道arr[0] <==>*(arr + 1),这里的p存储的是字符串的首元素地址,(字符a的地址),字符串相当于一个数组arr, 首元素的地址,大小为4/8个字节
解释5
这里的大小为4/8个字节,类型是char**
解释6
这里的结果是4/8个字节,&p + 1跳过4/8个字节,类型是char**
解释7
前面我们讲过p[0] ==> arr[0],也就是字符串的首元素的, &p[0] == > &arr[0] ,所以这里访问的是第二个元素的地址 ,大小为4/8个字节
解释8
p存放的是字符a的地址,所以计算的长度为6,计算’\0’前的字符个数
解释9
这里传入的是p的地址,所以大小是随机值