在前面讲解完毕指针相关内容后,下面便开始进行答题模拟
再进行做题以前,我建议自己先行快速浏览一下指针内容:
数组笔试题讲解
一维数组
整型数组
请问下面的答案依次是什么???
int a[] = {
1,2,3,4};
printf("%d\n",sizeof(a));// 1
printf("%d\n",sizeof(a+0));// 2
printf("%d\n",sizeof(*a));// 3
printf("%d\n",sizeof(a+1));// 4
printf("%d\n",sizeof(a[1]));// 5
printf("%d\n",sizeof(&a));// 6
printf("%d\n",sizeof(*&a));// 7
printf("%d\n",sizeof(&a+1));// 8
printf("%d\n",sizeof(&a[0]));// 9
printf("%d\n",sizeof(&a[0]+1));//10
/*
1. 16
2. 4 or 8
3. 4
4. 4 or 8
5. 4
6. 4 or 8
7. 16
8. 4 or 8
9. 4 or 8
10.4 or 8
*/
解析:
- 当只有数组名和sizeof结合时候,表示求整个数组大小. 所以结果是 4 * 4 = 16
- a+0是一个表达式,sizeof(a+0)就认为a是地址,0是地址,地址加地址还是地址,地址(指针)的大小只能是4或者8
- *a是数组a第一个元素,是一个整型,整型的大小是4个字节,所以结果是 4
- a+1和第二个一样,测的地址,地址(指针)大小只能是4或者8
- a[1]是一个整数,同理,整型大小是4个字节
- &a是一个地址,同理只能是4或者8
- *&a中
*
与&是互相抵消的,所以*&a
等于a,而只有数组名和sizeof结合时,就代表求整个数组大小,所以是4 * 4=16- 9.10都是地址相加减,所以只能是4或者8
字符数组
sizeof求字符大括号数组
下面会出现三组题,有一定的坑洞;
char arr[] = {
'a','b','c','d','e','f'};
printf("%d\n", sizeof(arr));// 1
printf("%d\n", sizeof(arr+0));// 2
printf("%d\n", sizeof(*arr));// 3
printf("%d\n", sizeof(arr[1]));// 4
printf("%d\n", sizeof(&arr));// 5
printf("%d\n", sizeof(&arr+1));// 6
printf("%d\n", sizeof(&arr[0]+1));// 7
/*
答案:
1. 6
2. 4 or 8
3. 1
4. 1
5. 4 or 8
6. 4 or 8
7. 4 or 8
*/
解析:
当数组名与
sizeof
结合时候,表示求整个数组大小,所以arr
数组的大小是6;
sizeof
中不是单独的数组名,所以arr+0
这时候表示arr是一个地址,arr+0还是等于arr,一个地址的大小就是4 or 8
*arr
表示首元素,而首元素是字符'a'
,一个字符的大小是一个字节,所以答案是1;
arr[1]
代表的是第二个元素,第二个元素是'b'
,所以一个字符的大小就是一个字节,所以答案是1;
- 题目中 &arr表示的是数组地址,所以后面无论怎么加减都是地址,地址的大小是4 or 8;
&arr[0] + 1
还是一个地址,只是第二个元素的地址,而地址的大小是 4 or ;
strlen求字符数组标准写法
char arr[] = {
'a','b','c','d','e','f'};
printf("%d\n", strlen(arr));// 1
printf("%d\n", strlen(arr+0));// 2
printf("%d\n", strlen(*arr));// 3
printf("%d\n", strlen(arr[1]));// 4
printf("%d\n", strlen(&arr));// 5
printf("%d\n", strlen(&arr+1));// 6
printf("%d\n", strlen(&arr[0]+1));// 7
/*
答案:
1. 随机值
2. 随机值
3. 报错
4. 报错
5. 随机值
6. 在第一题的随机值上减6
7. 在第一题的随机值上减1
*/
解析:
注意: 这里是
strlen
,不再是sizeof
,strlen
接收的参数是地址,并从该地址后面进行查找\0
,直到找到为止
- arr是首元素地址,所以
strlen
变开始从a
进行往后找寻\0
,但是arr数组是没有的\0
的,所以他会越过数组界限,直到找到为止,因此说最后返回的是一个随机值.
arr+0还是arr,因此照样从
a
开始往后面进行查询,与第一天一样,还是一个随机值4.题都是传送的一个字符,即传送了一个整数(字符本质还是整数),但是要求传送地址,所以报错
&arr是数组地址,但是&arr的值和 arr一模一样,所以还是会返回一个随机值.
&arr+1,表示跨越整个数组,最后指向数组末尾,于是从
数组末尾
开始向后查找\0
的值,因此返回的随机值是相比于第一题减去6
&arr[0]+1
首元素地址加一,代表第二个元素地址,因此从此开始向后查询\0
,因此最后返回的随机值相比第一题减去1;下面是除去3.4.两个报错题其他题的结果:完全与解析温吻合
sizeof求取标准写法数组
char arr[] = "abcdef"; printf("%d\n", sizeof(arr));// 1 printf("%d\n", sizeof(arr+0));// 2 printf("%d\n", sizeof(*arr));// 3 printf("%d\n", sizeof(arr[1]));// 4 printf("%d\n", sizeof(&arr));// 5 printf("%d\n", sizeof(&arr+1));// 6 printf("%d\n", sizeof(&arr[0]+1));// 7 /* 答案: 1. 7 2. 4 or 8 3. 1 4. 1 5. 4 or 8 6. 4 or 8 7. 4 or 8 */
解析:
当只有数组名与
sizeof
结合时候,表示求得是整个数组大小,所以整个数组大小是 7(包括\0
)
arr+0
不是单独的数组名与sizeof
结合在一起.所以arr是一个地址,地址的大小是 4 or 84.题目都是一个确定的字符,一个字符的大小是 1直字节,所以答案是
1字节
6.7.都是求取的地址,地址的大小是 4 or 8;
strlen求取标准字符串
char arr[] = "abcdef";
printf("%d\n", strlen(arr));//1
printf("%d\n", strlen(arr+0));//2
printf("%d\n", strlen(*arr));//3
printf("%d\n", strlen(arr[1]));//4
printf("%d\n", strlen(&arr));//5
printf("%d\n", strlen(&arr+1));//6
printf("%d\n", strlen(&arr[0]+1));//7
/*
答案:
1. 6
2. 6
3. 报错
4. 报错
5. 6
6. 随机值
7. 5
*/
解析: strlen是接收地址并且往后查找\0,然后停止
arr
是首元素地址,往后查找,可以找到\0
,所以是 6
arr+0
还是首元素地址,往后查找,可以找到\0
,所以还是 64.之前我们讲过的类似,他们传的是整型(字符本质),不是地址,所以报错
&arr
是数组地址,但是&arr
与arr
的值一样,都是从第一个元素开始查找,所以答案还是6
&arr[0] + 1
是第二个元素的地址,所以从他开始往后查找们就会少一个首元素,所以值 是 6-1 = 5下面还是除去报错的3.4.题之外的结果:
常量字符指针
注意这里的区别,很多人会容易搞错
char *p = "abcdef"; //这种写法代表"abcdef"是常量字符串,只给p存放了一个a的地址,且*p路不可修改
printf("%d\n", sizeof(p));// 1
printf("%d\n", sizeof(p+1));// 2
printf("%d\n", sizeof(*p));// 3
printf("%d\n", sizeof(p[0]));// 4
printf("%d\n", sizeof(&p));// 5
printf("%d\n", sizeof(&p+1));// 6
printf("%d\n", sizeof(&p[0]+1));// 7
/*
答案:
1. 4 or 8
2. 4 or 8
3. 1
4. 1
5. 4 or 8
6. 4 or 8
7. 4 or 8
*/
解析:
p
是指针,即字符a
的地址,所以在上面中除了 3 4小题之外,其余的所有表达式都是求取指针(地址)大小,所以指针大小只能是4 或者 8而2 3小题中
*p
等于a
,而p[0]
等于*(p + 0)
也是字符a
(不明白这个关系的请看这里数组名与地址关系) 所以3 4小题都是 1
关于strlen
求常量字符串长度
char* p = "abcdef"; //这种写法代表"abcdef"是常量字符串,只给p存放了一个a的地址;
printf("%d\n", strlen(p)); //1
printf("%d\n", strlen(p + 1));//2
printf("%d\n", strlen(*p));//3
printf("%d\n", strlen(p[0]));//4
printf("%d\n", strlen(&p));//5
printf("%d\n", strlen(&p + 1));//6
printf("%d\n", strlen(&p[0] + 1));//7
/*
答案:
1. 6
2. 5
3. error
4. error
5. 随机值
6. 随机值
7. 随机值
*/
解析:
p是
a
的地址,所以向a的地址后面查找\0
,所以 答案是 6p+1是地址向后移动一位,然后从该地址处向后找
\0
,所以答案是 54…这两个题和之前的一样,
strlen
要求接收地址,但是*p
与p[0]
都是确切的数值,所以报错5.6.7.题都是在 p的地址上操作,而p后面的地址所指向元素是否为0,不可知,所以都是随机值.
二维数组
这里要求对数组名关系掌握准确,可以看这篇文章数组名与地址
并且一个二维数组是多个一维数组串联在一起的.
int a[3][4] = {
0};
printf("%d\n",sizeof(a)); // 1
printf("%d\n",sizeof(a[0][0]));// 2
printf("%d\n",sizeof(a[0]));// 3
printf("%d\n",sizeof(a[0]+1));// 4
printf("%d\n",sizeof(*(a[0]+1)));// 5
printf("%d\n",sizeof(a+1));// 6
printf("%d\n",sizeof(*(a+1)));// 7
printf("%d\n",sizeof(&a[0]+1));// 8
printf("%d\n",sizeof(*(&a[0]+1)));// 9
printf("%d\n",sizeof(*a));// 10
/*
答案:
1. 48
2. 4
3. 16
4. 4 or 8
5. 4
6. 4 or 8
7. 16
8. 4 or 8
9. 16
10.16
*/
解析:
- a是数组名,单独与
sizeof
在一起表示求整个数组大小,所以大小是3*4*4=48
a[0][0]
是一个确切的数字,即第一行第一列的数字,整型的大小是4个字节- a[0]代表的是第一行数组的数组名,而数组名单独与
sizeof
在一起表示求的是整个数组的大小,所以大小是4 * 4 = 16
- a[0]是第一行数组名,即地址,地址加1还是地址,所以地址的大小是
4 or 8
a[0] + 1
代表第一行第二列的元素地址,前面有*
,所以是求第一行第二列元素的大小,所以是 4个字节- a是地址,a+1还是地址,地址的大小是
4 or 8
- a是
首元素地址
,即第一行的数组地址,a+1代表是第二行的数组地址,前面有个*
,所以*(a+1)
是第二行的数组名,数组名单独与sizeof
在一起表示求的是整个数组大小
,所以第二行的数组大小是4 * 4 = 16
- &a[0]是地址,地址加一还是地址,地址的大小是
4 or 8
&a[0]+1
代表第二行的数组地址,前面有个*
,则变成了第二行的数组名 ,数组名单独与sizeof
结合在一起,表示整个数组大小,所以第二行的数组大小是4 * 4 = 16
- a是第一行数组地址,前面有个
*
,表示第一行数组的数组名
,数组名单独与sizeof结合在一起.所以大小是4 * 4 = 16
下面是32位与64位机器下的运行结果:
总结 :数组名的意义:
sizeof(数组名)
,这里的数组名表示整个数组,计算的是整个数组的大小。&数组名,这里的数组名表示整个数组,取出的是整个数组的地址。
除此之外所有的数组名都表示首元素的地址。
指针笔试题讲解
在讲解下面的例子时候,我强烈建议大家先看看 数组名与地址关系文章
笔试题1
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+1
是指针指向数组a末尾,前面有一个强制性整型指针转换,此时的ptr
则指向数组末尾,且是一个整型指针所以
ptr-1
就是5的地址,所以解引用就是 5
a
是数组第一个元素地址,加1,就是第二个元素地址,解引用就是 2下面是图解
笔试题2
//由于还没学习结构体,这里告知结构体的大小是20个字节
//假设p 的值为0x100000。 如下表表达式的值分别为多少?
struct Test
{
int Num;
char *pcName;
short sDate;
char cha[2];
short sBa[4];
}*p;
int main()
{
p = 0x00100000;
printf("%p\n", p + 0x1);
printf("%p\n", (unsigned long)p + 0x1);
printf("%p\n", (unsigned int*)p + 0x1);
return 0;
}
/*
答案:
0x00100014
0x00100001
0x00100004
*/
解析:
p是结构体指针,可以跨越20个字节,所以
p+1
就是地址跨越20个,所以在0x00100000
上增加0x00000014(16进制的20)
,所以答案是
0x00100014
p是结构体指针,但是前面有强制性无符号整型类型转化,所以把p变成了一个长整型,即一个**数字,**那么数字加一,就是在
0x00100000
上加0x00000001
,所以结果是0x00100001
p是结构体指针,但是前面有强制性无符号整型指针类型转化,所以p是一个整型指针,所以整型指针加1,地址跳跃4个字节,所以在原来的基础上加
0x00000004;
, 所以结果是0x00100004
笔试题3
int main()
{
int a[5] = {
1, 2, 3, 4 ,5};
int *ptr1 = (int *)(&a + 1);
int *ptr2 = (int *)((int)a + 1);
printf( "%x,%x", ptr1[-1], *ptr2);
return 0;
}
/*答案:
0x00000005
0x02000000
*/
解析:
&a+1
是数组地址加一,则指向了数组末尾,然后前面有强制整型指针类型转换,所以此时ptr1
是整型指针,且指向数组的末尾又因为
ptr1[-1]
等于*(ptr1 -1 )
,代表指针位置向前移动一位,即指向4,所以答案是0x00000004
a是数组的首元素1的地址,前面又强制类型转换为整数,然后在整数的基础上加一,最后再换成整型指针,我们以图说明
*可以看到经过小端存储以后,随着地址的变化,就可以知道值,第二问中 (int )((int)a + 1)指向了00解引用即向后访问4个字节
即得到 00 00 00 02,由于这是小端存储,所以真正的数字是 02 00 00 00
笔试题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]);
return 0;
}
/*
答案:
1
*/
解析:
(0,1)这种是逗号表达式,所以真正存进数组a的是
1 ,3 ,5
由于这是一个二维数组,所以a[0]是第一行的数组名,即第一行第一列的元素的地址.
所以
p[0]
等价于*(a[0] + 0)
即第一行第一列的元素 所以答案是 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;
}
10000000 00000000 00000000 00000100
11111111 11111111 11111111 11111011
1111 1111 1111 1111 1111 1111 1111 1100
FFFFFFFC
/*
答案:
FFFFFFFC,-4
*/
解析:
由图中所画可以知道
&p[4][2] - &a[4][2]
的结果是-4(地址相减代表中间元素个数)-4的补码是
11111111 11111111 11111111 11111100
因此上式化成
%p
形式是FFFFFFFC
化成
%d
形式是-4
笔试题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", *(ptr1 - 1), *(ptr2 - 1));
return 0;
}
/*
答案:
10 , 5
*/
解析:
首先这是一个二维数组.
- 所以
&aa+1
代表跨越整个二维数组.即指向10的末尾.然后前面强制类型转换为int*
,所以ptr1
此时是整型指针.指向10的末尾. 所以
*(ptr1 - 1)
就是10
aa
是第一行的数组地址,aa+1
就是5的末尾.*(aa+1)
即是第二行的数组名,即元素6的地址前面又强制类型转换为int*
,所以此时ptr2
是整型指针.*(ptr-1)
就是 5
笔试题7
#include <stdio.h>
int main()
{
char* a[] = {
"work","at","alibaba"};
char** pa = a;
pa++;
printf("%s\n", *pa);
return 0;
}
/*
答案:
at
*/
由此可以看出,pa++以后指向了数组a的第二块空间,
*pa
即得到了a
的地址,因此打印出来就是at
笔试题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; }
/*
答案:
POINT
ER
ST
EW
*/
- 同理,
char** * cpp
代表cpp
可以跳过char**大小
,因此++cpp
,即指向了数组cp[1]
的位置,第一次解引用,即得到c+2
,而c+2
则是指向数组c[2]
,所以第二次解引用,即得到c[2]
里面的内容(p的地址),所以打印就是POINT
注意:第一题以后,
cpp
的值就是加1过后了,即指向cp[1]
.所以第二题注意细节
*++cpp
就如上一题说的一样,即得到c+1(因为cpp已经在第一题加了一次)
,然而c+1
前面有个--
,所以c+1变成了c,而c指向的是数组c[0]
,所以再次解引用就得到了c[0]里面的内容(E的地址),然而,E却加3,即得到第二个E的地址,所以打印出来就是ER
注意:同理注意经过两次变化的
cpp
的值.
- 因为
cpp[-2]
等价于*(cpp-2)
所以cpp-2
便指向了cp[0]
,所以cpp[-2]
的内容便是c+3
, 然后*cpp[-2]
就是解引用c+3,而c+3指向的是c[3]位置,解引用因此得到了F的地址,而F又加3,所以得到了S地址,所以最后打印ST
cpp[-1][-1]
相当于*(*(cpp-1) -1)
,而cpp-1
指向的是cp[1]
位置,解引用的到时c+2
然后c+2又减1.所以得到c+1,又因为c+1指向的是c[1]位置,所以*(c+1)
便得到N的地址,而N又加一,所以得到E的地址,所以打印EW
转载:https://blog.csdn.net/m0_51723227/article/details/116076948