Exercise 1
求下列代码的运行结果
#include <stdio.h>
int main()
{
int a[5] = { 1, 2, 3, 4, 5 };
int* ptr = (int*)(&a + 1);
printf("%d",*(ptr - 1));
return 0;
}
答案速查:
分析:
Exercise 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;
}
答案速查:
分析:
☑ printf("%p\n", p + 0x1);
出现单个p,代表结构体中首元素的地址,类比&数组名,+0x1跳过整个结构体
十进制20=0x14
即0x100000+0x14==0x100014,x86环境下输出结果为00100014
☑ printf("%p\n", (unsigned long)p + 0x1);
p被强制类型转换为unsigned long,p不再是struct Test*指针类型(不考虑+0x1跳过整个结构体)即0x100000+0x1=0x100001,x86环境下输出结果为00100011
☑ printf("%p\n", (unsigned int*)p + 0x1);
p被强制类型转换为unsigned long*指针类型,之前讲过,指针+1表示跳过4个字节,即
0x100000+4==0x100004,x86环境下输出结果为00100004
Exercise 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;
}
答案速查:
分析:
错误思路:
认为二维数组的元素排布是这样的:
在13.5.【C语言】二维数组里讲过:只有这样写int a[3][2] = { {0, 1}, {2, 3}, {4, 5} };(内部是大括号不是圆括号)才是上方的排布!
写成这样int a[3][2] = { (0, 1), (2, 3), (4, 5) };内含逗号表达式
在15.【C语言】初识操作符 下里讲过
exp1,exp2,exp3,……,expn
程序从左向右依次执行exp
整个exp的结果是最后一个exp的结果
所以变成int a[3][2] = { 1, 3, 5 };
画成图是这样的:
回看代码:p[0]即a[0][0],所以输出1
Exercise 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* ptr1 = (int*)(&aa + 1);
\
"&数组名"取的是整个数组的地址,+1跳过整个数组,在*(ptr1-1)又往回4个字节,解引用是10
*(aa+1)相当于aa[1],二维数组的一行就是一维数组,aa代表第一行的地址,+1转到第二行的6,输出*(ptr2-1)解引用是5
结果为10,5
★Exercise 5:指针-指针
求下列代码的运行结果
//假设环境是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;
}
答案速查:
分析:
分析上方代码前先回顾下数组和指针
#include <stdio.h> int main() { int arr1[5] = { 1,2,3,4,5 }; int* p1 = &arr1; int arr2[5]={ 0 }; int *p2[5] = &arr2; int arr3[5]={ 0 }; int (*p3)[5] = &arr3; int arr4[5]={ 0 }; int* (*p4)[5] = &arr4; return 0; }
上方代码运行是否有错误?写的是否规范呢?
逐条分析:
int arr1[5] = { 1,2,3,4,5 };定义了一个名为arr1的数组,其类型为int[5]
int* p1 = &arr1;&arr1的类型在int[5]的基础上加个*,即int[5]
这里int[5]与int[5]类型不匹配
因此编译器会报警告:
必须强制让*与p1结合,建议改成:
int (*p1)[5] = &arr1;//p1是int(*)[5],指向含五个整型元素的数组
int arr2[5] = { 1,2,3,4,5 };定义了一个名为arr2的数组,其类型为int[5]
int *p2[5] = &arr2;出现了严重的问题!!
报错:
编译器认为p2是数组其包含5个元素(p2[5]),数组的类型为int*
&arr2的类型为int(*)[5]
这里int*与int(*)[5]类型不匹配
必须强制让*与p2结合,建议改成:
int (*p2)[5] = &arr2;//p2是int(*)[5],指向含五个整型元素的数组
int arr3[5]={ 0 };与int (*p3)[5] = &arr3;写法无误,解释同上
int arr4[5] = { 0 };定义了一个名为arr4的数组,其类型为int[5]
int* (*p4)[5] = &arr4;但p4类型有问题
arr4类型为int[5]
&arr4类型为int(*)[5]
但p4类型为int* (*)[5] 意思是p4是指向含5个int*类型指针元素的数组的指针
所以int(*)[5]与int* (*)[5] 类型不匹配
因此编译器会报警告:
建议改成:
int* arr4[5] = { 0 }; int* (*p4)[5] = &arr4;
回到本练习:
a的类型为int[5][5],p的类型为int(*)[4],两者类型不一样,p=a;会发生类型的转换
因此会报警告
二维数组在内存中的排布(图中一个格子代表一个元素)可以按两种形式理解:p形式和a形式
对于p[4][2]:由于p是int(*)[4]类型,p+1代表跳过二维数组(这里p形式的二维数组是每4个元素一行因为int(*)[4])的第一行至第二行,因此p[4][2]即第4行(从第0行开始算)中的第2个元素
对于a[4][2]:由于a是int[5][5]类型,因此是第4行(从第0行开始算)中的第2个元素(这里a形式的二维数组是每5个元素一行)
指针-指针是两个指针之间的元素个数,%p以补码形式打印,%d以原码形式打印
&p[4][2] - &a[4][2]==小-大==负数,所以为-4(原码)-->FFFFFFFC(补码)
结果:FFFFFFFC,-4