指针和数组笔试题解析

本文详细解析了指针与数组的关系,包括一维数组、字符数组和二维数组。介绍了数组名在不同场景下的含义,如在sizeof中的表现、指针运算及地址大小等。特别强调了sizeof和strlen的区别,前者关注内存占用,后者关注字符串长度。同时,文中通过大量实例分析了各种表达式在不同情况下的行为,帮助读者深入理解指针和数组的概念。
摘要由CSDN通过智能技术生成

目录

一、一维数组

二、字符数组

三、二维数组​​​​​​​


前言回顾:


数组名的理解:

数组名是数组首元素的地址,但是有2个例外:

1、sizeof(数组名),这里的数组名是表示整个数组,计算的是整个数组的大小,单位是字节。

2、&数组名,这里的数组名也表示整个数组,取出的是整个数组的地址。

除上面中的2种特殊情况外,所有的数组名都是数组首元素的地址。


指针的大小:

32位平台,(X86环境),指针大小是4个字节;

64位平台,(X64环境),指针大小是8个字节。


一、一维数组

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

printf("%d\n", sizeof(a));

数组名单独放在sizeof内部,计算的是整个数组的大小,单位是字节:4*4=16。

答案:16

printf("%d\n", sizeof(a + 0));

a表示的是首元素的地址,a+0还是数组首元素的地址,是地址大小就是(X86表示32环境)4或8(X64表示64环境)。

答案:4或8。

printf("%d\n", sizeof(*a));

a表示的是首元素的地址,*a就是对首元素地址的解引用,*a就是1,1的类型是整型,所以是4个字节。

答案:4。

printf("%d\n", sizeof(a + 1));

a表示的是首元素的地址,a是整型指针,a+1就跳过1个整型是第2个元素的地址,即是2的地址,是地址,其大小就是4或8个字节。

答案:4或8。

printf("%d\n", sizeof(a[1]));

a[1]是数组的第2个元素,sizeof计算它所占内存的大小。

答案:4。

printf("%d\n", sizeof(&a));

&a表示数组的地址,数组的地址也是地址,是地址其大小就是4或8。

答案:4或8。

printf("%d\n", sizeof(*&a));

第一种理解:
    可以理解为*和&抵消效果,*&a相当于a,sizeof(a)就是16。

第二种理解:
    &a——取出的是数组的地址,它的类型是数组指针:int(*)[4],表示它指向一个有4个整型的数组
    对指向一个数组的指针解引用的效果:访问的是整个数组(访问的是4个int的数组)大小是4个字节。

答案:16。

printf("%d\n", sizeof(&a + 1));

&a取出的是整个数组的起始地址,&a+1就会跳过整个数组(跳过16个字节)指向数组后面的空间,是指针(地址)其大小就是4或8字节。

&a是数组的地址,&a+1是跳过整个数组后的地址,是地址其大小就是4或8个字节。

答案:4或8。

printf("%d\n", sizeof(&a[0]));

&a[0]是取出数组第1个元素的地址,是地址其大小就是4或8个字节。

答案:4或8。

printf("%d\n", sizeof(&a[0] + 1));

&a[0] + 1是第2个元素的地址,是地址其大小就是4或8个字节。
&a[0]——它的地址类型是int*,int*+1就会跳过一个整型指向第2个元素,是第2个元素的地址。

答案:4或8。

综上:

#include <stdio.h>
int main()
{
	int a[] = { 1,2,3,4 };
	printf("%d\n", sizeof(a));//16
	printf("%d\n", sizeof(a + 0));//4或8
	printf("%d\n", sizeof(*a));//4
	printf("%d\n", sizeof(a + 1));//4或8
	printf("%d\n", sizeof(a[1]));//4
	printf("%d\n", sizeof(&a));//4
	printf("%d\n", sizeof(*&a));//16
	printf("%d\n", sizeof(&a + 1));//4或8
	printf("%d\n", sizeof(&a[0])); //4或8
	printf("%d\n", sizeof(&a[0] + 1));//4或8
	return 0;
}

二、字符数组

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

sizeof只关注它所占空间大小,对于字符数组而言不关心字符是否有\0。

printf("%d\n", sizeof(arr));

arr作为数组名,单独放在sizeof内部,计算的是整个数组的大小,单位是字节。

