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.题目2
在X86(32位)环境下,假设结构体的大小是20个字节,程序输出的结构是啥?
struct Test
{
int Num;
char* pcName;
short sDate;
char cha[2];
short sBa[4];
}*p = (struct Test*)0x100000;
int main()
{
printf("%p\n", p + 0x1);
printf("%p\n", (unsigned long)p + 0x1);
printf("%p\n", (unsigned int*)p + 0x1);
return 0;
}
解析:
考察的是“指针+-整数”的问题
+0x1就是结构体指针+1,要跳过一个结构体,结构体的大小是20个字节,所以就是+20字节 —— 如果是10进制则是:0x100020,但是现在是16进制,所以是0x100014
p 强制转换为 unsigned long,不是指针类型了,整型值+1,就是+1,所以是0x100001
unsigned int* 强制转换为,无符号整型的指针,+1是跳过一个整型的4个字节,所以是0x100004
运行结果:
3.题目3
#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;
}
解析:
调试一下,观察一下a中的内容:
不是:
0 | 1 |
2 | 3 |
4 | 5 |
的原因是因为:
int a[3][2] = { (0, 1), (2, 3), (4, 5) };
不是:
int a[3][2] = { {0, 1}, {2, 3}, {4, 5} };
所以,按照逗号表达式计算括号中保留了:1,3,5. 又因为是3行两列的,所以用 0 补位。
a[0] 是第一行的数组名,数组名表示首元素的地址,其实就是&a[0][0]
p[0] —— *(p + 0)——*p —— 1
运行结果:
4.题目4
假设环境是x86环境,程序输出的结果是啥?
#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;
}
解析:
考察的是指针 - 指针,其结果的绝对值是指针和指针之间的元素个数。
a 是二维数组
p 是数组指针,p 指向的数组是 4 个整型元素的
分析如下:
%d 是打印有符号的整数
%p 是打印地址的
-
数组和指针的类型差异:
a
是一个int[5][5]
类型的二维数组。p
是一个指向int[4]
的指针,即每次移动会跳过 4 个int
。
-
指针运算规则:
- 指针相减的结果是两个地址之间的元素个数,而非字节数。
&p[4][2]
相当于p + 4*4 + 2 = p + 18
(跳过 18 个int
)。&a[4][2]
相当于a + 4*5 + 2 = a + 22
(跳过 22 个int
)。
-
地址计算:
- 假设
a
的起始地址为0x0
,则:&p[4][2]
的地址为0x0 + 18*4 = 0x48
(每个int
占 4 字节)。&a[4][2]
的地址为0x0 + 22*4 = 0x58
。
- 地址差值为
0x48 - 0x58 = -0x10
(即 - 16 字节),但指针相减结果为-16 / 4 = -4
。
- 假设
运行结果:
5.题目5
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;
}
解析:
1 | 2 | 3 | 4 | 5 |
6 | 7 | 8 | 9 | 10 |
*(aa+1)—— aa[1]
aa[1] 是第二行的数组名
数组名表示首元素地址
aa[1] 也是 &aa[1][0]
&aa[1] 第二行的地址
sizeof(aa[1])计算的是第二行的大小
运行结果:
6.题目6
#include <stdio.h>
int main()
{
char *a[] = {"work","at","alibaba"};
char**pa = a;
pa++;
printf("%s\n", *pa);
return 0;
}
解析:
a 是指针数组
a 是 char* 型的
pa 是 char** 型的
*pa 就是解引用是 at
运行结果:
7.题目7
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;
}
解析:
初始状态:
-
c
是一个字符指针数组,包含 4 个字符串常量的地址。 -
cp
是一个二级指针数组,初始化为{c+3, c+2, c+1, c}
。 -
cpp
是一个三级指针,初始指向cp
的起始位置。
第一次 printf
:**++cpp
-
++cpp
使cpp
指向cp[1]
(即c+2
)。 -
*cpp
解引用得到c+2
。 -
**cpp
进一步解引用得到c[2]
,即字符串"POINT"
。
输出:POINT
第二次 printf
:*--*++cpp+3
-
++cpp
使cpp
指向cp[2]
(即c+1
)。 -
*cpp
得到c+1
。 -
--*cpp
将c+1
减 1,变为c
(即cp[3]
的原始值)。 -
*--*cpp
解引用得到c[0]
(字符串"ENTER"
)。 -
+3
跳过前 3 个字符,指向"ER"
。
输出:ER
第三次 printf
:*cpp[-2]+3
-
cpp[-2]
等价于*(cpp-2)
,指向cp[0]
(即c+3
)。 -
*cpp[-2]
解引用得到c[3]
(字符串"FIRST"
)。 -
+3
跳过前 3 个字符,指向"ST"
。
输出:ST
第四次 printf
:cpp[-1][-1]+1
-
cpp[-1]
指向cp[1]
(即c+2
)。 -
cpp[-1][-1]
等价于*((c+2)-1)
,即c[1]
(字符串"NEW"
)。 -
+1
跳过首字符,指向"EW"
。
输出:EW
关键注意点:
-
指针运算直接修改了
cpp
和cp
中的值(如--*cpp
)。 -
下标访问(如
cpp[-2]
)基于当前cpp
的位置计算。 -
字符串偏移(如
+3
)从字符串起始位置向后跳过指定字符数。
运行结果: