总结一部分指针面试题,让我们一起看看指针的使用细节。
运行环境VS2013 32位下测试
1.
源代码:
#include<stdio.h>
#include<windows.h>
int main(){
int a[5] = {
1, 2, 3, 4, 5 };
int *ptr = (int *)(&a + 1);
printf("%d,%d", *(a + 1), *(ptr - 1));
system("pause");
return 0;
}
运行结果:
讲解:
首先我们回顾下数组名的概念:
数组名是数组首元素的地址。(有两个例外)
sizeof(数组名),计算整个数组的大小,sizeof内部单独放一个数组名,数组名表示整个数 组。
&数组名,取出的是数组的地址。&数组名,数组名表示整个数组。
(1)*(a + 1) :数组名表示数组的首元素地址,+1表示数组的第二个元素即地址,再对其解引用得到数组的第二个元素2
等价于a【1】。
(2)int *ptr = (int *)(&a + 1); //&a表示整个数组,数组+1表示越过整个数组
如图:
*(ptr - 1) : (ptr-1)即指针向后移动四个字节,再对其解引用得到数组的第4个元素5
2.
#include<stdio.h>
#include<windows.h>
struct Test
{
int Num;
char *pcName;
short sDate;
char cha[2];
short sBa[4];
}*p;
//由于部分未学习结构体,这里我们告知结构体大小为20字节。
//假设p 的值为0x100000。 如下表表达式的值分别为多少?
int main()
{
p = (struct Test*)0x100000;
printf("%p\n", p + 0x1);
printf("%p\n", (unsigned long)p + 0x1);
printf("%p\n", (unsigned int*)p + 0x1);
system("pause");
return 0;
}
运行结果:
讲解:
注意:%p的意思为读取地址
(1)p + 0x1 :p表示整个结构体,0x1=1,即p+1,表示越过整个结构体,结构体大小为20个字节,转换为十六进制为0x00000014,p=0x001000000
p+0x1=0x00100014 以%p读取结果为00100014
(2)(unsigned long)p + 0x1 :unsigned long表示强转为整数
+1即加上数字1为0x00100001再以%p读取为00100001
(3)(unsigned int*)p + 0x1:unsigned int*表示强转为无符号整形
+1即跳过一个无符号整形大小为4,运行结果为00100004
3.
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<windows.h>
int main()
{
int a[4] = {
1, 2, 3, 4 };
int *ptr1 = (int *)(&a + 1);
int *ptr2 = (int *)((int)a + 1);
printf("%x,%x", ptr1[-1], *ptr2);
system("pause");
return 0;
}
运行结果:
讲解:
(1)ptr1[-1]:容易理解,&a+1相当于跨过整个数组,-1回到第4个元素的首地址,再以十六进制数打印结果为4
(2)重点来看看*ptr2
int *ptr2 = (int *)((int)a + 1);
我们先了解下数组a在内存中是如何存储的:扫描二维码关注公众号,回复: 13302921 查看本文章![]()
(int *)((int)a + 1)即表示越过一个字节
再以%x读取即向后读取四个字节结果为20 00 00
(涉及大小端问题)
4.
#include<stdio.h>
#include<windows.h>
int main()
{
int a[3][2] = {
(0, 1), (2, 3), (4, 5) };
int *p;
p = a[0];
printf("%d\n", p[0]);
system("pause");
return 0;
}
运行结果:
讲解:
有一个坑:{}和()的不同
这里用的()里面是逗号表达式,逗号表达式的结果为最后一个值,故首元素为1 数组实际的初始化内容为{1,3,5,0,0,0}
我们将()改为{}
数组实际存储的内容是{0,1,2,3,4,5}可以看到首元素的值变为了0。
5.
#include<stdio.h>
#include<windows.h>
int main()
{
int a[5][5];
int(*p)[4];
p = (int(*)[4])a;
printf("%p,%d\n", &p[4][2] - &a[4][2], &p[4][2] - &a[4][2]);
system("pause");
return 0;
}
运行结果:
讲解:
为了便于理解 我们用画图的方式
可以看到a【4】【2】的位置我们很好找如图 红色 所示
但由于p是一个数组指针,p【4】相当于p+4且每次跳过4个整形,+4即跳过16个整形,得到了一个指向4个int型的数组指针,p【4】【2】即指针向后偏移两个int类型如图 绿色 所示
接下来就好办了 指针相减得到是指针之间的元素个数
以"%d打印得到-4
以"%p打印得到-4的补码FFFFFFFC
6.
#include<stdio.h>
#include<windows.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));
system("pause");
return 0;
}
运行结果:
讲解:
概念同一维数组
(1)(&aa + 1)表示跨过整个数组,(ptr1 - 1)的输出结果为10。
(2)(*(aa + 1))表示得到第二个元素的首地址,对于二维数组来说第二个元素即aa【1】【0】, *(ptr2 - 1)得到的是a【0】【4】即5。
7.
#include<windows.h>
#include <stdio.h>
int main()
{
char *a[] = {
"work", "at", "alibaba" };
char**pa = a;
pa++;
printf("%s\n", *pa);
system("pause");
return 0;
}
运行结果:
讲解:
a是一个指针数组,里面存放了三个char※的指针分别指向三个字符串的首地址。 char**pa = a;
定义了一个二级指针pa,将a的首地址放进去,即pa指向上图第一个char*的首地址pa++得到第二个char※的首地址。
再对其解引用得到其里面的内容即字符串“at”中a的地址 再以%s的方式读取得到at
8.
#include<stdio.h>
#include<windows.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);
system("pause");
return 0;
}
** 有没有很懵,不要着急,做会这道题,你会对指针有一个更全面的认识**
讲解:
运行结果:
关于指针的学习到这里就结束了,感觉如何?如果对于指针的指针还有不懂,希望浏览下发博文关于指针的详解~~~大家一起学指针呀!