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

news2024/11/20 4:31:54

🍁博客主页:江池俊的博客

💫收录专栏: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));

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

在这里插入图片描述

解析:

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

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

在这里插入图片描述
解析:

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。

在这里插入图片描述


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

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

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

在这里插入图片描述
解析:

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

输出结果如下:

解析:

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;

输出结果如下:

在这里插入图片描述

解析:

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

输出结果如下:

在这里插入图片描述

解析:

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

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

在这里插入图片描述

解析:

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;
}
//程序的结果是什么?

输出结果如下:

在这里插入图片描述

解析:

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

输出结果如下:

在这里插入图片描述

解析:

在给定的代码中,有一个名为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;
}

输出结果如下:

在这里插入图片描述

解析:

  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的第一个元素之后的位置,它们访问的内存位置是不同的。然而,数组的元素在内存中是连续存储的,因此最终输出的结果将受到内存布局的影响。

在这里插入图片描述


🚀笔试题4:

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

输出结果如下:

在这里插入图片描述

解析:

在这段代码中,数组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以十进制格式打印出来。

在这里插入图片描述


🚀笔试题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;
}

输出结果如下:

在这里插入图片描述

解析:

  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

在这里插入图片描述


🚀笔试题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;
}

输出结果如下:

在这里插入图片描述

解析:

  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 指向第二行的起始位置,它们访问的内存位置是不同的。

在这里插入图片描述


🚀笔试题7:

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

输出结果如下:

在这里插入图片描述

解析:

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

输出结果如下:

在这里插入图片描述

解析:

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

  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
    在这里插入图片描述

  4. printf("%s\n", **++cpp);
    这里首先将指针 cpp 增加了一次,然后通过 ** 解引用两次得到 “POINT” 的字符串,然后输出。
    在这里插入图片描述

  5. printf("%s\n", *-- * ++cpp + 3);
    这里先将指针 cpp 增加一次,然后将其解引用得到指向 “NEW” 的指针。接着,*-- * ++cpp 操作会将 *++cpp 减少一次,然后将其解引用,得到字符串"ENTER" 的地址。最终, + 3 操作将 “ENTER” 的字符串地址向后偏移3,得到字符串 “ER”,然后输出。
    在这里插入图片描述

  6. printf("%s\n", *cpp[-2] + 3);
    这里使用指针数组的索引操作,首先cpp[-2] 得到指向字符串 “FIRST” 的指针,然后对其解引用,得到字符串"FIRST"的地址,最终+ 3 将 “FIRST” 的字符串地址向后偏移3,得到字符串 “ST”,然后输出。
    在这里插入图片描述

  7. printf("%s\n", cpp[-1][-1] + 1);
    这里 cpp[-1] 得到指向 “POINT” 的指针,然后 cpp[-1][-1] 得到 “NEW” 的字符串指针(地址),然后 + 1 将字符串地址向后偏移1,得到字符串 “EW”,然后输出。
    在这里插入图片描述

💥小结:

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

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

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/927660.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

最新docker多系统安装技术

在Ubuntu操作系统中安装Docker 在Ubuntu操作系统中安装Docker的步骤如下。 1&#xff0e;卸载旧版本Docker 卸载旧版本Docker的命令如下&#xff1a; $ sudo apt-get remove docker docker-engine docker.io 2&#xff0e;使用脚本自动安装 在测试或开发环境中&#xff0…

聊聊敏捷实践的“个体与交互”

这是鼎叔的第七十二篇原创文章。行业大牛和刚毕业的小白&#xff0c;都可以进来聊聊。 欢迎关注本公众号《敏捷测试转型》&#xff0c;星标收藏&#xff0c;大量原创思考文章陆续推出。 敏捷宣言有重要的一句话&#xff1a;个体和交互胜过过程和工具。作为工程师&#xff0c;…

【实训项目】“享学”APP设计

1.设计摘要 随着高等教育普及化程度提高&#xff0c;国家对大学生的要求也越来越高&#xff0c;在学习过程中总会遇到各种各样的阻碍&#xff0c;很多学生苦于找不到资料&#xff0c;苦于找不到适合自己的资料&#xff0c;苦于找不到能让自己提高的资料。不善交际&#xff0c;…

基于Java+SpringBoot+Vue前后端分离校园失物招领系统设计和实现

博主介绍&#xff1a;✌全网粉丝30W,csdn特邀作者、博客专家、CSDN新星计划导师、Java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专…

简单js逆向案例(1)

文章目录 声明模块分析完整代码python实现js扣代码实现 结尾 声明 本文章中所有内容仅供学习交流&#xff0c;严禁用于商业用途和非法用途&#xff0c;否则由此产生的一切后果均与作者无关&#xff0c;若有侵权&#xff0c;请联系我立即删除&#xff01; 模块 pip install Py…

Properties 中文配置乱码问题解决

file->setting->editor-> file encoding -> 选择Default encoding for properties files-> ISO-8859-1

springcloud3 GateWay章节-Nacos+gateway动态路由负载均衡4

一 工程结构 1.1 工程 1.2 搭建gatewayapi工程 1.pom文件 <dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.13</version><scope>test</scope></dependency><!--gateway--&g…

HTML的label标签有什么用?

