掌握指针和数组:经典笔试题攻略(万字详解)

博客主页:江池俊的博客

收录专栏:C语言刷题专栏

代码仓库:江池俊的代码仓库

我的社区:GeekHub

欢迎大家点赞评论收藏⭐

在这里插入图片描述

文章目录

  • 前言
  • 指针和数组笔试题
    • 一维数组
    • 字符指针
      • sizeof()的计算
      • strlen的计算
      • 总结
    • 二维数组
  • 指针笔试题
    • 笔试题1:
    • 笔试题2:
    • 笔试题3:
    • 笔试题4:
    • 笔试题5:
    • 笔试题6:
    • 笔试题7:
    • 笔试题8:
  • 小结:

前言

  • 当涉及到计算机编程的核心概念时,指针和数组无疑是最重要和基础的话题之一。无论你是初学者还是有经验的开发者,深入理解和掌握这两个概念都能让你的编程技能得到质的提升。
  • 在本篇博客中,我们将带你进入指针和数组的精彩世界,探索其在编程中的作用和应用。无论你是为了笔试准备,还是为了提升自己的技能,这里都将为你提供一个深入学习的机会。

在本篇文章中如有遇到不懂的知识点,可以参考以下文章《掌握指针进阶:探索字符指针、数组指针和指针数组的妙用》、《掌握指针进阶:一篇带你玩转函数指针、函数指针数组及指向函数指针数组的指针!!》希望对你有所帮助

注意: 本次讲解是在vs2022 x86的环境下进行的,即电脑的32位环境,所以默认为指针大小占4个字节


指针和数组笔试题

一维数组

//一维数组
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));

以上语句输出的结果是什么?结果如下:

掌握指针和数组:经典笔试题攻略(万字详解)_第1张图片

解析:

int main()
{
	//一维数组
	int a[] = { 1,2,3,4 };
	//数组中有4个元素,每个元素的类型为int类型,所以数组所占字节大小为4*4=16
	printf("%d\n", sizeof(a));
	//sizeof里面的a表示的是整个数组,所以这里求的是整个数组的大小,即4*4=16
	printf("%d\n", sizeof(a + 0));
	//a+0 其实是下标为0的元素的地址(即数组第一个元素的地址),是地址就是4/8字节,这里是32位环境,所以大小是4个字节
	printf("%d\n", sizeof(*a));
	//*a是数组首元素,计算的是数组首元素的大小,单位是字节,a为int类型,所以大小为4个字节
	printf("%d\n", sizeof(a + 1));
	//a为首元素地址,+1表示下表为1的元素地址(即第二个元素的地址),地址大小为4个字节
	printf("%d\n", sizeof(a[1]));
	//a[1]表示第二个元素,计算的是第二个元素的大小,大小为4个字节
	printf("%d\n", sizeof(&a));
	//&a是整个数组的地址,整个数组的地址也是地址,地址的大小为4个字节
	
	//&a---> 类型:int(*)[4] (数组指针类型)

	printf("%d\n", sizeof(*&a));
	//&a是数组的地址,*&a就是拿到了数组,*&a --> a,a就是数组名,sizeof(*&a)-->sizeof(a)
	//计算的是整个数组的大小,单位是字节,即大小是4*4 = 16个字节

	printf("%d\n", sizeof(&a + 1));
	//&a是整个数组的地址,&a+1,跳过整个数组,指向数组后边的空间,是一个地址,大小是4个字节
	printf("%d\n", sizeof(&a[0]));
	//&a[0]是首元素的地址,计算的是首元素地址的大小,为4个字节
	printf("%d\n", sizeof(&a[0] + 1));
	//&a[0]表示第一个元素的地址,+1就是第二个元素的地址,地址的大小为4个字节
	return 0;
}
//sizeof 类型:----> size_t ----> unsigned int

这里我们需要知道:

1. sizeof(数组名),数组名表示整个数组。计算的是整个数组的大小,单位是字节。
2. &数组名,数组名表示整个数组。取出的是整个数组的地址,+1跳过的是整个数组。

  • 除此之外,所有的数组名都是数组首元素的地址

字符指针

sizeof()的计算

//字符数组 ---> sizeof()
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));

以上语句输出的结果是什么?结果如下:

