知识点复习
做题之前复习一个知识点——数组名的理解
数组名代表该数组首元素的地址,但是有两个例外:
1> &数组名,数组名代表整个数组的地址;
2> sizeof(数组名),数组名代表整个数组地地址;
开始练习吧!
题目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 ;}
分析 :
a表示数组首元素的地址,a+1表示跳过一个整形的大小,*(a+1)表示指向这个地址的值,也就是2
&a 表示一个数组的地址,&a+1表示跳过一个数组的大小,然后(&a+1)强制转化成int*类型,让
ptr接收,那么ptr-1表示向后跳过一个整形的大小,*(ptr-1)表示指向这个地址的值,也就是5.
图解:
代码运行结果:
题目2
//在X86环境下
//假设结构体的⼤⼩是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;
}
分析:
我们知道结构体大小是20个字节(题上说的),定义一个结构体指针 p ,p里面放的是0x100000
因为p是结构体指针,并且结构体大小是20个字节,所以p+0x1表示跳过一个结构体的大小,也就是跳过20个字节,然后%p打印地址;
(unsigned long)p表示p是一个无符号长整形,p+1就是简单的+1,但这里用的%p打印,会出现警告,但这不影响,我们主要是理解指针加减的运算,先不用管它;
(unsigned int*)p表示将一个结构体指针转化成无符号整形指针,p+0x1表示跳过一个无符号整形的大小,也就是4个字节,然后%p打印地址;
图解:
代码运行结果:
题目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 ,注意给数组赋值是用的逗号表达式,(逗号表达式取最后一个逗号后面的值),
所以数组里面的值是 1,3,5,0,0,0
p 指向 a[0] ,也就是二维数组的首元素,p[0]表示*(p+0),这里的 p 表示一维数组的数组名,也就是一维数组首元素的地址,也就是1的地址,*(p+0)其实就是1。
图解:
代码运行结果:
题目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 ;}
分析:
int(*p)[4] 是一个数组指针,指向的数组有4个int类型的元素,将a赋值给p,但是类型有一些不一样,会出现警告,但我们就是要赋值给p,别问,问就是这么强横(还不是为了刁难你,该死的出题人),a表示二维数组的首元素的地址,也就是a[0],
p[4]表示*(p+4),这里的p表示指向一个有4个整形的数组的地址,p[4][2]表示*(p[4]+2),这里的p[4]
表示一个有4个整形的数组的数组名,也就是数组的首地址,p[4]+2表示跳过2个整形的大小,
&p[4][2] - &a[4][2]这个我们看图就很好理解了,指针相减的绝对值表示相差元素的个数。
图解:
代码运行结果:
题目5
#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;
}
分析:
&aa指的是整个二维数组的地址,&aa+1指的是跳过一个二维数组的大小,
&aa+1转化成int*类型,让ptr1接收,ptr1-1指的是向后跳过一个整型指针的大小*(ptr1-1)就是10
aa表示二维数组的首元素的地址也就是aa[0],aa+1指的是跳过一个一维数组的大小,然后解引用相当于aa[1]
将aa[1]转化成(int*)类型,让ptr2接收,prt2-1指的是向后跳过一个整形的大小,最后解引用*(ptr2-1),就是5
图解:
代码运行结果:
题目6
#include <stdio.h>
int main()
{
char* a[] = { "work","at","alibaba" };
char** pa = a;
pa++;
printf("%s\n", *pa);
return 0;
}
分析:
a是一个char类型的指针数组,放有三段字符串,
char* *pa=a;
pa 是一个指向char* 类型的指针,a相当于数组的首元素的地址
pa++;相当于跳过一个字符串,//类似于二维数组
此时pa指向第二个字符串的首地址,对 pa 解引用,
相当于得到的是 ’a‘ ,用 %s 打印字符串,遇到 \0 停止
图解:
代码运行结果:
题目7(较难)
#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;
}
分析1:
printf("%s\n", **++cpp);
++cpp,相当于 cpp 跳过一个 char** ,指向下标为1,也就是c+2的地址,
*(c+2) 解引用后表示 c+2指向的下标为2的char*类型的地址,
**(c+2) 解引用相当于 P , %s 打印字符串
结果就是:POINT
看图很容易理解,一次指针加法运算,两次解引用
图解1:
分析2:
printf("%s\n", *-- * ++cpp + 3);
在上一条语句运行的前提下,++cpp再跳过一个char**,指向下标为2,也就是c+1的地址,
*(++cpp)解引用后表示c+1,然后 --(c+1) , c+1 指向下标为1的char* 类型的地址,--(c+1)相当于
指向下标为0的char* 类型的地址,*(--(c+1)) 解引用后相当于E,最后在加3,跳过3个整形大小,用%s打印,
结果是:ER
比较绕,可以结合图解多看几遍,更容易理解。
图解2:
分析3:
printf("%s\n", *cpp[-2] + 3);
在前两条语句运行的前提下,cpp此时指向下标为2的地址
cpp[-2] 等价于*(cpp-2),解引用后相当于 c+3 , c+3 指向下标为3的char*类型的地址,
也就是F的地址,最后加3,跳过3个整形大小,%s打印,
结果是:ST
图解3:
分析4:
printf("%s\n", cpp[-1][-1] + 1);
在前三条语句运行的前提下,cpp此时依然指向下标为2的地址,因为在上一条printf语句中
cpp没有做运算
cpp[-1]等价于*(cpp-1),解引用后相当于 c+2, c+2 指向下标为2的char* 类型的地址,
c+2[-1] 等价于*((c+2)-1), 解引用相当于指向下标为1的元素,也就是N,
最后加1,跳过1个整形大小,%s打印,
结果是:EW
图解4:
代码运行结果:
制作不易,希望给你带来帮助!