C语言面试题详解(第11节)
发表于: 2019-03-03 19:35:30 | 已被阅读: 24 | 分类于: C语言
经过这几节文章的介绍,相信读者应该发现了:虽然指针是C语言中相对比较难的语法,但它也是C语言非常吸引人的点。指针的用途太多,以至于从初学者的角度来看,甚至显得有些“难以捉摸”了。
用途广泛的指针
请看下面这个C语言程序:
#include <stdio.h>
char* strA()
{
char str[] = "hello world";
return str;
}
int main()
{
printf("%s\n", strA());
return 0;
}
乍一看,答案似乎很明显,strA() 函数返回的是经典的“hello world”,那这段C程序也就会输出此了?然而事与愿违,我们实际编译执行这段C语言代码,发现:
# gcc t1.c
# ./a.out
#
输出为空!不过这一切都在预计中,在第 5 节我们讨论过类似的问题:str 只是 strA() 函数的局部变量,在 strA() 函数执行完毕的时候,它就被释放了。因此在 main() 中调用 strA() 函数,得到的返回值肯定是未定义的,因此最终输出什么也不会觉得奇怪。现在我们把 strA() 函数做如下修改:
char* strA()
{
char* str = "hello world";
return str;
}
再编译修改后的C语言代码并执行,结果如下:
# gcc t1.c
# ./a.out
hello world
#
输出正常了,怎么回事?str 仍然是局部变量啊,难道就因为它是指针,所以就没有被释放?指针还有这个特性?当然不是。还记得第5节文章中的这张图吗?
上面提到的其实是指针比较简单的用法,第9节还介绍了C语言中复杂指针的分析方法,相信看了文章的朋友应该明白如何定义各种复杂的指针数据类型。
再来道面试题练练手
为了加深对C语言中较复杂指针的理解,请读者再来看看下面这道题,C语言代码如下:
#include <stdio.h>
int main()
{
int a[] = {1, 2, 3, 4, 5};
int *ptr = (int*)(&a + 1);
printf("%d %d\n", *(a+1), *(ptr-1));
return 0;
}
# gcc t.c
# ./a.out
2 5
分析
答案是2和5,为什么呢?第一个数字好分析,*(a+1) 是指针的简单用法,等于 2 没什么好说的。这道题的难点就在于 int 型指针 ptr 的指向,它指向的 (&a + 1) 表示什么含义呢?
要想知道 &a + 1 的含义,分析出 &a 的含义就可以了,“+1”无非就是指针的移动,第 10 节已讨论的比较清楚。
a 是一个 int 型数组,它本身就相当于是一个“只读类型”的指针,再在 a 前加上一个取地址符 &,显然就成了双指针,那这个
int a[] = {1, 2, 3, 4, 5};
// 就相当于
int a[5] = {1, 2, 3, 4, 5};
在C语言中声明一个 int 型变量非常简单:
int v;
现在对变量 v 取地址,int* pv = &v,显然 pv 就是 int 型指针。相应的,对变量 a 取地址,(int [5])* ptr = &a,显然 ptr 就是 int [5] 类型的指针。C语言当然没有 int [5] 类型,int [5] 只是方便我们理解 &a 含义的。
事实上,第 9 节最后对 int [5] “类型”已经解释的比较详细,感到陌生的朋友可以再翻回去看一看。现在将 ptr 严格对应到 C语言语法,其实就相当于:
int (*ptr)[5];
到这里就一切清楚了,指针 ptr 其实就是一个数组指针。上面的面试题给出的C语言代码是:
int *ptr = (int*)(&a + 1);
其实就是多了指针的移动和强制转换的步骤而已,&a 的含义仍然相当于是一个
小结
可以看出,只要不把C语言指针特殊化对待,而是把它与 int 等普通数据类型对比来看,一切就很简单了。在分析复杂指针时,可以尝试使用本系列文章原创的“遮挡分析法”,相信会有所帮助的。