掌握指针和数组:经典笔试题攻略(万字详解)_第2张图片
解析:

int main()
{
	char arr[] = { 'a','b','c','d','e','f' };
	printf("%d\n", sizeof(arr));
	//arr单独放在sizeof内部,计算的是整个数组的大小,单位是字节,所以大小为 6*1 = 6个字节
	printf("%d\n", sizeof(arr + 0));
	//arr表示首元素地址,+0表示下标为0的元素地址(即数组首元素的地址),大小为4个字节
	printf("%d\n", sizeof(*arr));
	//arr是数组的首元素的地址,*arr表示对arr解引用,得到字符'a',大小为1字节
	printf("%d\n", sizeof(arr[1]));
	//arr[1]是第二个元素,因为数组元素为char类型,所以大小为1字节
	printf("%d\n", sizeof(&arr));
	//&arr表示取出整个数组的地址,数组的地址也是地址,大小为4个字节
	printf("%d\n", sizeof(&arr + 1));
	//&arr表示取出整个数组的地址,+1表示跳过整个数组,指向数组后边空间(即'f'后面)的地址,大小为4个字节
	printf("%d\n", sizeof(&arr[0] + 1));
	//&arr[0] + 1表示数组第二个元素的地址,大小为4个字节
	return 0;
}

注意: 如果 char arr[] = "abcdef"; 这里的arr数组有7个元素(加上一个’\0’),编译器会默认为arr数组末尾添加结束标志‘\0’,所以计算数组大小时结果不再是6而是7。

掌握指针和数组:经典笔试题攻略(万字详解)_第3张图片


当使用指针存放字符串又会怎样?

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));

以上语句输出的结果是什么?结果如下:

掌握指针和数组:经典笔试题攻略(万字详解)_第4张图片
解析:

int main()
{
	char* p = "abcdef";
	printf("%d\n", sizeof(p));
	//p是存放字符串"abcdef"的指针变量,是指针,所以大小为4个字节
	printf("%d\n", sizeof(p + 1));
	//p表示首元素的地址,+1表示下表为1的元素的地址(即b的地址),大小为4个字节
	printf("%d\n", sizeof(*p));
	//p表示首元素的地址,对p解引用,*p就是字符'a',sizeof(*p)计算的是字符的大小,是1个字节
	printf("%d\n", sizeof(p[0]));
	//p[0]-->*(p+0) --> *p  表示第一个元素'a',大小是1个字节
	printf("%d\n", sizeof(&p));
	//&p是二级指针,是指针大小为4个字节
	printf("%d\n", sizeof(&p + 1)); 
	//&p是二级指针,+1跳过的是p变量后(即指针大小为4个字节)的地址,因为还是指针,所以大小为4个字节
	printf("%d\n", sizeof(&p[0] + 1));
	//p[0]就是‘a’,&p[0]就是a的地址,+1,就是b的地址,是地址大小为4个字节
	return 0;
}

strlen的计算

//size_t strlen(const char* str)
//strlen返回类型为size_t ----> unsigned int
char arr[] = { 'a','b','c','d','e','f' };
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));

输出结果如下:

掌握指针和数组:经典笔试题攻略(万字详解)_第5张图片

解析:

int main()
{
	//size_t strlen(const char* str)
	//strlen返回类型为size_t ----> unsigned int
	char arr[] = { 'a','b','c','d','e','f' };
	printf("%d\n", strlen(arr));
	//strlen统计字符串长度时直到遇到 '\0'才停止,但是在初始化arr时我们没有加上'\0',
	//即在arr数组中我们不知道'\0'的位置,所以这里计算的结果是随机值
	printf("%d\n", strlen(arr + 0));
	//这里计算的是从下标为0的元素位置开始计算字符串长度,由于无法得知结束标志'\0'的位置,所以结果同上,为随机值
	//printf("%d\n", strlen(*arr)); ---> //strlen需要接收的是一个地址,strlen('a') -> strlen(97),非法访问 - error
	//printf("%d\n", strlen(arr[1])); ---> //'b'->98,和上面的代码类似,是非法访问 - error
	printf("%d\n", strlen(&arr));
	//&arr虽然是整个数组的地址,但是也是从数组起始位置开始的,计算的结果同第一个一样还是随机值
	//&arr类型 ---> char(*)[6](数组指针类型)
	printf("%d\n", strlen(&arr + 1));
	//&arr是整个数组的地址,&arr+1是跳过整个数组的地址即'f'后面的地址,计算的是'f'后面的字符串长度,
	//结果也是随即值,但是这个随机值比第一个少6,少的是字符串"abcdef"
	printf("%d\n", strlen(&arr[0] + 1));
	//&arr[0]表示的是第一个元素的地址, + 1表示第二个元素的地址,是'b'的地址,计算的是从'b'开始的字符串的长度,
	//结果也是随机值,但是这个随机值比第一个少1,少的是字符'a'
	return 0;
}

