八道C语言指针笔试题——拿捏指针

第一题

//请问输出什么?
#include <stdio.h>
int main()
{
	int a[5] = { 1, 2, 3, 4, 5 };
	int* ptr = (int*)(&a + 1);
	printf("%d,%d", *(a + 1), *(ptr - 1));
	return 0;
	return 0;
}

分析:

我们有必要搞清楚整形指针 ptr 是什么来头。

这个图我们可以明显看到 &a+1 之后指向哪个位置。我们要注意的是,我们有 & 这个取地址符号,即代表我们取出的是整个数组的地址,所以是一个数组指针类型的地址再通过强制类型转换得到整型指针 ptr 。

*(a+1),不难理解得到数组的第二个元素,即 2 。

*(ptr-1),同样也不难理解,得到数组的第五个元素,即 5 。

 

故本题输出为: 2,5 

第二题

#include <stdio.h>

//这里告知结构体大小为20个字节
struct Test
{
	int Num;
	char* pcName;
	short sDate;
	char cha[2];
	short sBa[4];
}*p;

int main()
{
	printf("%p\n", p + 0x1);
	printf("%p\n", (unsigned long)p + 0x1);
	printf("%p\n", (unsigned int*)p + 0x1);
	return 0;
}

分析:

注意我们的程序,定义了一个全局结构体指针变量 p ,那么这个指针变量的值默认初始化为0

 那么 p+0x1 就可以理解为:指向结构体指针 p 向后移动一个结构体大小的地址。

 

那就应该得到答案: p+0x1 = 20 ,需要注意的是,打印格式为 %p 。

这里普及一下:%p 是以地址的形式来打印数字的。

那么 (unsigned long)p+0x1 更简单了,即把结构体指针变量转化为无符号长整型数字了,数学上如何计算加法,那么这里就怎么计算加法,所以这里输出 1

对于(unsigned int*)p+0x1 ,就是把结构体指针变量强制类型转换为了无符号整型指针了,那么整型指针+1,无非就是向后跨越4(整型大小)个字节。故答案为 4

一定要注意是以 %p 形式打印的。

第三题

#include <stdio.h>
int main()
{
	int a[4] = { 1, 2, 3, 4 };
	int* ptr1 = (int*)(&a + 1);
	int* ptr2 = (int*)((int)a + 1);
	printf("%x,%x", ptr1[-1], *ptr2);
	return 0;
}

分析:

整型指针 ptr1 与第一题类似,只不过输出的时候 将 *(a-1) 写为了 a[-1]

重点是如何理解整型指针 ptr2 ,假设我们的机器是小端存储模式,那么数组在内存中的存储形式应该是这样:

题目将数组首元素地址强转为了整型变量并+1,即指针变成了数字+1,再将其强转为整型指针。现在我们假设数组首元素的地址为 0 ,即a的地址为0,那么有:

通过这个图,就可以明显看出 ptr2 内存放的是 a+1 的地址,但因其是整型指针,所以它会包括四个字节:

 

那么输出结果为:

 

需要注意,本题若是使用比较高版本的VS编译器,需要再x86环境下才能正常输出结果。

第四题

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

 分析:

这个题的重点在于逗号表达式。

一定要注意,数组中经过逗号表达式运算后,存储的应该是:

内存布局应该是:

整型指针 p 等于 a[0] ,a[0] 是指针操作的简写,那么完整的指针形式为:*(*(a+0)+0) ,即二维数组的第一个数组元素的第一个元素 ,即 1

第五题

#include <stdio.h>
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;
}

分析:

这个题乍一看是又一些难度的,那我们仔细分析:

二维数组的大小为 5*5 ,但数组指针指向的数组大小为 n*4 ,并且把二维数组的数组名赋给了指针p,这就导致了指针 p 与数组 a 的类型不同,但数据的存放顺序都是一样的。

我们来看图:

明显直观地看到 p[4][2] 与 a[4][2] 之间相差四个元素,正巧的是,指针相减得到的正是两个指针中间的元素个数。那么 p[4][2] - a[4][2] 得到的就是 -4 。

但是,-4 是个负数,而%p是一个无符号的格式,所以-4的原反补为:

-4存储在内存中的是补码,那么以 %d 形式打印的话,会将补码翻译成原码,打印:-4

以 %p 打印的话,是以无符号形式打印的,所以会直接以无符号十六进制打印补码。

故输出为:

第六题

#include <stdio.h>
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", *(ptr1 - 1), *(ptr2 - 1));
	return 0;
}

分析:

整型指针变量 ptr1 想必不用多说,打印的结果也就为 10 。

对于整型指针变量 ptr2 ,*(aa+1),找到的也就是二维数组的第二个数组元素的首元素地址,所以前面的强制类型转换是为了混淆我们的。

那么 ptr2 也就保存元素 6 的地址,这个地址 -1 得到 5 的地址,所以输出5。

 

第七题

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

分析:

这段代码非常简单。

首先是一个指针数组,指针数组里面放的是三个字符串常量(字符串常量不做介绍了),二级指针 pa 放的是数组名,即首元素地址,通过 pa++ 得到了第二个元素的地址,故打印 at 。

第八题

#include <stdio.h>
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;
}

分析:

我们先看一副关系图

 那么 **++cpp ,我们按照运算符优先级来分析:

++cpp:

**++cpp:

 

所以这里输出:POINT ,并且要注意,++cpp 可以表示为 cpp=cpp+1,也就是说,cpp的值是改变了的。

我们来看 *--*++cpp+3 如何分析:

++cpp:

 *++cpp得到 c+1 ,--*++cpp 得到 c

 

 *--*++cpp得到:

即得到 ENTER 的首元素地址。 

*--*cpp+3得到 E 的地址,往后打印即为 ER

我们再看如何理解 *cpp[-2]+3 ,这个表达式可以化为: *(*(cpp-2))+3

那么 *(cpp-2) 得到 c+3:

*(*(cpp-2))为:

 即得到 F的地址。

*(*(cpp-2))+3即为 S 的地址,故打印 ST

 对于cpp[-1][-1]+1 这个表达式可以化为 *(*(cpp-1)-1)+1

则*(*(cpp-1)-1)表示为:

 即得到 N 的地址,那么 *(*(cpp-1)-1)+1 得到 E的地址 ,向后打印得 EW

 

猜你喜欢

转载自blog.csdn.net/weixin_59913110/article/details/125278431