当你想要将表单元素&#xff08;如输入框、复选框、单选按钮等&#xff09;与其描述文本关联起来&#xff0c;以便提供更好的用户界面和可访问性时&#xff0c;就可以使用HTML中的<label>标签。<label>标签用于为表单元素提供标签或标识&#xff0c;使用户能够更清…

ssm端游游戏账号销售管理系统源码和论文

ssm端游游戏账号销售管理系统源码和论文069 开发工具&#xff1a;idea 数据库mysql5.7 数据库链接工具&#xff1a;navcat,小海豚等 技术&#xff1a;ssm 摘 要 互联网发展至今&#xff0c;无论是其理论还是技术都已经成熟&#xff0c;而且它广泛参与在社会中的方方面面…

如何更改自己电脑上的microsoft账户?

方法/步骤 1 正常我们在登录时就只有一个本地账户或者是微软账户&#xff0c;没有地方可以输入新的账号。那怎么样才能切换到其他账户或者是使用新的账户呢。 首先我们登录进入系统。然后打开用户账户添加一个新的账户。 在键盘上按winr键打开运行输入&#xff0c;输入netpl…

醇音典范 一脉相承! 第五届中国(北京)国际耳机展,索尼期待与你相遇

2023年8月26日至8月27日&#xff0c;第五届中国&#xff08;北京&#xff09;国际耳机展将在北京亚洲大酒店盛大开启。索尼音频产品秉持"For the Music"品牌理念&#xff0c;借助四十余年浸润音乐领域的技术深耕和产业积累&#xff0c;构筑了从声音的录制、制作、到播…

“好声音”下线了?罪魁祸首纷纷扒出来了

开脑洞&#xff0c;浙江卫视是不是要完蛋了&#xff1f;先是刀郎新歌罗刹海&#xff0c;是一句歌词为转身子先扭定矛头直指好声音。然后所有攻击和侮辱过刀郎的明星均遭到了网友们的强烈讨伐&#xff0c;里边的主力就是好声音当年的评委。 接下来&#xff0c;李玟生前录音曝光&…

多线程MySQL分页查询-性能优化

MySQL分页查询优化 一、背景二、原因三、解决四、原理探究 https://blog.csdn.net/hollis_chuang/article/details/130570281 总结&#xff1a; 一、背景 业务背景&#xff1a;给C端10万级别的用户&#xff0c;同时发送活动消息&#xff0c;活动消息分为6类。数据背景&#…

《华为认证》6to4自动隧道

实验需求&#xff1a; 在NE1和NE3之间使用tunnel 口创建6to4自动隧道&#xff0c;实现PC1和PC2互访。 步骤1:配置ipv4地址&#xff0c;如图所示&#xff1a; 步骤2&#xff1a;配置NE1和NE3的ipv4路由&#xff0c;是两端的ipv4网络能够互访 R1: ip route-static 0.0.0.0 0…

msvcp120.dll文件缺失一键修复方法,靠谱的多种修复msvcp120.dll方案

msvcp120.dll文件的丢失&#xff0c;其实是比较常见的&#xff0c;msvcp120.dll是一个Microsoft Visual C Redistributable的关键文件&#xff0c;它包含了一些用于C编程的标准函数和类的定义。如果msvcp120.dll丢失了&#xff0c;那么你的一些与这个文件相关的程序是打不开的&…

小程序订阅消息

注意&#xff1a;小程序订阅消息和小程序类目挂钩&#xff0c;只有设置对应的类目后才能发送相应的公共模板小程序订阅消息 1、类目 1/1 获取微信所有类目信息 https://api.weixin.qq.com/cgi-bin/wxopen/getallcategories?access_token1/2 获取当前账号配置的类目 https:…

Vue学习之Vue组件的核心概念

组件是什么 vue组件就是一个个独立的小型的ui模块&#xff0c;整个大型的系统就是由一个个小型的UI模块拼接而成的 vue组件就是vue实例&#xff0c;通过new Vue函数来创建的一个vue实例&#xff0c;不同的组件只不过是options的不同&#xff0c;我们基本百分之90的开发工作都…

LeetCode 1448. 统计二叉树中好节点的数目:DFS

【LetMeFly】1448.统计二叉树中好节点的数目 力扣题目链接&#xff1a;https://leetcode.cn/problems/count-good-nodes-in-binary-tree/ 给你一棵根为 root 的二叉树&#xff0c;请你返回二叉树中好节点的数目。 「好节点」X 定义为&#xff1a;从根到该节点 X 所经过的节点…

4 多层感知机

多层感知机是一组前向结构的人工神经网络&#xff0c;映射一组输入向量到一组输出向量。除了输入节点&#xff0c;每一个节点都是一个带有非线性激活函数的神经元。多层感知机在输入层和输出层之间添加了一个或者多个隐藏层&#xff0c;并通过激活函数转换隐藏层输出。以下介绍…

Unity 应用消息中心-MessageCenter

Ps&#xff1a;主要解决耦合问题&#xff0c;把脚本之间的联系通过不同消息类型事件形式进行贯通 1.MessageCenter主脚本 2.DelegateEvent消息类型脚本 3.MC_Default_Data具体接收类脚本 using System; using System.Collections; using System.Collections.Generic; using …