char arr[] = "abcdef";
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;

输出结果如下:

掌握指针和数组:经典笔试题攻略(万字详解)_第6张图片

解析:

int main()
{
	char arr[] = "abcdef";//数组是7个元素
	//[a b c d e f \0]
	printf("%d\n", strlen(arr));
	//arr是数组首元素的地址,strlen从首元素的地址开始统计'\0'之前出现的字符个数,结果是6
	printf("%d\n", strlen(arr + 0));
	//arr + 0是数组首元素的地址,同第一个,结果是6
	//printf("%d\n", strlen(*arr));//*arr是'a',ASC II码值是97,传给strlen是一个非法的地址,造成非法访问
	//printf("%d\n", strlen(arr[1]));//'b' --> 98 ,同上
	printf("%d\n", strlen(&arr));
	//&arr虽然是整个数组的地址,但是也是从数组起始位置开始的,计算结果同第一个是6
	printf("%d\n", strlen(&arr + 1));
	//&arr + 1是跳过整个数组后的地址,统计字符串的长度是随机值
	printf("%d\n", strlen(&arr[0] + 1));
	//&arr[0]+1是b的地址,从第二个字符往后统计字符串的长度,大小是5
	return 0;
}

char* p = "abcdef";
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));

输出结果如下:

掌握指针和数组:经典笔试题攻略(万字详解)_第7张图片

解析:

int main()
{
	char* p = "abcdef";
	printf("%d\n", strlen(p));
	//6- 求字符串长度
	printf("%d\n", strlen(p + 1));
	//p + 1是b的地址,求字符串长度就是5
	//printf("%d\n", strlen(*p));//error,*p是'a'
	//printf("%d\n", strlen(p[0]));//error --> 同上一个
	printf("%d\n", strlen(&p));
	//&p拿到的是p这个指针变量的起始地址,从这里开始求字符串长度完全是随机值
	printf("%d\n", strlen(&p + 1));
	//&p+1是跳过p变量的地址,从这里开始求字符串长度也是随机值
	printf("%d\n", strlen(&p[0] + 1));
	//&p[0]表示'a'的地址, + 1是b的地址,从b的地址向后数字符串的长度是5
	return 0;
}

总结

  • sizeof 只关注占用内存空间的大小,单位是字节,不关心内存中存放的是什么
    sizeof 操作符
  • strlen是求字符串长度的,统计的是\0之前出现的字符个数,一定要找到 ‘\0’ 才算结束,所以可能存在越界访问的
    strlen库函数

二维数组

//二维数组
int a[3][4] = {0};
printf("%d\n",sizeof(a));
printf("%d\n",sizeof(a[0][0]));
printf("%d\n",sizeof(a[0]));
printf("%d\n",sizeof(a[0]+1));
printf("%d\n",sizeof(*(a[0]+1)));
printf("%d\n",sizeof(a+1));
printf("%d\n",sizeof(*(a+1)));
printf("%d\n",sizeof(&a[0]+1));
printf("%d\n",sizeof(*(&a[0]+1)));
printf("%d\n",sizeof(*a));
printf("%d\n",sizeof(a[3]));

以上语句输出的结果是什么?结果如下:

掌握指针和数组:经典笔试题攻略(万字详解)_第8张图片

解析:

