当前位置:网站首页>深度解析指针与数组笔试题

深度解析指针与数组笔试题

2022-07-06 03:02:00 iYYu


前言

上一篇深入探究了指针和指针类型相关的知识,而本文的目的在于解析指针和数组的各种笔试题来巩固对指针的理解以及加深对指针掌握。


先来回顾下数组名的含义:

数组名在绝大多数情况下代表的就是数组首元素地址,但是有两个例外:

  1. sizeof(数组名),这里的数组名代表的是整个数组,sizeof计算的是整个数组所占的空间大小。
    注:必须是sizeof内部单独放一个数组名才代表整个数组,否者就是数组首元素地址。
  2. &数组名,这里取出的地址是整个数组的地址。

只有以上两种特例,其余的数组名都代表的是数组首元素的地址。


32位机器下指针所占空间大小是4个字节,64位下是8.

1. 指针和数组笔试题解析

1.1 一维数组

int a[] = {
    1,2,3,4};

printf("%d\n",sizeof(a));//16
//这里就是sizeof(数组名),表示整个数组
//计算的是整个数组的大小单位是字节

printf("%d\n",sizeof(a+0));//4/8
//乍一看a+0等于没加,表示的还是整个数组
//但是sizeof内部并不是单独放一个数组名,也没有取地址
//所以不是代表的整个数组
//因此这里的a代表的是
//数组首元素的地址,+0等于没加,是地址,就是4/8个字节。

printf("%d\n",sizeof(*a));//4
//这里也不是单独放在sizeof内部,因此代表的还是首元素地址
//即对首元素地址解引用,得到数组的首元素,大小是4个字节

printf("%d\n",sizeof(a+1));//4/8
//同样a没有单独放在sizeof内部,代表的是首元素地址
//首元素地址+1是第二个元素的地址,地址的大小是4/8个字节

printf("%d\n",sizeof(a[1]));//4
//这里计算的是数组第二个元素所占内存空间的大小,是4个字节

printf("%d\n",sizeof(&a));
//这里取出数组的地址,是地址也是4/8个字节

printf("%d\n",sizeof(*&a));///16
//取出数组的地址 &a--> int (*)[4]它的类型
//再对该数组指针解引用,又得到了数组
//因此计算的是数组所占空间的大小
//也可以这么理解 *和& 抵消了,就剩个数组名了

printf("%d\n",sizeof(&a+1));//4/8
//这里取出的地址是整个数组的地址&a--> int (*)[4]
//数组指针+1向后跳过整个数组,指向下一个数组的起始位置
//因此是地址,就是4/8个字节

printf("%d\n",sizeof(&a[0]));//4/8
//取出第一个元素的地址
//计算的是地址的大小

printf("%d\n",sizeof(&a[0]+1));//4/8
//取出第一个元素的地址,+1是第二个元素的地址
//计算的是地址的大小

1.2 字符数组

char arr[] = {
    'a','b','c','d','e','f'};

printf("%d\n", sizeof(arr));//6
//这里是sizeof内部单独放个数组名,那么计算的就是
//整个数组所占空间的大小,单位是字节

printf("%d\n", sizeof(arr+0));//4/8
//arr+0代表是数组首元素的地址
//地址大小4/8个字节

printf("%d\n", sizeof(*arr));//1
//没有单独放在sizeof内部那么arr是首元素地址
//首元素地址解引用得到的是数组首元素,大小是一个字节

printf("%d\n", sizeof(arr[1]));//1
//计算的第二个元素的大小

printf("%d\n", sizeof(&arr));//4/8
//取地址arr取出的是整个数组的地址
//是地址大小就是4/8个字节

printf("%d\n", sizeof(&arr+1));//4/8
//取出整个数组的地址+1,代表的是跳过整个数组
//指向下一个数组的起始位置。
//那么也是地址,大小是4/8个字节

printf("%d\n", sizeof(&arr[0]+1));//4/8
//&arr[0]+1是第二个元素的地址
//地址大小4/8

注意:同一个数组,但不是sizeof了。

char arr[] = {
    'a','b','c','d','e','f'};

printf("%d\n", strlen(arr));//随机值
//strlen是求字符串长度,求出\0之前的长度
//上面字符数组的初始化并没有\0
//而内存中的数据又是连续存储的
//因此strlen会一直向后找,直到找到\0
//所以最后的结果是随机的

printf("%d\n", strlen(arr+0));//随机值
//arr+0还是首元素地址,所以原理同上

printf("%d\n", strlen(*arr));//报错
//strlen的参数是数组的起始地址
//*arr是把字符a作为参数传递给strlen
//字符a的ASCII码值也就是97
//strlen(97),97的地址明显不属于我们
//也就造成了野指针的问题,因此程序会报错

printf("%d\n", strlen(arr[1]));//报错
//把字符b传给strlen,原理同上

printf("%d\n", strlen(&arr));//随机值
//也是从数组的起始地址开始向后寻找\0

printf("%d\n", strlen(&arr+1));//随机值-6
//取出arr的地址+1跳过整个字符数组
//指向后面数组的起始地址
//一个从&arr开始寻找,一个从&arr+1开始寻找 
//因此最后的结果是随机值-6

printf("%d\n", strlen(&arr[0]+1));//随机值-1
//从第二个元素地址开始向后找 

字符串初始化和上面有什么不同呢?

char arr[] = "abcdef";

printf("%d\n", sizeof(arr));//7
//[a b c d e f \0]
//字符串末尾自带\0,且这里sizeof内部单独一个数组名
//计算的是整个数组所占的内存空间大小,7个字节

printf("%d\n", sizeof(arr+0));//4/8
//没有单独放在sizeof内部,这里的arr代表的是首元素地址
//arr+0没变,因此是地址,大小就是4/8个字节

printf("%d\n", sizeof(*arr));//1
//没有单独放在sizeof内部,arr是首元素地址
//解引用得到首元素,大小是1个字节

printf("%d\n", sizeof(arr[1]));//1
//计算第二个元素的大小,1个字节

printf("%d\n", sizeof(&arr));//4/8
//取出整个数组的地址,是地址就是4或8个字节

printf("%d\n", sizeof(&arr+1));//4/8
//取出整个数组的地址+1向后跳过整个数组
//指向下一个数组的起始位置地址,地址就是4/8个字节

printf("%d\n", sizeof(&arr[0]+1));//4/8
//取出首元素地址+1是第二个元素的地址
//是地址大小就是4/8个字节

注意:同一个数组,但不是sizeof了。

char arr[] = "abcdef";

printf("%d\n", strlen(arr));//6
//这里arr是首元素地址,计算\0之前字符串长度

printf("%d\n", strlen(arr+0));//6
//arr+0还是首元素地址

printf("%d\n", strlen(*arr));//报错
//strlen的函数参数为地址
//而这里是个字符变量

printf("%d\n", strlen(arr[1]));//报错
//原理同上

printf("%d\n", strlen(&arr));//6
//&arr取出数组的地址,而数组的地址也是从数组首元素地址开始
//长度还是6

printf("%d\n", strlen(&arr+1));//随机值
//跳过整个arr数组,从数组后面开始找\0
//并不知道什么时候会遇到\0

printf("%d\n", strlen(&arr[0]+1));//5
//首元素地址+1,从第二个元素地址向后数
char *p = "abcdef";
//p是指针,把常量字符串abcdef的首元素地址存放在p里

printf("%d\n", sizeof(p));//4/8
//p是指针变量,指针大小就是4/8个字节

printf("%d\n", sizeof(p+1));//4/8
//p是char类型指针,那么p+1指向下一个元素的地址
//是地址就是4/8个字节

printf("%d\n", sizeof(*p));//1
//p存放的首元素地址,解引用访问字符a
//a占一个字节