答案:6。

printf("%d\n", sizeof(arr + 0));

arr就是首元素地址,arr+0还是首元素地址,大小就是4或8字节。

答案:4或8。

printf("%d\n", sizeof(*arr));

arr是首元素地址,*arr是首元素,是一个字符,是1个字节大小。

答案:1。

printf("%d\n", sizeof(arr[1]));

arr[1]是数组的第2个元素,是一个字符,是1个字节大小。

答案:1。

printf("%d\n", sizeof(&arr));

特殊情况之一,对数组名取地址,&arr取出的是数组的地址,数组的地址也是地址,是地址其大小就是4或8字节。

答案:4或8。

printf("%d\n", sizeof(&arr + 1));

&arr取出的是数组的地址,&arr+1跳过了整个数组,指针指向了f的后面,还是地址,是地址其地址大小就是4或8字节。

答案:4或8。

printf("%d\n", sizeof(&arr[0] + 1));

&arr[0]是第一个元素的地址,&arr[0]+1就是第二个元素的地址,是地址其大小就是4或8字节。

答案:4或8。

综上:

#include <stdio.h>
int main()
{
	//字符数组
	char arr[] = { 'a','b','c','d','e','f' };
	printf("%d\n", sizeof(arr));//6
	printf("%d\n", sizeof(arr + 0));//4
	printf("%d\n", sizeof(*arr));//1
	printf("%d\n", sizeof(arr[1]));//1
	printf("%d\n", sizeof(&arr));//4
	printf("%d\n", sizeof(&arr + 1))//4;
	printf("%d\n", sizeof(&arr[0] + 1))//4;
	return 0;
}

sizeof只关注占用内存空间的大小,单位是字节。

sizeof不关注对象,整型、字符、指针等类型都可以。

sizeof是操作符。


strlen是求字符串长度的,strlen关注的是字符串中\0的位置,计算的是\0之前出现的字符的个数。

strlen只针对字符串。

strlen是库函数。


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

printf("%d\n", strlen(arr));

arr没有单独放到sizeof内部,没有进行取地址,所以arr作为数组名就是数组首元素的地址即是a的地址,arr数组中没有\0,f的后面未知,计算的时候就不知道什么时候停止,但长度>=6。

答案:随机值。

printf("%d\n", strlen(arr + 0));

arr没有单独放到sizeof内部,没有进行取地址,所以arr作为数组名就是数组首元素的地址,arr+0还是首元素的地址。

答案:随机值。

printf("%d\n", strlen(*arr));

arr是数组名,是数组首元素地址,*arr就是字符a,a的ASCII码值是97,即相当于给strlen()函数传的是97。

分析前面两种情况,给strlen()传的都是a的地址,strlen就会从a地址向后数字符;同理,strlen会认为现在传的97就是地址,会从内存中地址为97的位置处开始向后数字符,统计字符串,但是97这个地址不是合法地址,不是程序分配的空间,会导致非法访问内存。

答案:错误。(程序挂掉)程序崩溃。调试时:读取位置发生访问冲突。

所以:

strlen需要的是一个地址,从这个地址开始向后找字符,直到\0,统计字符的个数。

strlen会检查参数类型。

printf("%d\n", strlen(arr[1]));

和上一个一样。

答案:错误。内存访问冲突。

printf("%d\n", strlen(&arr));

&arr取出的是数组的起始地址(a位置地址),这个起始地址是数组指针类型:char(*)[6]。

&arr是数组的地址,虽然类型和strlen的参数类型有所差异,但是传参过去后还是从第一个字符的位置向后数字符,结果还是随机值。

答案:随机值。

printf("%d\n", strlen(&arr + 1));

&arr是起始位置a的地址,&arr + 1是数组的地址+1,就会跳过整个数组指向f后面,即是把f后面的地址传给了strlen,也是随机值。

答案:随机值。

printf("%d\n", strlen(&arr[0] + 1));

从b的位置向后数字符,没有\0也是随机值。

答案:随机值。

综上:

#include <stdio.h>
#include <string.h>
int main()
{
	char arr[] = { 'a','b','c','d','e','f' };
	printf("%d\n", strlen(arr));//19
	printf("%d\n", strlen(arr + 0));//19
	//printf("%d\n", strlen(*arr));//错误
	//printf("%d\n", strlen(arr[1]));//错误
	printf("%d\n", strlen(&arr));//19
	printf("%d\n", strlen(&arr + 1));//13
	printf("%d\n", strlen(&arr[0] + 1));//18
	return 0;
}

举例:char arr[] = "abcdef";

printf("%d\n", sizeof(arr));

arr单独放到sizeof内部,有\0的存在,计算的是整个数组的大小,是7,单位是字节。

答案:7。

printf("%d\n", sizeof(arr+0));

数组名表示数组首元素的地址,是a的地址。

答案:4或8。

printf("%d\n", sizeof(*arr));

一种理解方式:*arr等价于*(arr+0)就等价于arr[0],就是第一个元素:a

答案:1。

printf("%d\n", sizeof(arr[1]));

b的大小也是1。

答案:1。

printf("%d\n", sizeof(&arr));

&arr取出的是数组的地址,数组的地址也是地址,是地址其大小就是4或8。

答案:4或8。

printf("%d\n", sizeof(&arr+1));

数组地址+1还是地址。

答案:4或8。

printf("%d\n", sizeof(&arr[0]+1));

b的地址。

答案:4或8。

综上:

#include <stdio.h>
int main()
{
	char arr[] = "abcdef";
	printf("%d\n", sizeof(arr));//7
	printf("%d\n", sizeof(arr + 0));//4
	printf("%d\n", sizeof(*arr));//1
	printf("%d\n", sizeof(arr[1]));//1
	printf("%d\n", sizeof(&arr));//4
	printf("%d\n", sizeof(&arr + 1));//4
	printf("%d\n", sizeof(&arr[0] + 1));//4
	return 0;
}

举例:char arr[] = "abcdef";

printf("%d\n", strlen(arr));

给strlen传的arr数组名,没有取地址,也没有单独放在sizeof内部,所以数组名表示数组首元素地址:a的地址。在\0的前面有6个字符,所以字符串的长度是6。

答案:6。

printf("%d\n", strlen(arr+0));

arr+0表示数组首元素地址,和上一个一样。注意这里是strlen中,并不是单独放在sizeof内部表示整个数组。

答案:6。

printf("%d\n", strlen(*arr));

传a,即传的是97,srlen需要的是地址,把97作为地址,非法访问。

答案:错误。

printf("%d\n", strlen(arr[1]));

传的是b,是98,非法访问。

答案:错误。

printf("%d\n", strlen(&arr));

&arr取出的是整个数组的地址,但整个数组的地址还是地址,而且还是数组的起始地址。在\0之前还是有6个字符。

答案:6。

printf("%d\n", strlen(&arr+1));

&arr+1跳过整个数组,指向\0后面的位置。(注意把\0也跳过去了)

 答案:随机值。

printf("%d\n", strlen(&arr[0]+1));

b的位置,从b到\0有5个字符。

答案:5。

综上:

#include <stdio.h>
#include <string.h>
int main()
{
	char arr[] = "abcdef";
	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));//12
	printf("%d\n", strlen(&arr[0] + 1));//5
	return 0;
}

举例:char *p = "abcdef";

指针变量p,p存的是a的地址,假设a的地址是0x0012ff40,p指向了a。

printf("%d\n", sizeof(p));

sizeof(p)计算的是指针变量p的大小,是4或8个字节。

答案:4或8。

printf("%d\n", sizeof(p+1));

p是字符指针,即是0x00ff240+1=0x0012ff41,还是地址,指向了字符b。

p是指针变量,是存放地址的,p+1也是地址,地址大小就是4或8个字节。

答案:4或8。

printf("%d\n", sizeof(*p));

p是字符指针,(p是char*的指针),对p解引用访问的是一个字符,*p=a,即是1个字节。

答案:1。

printf("%d\n", sizeof(p[0]));

p[0] = *(p+0) 。p本来是首元素的地址,p+0还是首元素地址,*(p+0)就等价于*p。

p[0] = *(p+0) = *p;即是1个字节。

答案:1。

printf("%d\n", sizeof(&p));

p是变量,p在内存中也有自己的地址,&p取出p在内存中的地址,&p也是地址,是地址就是4或8个字节。p是char*类型的指针,&p就是char**的二级指针。二级指针还是指针就是4或8个字节。

