🍁博客主页:江池俊的博客
💫收录专栏:C语言刷题专栏
💡代码仓库:江池俊的代码仓库
🎪我的社区:GeekHub
🎉欢迎大家点赞👍评论📝收藏⭐
文章目录
前言
- 当涉及到计算机编程的核心概念时,指针和数组无疑是最重要和基础的话题之一。无论你是初学者还是有经验的开发者,深入理解和掌握这两个概念都能让你的编程技能得到质的提升。
- 在本篇博客中,我们将带你进入指针和数组的精彩世界,探索其在编程中的作用和应用。无论你是为了笔试准备,还是为了提升自己的技能,这里都将为你提供一个深入学习的机会。
在本篇文章中如有遇到不懂的知识点,可以参考以下文章《掌握指针进阶:探索字符指针、数组指针和指针数组的妙用》、《掌握指针进阶:一篇带你玩转函数指针、函数指针数组及指向函数指针数组的指针!!》希望对你有所帮助
注意: 本次讲解是在vs2022 x86的环境下进行的,即电脑的32位环境,所以默认为指针大小占4个字节
📍指针和数组笔试题
🚀一维数组
c
//一维数组
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));
以上语句输出的结果是什么?结果如下:
解析:
c
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()的计算
c
//字符数组 ---> 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));
以上语句输出的结果是什么?结果如下:
解析:
c
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。
当使用指针存放字符串又会怎样?
c
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));
以上语句输出的结果是什么?结果如下:
解析:
c
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的计算
c
//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));
输出结果如下:
解析:
c
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;
}
c
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;
输出结果如下:
解析:
c
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;
}
c
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));
输出结果如下:
解析:
c
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
是库函数
🚀二维数组
c
//二维数组
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]));
以上语句输出的结果是什么?结果如下:
解析:
c
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:
c
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;
}
//程序的结果是什么?
输出结果如下:
解析:
int a[5] = { 1, 2, 3, 4, 5 };
: 这行代码定义了一个包含5个整数的整型数组,初始化为从1到5的连续整数。int* ptr = (int*)(&a + 1);
:&a表示的是整个数组的地址,+1跳过整个数组,指向数组末尾后的地址(此时是int()a[5]类型),(int )表示的是将此地址强制转换成 int* 类型,所以这行代码将一个指向整型的指针ptr
指向数组a
之后的内存位置,也就是数组的末尾之后的位置。*(a + 1)
:a
表示数组首元素的地址,+1表示下标为1的元素的地址(即第二个元素的地址),对其解引用,*(a+1)
表示第二个元素,即2。*(ptr - 1)
:ptr
表示的是5后面的地址,-1就表示在该地址的条件下向前移动一个位置,即指向5的地址,对其解引用就得到元素5。
🚀笔试题2:
c
//由于还没学习结构体,这里告知结构体的大小是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语言中的指针算术规则,指针在增加时会根据所指向类型的大小进行适当的偏移。
-
printf("%p\n", p + 0x1);
因为p是结构体指针类型,所以这里的表达式p + 0x1
会将指针p
增加一个结构体的大小,即20个字节(20的16进制为0x14)。故结果将是:0x100014
。 -
printf("%p\n", (unsigned long)p + 0x1);
在这个表达式中,将指针p
先转换为unsigned long
类型,这时原来的结构体指针p就变成了unsigned long
类型的数组,加上0x1,就是单纯的将0x100000加上0x1。所以结果将是:0x100001
。 -
printf("%p\n", (unsigned int*)p + 0x1);
这个表达式中,将指针p
转换为unsigned int*
类型,然后增加0x1。因为unsigned int*
大小是4个字节,所以增加1个字节相当于增加0x4。故结果将是:0x100004
。
需要注意的是,指针的算术运算会根据指针指向的类型来调整偏移量,这取决于所使用的编译器和系统架构。此外,将指针转换为不同类型可能会引起对齐和字节顺序等问题,因此需要谨慎使用。
🚀笔试题3:
c
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;
}
输出结果如下:
解析:
-
int a[4] = { 1, 2, 3, 4 };
这行代码定义了一个包含4个整数的数组,初始化为 1, 2, 3, 和 4。 -
int* ptr1 = (int*)(&a + 1);
这里取数组a
的地址,表示整个数组的地址,加1,跳过了4个整型的大小,然后将结果转换为int*
类型的指针。这使得ptr1
指向数组a
之后的内存位置(即4后面的地址)。 -
int* ptr2 = (int*)((int)a + 1);
这里将数组a
的值(即数组的第一个元素的地址)转换为int
类型,然后加1(注意如果没有转换为int
类型,就是指针+1,实际上加了4个字节),最后将结果转换为int*
类型的指针。这使得ptr2
指向数组a
的第一个元素之后的内存位置。 -
printf("%x,%x\n", ptr1[-1], *ptr2);
这里使用指针ptr1
访问了其前一个位置的值(即数组a
的最后一个元素),并将它以十六进制格式打印出来。然后,使用指针ptr2
访问了其所指向位置的值(即数组a
的第二个元素),并同样以十六进制格式打印出来。
由于ptr1
指向数组a
之后的位置,而ptr2
指向数组a
的第一个元素之后的位置,它们访问的内存位置是不同的。然而,数组的元素在内存中是连续存储的,因此最终输出的结果将受到内存布局的影响。
🚀笔试题4:
c
#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的二维数组,每个元素都是一个包含两个整数的小数组。
-
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}
。 -
int* p;
这里声明了一个指向整数的指针p
。 -
p = a[0];
这里将指针p
指向数组a
的第一个元素,即{1, 3}
的首地址。 -
printf("%d\n", p[0]);
这里通过指针p
访问了其第一个元素,即数组a
的第一个元素的第一个整数元素,即1。然后将1以十进制格式打印出来。
🚀笔试题5:
c
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;
}
输出结果如下:
解析:
-
int a[5][5];
这行代码定义了一个5x5的二维整数数组。 -
int(*p)[4];
这里定义了一个指向包含4个整数的数组的指针。 -
p = a;
这里将指针p
指向数组a
的第一个子数组,即a[0]
(二维数组的第一行地址)。 -
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:
c
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;
}
输出结果如下:
解析:
-
int aa[2][5] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
这行代码定义了一个2x5的二维整数数组,并用初始化值填充它。 -
int* ptr1 = (int*)(&aa + 1);
这里取数组aa
的地址,去出的是整个二维数组的地址,加1,跳过整个二维数组,指向二维数组末尾后面的地址,然后将结果转换为int*
类型的指针。这将使得ptr1
指向数组aa
之后的内存位置(即10
后面的地址)。 -
int* ptr2 = (int*)(*(aa + 1));
这里aa
表示的是二维数组首元素的地址(即第一行的地址),+1
跳过一行元素,指向第二行的地址,然后对其解引用得到第二行第一个元素的地址,最后将结果转换为int*
类型的指针。这将使得ptr2
指向第二行的起始位置。 -
printf("%d,%d\n", *(ptr1 - 1), *(ptr2 - 1));
这里使用指针算术访问了指针ptr1
的前一个位置的值(即数组aa
的最后一个元素),并将它打印出来。然后,使用指针算术访问了指针ptr2
的前一个位置的值(即第二行的最后一个元素),并将它打印出来。
注意:ptr1
指向整个二维数组 aa
之后的位置,而 ptr2
指向第二行的起始位置,它们访问的内存位置是不同的。
🚀笔试题7:
c
#include <stdio.h>
int main()
{
char *a[] = {"work","at","alibaba"};
char**pa = a;
pa++;
printf("%s\n", *pa);
return 0;
}
输出结果如下:
解析:
-
char *a[] = {"work","at","alibaba"};
这行代码定义了一个字符指针数组
a
,其中每个元素是一个指向字符串常量的指针。数组中有三个元素,分别指向字符串 "work"、"at" 和 "alibaba"。 -
char**pa = a;
这里定义了一个指向字符指针的指针
pa
,将其指向数组a
的第一个元素,即指向字符串 "work" 的指针。 -
pa++;
这里将指针
pa
增加了一次,使其指向数组a
的下一个元素,即指向字符串 "at" 的指针。 -
printf("%s\n", *pa);
这里通过指针
pa
访问了其所指向的字符串(这里*pa表示的是字符串"at"的地址),然后使用%s
格式化输出该字符串。
🚀笔试题8:
c
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;
}
输出结果如下:
解析:
这段代码涉及指针和指针数组的操作,有点复杂,让我们逐行解释:
-
char* c[] = { "ENTER","NEW","POINT","FIRST" };
这行代码定义了一个字符指针数组
c
,其中每个元素是一个指向字符串常量的指针。数组中有四个元素,分别指向字符串 "ENTER"、"NEW"、"POINT" 和 "FIRST"。 -
char** cp[] = { c + 3,c + 2,c + 1,c };
这里定义了一个指向字符指针的指针数组
cp
,其中每个元素是一个指向数组c
中的某个元素的指针。c + 3
表示指向 "FIRST" 的指针,c + 2
表示指向 "POINT" 的指针,以此类推。 -
char*** cpp = cp;
这里定义了一个指向指针数组
cp
的指针cpp
。
-
printf("%s\n", **++cpp);
这里首先将指针
cpp
增加了一次,然后通过**
解引用两次得到 "POINT" 的字符串,然后输出。
-
printf("%s\n", *-- * ++cpp + 3);
这里先将指针
cpp
增加一次,然后将其解引用得到指向 "NEW" 的指针。接着,*-- * ++cpp
操作会将*++cpp
减少一次,然后将其解引用,得到字符串"ENTER" 的地址。最终,+ 3
操作将 "ENTER" 的字符串地址向后偏移3,得到字符串 "ER",然后输出。
-
printf("%s\n", *cpp[-2] + 3);
这里使用指针数组的索引操作,首先
cpp[-2]
得到指向字符串 "FIRST" 的指针,然后对其解引用,得到字符串"FIRST"的地址,最终+ 3
将 "FIRST" 的字符串地址向后偏移3,得到字符串 "ST",然后输出。
-
printf("%s\n", cpp[-1][-1] + 1);
这里
cpp[-1]
得到指向 "POINT" 的指针,然后cpp[-1][-1]
得到 "NEW" 的字符串指针(地址),然后+ 1
将字符串地址向后偏移1,得到字符串 "EW",然后输出。
💥小结:
理解和掌握指针和数组的概念是编程中的关键。通过练习和实践,你将能够更好地处理与指针和数组相关的问题。遇到困难并不是失败,而是在通向成功的道路上的一部分。每次犯错,你都在学习如何做得更好。我们一起加油!!✨☄
🔥今天的分享就到这里了, 如果觉得博主的文章还不错的话, 请👍三连支持一下博主哦🤞