printf("%d\n", sizeof(p[0]));//1
//同上

printf("%d\n", sizeof(&p));//4/8
//指针变量p也会在内存中开辟自己的内存空间
//取出p的地址,是地址4/8个字节

printf("%d\n", sizeof(&p+1));//4/8
//&p是个地址,+1也是个地址

printf("%d\n", sizeof(&p[0]+1));//4/8
//取出第一个字符的地址,+1是第二个元素的地址

char *p = "abcdef";

printf("%d\n", strlen(p));//6
//p里面存放的是a的地址,所以从a向后数

printf("%d\n", strlen(p+1));//5
//p+1是第二个元素的地址,从第二个元素向后数

printf("%d\n", strlen(*p));//报错
//strlen函数的参数是地址,而这里是'a'

printf("%d\n", strlen(p[0]));//报错
//同上

printf("%d\n", strlen(&p));//随机值
//和数组名不同,取出p在内存中所开辟的空间的地址和
//字符串首元素的地址就没有关系了,从p向后数
//什么时候遇到\0是随机的 

printf("%d\n", strlen(&p+1));//随机值
//同上

printf("%d\n", strlen(&p[0]+1));//5
//从第二个元素的地址向后数

1.3 二维数组

int a[3][4] = {
    0};

printf("%d\n",sizeof(a));//48
//三行四列的二维数组
//数组总大小为3*4*4 = 48

printf("%d\n",sizeof(a[0][0]));//4
//第一行第一个元素,大小为4

printf("%d\n",sizeof(a[0]));//16
//可以这么理解:
//a[0] 是第一行的数组名
//a[1] 是第二行的数组名
//a[2] 是第三行的数组名
//数组名单独放在sizeof内部计算的是整个第一行数组的大小

printf("%d\n",sizeof(a[0]+1));//4/8
//a[0]+1 == &a[0][0]+1
//没有单独放在sizeof内部
//这里啊a[0]表示的是第一行数组首元素的地址
//+1就是第一行第二个元素的数组
//是地址就是4/8个字节

printf("%d\n",sizeof(*(a[0]+1)));//4
//上面说到了a[0]+1是第一行第二个元素的地址
//那么在对它解引用得到的就是第二个元素
//大小就是4个字节

printf("%d\n",sizeof(a+1));//4/8
//a没有单独放在sizeof内部也没有取地址
//这里的a代表的是二维数组第一行的地址
//+1是第二行的地址
//是地址大小就是4个字节

printf("%d\n",sizeof(*(a+1)));//16
//a+1是第二行的地址,解引用得到第二行的数组
//这里计算的是第二行数组的大小

printf("%d\n",sizeof(&a[0]+1));//4/8
//取出第一行数组的地址
//+1得到第二行的地址,地址是4/8个字节

printf("%d\n",sizeof(*(&a[0]+1)));//16
//取出一行的地址+1是第二行的地址
//再解引用得到第二行的数组
//这里计算的是第二行数组的大小

printf("%d\n",sizeof(*a));//16
//a表示首元素地址,就是第一行的地址
//对第一行解引用拿到的就是第一行
//计算的是这一行数组的大小

printf("%d\n",sizeof(a[3]));//16
//没有第四行,sizeof不会去访问该行
//sizeof只关注它的类型,所以就等价于a[0]
//数组名单独放在sizeof内部
//计算的是该行数组的大小
//注意:sizeof只要知道它的类型,就可以分析出
//该类型所占空间大小

总结

数组名的意义:

  1. sizeof(数组名),这里的数组名表示整个数组,计算的是整个数组的大小。
  2. &数组名,这里的数组名表示整个数组,取出的是整个数组的地址。
  3. 除此之外所有的数组名都表示首元素的地址。
原网站

版权声明
本文为[iYYu]所创,转载请带上原文链接,感谢
https://blog.csdn.net/weixin_66672501/article/details/125608069