int main()
{
	//二维数组
	int a[3][4] = { 0 };
	printf("%d\n", sizeof(a));
	//数组名单独放在sizeof内部,a表示整个数组的地址,计算的是整个数组的大小,整个数组有3*4=12个元素,
	//每个元素为int类型,所以计算的大小为 3*4*4 = 12*4 = 48个字节
	printf("%d\n", sizeof(a[0][0]));
	//a[0][0]表示第一行第一列的元素(即数组首元素),大小为4个字节
	printf("%d\n", sizeof(a[0]));
	//a[0]是二维数组第一行的数组名,数组名单独放在sizeof内部,计算的就是数组(第一行)的大小,为4*4 = 16个字节
	printf("%d\n", sizeof(a[0] + 1));
	//a[0]作为第一行的数组名,没有单独放在sizeof内部,没有取地址,表示的就是数组首元素的地址
	//那就是a[0][0]的地址,a[0]+1就是第一行第二个元素的地址,是地址,计算的大小就是4个字节
	printf("%d\n", sizeof(*(a[0] + 1)));
//a[0] + 1表示的是第一行第二个元素的地址,对其解引用,*(a[0] + 1)就是是第一行第二个元素,计算的是元素的大小,为 4个字节
	printf("%d\n", sizeof(a + 1));
	//a是二维数组的数组名,数组名表示首元素的地址,就是第一行的地址,a+1就是第二行的地址
	//第二行的地址也是地址,是地址,计算的大小就是4个字节   
	//a 类型: --> int (*)[4](数组指针类型)
	//a+1 类型: --> int(*)[4](数组指针类型)

	printf("%d\n", sizeof(*(a + 1)));
	//a+1是第二行的地址,对其解引用,*(a+1)表示的就是第二行,*(a+1) --> a[1]
	//因为第二行有4个元素,每个元素为int类型,所以计算大小为 4*4 =16个字节
	printf("%d\n", sizeof(&a[0] + 1));
	//&a[0]是第一行的地址,&a[0]+1就是第二行的地址,是地址,大小就是4个字节
	printf("%d\n", sizeof(*(&a[0] + 1)));
	//*(&a[0] + 1) 是对第二行的地址解引用,得到的就是第二行,计算的就是第二行的大小,为4*4 = 16个字节
 	printf("%d\n", sizeof(*a));
	//a表示首元素的地址,就是第一行的地址,*a就是第一行,计算的就是第一行的大小,为 4*4 = 16个字节
	//*a --> *(a+0) --> a[0]
	printf("%d\n", sizeof(a[3]));
	//结果是16个字节 <-- int[4]
	//如果数组存在第四行,a[3]就是第四行的数组名,数组名单独放在sizeof内部,计算的是第四行的大小,为 4*4 =16个字节
	return 0;
}

指针笔试题

笔试题1:

int main()
{
    int a[5] = { 1, 2, 3, 4, 5 };
    int *ptr = (int *)(&a + 1);
    printf( "%d,%d\n", *(a + 1), *(ptr - 1));
    return 0;
}
//程序的结果是什么?

输出结果如下:

掌握指针和数组:经典笔试题攻略(万字详解)_第9张图片

解析:

  1. int a[5] = { 1, 2, 3, 4, 5 };: 这行代码定义了一个包含5个整数的整型数组,初始化为从1到5的连续整数。
  2. int* ptr = (int*)(&a + 1);:&a表示的是整个数组的地址,+1跳过整个数组,指向数组末尾后的地址(此时是int()a[5]类型),(int)表示的是将此地址强制转换成 int* 类型,所以这行代码将一个指向整型的指针ptr指向数组a之后的内存位置,也就是数组的末尾之后的位置。
  3. *(a + 1)a表示数组首元素的地址,+1表示下标为1的元素的地址(即第二个元素的地址),对其解引用,*(a+1)表示第二个元素,即2。
  4. *(ptr - 1)ptr表示的是5后面的地址,-1就表示在该地址的条件下向前移动一个位置,即指向5的地址,对其解引用就得到元素5。

笔试题2:

//由于还没学习结构体,这里告知结构体的大小是20个字节
struct Test
{
 int Num;
 char *pcName;
 short sDate;
 char cha[2];
 short sBa[4];
}*p;
//假设p 的值为0x100000。 如下表表达式的值分别为多少?
//已知,结构体Test类型的变量大小是20个字节
int main()
{
 printf("%p\n", p + 0x1);
 printf("%p\n", (unsigned long)p + 0x1);
 printf("%p\n", (unsigned int*)p + 0x1);
 return 0;
}

