指针重难点:
难点一:以下程序的执行结果是?
int a[5] = { 1, 2, 3, 4, 5 };
int *ptr = (int *)(&a + 1); //&a 得到的是int(*)[5]型
//&a + 1 得到的是跳过a[5]这个数组的下一个元素
//(int *) 强制转换为int *,也就是a[5]数组后的下一个元素地址
printf("%d,%d", *(a + 1), *(ptr - 1));
//*(a + 1) ==> a[1] ==> 2
//ptr 为int* 型,自然而然的ptr-1 为a[4]的地址
//* 解引用得到a[4] ===> 5
所以打印的结果是 2 , 5
难点二:以下程序的执行结果是?
struct Test{
int Num;
char *pcName;
shortsDate;
char cha[2];
shortsBa[4];
}*p;
//假设p 的值为0x100000。 如下表表达式的值分别为多少?
//p + 0x1 = 0x___ ?
//(unsigned long)p + 0x1 = 0x___ ?
//(unsigned int*)p + 0x1 = 0x___ ?
上面代码中的结构体可以换种写法:
struct Test{
int Num;
char *pcName;
shortsDate;
char cha[2];
shortsBa[4];
};
struct Test *p = 0x100000;
//假设p 的值为0x100000。 如下表表达式的值分别为多少?
printf("%p\n", p + 0x1);
//p + 0x1 = 0x___ ? //这个结构体占用20个字节, p指向的是struct Test类型的结构体,+1操作执行的是跳过整个结构体的下一个元素的首地址; 故: 0x100000 + 0x14(20的16进制表示) => 0x100014
下面的强制类型转换, 导致 p 就变成了一个 unsigned long, 再去 + 1 , 就只是单纯的整数 + 1
printf("%p\n", (unsigned long)p + 0x1);//(unsigned long)p + 0x1 = 0x___ ? 0x100000 + 0x1 => 0x100001
//下面的强制类型转换中,导致 P 转换为unsigned int(*) 类型的指针, 再 + 1 ,得到的是跳过四个字节后的指针 0x100000 + 0x1 => 0x100004
printf("%p\n", (unsigned int*)p + 0x1);//(unsigned int*)p + 0x1 = 0x___ ?
在这种 + 1 操作中,首先要注意的就是 + 1 前它的类型,然后再去执行 + 1 操作
难点三:以下程序的执行结果是?
int main(){
int a[4] = { 1, 2, 3, 4 };
int *ptr1 = (int *)(&a + 1); //&a得到的是int(*)[4]类型的指针, + 1 跳过整个数组,强制转换后得到的是 int(*)指针;
int *ptr2 = (int *)((int)a + 1); //a是数组名,(int)强制转换后得到的是整型, + 1就是加一个字节,强制转换得到的是int(*)指针
printf( "%x,%x", ptr1[-1], *ptr2); // 4 和 02000000
return 0;
}
这里我们要借用画图板来假设内存中的分布:
(int) a + 1得到的是0x101 ===> ptr2 ,因为 ptr2 是int * 型,所以对其解引用得到的是四个字节的int型,往后数四个字节即可.
难点四:
#include <stdio.h>
int main(int argc, char * argv[])
{
int a[3][2] = { (0, 1), (2, 3), (4, 5) }; //局部初始化,一定要注意这个地方使用的是逗号运算符,逗号运算符的作用就是:执行完代码后返回的是最后一个值;
int *p;
p = a[0]; //a[0]是int[2]型,
printf( "%d", p[0]); //p[0] ==> a[0][0] ==> 1
}
难点五:
int main()
{
int a[5][5]; //二维数组
int(*p)[4]; //数组指针,长度为四个元素的数组指针
p = a;
printf( "a_ptr=%#p,p_ptr=%#p\n" , &a[4][2], &p[4][2]);
printf( "%p,%d\n", &p[4][2] - &a[4][2], &p[4][2] - &a[4][2]);
return 0;
}
同样借助画图板来表示一下内存分布:
一目了然,我们得到的是: 错位的读取:
&p[4][2] - &a[4][2] 指针相减得到的是 -4 的元素空间大小, 最后打印的也是 16进制下的 -4.和十进制下的-4;
难点六
int main()
{
int aa[2][5] = { {1, 2, 3, 4, 5}, {6, 7, 8, 9, 10} };
int *ptr1 = (int *)(&aa + 1); //&aa 得到的是int(*)[2][5], 加 1 得到的是跳过整个数组元素的下一个地址
int *ptr2 = (int *)(*(aa + 1)); // aa + 1得到是aa[1], 它是int (*)[5]型,强制转换为int* 得到的是aa[1][0]的地址
printf( "%d,%d", *(ptr1 - 1), *(ptr2 - 1)); //打印的是 10 5
return 0;
}
难点七
char *a[] = { "work","at","alibaba" };//定义字符串指针,需要注意的是字符串常量的存储位置在常量区,而非栈区
char**p = a; // a是char*[3]型
p++;
printf("%s\n", *p);
借助画图板看一下内存分布:
p + 1 ==> 0x10004
解引用得到的是: 0x200
%s打印找0x200为开始地址数,直到遇到下一个\0结束.
难点八:难点中的难点
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);
// cpp[-2] => *(cpp-2) 这个操作没有修改 cpp 的内容. 而上面的 ++ 操作修改 cpp
printf("%s\n", *cpp[-2] + 3);
//printf("%s\n", cpp[-1][-1] + 1);
同样先从画图板中的假设内存分布说起: