#include
int main()
{
int a[5] = { 1, 2, 3, 4, 5 };
int *ptr = (int *)(&a + 1);
printf( "%d,%d", *(a + 1), *(ptr - 1));
return 0;
}
数组名的理解
- sizeof(数组名) 计算的是整个数组的大小
- &数组名 取出的是整个数组的地址
- 除上面两种情况,数组名都指数组的首元素地址
有了对数组名的理解,这题就好做了
&a+1 取出数组的地址然后+1,指跳过这个数组,指向数组之后的空间
数组地址应存放在数组指针中,int (*p)[ 5 ]中,强制类型转换成 int *类型存放在int *类型的指针中
无论什么类型的指针都可以存放任意类型的地址
*(a + 1) a是首元素的地址,+1指向第二个元素,解引用得到第二个元素 2
*(ptr - 1) ptr存放的是数组+1之后的地址,指向数组之后,-1之后指向数组中最后一个元素 5
//在X86环境下
//假设结构体的⼤⼩是20个字节
//程序输出的结构是啥?
#include
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;
}
printf("%p\n", p + 0x1);
%p打印的是地址,0x1表示16进制,也就是1,指针加整数跳过多少字节的空间取决于指针的类型,在上述代码中p是结构体指针,同时题目给的结构体为20个字节,所以p + 0x1 跳过了20个字节,也就是 100014,%p打印的是十六进制的,所以是01000014
printf("%p\n", (unsigned long)p + 0x1);
在上述代码中将p的类型从结构体指针强制类型转换成了unsigned int类型,即无符号整型,p + 0x1也就变成了整数之间的加减,100001,由于是以十六进制的方式打印的,所以是00100001
printf("%p\n", (unsigned int*)p + 0x1);
在上述代码中将p的类型从结构体指针强制类型转换成了unsigned int*类型,是无符号整型指针,指针加减整数取决与类型的大小,unsigned int是4个字节的,所以p + 0x1 跳过了4个字节,也就是100004,由于打印的是十六进制的,所以是00100004
#include
int main()
{
int a[3][2] = { (0, 1), (2, 3), (4, 5) };
int* p;
p = a[0];
printf("%d", p[0]);
return 0;
}
代码运行结果如下:
在上述代码中,给二维数组进行初始化,对二维数组的初始化得用 { } 才是对数组指定位置的初始化,上述代码中使用的是 ( ),也就是逗号表达式,逗号表达式取最后一个表达式的值,所以对数组初始化只有1,3,5
然后定义了一个整型指针指向二维数组的首元素,然后跳过p[0]访问的也是首元素,所以打印的是 1
#include
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;
}
代码运行结果如下:
指针数组(*p)[4]是指向数组中4个int类型的元素,数组a[5][5]的地址要存到int (*ptr) [5] 这样的数组指针中去,
两者存在着类型差异,但是不影响,a数组每四个元素存到p中去,二维数组是连续存放的,示意图如下:
&p[4][2] - &a[4][2] 也就是指针 - 指针 的绝对值计算的是两个地址之间的元素个数
所以得到 -4
%p打印的是地址,地址在内存中是补码的形式 十六进制打印
所以我们要将-4转化为二级制的补码
-4的原码1000 0000 0000 0000 0000 0000 0000 0100
-4的反码1111 1111 1111 1111 1111 1111 1011
-4的反码1111 1111 1111 1111 1111 1111 1100
每四个二级制位转换为
FF FF FF FC
%d打印的整型,也就是 -4
![#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;
}
代码运行结果如下:
(int*)(&aa + 1) 是取出数组的地址然后+1,跳过整个数组,指向数组之后的空间,然后将其强制类型转换为int类型存放到ptr1中
(int)(*(aa + 1))是数组首元素地址+1,二维数组的首元素是a[0],也就是第一行的元素,得到aa[1][0]的地址,然后解引用再强制类型转换,存放到ptr2中
所以ptr1指向的是数组之后的空间,ptr2指向的是数组aa[1][0]
与题目一致
*(ptr1-1) ptr1 - 1 指向的是10这个元素的地址,然后解引用得到10
*(ptr2-1) ptr2 - 1 指向的是5这个元素的地址,然后解引用得到5
#include
int main()
{
char* a[] = { "work","at","alibaba" };
char** pa = a;
pa++;
printf("%s\n", *pa);
return 0;
}
代码运行结果如下:
a[]是个指针数组,存放的是三个字符串的地址,pa指向a的首元素地址,示意图如下:
pa++,指针偏移一个字节,得到第二个元素的地址,也就是 at
所以打印的是 at
#include
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;
}
printf("%s\n", **++cpp);
一开始cpp是指向cp的首元素的,当使用++之后cpp指向了cp的第二个元素,也就是c+2的地址
然后第一次解引用拿到了c+2,
再解引用得到c的第三个元素,所以会打印POINT
现在cpp指向cp的第二个元素
printf("%s\n", *--*++cpp+3);
由于加法的优先级最低,使用+3最后计算
++cpp,cpp从指向cp的第二个元素变成了指向第三个元素
解引用拿到了cp第三个元素c+1
然后再–c+1得到c这个地址
解引用就得到c 也就是c的首元素ENTER
最后+3 从第四个字符开始打印
所以会打印ER
现在cpp指向cp的第三个元素
printf("%s\n", *cpp[-2]+3);
cpp[-2] 等价于 *( *(cpp-2))+3
cpp-2,cpp从指向第三个元素变成了指向第一个元素
第一次解引用得到 c+3
第二次解引用得到了c+3指向的元素 也就是FIRST
最后再+3 从这个元素的第四个字符开始打印
所以会打印ST
现在cpp还是指向第三个元素
printf("%s\n", cpp[-1][-1]+1);
cpp[-1][-1]+1 等价于 *( *(cpp-1)-1)+1
cpp-1 cpp从指向cp第三个元素变成指向了第二个元素
然后解引用得到c+2
然后再减去1 c+2-1
然后解引用得到了c+1 也就是NEW
最后再+1 从第二个字符开始打印
所以会打印EW