输出结果如下:

掌握指针和数组:经典笔试题攻略(万字详解)_第10张图片

解析:

在给定的代码中,有一个名为struct Test的结构体类型,大小为20个字节。变量p是一个指向这种结构体类型的指针,并被赋值为0x100000。

根据C语言中的指针算术规则,指针在增加时会根据所指向类型的大小进行适当的偏移。

  1. printf("%p\n", p + 0x1); 因为p是结构体指针类型,所以这里的表达式p + 0x1会将指针p增加一个结构体的大小,即20个字节(20的16进制为0x14)。故结果将是:0x100014

  2. printf("%p\n", (unsigned long)p + 0x1); 在这个表达式中,将指针p先转换为unsigned long类型,这时原来的结构体指针p就变成了unsigned long类型的数组,加上0x1,就是单纯的将0x100000加上0x1。所以结果将是:0x100001

  3. printf("%p\n", (unsigned int*)p + 0x1); 这个表达式中,将指针p转换为unsigned int*类型,然后增加0x1。因为unsigned int*大小是4个字节,所以增加1个字节相当于增加0x4。故结果将是:0x100004

需要注意的是,指针的算术运算会根据指针指向的类型来调整偏移量,这取决于所使用的编译器和系统架构。此外,将指针转换为不同类型可能会引起对齐和字节顺序等问题,因此需要谨慎使用。


笔试题3:

int main()
{
    int a[4] = { 1, 2, 3, 4 };
    int *ptr1 = (int *)(&a + 1);
    int *ptr2 = (int *)((int)a + 1);
    printf( "%x,%x\n", ptr1[-1], *ptr2);
    return 0;
}

输出结果如下:

掌握指针和数组:经典笔试题攻略(万字详解)_第11张图片

解析:

  1. int a[4] = { 1, 2, 3, 4 }; 这行代码定义了一个包含4个整数的数组,初始化为 1, 2, 3, 和 4。

  2. int* ptr1 = (int*)(&a + 1); 这里取数组a的地址,表示整个数组的地址,加1,跳过了4个整型的大小,然后将结果转换为int*类型的指针。这使得ptr1指向数组a之后的内存位置(即4后面的地址)。

  3. int* ptr2 = (int*)((int)a + 1); 这里将数组a的值(即数组的第一个元素的地址)转换为int类型,然后加1(注意如果没有转换为int类型,就是指针+1,实际上加了4个字节),最后将结果转换为int*类型的指针。这使得ptr2指向数组a的第一个元素之后的内存位置。

  4. printf("%x,%x\n", ptr1[-1], *ptr2); 这里使用指针ptr1访问了其前一个位置的值(即数组a的最后一个元素),并将它以十六进制格式打印出来。然后,使用指针ptr2访问了其所指向位置的值(即数组a的第二个元素),并同样以十六进制格式打印出来。

由于ptr1指向数组a之后的位置,而ptr2指向数组a的第一个元素之后的位置,它们访问的内存位置是不同的。然而,数组的元素在内存中是连续存储的,因此最终输出的结果将受到内存布局的影响。

掌握指针和数组:经典笔试题攻略(万字详解)_第12张图片


笔试题4:

#include 
int main()
{
    int a[3][2] = { (0, 1), (2, 3), (4, 5) };
    int *p;
    p = a[0];
    printf( "%d\n", p[0]);
 return 0;
}

输出结果如下:

掌握指针和数组:经典笔试题攻略(万字详解)_第13张图片

解析:

在这段代码中,数组a是一个3x2的二维数组,每个元素都是一个包含两个整数的小数组。

  1. int a[3][2] = { (0, 1), (2, 3), (4, 5) }; 这里使用逗号运算符初始化二维数组a的元素。逗号运算符的结果是它的最后一个表达式的值,因此实际上是初始化了数组a的元素为 {1,3, 5},而0, 2, 4被忽略了。所以,a的内容实际上是 {1, 3}, {5, 0}, {2, 4}

  2. int* p; 这里声明了一个指向整数的指针p

  3. p = a[0]; 这里将指针p指向数组a的第一个元素,即{1, 3}的首地址。

  4. printf("%d\n", p[0]); 这里通过指针p访问了其第一个元素,即数组a的第一个元素的第一个整数元素,即1。然后将1以十进制格式打印出来。

