2024 - 10 - 10 - 笔记 - 25
作者(Author): 郑龙浩 / 仟濹(CSDN 账号名)
【指针 + 数组】的 各种题型(笔试题)
来自于鹏哥的网课,我做一下笔记
119. 【C语言进阶】笔试题详解(4)_哔哩哔哩_bilibili
① 题
#include <stdio.h>
int main()
{
int a[5] = { 1, 2, 3, 4, 5 };
//&a 是整个数组的地址,+1是跳过这个数组,所以该指针指向到了数组后面
int* ptr = (int*)(&a + 1);
//*(a + 1): a 为数组的首元素地址(第 0 个元素地址),a + 1 是数组第 1 个元素地址,解引用第 1 个元素地址得出的就是 第 1 个元素
//ptr - 1: 上面知道了ptr指向的是数组的后边,那么 ptr - 1指向的就是数组的最后一个元素
printf("%d, %d", *(a + 1), *(ptr - 1) );
return 0;
}
打印结果:
2, 5
② 题
注:结构体大小是按照最大的成员来计算的
//结构体的大小是 4 byte * 5 == 20 byte
struct Test
{
int Num;
char *pcName;
short sDate;
char cha[2];
short sBa[4];
}*p = (struct Test*)0x100000;
//这里的p是一个结构体指针变量,是可以指向如上这样结构体的一个指针
假设p 的值为0x100000。 如下表表达式的值分别为多少?
已知,结构体Test类型的变量大小是 20 byte
int main()
{
//结构体指针 + 1 --> 跳过整个结构体
printf("%p\n", p + 0x1);//0x100000 + 0x1 ==> 0x100000 + 20 ==> 0x100000 + 0x14 ==> 0x100014
//长整形 + 1 --> 长整形 + 1(记住,这是一个坑,这里的指针p被强制类型转换为了【无符号长整形】的数据,表示的不是指针了)
printf("%p\n", (unsigned long)p + 0x1);//0x100000 + 0x1 ==> 1,048,576 + 1 ==> 0x100000 + 0x1 ==> 0x100001
//整型指针 + 1 --> 跳过一个int类型的大小
printf("%p\n", (unsigned int*)p + 0x1);//0x100000 + 0x1 ==> 0x100000 + 4 ==> 0x100000 + 0x4 ==> 0x10004
return 0;
}
打印结果:
0x100014
0x100001
0x10004
③ 题
int main()
{
int a[4] = { 1, 2, 3, 4 };
//&a + 1 跳过了整个数组,再将【数组地址】强制类型【元素地址】赋给 ptr1,此时ptr指向数组后边
int *ptr1 = (int *)(&a + 1);
//a是首元素地址,将元素地址强制转换为【int型的数值】,得出int型的数值 + 1,再强制类型转换为 int*
int *ptr2 = (int *)((int)a + 1);
//ptr[-1]就是从ptr位置往前走了一个位置
//从原来a数组的后边位置(可以想成a[5])变成了a[4]
printf( "%x,%x", ptr1[-1], *ptr2);
return 0;
}
下面进行逐个分析,因为电脑作图不是很方便,所以在纸上写的:
打印结果:
4,2000000
④ 题
#include <stdio.h>
int main()
{
//在括号中数字用逗号分隔,那么得出的结果为最后一个逗号后边的数
int a[3][2] = { (0, 1), (2, 3), (4, 5) };
int *p;
//p指向首元素地址,所以取出来的值为1
p = a[0];
printf( "%d", p[0]);//1
return 0;
}
打印结果:
1
⑤ 题
int main()
{
int a[5][5];
int(*p)[4];
//p为数组指针,指向了a的第一行
p = a;
printf( "%p,%d\n", &p[4][2] - &a[4][2], &p[4][2] - &a[4][2]);
return 0;
}
这个就要画图理解了, 其实和第 ③ 题还是蛮像的,下面为手绘的图:
FFFFFC,-4
⑥ 题
int main()
{
int aa[2][5] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
//aa第 0 行的地址,&aa表示整个数组的地址
//+ 1 就是跨过了整个数组,指向了整个数组的后边
//然后再将二维数组地址类型的地址强制类型转换为一个元素的地址,或者说是一个变量的地址
//所以ptr1就是指向了aa数组的后边(挨着)
int *ptr1 = (int *)(&aa + 1);
//aa是第 0 行的地址
//aa + 1 就是第 1 行的地址
//*(aa + 1) 就是第 1 行 第 0 个元素的地址
//ptr2 指向第 1 行 第 0 个元素的地址,也就是&a[1][0]
int *ptr2 = (int *)(*(aa + 1));
//因为在二维数组中,所有元素的地址都是连续的,所以-1后,打印的元素是都是原本指向位置的前一个
printf( "%d,%d", *(ptr1 - 1), *(ptr2 - 1));
return 0;
}
打印结果:
10,5
⑦ 题
#include <stdio.h>
int main()
{
char *a[] = {"work","at","alibaba"};
//pa指向a的首元素地址
char**pa = a;
//指向下一个元素
pa++;
printf("%s\n", *pa);//at
return 0;
}
打印结果:
at
⑧ 题 - 挺复杂的
int main()
{
char *c[] = {"ENTER","NEW","POINT","FIRST"};
char**cp[] = {c+3,c+2,c+1,c};
char***cpp = cp;
//cpp指向的是cp的首地址,即cp[0],也就是元素 c + 3 的位置,而cpp中存的是&cp[0]
//++cpp,指向cp[1],也就是元素 c + 2 的位置,而cpp中存的是&cp[1]
//*++cpp,是解引用&cp[1],得出cp[1]
//**++cpp,接上文,解引用cp[1],即解引用 c + 2,或者说是*(c + 2),得出来的是POINT
printf("%s\n", **++cpp);//POINT
//如果cpp前面没有另一个变量或者常量或者地址,那么前面这些符号*--*++就没有所谓的计算顺序,优先级也不存在了,此时哪个符号离cpp最近先算哪一个
//++cpp cpp指向了cp[2],此时cpp中存储的是 &cp[2]
//*++cpp 相当于对&cp[2]进行解引用,得出来的是cp[2]
//--*++cpp 相当于是--cp[2],也就是相当于是--(c + 1),这里更改了cp数组中存储的地址,将cp[2]的元素--后,c + 1后,cp[2]就是c
//*--*++cpp 相当于 *c,是字符串"ENTER"中的第 1 个字符'E'的地址,如果打印格式为“ %s ”,得出来的是ENTER
//*--*++cpp 相当于*(c + 3),此时是字符串"ENTER"中的第 2 个字符'E'的地址
printf("%s\n", *--*++cpp+3);//ER
//在理解次行代码之前,要先知道前面的运算已经
//1. 将cp[2]变成 c
//2. 将cpp指向了cp[2],即cpp存 &cp[2]
//cpp[-2] 相当于*( cpp - 2 ),由上可得,此时 cpp 指向的是cp[2],也就是 cpp 中存储的是 &cp[2],那么cpp - 2指向的就是cp[0],也就是cpp - 2表示的地址实际上是&cp[0],所以*cpp[2]可以相当于是cp[0] --> 得出 *cpp[-1] 就是 c + 3
//*cpp[-2] 此时将cpp[-2]进行解引用,其实就是*( c + 3 ),解引用后指向的就是字符串"FIRST"中的字符'F'
//*cpp[-2] + 3 此时指向了常量字符串"FIRST"中的字符'f'
printf("%s\n", *cpp[-2]+3);
//在理解次行代码之前,要先知道前面的运算已经
//1. 将cp[2]变成 c
//2. 将cpp指向了cp[2],即cpp存 &cp[2]
//cpp[-1][-1] 意思其实就相当于 *( *(cpp - 1) - 1 ),实际上就是*( *( &cp[2] - 1) - 1 ),得出来就是*( *cp[1] - 1 ) < == > *( c + 2 - 1 ) < == > * ( c + 1 ) 此时意思就是指向了常量字符串"NEW"的常量'N'
//cpp[-1][-1] + 1 就是 * ( c + 1 ) + 1,就是指向了常量'N'的下一个常量,即指向了'E'
printf("%s\n", cpp[-1][-1]+1);//EW
return 0;
}
打印结果:
POINT
ER
ST
EW