答案:4或8。

printf("%d\n", sizeof(&p+1));

&p的类型是char**,若表示出来:char* *pp,第二个*说明pp是指针,它指向的对象的类型是char*。(&p+1)一个二级指针+1要跳过一个char*的元素,即要跳过一个指向的元素,因为p就是char*类型的指针。

&p是地址,&p+1后还是地址,是地址其大小就是4或8个字节。&p+1是p的地址+1,在内存中是跳过p变量的地址,&p拿到的是0x0012ff80,&p+1就是0x0012ff84或0x0012ff88。

答案:4或8。

printf("%d\n", sizeof(&p[0]+1));

 p[0]就是*(p+0)就是*p,p[0]就是a,&p[0]即是取a的地址,&p[0]+1就是b的地址。

答案:4或8。

综上:

#include <stdio.h>
int main()
{
	char* p = "abcdef";
	printf("%d\n", sizeof(p));//4
	printf("%d\n", sizeof(p + 1));//4
	printf("%d\n", sizeof(*p));//1
	printf("%d\n", sizeof(p[0]));//1
	printf("%d\n", sizeof(&p));//4
	printf("%d\n", sizeof(&p + 1));//4
	printf("%d\n", sizeof(&p[0] + 1));//4
	return 0;
}

举例:char *p = "abcdef";

printf("%d\n", strlen(p));

p中存放的是字符a的地址,strlen(p)就是从a位置向后数字符串长度,\0之前有6个字符。

答案:6。

printf("%d\n", strlen(p+1));

p中存放的是a的地址,p+1就跳过一个字符,指向了b,即是b的地址,strlen(p+1)就是从b位置开始数字符串长度。

答案:5。

printf("%d\n", strlen(*p));

给strlen传的是字符a,传的就是97,strlen需要的是地址。

答案:错误。

printf("%d\n", strlen(p[0]));

p[0]就是*p。p[0] = *(p+0) = *p,传的也是字符a。

答案:错误。

printf("%d\n", strlen(&p));

p所指向的空间在内存中也是内存块,不知道向后访问的是什么。

 答案:随机值。

printf("%d\n", strlen(&p+1));

和上一个一样。

答案:随机值。

printf("%d\n", strlen(&p[0]+1));

p[0]就是a,p[0] = *(p+1) = *p -> 'a',&p[0]+1就是b的位置。(&p[0]+1 = p+1)

从第2个字符的位置向后数字符串长度,是5。

答案:5。

注意:&p是取出指针变量p的地址,p有自己的地址,p与字符串的唯一关系就是p里存放的是字符串的地址,p里面的值是字符串的地址。&p是取的是p变量所占内存空间的地址,不是取出它里面的值。

综上:

#include <stdio.h>
int main()
{
	char* p = "abcdef";
	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));//3
	printf("%d\n", strlen(&p + 1));//11
	printf("%d\n", strlen(&p[0] + 1));//5
	return 0;
}

三、二维数组

举例:int a[3][4] = {0};

​​​​​​​

 

printf("%d\n",sizeof(a));

数组名单独放在sizeof内部,此时数组名表示整个数组,sizeof(a)计算的就是整个数组的大小:3*4个元素*一个元素是4个字节大小 = 3*4*4 = 48。

答案:48。

printf("%d\n",sizeof(a[0][0]));

第一行第一个元素的大小。

答案:4。

printf("%d\n",sizeof(a[0]));

对于二维数组来说:

表示第一行元素:a[0][j],j的取值范围是0,1,2,3;

表示第二行元素:a[1][j],j的取值范围是0,1,2,3;

表示第三行元素:a[2][j],j的取值范围是0,1,2,3;

a[0]就相当于第一行的数组名,则a[1]就相当于第二行的数组名,则a[2]就相当于第三行的数组名。

所以由分析得出:a[0]表示第一行的数组名,a[0]作为数组名单独放在sizeof内部,计算的是整个数组的大小,这个整个数组仅仅代表一行,即计算的是第一行的大小:4*4 = 16个字节

答案:16。

printf("%d\n",sizeof(a[0]+1));