掌握指针和数组:经典笔试题攻略(万字详解)_第14张图片


笔试题5:

int main()
{
    int a[5][5];
    int(*p)[4];
    p = a;
    printf( "%p,%d\n", &p[4][2] - &a[4][2], &p[4][2] - &a[4][2]);
    return 0;
}

输出结果如下:

掌握指针和数组:经典笔试题攻略(万字详解)_第15张图片

解析:

  1. int a[5][5]; 这行代码定义了一个5x5的二维整数数组。

  2. int(*p)[4]; 这里定义了一个指向包含4个整数的数组的指针。

  3. p = a; 这里将指针p指向数组a的第一个子数组,即a[0](二维数组的第一行地址)。

  4. printf("%p,%d\n", &p[4][2] - &a[4][2], &p[4][2] - &a[4][2]); 这行代码计算了两个地址之间的偏移量,并打印出结果。

现在我们来计算偏移量:

  • &p[4][2] 表示指针p指向的数组的第5个子数组(即第五行的元素),然后取其中的第3个整数元素的地址。

  • &a[4][2] 表示数组a的最后一行的第3个元素的地址。

因此,表达式 &p[4][2] - &a[4][2] 会计算两个指针之间的偏移量,这是一个指针运算,计算的结果将是两个指针相差的数组元素个数。在这个特定的情况下,由于p指向的数组每行有4个元素,所以&p[4][2]会在&a[4][2]前面,如图,&p[4][2]&a[4][2]之间有四个元素,因此这个计算结果将是-4。 而%p是以16进制的形式打印其补码(因为数据在内存中存储是以补码的形式存储的,所以打印的时候也是打印补码),-4的原、反、补码如下;:
10000000 00000000 00000000 00000100
111111111 111111111 111111111 111111011
111111111 111111111 111111111 111111100
故其补码的16进制形式为 FFFFFFFC

掌握指针和数组:经典笔试题攻略(万字详解)_第16张图片


笔试题6:

int main()
{
    int aa[2][5] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
    int *ptr1 = (int *)(&aa + 1);
    int *ptr2 = (int *)(*(aa + 1));
    printf( "%d,%d\n", *(ptr1 - 1), *(ptr2 - 1));
    return 0;
}

输出结果如下:

掌握指针和数组:经典笔试题攻略(万字详解)_第17张图片

解析:

  1. int aa[2][5] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; 这行代码定义了一个2x5的二维整数数组,并用初始化值填充它。

  2. int* ptr1 = (int*)(&aa + 1); 这里取数组aa的地址,去出的是整个二维数组的地址,加1,跳过整个二维数组,指向二维数组末尾后面的地址,然后将结果转换为int*类型的指针。这将使得ptr1指向数组aa之后的内存位置(即10后面的地址)。

  3. int* ptr2 = (int*)(*(aa + 1)); 这里aa表示的是二维数组首元素的地址(即第一行的地址),+1跳过一行元素,指向第二行的地址,然后对其解引用得到第二行第一个元素的地址,最后将结果转换为int*类型的指针。这将使得ptr2指向第二行的起始位置。

  4. printf("%d,%d\n", *(ptr1 - 1), *(ptr2 - 1)); 这里使用指针算术访问了指针ptr1的前一个位置的值(即数组aa的最后一个元素),并将它打印出来。然后,使用指针算术访问了指针ptr2的前一个位置的值(即第二行的最后一个元素),并将它打印出来。

注意:ptr1 指向整个二维数组 aa 之后的位置,而 ptr2 指向第二行的起始位置,它们访问的内存位置是不同的。

掌握指针和数组:经典笔试题攻略(万字详解)_第18张图片


笔试题7:

#include 
int main()
{
 char *a[] = {"work","at","alibaba"};
 char**pa = a;
 pa++;
 printf("%s\n", *pa);
 return 0;
}

输出结果如下:

掌握指针和数组:经典笔试题攻略(万字详解)_第19张图片

