指针和数组笔试题解析

时间:2023-03-03 21:01:16

在大多数情况下,数组名就是数组首元素的地址,但是有两种特殊情况:

一、sizeof(数组名):当数组名单独放在sizeof内部,指的是整个数组

二、&数组名:取的是整个数组的地址,但是结果和首元素的地址是一样的,但是在指针运算的时候不同

一维数组:

int a[] = { 1,2,3,4 };
printf("%d\n", sizeof(a));//a单独放在里面,表示整个数组,16
printf("%d\n", sizeof(a + 0));//a不是单独放在里面,此时表示首元素的地址,4/8
printf("%d\n", sizeof(*a));//a表示首元素的地址,取地址后就是元素1,所以是4
printf("%d\n", sizeof(a + 1));//a表示首元素的地址,加一,指向后面一个元素的地址,就是4/8
printf("%d\n", sizeof(a[1]));//第2个元素,大小是4
printf("%d\n", sizeof(&a));//取出整个数组的地址,但是结果还是首元素的地址,4/8
printf("%d\n", sizeof(*&a));//取出整个数组的地址,解引用,为整个数组,也就是16
printf("%d\n", sizeof(&a + 1));//取整个数组的地址再加一,跳过整个数组,指针指向元素4的后面,4/8
printf("%d\n", sizeof(&a[0]));//取地址第一个元素,4/8
printf("%d\n", sizeof(&a[0] + 1));//取地址第一个元素,再加一,指向第二个元素,4/8

字符数组:

1.sizeof

字符数组和字符串的区别在于'\0',字符串最后都会有一个'\0',但是字符数组没有,我们下面会分析到

//字符数组
char arr[] = {'a','b','c','d','e','f'};
printf("%d\n", sizeof(arr));//arr单独放,表示整个数组,6
printf("%d\n", sizeof(arr+0));//arr表示首元素的地址,所以结果就是4/8
printf("%d\n", sizeof(*arr));//解引用第一个元素的地址,得到第一个元素'a',1
printf("%d\n", sizeof(arr[1]));//第二个元素'b',1
printf("%d\n", sizeof(&arr));//取整个数组的地址,但是值还是首元素的地址,4/8
printf("%d\n", sizeof(&arr+1));//跳过整个数组,指向'f'的后面,4/8
printf("%d\n", sizeof(&arr[0]+1));//指向第二个元素的地址,4/8

2.strlen

指针和数组笔试题解析

可以看到,strlen的参数是字符指针,也就是说传参传的是一个指针,;返回的是字符串的长度。

长度是从第一个字符到'\0',不包括'\0'。

以及关于strlen和sizeof的比较:例如char mystr[100]="test string";sizeof的结果是100,但是strlen的结果是11。

char arr[] = {'a','b','c','d','e','f'};
printf("%d\n", strlen(arr));//注意这里的arr是首元素的地址,而'\0'的位置不可知,所以是随机值
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));//这里跳过整个数组,指向字符f的后面,随机值,而且比第一个例子小6
printf("%d\n", strlen(&arr[0]+1));//这里从第二个元素开始,随机值,比第一个例子小1

第三个:当我们调式的时候,会报错,像这样:

指针和数组笔试题解析

在这里,arr是首元素的地址,解引用后得到'a',这里我们是要把字符当作指针传进去,所以要把字符a转换成数字97,再将97作为地址传进去,这里我是用的x64(因为x86的指针为4个字节,整型的大小也是4个字节,像sizeof这样的操作符,为了区分结果是元素还是指针,所以我用了x64来加以区分),所以这里把97作为地址传入,地址就为0x0000000000000061(这里地址是用16进制表示的),因此就会发生越界访问,也就不会打印。


字符串:

char arr[] = "abcdef";
printf("%d\n", sizeof(arr));//--7
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));//--指向字符b,4/8
printf("%d\n", strlen(arr));//--6
printf("%d\n", strlen(arr+0));//--6
printf("%d\n", strlen(*arr));//--越界访问,不打印
printf("%d\n", strlen(arr[1]));//--越界访问,不打印
printf("%d\n", strlen(&arr));//--虽然取的是整个字符串的地址,但是结果还是首元素的地址,6
printf("%d\n", strlen(&arr+1));//--跳过整个字符串,之后什么时候到'\0'未知,随机值
printf("%d\n", strlen(&arr[0]+1));//--从第二个元素开始,5


