发布时间:2022-11-20 13:30
在此,我们将讲述C语言的精髓——指针的八道经典题目,这里我们主要以画图的方式解答。
提前说明:二维数组我们可以想象成由几个简单的一维数组组成。
题目一:
-
int main()
-
{
-
int a[
5] = {
1,
2,
3,
4,
5 };
-
int *ptr = (
int *)(&a +
1);
-
printf(
"%d,%d", *(a +
1), *(ptr -
1));
-
//
2,
5
-
system(
"pause");
-
return
0;
-
}
题目分析:数组名a表示数组首元素的地址,(&a+1)表示将整个数组的地址加一,(int*)(&a+1)表示将(&a+1)强制类型转换为int*类型。
所以本题的结果为:
题目二:
-
struct Test
-
{
-
int mun;
-
char *pcNmae;
-
short sDate;
-
char cha[
2];
-
short sBba[
4];
-
}*p;
-
//假设p的值为0x100000
-
int main()
-
{
-
p = (struct Test *)
0x100000;
-
printf(
"%p\n", p +
0x1);
-
printf(
"%p\n", (
unsigned
long)p +
0x1);
-
printf(
"%p\n", (
unsigned
int *)p +
0x1);
-
printf(
"%p\n", (
unsigned
char*)p +
0x1);
-
printf(
"%p\n", (
unsigned
char**)p +
0x1);
-
system(
"pause");
-
return
0;
-
}
题目分析:首先结构体为20个字节;
-
printf(
"%p\n", p +
0x1);
//0x00 10 00 14
-
//p是(struct Test *)类型,是指针,该结构体大小为20个字节
-
//p:0x00 10 00 00
-
//0x1:在十进制中为1
-
//p+0x1=p+1=p+20个字节=0x00 10 00 14
-
printf(
"%p\n", (
unsigned
long)p +
0x1);
//0x00 10 00 01
-
//p是(unsigned long)类型
-
//p:0x00 10 00 00
-
//0x1:0x00 00 00 01
-
//p+0x1=0x00 10 00 01
-
printf(
"%p\n", (
unsigned
int *)p +
0x1);
//0x00 10 00 04
-
//p是(unsigned int *)类型,是指针,32位平台下是4个字节,指向int类型的数据
-
//p:0x00 10 00 00
-
//0x1:在十进制中为1
-
//p+0x1=p+1=p+4个字节=0x00 10 00 04
-
printf(
"%p\n", (
unsigned
char*)p +
0x1);
//0x00 10 00 01
-
//p是(unsigned char*)类型,是指针,32位平台下是4个字节,指向char类型的数据
-
//p:0x00 10 00 00
-
//0x1:在十进制中为1
-
//p+0x1=p+1=p+1个字节=0x00 10 00 01
-
printf(
"%p\n", (
unsigned
char**)p +
0x1);
//0x00 10 00 04
-
//p是(unsigned char**)类型,是指针,32位平台下是4个字节,指向char*类型的数据
-
//p:0x00 10 00 00
-
//0x1:在十进制中为1
-
//p+0x1=p+1=p+4个字节=0x00 10 00 04
所以,题目的运行结果为:
题目三:
-
int main()
-
{
-
int a[
4] = {
1,
2,
3,
4 };
-
int *ptr1 = (
int *)(&a +
1);
-
int *ptr2 = (
int *)((
int)a +
1);
-
printf(
"%x %x\n", ptr1[-
1], *ptr2);
-
system(
"pause");
-
return
0;
题目分析:首先我们先介绍一下什么是大小端存储,大端:是指数据的高字节保存在内存的低地址中,而数据的低字节保存在内存的高地址中;小端:是指数据的高字节保存在内存的高地址中,而数据的低字节保存在内存的低地址中。而数组是在栈中存储的。题目中(int)a是把地址强制类型转换成数值,则+1加1个字节;(int *)((int)a+1)则把上一步转换得到数值强制类型转换成(int *)类型。小段存储,大端打印。
数组的存储方式:
题目中的指针指向:
数据的小端存储:
所以,题目的结果为:
题目四:
-
int main()
-
{
-
int a[
3][
2] = { (
0,
1 ), (
2,
3 ), (
4,
5 ) };
-
int *p;
-
p = a[
0];
-
printf(
"%d\n", p[
0]);
-
system(
"pause");
-
return
0;
-
}
题目分析:本题二维数组中蕴含着逗号表达式,在逗号表达式中,取每一个表达式中最后一个数据。
所以,指针P的指向以及数组a的存储我们可以想象成是这样的:
题目中的p[0]=*(p+0),所以本题的结果为:
题目五:
-
int main()
-
{
-
int a[
5][
5];
-
int(*p)[
4];
-
p = (
int(*)[
4])a;
-
printf(
"%p,%d\n", &p[
4][
2] - &a[
4][
2],&p[
4][
2] - &a[
4][
2]);
-
-
system(
"pause");
-
return
0;
-
}
题目分析:在做本题之前首先需要搞清楚p[4][2]以及a[4][2]分别代表什么,题目中二维数组a为5行5列,而数组指针p指向一个为5行4列的二维数组,p[4][2]=*(*(p+4)+2),a[4][2]=*(*(a+4))。我们在开篇已经讲过,二维数组我们可以想象成由几个简单的一维数组组成,所以数组指针p指向的地址就是5行4列数组的第一行的地址,所以p+4加16个字节。如图所示:
由图可知:p[4][2]与a[4][2]之间差了4个字节
所以&p[4][2] - &a[4][2]以%d形式打印-4
而-4的二进制表示为:
原码:1000 0000 0000 0000 0000 0000 0000 0100
反码:1111 1111 1111 1111 1111 1111 1111 1011
在内存中是以补码存储:
补码:1111 1111 1111 1111 1111 1111 1111 1100
而地址是以16进制打印:
16进制:FF FF FF FC
所以&p[4][2] - &a[4][2]以%p形式打印FF FF FF FC
所以本题的结果为:
题目六:
-
int main()
-
{
-
int a[
2][
5] = {
1,
2,
3,
4,
5,
6,
7,
8,
9,
10 };
-
int *ptr1 = (
int *)(&a +
1);
-
int *ptr2 = (
int *)(*(a +
1));
-
printf(
"%d %d\n", *(ptr1 -
1), *(ptr2 -
1));
-
system(
"pause");
-
return
0;
-
}
题目分析:首先,需要明确&(a+1)表示整个数组+1,(int*)(*(a+1))表示将*(a+1)强制类型转换成为(int*)类型,其次在二维数组中数组名可以理解为第0行的地址,所以a+1表示第一行的地址。画图解题即可,如图所示:
所以,本题的结果为:
题目七:
-
int main()
-
{
-
char *a[] = {
"work",
"at",
"alibaba" };
-
char **pa = a;
-
pa++;
-
printf(
"%s\n", *pa);
-
system(
"pause");
-
return
0;
-
}
题目分析:本题难度不大,只需找到指针pa初始指向的位置以及指针变量pa++以后所指向的字符串的首地址即可,如图所示:
所以,本题的结果为:
题目八:
-
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);
-
system(
"pause");
-
return
0;
-
}
题目分析:在做题目之前,首先需要明确题目中出现的操作符的优先级以及结合性,题目中出现的操作符优先级以及结合性从高到低依次为下标引用[ ]、从左向右;前置++、从右向左;前置--、从右向左;解引用*、从右向左;加法+、从左向右。还要注意前置++与前置--运算后,cpp将会被改变,如果上一个语句中使用前置++或前置--运算后,下一条语句将使用改变后的cpp。
初始时指针数组c、cp以及指针cpp所指向的位置如图所示:
printf("%s\n", **++cpp):图示:
所以*(*++cpp)后得到POINT。
printf("%s\n", *--*++cpp+3):图示:
所以*--*++cpp运算后得到ENTER,+3得到ER。
printf("%s\n", *cpp[-2]+3):*cpp[-2]+3=*(*(p-2)+3)图示:
所以*cpp[-2]运算完后得到FIRST,+3得到ST。
printf("%s\n", cpp[-1][-1]+1):cpp[-1][-1]+1=*(*(cpp-1)-1)+1。图示:
所以cpp[-1][-1]运算完以后得到NEW,+1得到EW。
所以本题的运行结果为:
总结:这8道题考查了二维数组的访问、数组的存储、大端模式、小端模式、数组名与&数组名+1所代表的含义、操作符结束运算后变量是否发生改变以及指针的运算,画图更加便于理解数组与指针相结合的题目,希望大家可以加深理解。