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

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

💫收录专栏: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=16printf("%d\n", sizeof(a));//sizeof里面的a表示的是整个数组,所以这里求的是整个数组的大小,即4*4=16printf("%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 intchar 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,和上面的代码类似,是非法访问 - errorprintf("%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'之前出现的字符个数,结果是6printf("%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虽然是整个数组的地址,但是也是从数组起始位置开始的,计算结果同第一个是6printf("%d\n", strlen(&arr + 1));//&arr + 1是跳过整个数组后的地址,统计字符串的长度是随机值printf("%d\n", strlen(&arr[0] + 1));//&arr[0]+1是b的地址,从第二个字符往后统计字符串的长度,大小是5return 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的地址向后数字符串的长度是5return 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.mzph.cn/news/53092.shtml

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

相关文章

电脑显示“Operating System not found”该怎么办?

“Operating System not found”是一种常见的电脑错误提示&#xff0c;这类错误会导致你无法成功启动Windows。那么电脑显示“Operating System not found”该怎么办呢&#xff1f; 方法1. 检查硬盘 首先&#xff0c;您可以测试硬盘是否存在问题。为此&#xff0c;您可以采取以…

基于JSP+Servlet+Mysql停车场管理系统(含实训报告)

TOC 一、系统介绍 项目类型&#xff1a;Java web项目 项目名称&#xff1a;基于JSPServlet的停车场管理系统 项目架构&#xff1a;B/S架构 开发语言&#xff1a;Java语言 前端技术&#xff1a;HTML、CSS、JS、JQuery等技术 后端技术&#xff1a;JSP、Servlet、JDBC等技术…

Python案例|Matplotlib库实现的数据分析

数据展示是数据分析和挖掘中的重要环节&#xff0c;通过图形的形式可以直观、清晰地呈现数据内在的规律。 本文所用数据采用上一篇案例实现后的数据表&#xff0c;数据存储在newbj_lianJia.csv文件中&#xff0c;具体代码如下。 import pandas as pd #导入库 import matplot…

自定义拖拽功能,上下拖拽改变盒子高度

核心在于监听鼠标的move来改变div的高度&#xff0c;抽成了组件 <template><div ref"container" class"drag"><z-tooltip v-if"isShowIcon" effect"dark" content"格式化" placement"top-start"&…

视频分割合并工具说明

使用说明书&#xff1a;视频分割合并工具 欢迎使用视频生成工具&#xff01;本工具旨在帮助您将视频文件按照指定的规则分割并合并&#xff0c;以生成您所需的视频。 本程序还自带提高分辨率1920:1080&#xff0c;以及增加10db声音的功能 软件下载地址 https://github.com/c…

树模型与集成学习:LightGBM

目录 树模型与集成学习 LightGBM 的贡献 LightGBM 的贡献&#xff1a;单边梯度抽样算法 LightGBM 的贡献&#xff1a;直方图算法 LightGBM 的贡献&#xff1a;互斥特征捆绑算法 LightGBM 的贡献&#xff1a;深度限制的 Leaf-wise 算法 树模型与集成学习 树模型是非常好的…

Windows系统下,将文件夹中文件名字全部复制提取出来

一、使用命令提示符&#xff08;Command Prompt&#xff09;&#xff1a; 1、打开文件夹&#xff0c;确保你所需的文件都在该文件夹中。 2、在文件夹的路径栏中&#xff0c;输入 “cmd” 并按下 Enter 键&#xff0c;以打开命令提示符并将其当前路径设置为所选文件夹的路径。 …

Java——一个简单的使用JPanel和JButton来设计窗口界面

这段代码是一个简单的使用JPanel和JButton来设计窗口界面的例子。 在designFrame方法中&#xff0c;创建了一个JFrame对象作为窗口&#xff0c;然后创建了两个JPanel对象作为面板&#xff0c;分别用于放置按钮。 创建了两个JButton对象作为按钮&#xff0c;并设置按钮的文本内容…

AI 时代,程序员无需焦虑 | 《服务端开发:技术、方法与实用解决方案》(文末送书福利4.0)

文章目录 &#x1f4cb;前言&#x1f3af;程序员会被 AI 取代么&#xff1f;&#x1f3af;服务端开发尚难被 AI 取代&#x1f3af; 服务端开发何去何从&#xff1f;&#x1f3af;业界首部体系化、全景式解读服务端开发的著作&#x1f4ac;读者对象&#x1f4da;本书优势&#x…

k-近邻算法概述,k-means与k-NN的区别对比

目录 k-近邻算法概述 k-近邻算法细节 k值的选取 分类器的决策 k-means与k-NN的区别对比 k-近邻算法概述 k近邻&#xff08;k-nearest neighbor, k-NN&#xff09;算法由 Cover 和 Hart 于1968年提出&#xff0c;是一种简单的分类方法。通俗来说&#xff0c;就是给定一个…

3D角色展示

先看效果&#xff1a; 再看代码&#xff1a; <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>3D卡片悬停</title><style>font-face {font-family: "Exoct";src: url("htt…

centos7设置java后端项目开机自启【脚本、开机自启】

1.切换目录 cd /etc/init.d/2.编辑脚本 vim wbs-service-start.sh编辑内容 #!/bin/bash # chkconfig: 2345 80 90 # description: auto_runnohup java -jar /usr/java/wbs-service.jar > /dev/null 2>&1 & echo $! > /var/run/wbs-service.pid3.添加进入系…

Hugo·Stack主题·杂记

运行环境 hugo版本&#xff1a;v0.117.0&#xff08;扩展版&#xff09; go&#xff1a;1.21.0 PowerShell 7&#xff08;x64&#xff09; Windows10 添加主题 git submodule add https://github.com/CaiJimmy/hugo-theme-stack/ themes/hugo-theme-stack修改配置文件 m…

C++ 编译器优化与SIMD指令集

C 编译器优化与SIMD指令集 1. 汇编语言1.1 通用寄存器&#xff1a;32位时代1.2 通用寄存器&#xff1a;64位时代1.3 8位&#xff0c;16位&#xff0c;32位&#xff0c;64位版本1.4 AT&T 汇编语言1.5 返回值&#xff1a;通过 eax 传出1.6 前6个参数&#xff1a;分别通过 edi…

用好「留存」,闭环小程序运营链路

如何通过线上小程序获取用户线索&#xff0c;提高企业抗风险能力&#xff0c;建立有效的营销数字化系统一直是困扰每一个小程序开发者与运营者的问题。 当我们选择使用小程序设计自己的运营流程时&#xff0c;从「推广」到「转化」&#xff0c;再到最终的「留存」都是运营过程…

开始MySQL之路——MySQL安装和卸载

MySQL的介绍 MySQL数据库管理系统由瑞典的DataKonsultAB公司研发&#xff0c;该公司被Sun公司收购&#xff0c;现在Sun公司又被Oracle公司收购&#xff0c;因此MySQL目前属于Oracle旗下产品。 MySQL所使用的SQL语言是用于访问数据库的最常用标准化语言。MySQL软件采用了双授权…

使用kubeadm方式快速部署一个K8S集群

目录 一、环境准备 二、环境初始化 三、在所有主机上安装相关软件 1、安装docker 2、配置k8s的yum源 3、安装kubelet、kubeadm、kubectl 四、部署Kubernetes Master 五、加入Kubernets Node 六、部署CNI网络插件 七、测试k8s集群 一、环境准备 我的是CentOS7系统&am…

Django(6)-django项目自动化测试

Django 应用的测试应该写在应用的 tests.py 文件里。测试系统会自动的在所有以 tests 开头的文件里寻找并执行测试代码。 我们的 polls 应用现在有一个小 bug 需要被修复&#xff1a;我们的要求是如果 Question 是在一天之内发布的&#xff0c; Question.was_published_recentl…

【业务功能篇82】微服务SpringCloud-ElasticSearch-Kibanan-docke安装-进阶实战

四、ElasticSearch进阶 https://www.elastic.co/guide/en/elasticsearch/reference/7.4/getting-started-search.html 1.ES中的检索方式 在ElasticSearch中支持两种检索方式 通过使用REST request URL 发送检索参数(uri检索参数)通过使用 REST request body 来发送检索参数…

燃气管网监测系统,24小时守护燃气安全

随着社会的发展和人民生活水平的提高&#xff0c;燃气逐渐成为人们日常生活和工作中不可或缺的一部分。然而&#xff0c;近年来&#xff0c;屡屡发生的燃气爆炸问题&#xff0c;也让人们不禁对燃气的安全性产生了担忧。因此&#xff0c;建立一个高效、实时、准确的燃气管网监测…