指针笔试题

目录

1、笔试题1

2、笔试题2

3、笔试题3

4、笔试题4

5、笔试题5

6、笔试题6

7、笔试题7

8、笔试题8



1、笔试题1

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

正确答案:2,5。

解题:

&a是取出整个数组的地址,&a指向a,&a+1就指向了5的后面。
&a是指向数组的,是数组指针,其类型是:int (*)[5],&a+1之后还是数组指针,还是这种类型;
但是(int*)(&a+1)强制类型转换为整型指针:(int *)。
现在吧整形指针(&a+1)赋给ptr,则ptr就是整形指针,指向5的后面,-1移动一个整形指向5,解引用就是5。
a是数组名,是1的地址,2的地址解引用就是2。

2、笔试题2

#include <stdio.h>
//由于还没学习结构体,这里告知结构体的大小是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;
}

正确答案:

00000014
00000001
00000004

解题:

p是结构体指针,用结构体类型创建了指针变量p,假设p的值为0x100000,p =0x100000,0x100000是十六进制数字而且是整型类型,p是整形指针,若想0x100000是一种地址或指针类型则可以对它强制类型转换。
p =(sttuct Test*)0x100000,这里p的类型和数字0x100000的类型是一样的,赋过去。
问:
p是结构体指针,0x1(写成了十六进制)就是1,指针+1到底+几?

指针+1是+几取决于类型。(int*)整型指针+1就是+4个字节。因为整形指针是指向整形的,+1就会跳过一个整形就是4个字节;(char*)字符指针+1就是+1个字节。因为字符指针是指向字符的,+1就会跳过一个字符就是1个字节。
注意:一个结构体指针+1就是跳过一个结构体大小。

①p+0x1是多少?

这里已知结构体是20个字节,所以p+0x1就是跳过20个字节,p变成了0x100014(十六进制的14就是十进制的20),p+0x1就是0x100014。

②p强制类型转换为unsigned long后+0x1是多少?

p本来是结构体指针,此时强制类型转换为unsigned long,则此时p就是无符号整形,所以会把p里面的值当成是一个数字0x100000,这个十六进制0x100000对应的十进制是1048576,这个整形数字+1就仅仅是+1(不是指针+1,指针+1时才会考虑跳的字节数),所以数字0x100000+1就是0x100001。

③p强制类型转换为unsigned int*后+0x1是多少?

p此时是整形指针,整型指针+1就是+4个字节,就是0x100004。

总结知识:


十六进制数字是以0x开头的。0开头的是八进制数字。
%p是以地址的形式打印,把数字以十六进制形式打印,不省略高位的0。(如打印出的00100014、00100001、00100004)。
%x是打印十六进制的,会省略高位的0。(如打印出的100014、100001、100004)。
注意:地址本来是二进制的;二进制,八进制、十进制、十六进制只是数值的表现形式,地址是值,可以用十进制、十六进制等多种形式表示。
 

3、笔试题3

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

错误答案:十六进制的4和2。

正确答案:4,2000000。

解题:

&a取出的是数组的地址,&a+1就会跳过整个数组指向4的后面,然后把这个指针强制类型转换为整形指针赋给ptr1,即ptr1指向4的后面。
ptr1(-1)就是*(ptr1 + (-1))就等价于*(ptr1-1),是4,用十六进制打印也是4。

a是数组名,是首元素地址,即是1的地址,(int)a,是把地址强制类型转换为整形,是值,所以仅仅只是数字+1,(数字+1)作为地址只是向后跳了一个字节,(例如500作为地址和501作为地址这两者之间只是相差了一个字节。) 而整形是4个字节,把a数组的1,2、3、4这四个整形以字节为单位写出来:
小端排序:
01 00 00 00
02 00 00 00
03 00 00 00
04 00 00 00
(复习:低位放在低地址是小端排序,1本来是0x 00 00 00 01,01是低位)
如a的起始地址就是0x10,即1的01处的地址是0x10,地址0x10转换为值(整形值)后+1就是0x11(十六进制的0x10转换为十进制是16,+1是17,17作为十六进制就是0x11)加完之后(int*)0x11是强制类型转换为整形指针赋给ptr2,所以是地址0x11,这个0x11地址相较于起始地址0x10是向后偏移了一个字节,ptr2作为整形指针指向了1的第一个00处,则*ptr2是:以ptr作为起始地址向后访问一个整形,即访问4个字节,一个整形的内容,在内存中以小端形式存储访问的是:00 00 00 02,以小端形式拿出来还原出来的数字就是0x 02 00 00 00,以%x形式打印高位的0会省略,所以打印的是2 00 00 00。

对于指针强制类型转换为整形(值)+1就仅仅只是+1。整形+1就是值+1。
只有是指针+1的时候才会考虑到底+几。

a作为数组名,它的类型是:int*
a+1对整形指针+1就是+4;

把a强制类型转换为整形,则a就是整形数字,整形数字+1就是+1(5+1=6),两个地址差1就是差1个字节(在内存块中,每个内存单元给一个编号,一个编号就是一个地址,大小就是一个字节)。

若转换为char,(char)a+1,是以ASCII码值解读的,+1还是+1。

温故而知新:


数据在内存中存储是有顺序的。
例如数据:0x11223344
这里44是低位字节,11是高位字节。从11到44从左到右依次是逐次增高的字节位。
数据在内存中存储:
预先规定:若内存中从左到右是从低地址到高地址。

若是按大端排序:
11223344
把一个数字的低位字节的内容放到高地址处,高位字节的内容放到低地址处,是大端存储。

若是按小端排序:
44332211
把一个数字的低位字节的内容放到低地址处,高位字节的内容放到高地址处,是小端存储。

以小端形式存储则以小端形式取出。数据还是原来的数据。


4、笔试题4

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

错误答案:0。

正确答案:1。

解题:

 注意数组中放的不是0,1、2、3、4、5;

不是 { {0,1},{2,3},{4,5} };而是“(0,1),(2,3),(4,5)”,是逗号表达式。

若是 { {0,1},{2,3},{4,5} },意思是0、1放在第一行;2、3放在第二行;4、5放在第三行;

是“(0,1),(2,3),(4,5)”,则意思是:(0,1)这个逗号表达式的结果是1,(2,3)这个逗号表达式的结果是3,(4,5)这个逗号表达式的结果是5,所以这里面没有0、2、4的数字。

int a[3][2] = { (0,1),(2,3),(4,5) }; 就等同于:int a[3][2] = { 1,3,5 };

int a[3][2] = { 1,3,5 };三行两列表示的就是:

第一行:1 3

第二行:3 5

第三行:0 0

a[0]是第一行的数组名,数组名没有&,没有单独放在sizeof内部,所以数组名就表示数组首元素地址,a[0]就是第一行第一个元素的地址,即p指向1。因为p[0] = *(p+0) = *p,即访问的是1。

5、笔试题5

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

错误答案:随机值,0

正确答案:FFFFFFFC,-4

解题:

&a[4][2]是一个整型指针;p是一个数组指针,p能够指向的是4个元素的数组,每个元素是整型,

p+1会跳4个整型,跳的是16个字节。p它自己认为它指向的是一个数组,+1就会跳过这个数组。

p[4][2] = *(*(p+4)+2)

a作为二维数组的数组名表示的是第一行的地址,但指向的也是数组的起始位置。p = a,则p指向的也是数组的起始位置,p认为它指向的数组是4个元素,所以p+1会跳过4个整型(跳过4个元素的数组),找到p+4的位置后,指向p+4,因为p认为它指向是4个元素的数组,所以*(p+4)就是向后访问4整型,就是4个元素的数组,此时*(p+4)相当于4个整型的数组名,数组名又表示数组首元素地址,所以*(p+4)就是指向的起始位置的地址,所以*(p+4)+2就是该4个整型的数组中的下标为2的地址;即找到了元素:*(*(p+4)+2),就是p[4][2]

&p[4][2] - &a[4][2] = ?

两个整型地址相减,小地址 - 大地址,减的是中间元素的个数,这两个指针都是int*的指针,所以这2个指针相减计算的是二者之间int的个数,是 -4。

-4:

以%d形式打印,打印的就是-4。

以%p形式打印,就认为-4是地址,直接打印-4在内存中的补码:

-4的原码:10000000000000000000000000000100

-4的反码:11111111111111111111111111111011

-4的补码:11111111111111111111111111111100

11111111111111111111111111111100这个值以16进制写是:fffffffc(4个1就是1个f)

a数组中有25个int,一个元素是一个int

总结知识:

数组指针+1——>会跳过一个数组

6、笔试题6

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

错误答案:0,0

正确答案:10,5

解题:

二维数组的数组名+1就跳过了整个二维数组,强制类型转换为整形指针赋给ptr1,ptr1为整形指针,ptr1-1就是向前移动一个字节,解引用就是10。
aa是二维数组的数组名就是第一行的地址,aa+1就跳了一行指向第二行,*(aa+1)相当于第二行的数组名,第二行的数组名又相当于首元素的地址,即拿到的是6的地址,6的地址强制类型转换为整形指针赋给ptr2,ptr2是整形指针,指向6的地址。
第二行的地址解引用就相当于拿到第二行的数组名:aa[1],第二行的数组名又相当于首元素地址,就是6的地址,6的地址强制类型转换为整形指针(int*)(这里其实不用强制类型转换为int*,因为6的地址本来就是int*)
ptr2-1是向前移动一个整形,解引用就是5。

7、笔试题7

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

正确答案:at

解题:

a首先与[结合说明a是数组,数组的每个元素是char*类型,所以a是指针数组。
后面的{}是a的初始化,拿字符串首字符的地址即w的地址初始化了数组a的第一个元素;at中a的地址初始化了数组第二个元素,alibaba中a的地址初始化了数组第三个元素。
a是数组名,是首元素地址,就是a数组中第一个元素的地址,该元素是char*类型,它的地址是char**,(一级指针变量的地址就是char**)

pa = a;a是数组名,是数组首元素的地址;则pa中存的是a数组中第一个元素的地址,即pa指向了a数组中第一个元素;pa是char**类型,pa指向的元素是char*类型的:char**pa——>char* *pa;第二个*说明pa是指针,第一个*和char合起来说明pa指向的元素对象是char*类型的;pa+1就相当于跳过一个char*类型的元素,此时pa指向了第二个元素,相当于指向了at中的a,*pa,对pa解引用,就是向后访问了一个元素,这个元素里放的是a的地址,当把at的地址放到printf()函数中打印时就是打印字符串at。

8、笔试题8

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

正确答案:

POINT
ER
ST
EW

解题:

 

 

 

注意:

%s就是打印字符串的,要打印字符串,那就需要把字符串的首字所在空间地址传进去,才可以从起始位置开始一个一个往后打印 ,直到遇到\0位置。

所以:

#include <stdio.h>
#include <string.h>
int main()
{
	char arr1[] = { 'a','b','c','d','e','f','\0'};
	char arr2[20] = "xxxxxxxx";
	printf("%s\n", strcpy(arr2, arr1));
	//注意这里strcpy()函数返回的是arr2这块空间的起始地址,以%s形式打印
	//实际输出的是字符串abcdef
	return 0;
}
#include <stdio.h>
int main()
{
	char* p = "ENTER";
	p++;
	printf("%s\n", p);
	return 0;
}//NTER
  • 32
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 40
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 40
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值