a[0]作为第一行的数组名没有&,没有单独放在sizeof内部,所以a[0]这个数组名表示的就是第一行数组首元素的地址,即是a[0][0]的地址。a[0]+1就是第一行第二个元素的地址,即是a[0][1]的地址,是地址其大小就是4或8个字节。

答案:4或8。

printf("%d\n",sizeof(*(a[0]+1)));

对第一行第二个元素的地址解引用,访问的就是第一行第二个元素内容。

答案:4。

printf("%d\n",sizeof(a+1));

a是二维数组的数组名,这个数组名没有&,没有单独放在sizeof内部,此时数组名a表示数组首元素地址,二维数组的首元素地址就是第一行的地址,第一行的地址a+1就跳过一行指向第二行a+1就是第二行的地址,(注意不是第二行第一个元素的地址),a+1的类型是:数组指针:int (*)[4],还是地址,是地址其大小就是4或8个字节。

第一行的地址和第二行的地址相差16个字节。

答案:4或8。

printf("%d\n",sizeof(*(a+1)));

a+1是第二行的地址,对第二行解引用就找到了第二行,即*(a+1)就是第二行,相当于第二行的数组名:*(a+1) -> a[1](因为a[1]就是第二行的数组名);把*(a+1)数组名单独放在sizeof内部计算的就是整个第二行数组的大小:16个字节。

答案:16。

printf("%d\n",sizeof(&a[0]+1));

a[0]是第一行的数组名,&a[0]取出的是整个一行的地址,&a[0]+1跳过一行指向了第二行,就是第二行的地址,是地址其大小就是4或8个字节。

答案:4或8。

printf("%d\n",sizeof(*(&a[0]+1)));

对第二行的地址解引用就相当于拿到了第二行,就相当于第二行的数组名,即也就是a[1],就是sizeof(a[1]),所以就相当于数组名单独放在了sizeof内部,计算的是整个第二行数组大小:16个字节。

答案:16。

printf("%d\n",sizeof(*a));

a是二维数组的数组名,没有&,没有单独放到sizeof内部,则a表示的是数组首元素地址,则*a就是首元素,是二维数组的首元素,就是第一行。sizeof(*a):第一行的大小就是16个字节。

*a -> *(a+0) -> a[0] (第一行数组名就是a[0])

答案:16。

printf("%d\n",sizeof(a[3]));

a[3]越界了吗?

int a = 10;

sizeof (int) = 4;

sizeof (a) = 4;

这两种方式都可以,sizeof是通过a变量推导它的类型int,所以sizeof不会真的去访问a,用sizeof(int)就可以计算。

所以,sizeof不会访问第四行,就相当于看到a[0]、a[1]、a[2];a[3]表示的是第四行的数组名,第四行的数组名的类型是数组类型,是一个一维数组,则计算大小就是16。sizeof会推导数组类型:int [4]

答案:16。

综上:

#include <stdio.h>
int main()
{
	//二维数组
	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

	/*printf("%p\n", &a[0][0]);
	printf("%p\n", a[0] + 1);*/

	printf("%d\n", sizeof(*(a[0] + 1)));//4
	printf("%d\n", sizeof(a + 1));//4

	/*printf("%p\n", &a[0][0]);
	printf("%p\n", a + 1);*/
	/*int* p = a + 1;*///编译器会报警告:int *”与“int (*)[4]”的间接级别不同
	//等号左边是整型指针,等号右边是指向数组的指针,是数组指针

	printf("%d\n", sizeof(*(a + 1)));//16
	printf("%d\n", sizeof(&a[0] + 1));//4
	printf("%d\n", sizeof(*(&a[0] + 1)));//16
	printf("%d\n", sizeof(*a));//16
	printf("%d\n", sizeof(a[3]));//16
	return 0;
}

&a+1:

对二维数组取地址,a是二维数组的数组名,二维数组的地址+1就会跳过整个二维数组。二维数组在内存中是连续存放的,所以&a+1就会跳过三行,跳了48个字节。

#include <stdio.h>
int main()
{
	int a[3][4] = { 0 };
	printf("%p\n", &a[0][0]);
	printf("%p\n", &a + 1);
	return 0;
}
//00EFFAC0
//00EFFAF0
//相差48个字节,地址的大小是4或8个字节。

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值