指针:

char *p = "abcdef";这里的p是首元素的地址
printf("%d\n", sizeof(p));//--4/8
printf("%d\n", sizeof(p+1));//--指向第二个元素的地址,4/8
printf("%d\n", sizeof(*p));//--字符a,1
printf("%d\n", sizeof(p[0]));//--等于(*p+0),1
printf("%d\n", sizeof(&p));//--p的地址,4/8
printf("%d\n", sizeof(&p+1));//--跳过p,4/8
printf("%d\n", sizeof(&p[0]+1));//--p[0]就是a,再取a的地址再加一,就是b的地址,4/8

这图画的是x86的情况,这时指针是4个字节:

指针和数组笔试题解析


char *p = "abcdef";这里的p是首元素的地址
printf("%d\n", strlen(p));//--6
printf("%d\n", strlen(p+1));//--5
printf("%d\n", strlen(*p));//--越界访问,不打印
printf("%d\n", strlen(p[0]));//--越界访问,不打印
printf("%d\n", strlen(&p));//--见上图,不知道指向p的指针什么时候指到'\0',所以是随机值
printf("%d\n", strlen(&p+1));//--和上面的差不多,随机值
printf("%d\n", strlen(&p[0]+1));//--指向字符b,5


二维数组:

int a[3][4] = {0};
printf("%d\n",sizeof(a));//--48
printf("%d\n",sizeof(a[0][0]));//--4
printf("%d\n",sizeof(a[0]));//--单独数组名,第一行,16
printf("%d\n",sizeof(a[0]+1));//--第一行第一个元素的地址再加一,表示第一行第二个元素的地址,4/8
printf("%d\n",sizeof(*(a[0]+1)));//--表示第一行第二个元素,4
printf("%d\n",sizeof(a+1));//--第二行的地址,4/8
printf("%d\n",sizeof(*(a+1)));//--表示第二行,16
printf("%d\n",sizeof(&a[0]+1));//--取出第一行的地址再加一,表示第二行的地址,4/8
printf("%d\n",sizeof(*(&a[0]+1)));//--表示第二行,16,其实相当于写了个a[1]
printf("%d\n",sizeof(*a));//--表示第一行,16,可以写成 *(a+0)
printf("%d\n",sizeof(*a+1));//--这里*a表示的是第一行,第一行再加一,就是第一行第二个元素的地址,4/8
printf("%d\n",sizeof(a[3]));//--单独数组名,表示第四行,16

这里的最后一个例子表示的是第四行,越界了吗?

其实这个地方不存在越界,其实sizeof内部的表达式并不会真的去访问或者计算,我们看下面这个代码:

int main()
{
int a = 5;
short s = 11;
printf("%d\n", sizeof(s = a + 2));
printf("%d\n", s);

return 0;
}

这里a+2结果是7,放到 s 里面去,发生截断,但是 s 依然可以存7,所以按理来说,打印出来 s 应该等于7,sizeof看里面的表达式计算的结果是多少字节,按理来说应该是2,因为是short型。

指针和数组笔试题解析

但是我们却看到,s的值是11,为什么呢?

其实,sizeof内部的表达式并没有真正计算,并没有把7赋给s,而是通过类型就判断出来了结果是2,第二个原因,是因为

指针和数组笔试题解析

一般表达式是在生成了可执行程序exe之后才开始计算,但是sizeof的结果早在编译期间就已经处理了,编译期间就已经通过类型属性确定结果是2了。所以在sizeof内部的表达式其实是不会去计算的。

所以在这里不会发生越界访问,因为sizeof已经通过类型得到结果了,并不会去真的访问。


好了,以上就是指针和数组面试题的全部内容了,希望大家在读后可以对指针和数组相关的认识有更加深入的理解。