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就是第二个元素的地址,*(a+1)==arr[1]==2
- ptr是一个整形指针,-1后退一个元素,就是arr[4]==5
知识点:数组名的理解,指针的加减运算
2.题目二
[简单]假设在32位环境下,结构体的大小为20字节,打印的是什么?
#include<stdio.h> struct Test { 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; }
答案:
分析:
- 0x100000一个数,(struct Test*)0x100000强制类型转换为指针变量,就成地址了
- p + 0x1,0x1就是1,地址加一加的就是指针所指向类型的大小(字节数),就是0x100000+24=0x100000+0x18=0x100018
- (unsigned long)p,将地址转为无符号的8个字节的整形,那么就是加数字1,结果就是0x100001
- (unsigned int*)p,将指针变量的类型转换为无符号的整形,那么加1就是加上无符号整形类型的字节数,就是0x100004
知识点:指针加减运算
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; }
答案:
分析:
- { (0, 1), (2, 3), (4, 5) },这里面为逗号表达式,结果为最后的元素,那么就是{1,3,5},剩余的为0
- a[0]是第一行数组名,那就是第一个元素的地址,为整形指针,那么p就是第一个元素的地址
- p[0]==*(p+0)==a[0][0]=第一行数组名[0]==1
知识点:二维数组名的理解,逗号表达式的使用
4.题目四
[简单]在32位环境下,程序打印的结果是什么?
#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; }
答案:
分析:
- p是数组指针,该指针指向的数组大小为4
- a是数组的地址,存的是首元素的地址,传给p,指针类型会发生改变
- &p[4][2]==&*(*(p+4)+2),数组指针p加4就跳过4个数组,解引用就是第5个数组的数组名
- 数组名表示首元素的地址,那就是第五个数组的第一个元素的地址,+2跳过两个元素,一共跳过4*4+2=18个元素,那就是第19个元素的地址
- &a[4][2]就是第4*5+3==23个元素的地址,因为数组元素的地址是连续的,那么第19个元素的地址-第23个元素的地址就是相差的元素个数,就是-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 1101
- 以地址形式打印就是0xFFFFFFFC,以整形打印就是-4
知识点:二维数组名的理解,不同类型数组指针的访问特性,指针减指针的运算
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是整个数组的地址,加一跳过整个数组,地址就是10后面的那个地址,然后从数组指针转为整形指针
- aa就是数组名,就是第一行数组的地址,第一行数组地址加就跳过一行数组,就是第二行数组地址,解引用就是第二行数组名,强转为整形指针,就是6的地址
- 那么*(ptr1 - 1)==10, *(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是一个字符指针数组,存的是这几个字符串的首地址
- aa是一个二级指针,存的是数组名a的地址,而数组名就是数组首元素的地址,aa就是"work"首字符的地址的地址
- 因为地址都是在数组里面的,数组的元素的地址是连续的,pa++就是第二个元素的地址,就是"at"首字符的地址
- 以字符串的形式打印,结果就是"at"
知识点:数组元素地址的连续性,二级指针
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).**++cpp
- 先加加在使用,那就是数组cp第二个元素的地址,解引用一次就是第二个元素,存的是字符串"POINT"的首地址,打印的就是POINT
(2).*-- * ++cpp + 3
- *-- * ++cpp + 3,先加加cpp,在上一轮cpp已经自加了,那此时的++cpp就是数组cp中的第三个元素的地址
- * ++cpp就是数组cp第三个元素的值,就是c+1,就是指针数组c的第二个元素的地址
- //7.-- * ++cpp 就是c+1自身减一,就是指针数组c的第一个元素的地址
- //8.*-- * ++cpp就是指针数组c的第一个元素,就是"ENTER"字符串的首字符地址
- //9. *-- * ++cpp + 3就是首字符的地址+3,那就是"ENTER"字符串的字第4个字符的地址,打印的就是"ER"
(3).*cpp[-2] + 3
- *cpp[-2] + 3==*(*(cpp-2))+3,经过上两轮cpp已经自加两次了,此时cpp是指针数组cp第三个元素的地址
- 那么cpp-2就是指针数组cp第一个元素的地址,*(cpp-2)就是指针数组cp第一个元素
- cp的第一个元素的值是c+3,就是指针数组c第4个元素的地址,那么*(*(cpp-2))就是指针数组c中的第四个元素
- 指针数组c的第四个元素的值是"FIRST"首字符的地址,+3就是"FIRST"中第四个字符的地址,最后打印的就是"ST"
(4).cpp[-1][-1]+1
- cpp[-1][-1]==*(*(cpp-1)-1),cpp-1就是字符数组cp第二个元素的地址,解引用就是cp的第二个元素
- cp的第二个元素的值就是c+2,就是指针数组c的第三个元素的地址
- *(cpp-1)-1就是指针数组c的第二个元素的地址
- 再解引用*(cpp-1)-1就是指针数组c的第二个元素,就是"NEW"首字符的地址
- 首字符+1就是"NEW"中的'E'的地址,打印结果就是"EW"
知识点:指针数组,数组名的理解,二级指针,三级指针,指针加减
本章内容结束了,下章见,拜拜!!!