解析:

  1. char *a[] = {"work","at","alibaba"};
    这行代码定义了一个字符指针数组 a,其中每个元素是一个指向字符串常量的指针。数组中有三个元素,分别指向字符串 “work”、“at” 和 “alibaba”。

  2. char**pa = a;
    这里定义了一个指向字符指针的指针 pa,将其指向数组 a 的第一个元素,即指向字符串 “work” 的指针。

  3. pa++;
    这里将指针 pa 增加了一次,使其指向数组 a 的下一个元素,即指向字符串 “at” 的指针。

  4. printf("%s\n", *pa);
    这里通过指针 pa 访问了其所指向的字符串(这里*pa表示的是字符串"at"的地址),然后使用 %s 格式化输出该字符串。


笔试题8:

int main()
{
 char *c[] = {"ENTER","NEW","POINT","FIRST"};
 char**cp[] = {c+3,c+2,c+1,c};
 char***cpp = cp;
 printf("%s\n", **++cpp);
 printf("%s\n", *--*++cpp+3);
 printf("%s\n", *cpp[-2]+3);
 printf("%s\n", cpp[-1][-1]+1);
 return 0;
}

输出结果如下:

掌握指针和数组:经典笔试题攻略(万字详解)_第20张图片

解析:

这段代码涉及指针和指针数组的操作,有点复杂,让我们逐行解释:

  1. char* c[] = { "ENTER","NEW","POINT","FIRST" };
    这行代码定义了一个字符指针数组 c,其中每个元素是一个指向字符串常量的指针。数组中有四个元素,分别指向字符串 “ENTER”、“NEW”、“POINT” 和 “FIRST”。

  2. char** cp[] = { c + 3,c + 2,c + 1,c };
    这里定义了一个指向字符指针的指针数组 cp,其中每个元素是一个指向数组 c 中的某个元素的指针。c + 3 表示指向 “FIRST” 的指针,c + 2 表示指向 “POINT” 的指针,以此类推。

  3. char*** cpp = cp;
    这里定义了一个指向指针数组 cp 的指针 cpp
    掌握指针和数组:经典笔试题攻略(万字详解)_第21张图片

  4. printf("%s\n", **++cpp);
    这里首先将指针 cpp 增加了一次,然后通过 ** 解引用两次得到 “POINT” 的字符串,然后输出。
    掌握指针和数组:经典笔试题攻略(万字详解)_第22张图片

  5. printf("%s\n", *-- * ++cpp + 3);
    这里先将指针 cpp 增加一次,然后将其解引用得到指向 “NEW” 的指针。接着,*-- * ++cpp 操作会将 *++cpp 减少一次,然后将其解引用,得到字符串"ENTER" 的地址。最终, + 3 操作将 “ENTER” 的字符串地址向后偏移3,得到字符串 “ER”,然后输出。
    掌握指针和数组:经典笔试题攻略(万字详解)_第23张图片

  6. printf("%s\n", *cpp[-2] + 3);
    这里使用指针数组的索引操作,首先cpp[-2] 得到指向字符串 “FIRST” 的指针,然后对其解引用,得到字符串"FIRST"的地址,最终+ 3 将 “FIRST” 的字符串地址向后偏移3,得到字符串 “ST”,然后输出。
    掌握指针和数组:经典笔试题攻略(万字详解)_第24张图片

  7. printf("%s\n", cpp[-1][-1] + 1);
    这里 cpp[-1] 得到指向 “POINT” 的指针,然后 cpp[-1][-1] 得到 “NEW” 的字符串指针(地址),然后 + 1 将字符串地址向后偏移1,得到字符串 “EW”,然后输出。
    掌握指针和数组:经典笔试题攻略(万字详解)_第25张图片

小结:

理解和掌握指针和数组的概念是编程中的关键。通过练习和实践,你将能够更好地处理与指针和数组相关的问题。遇到困难并不是失败,而是在通向成功的道路上的一部分。每次犯错,你都在学习如何做得更好。我们一起加油!!✨☄

今天的分享就到这里了, 如果觉得博主的文章还不错的话, 请三连支持一下博主哦

掌握指针和数组:经典笔试题攻略(万字详解)_第26张图片

你可能感兴趣的:(C语言刷题专栏,c语言,开发语言,学习,经验分享)