C语言面试题详解(第9节)

基本上,每个C语言工作者都认可指针非常重要。事实也的确如此,正是因为指针的存在,C语言才能如此灵活,才能在计算机编程语言几十年的发展中,始终占有一席之地。

C语言的设计宗旨是:相信每个C语言使用者都是高手,它给予程序员最大的自由,基本不会太限制程序员的发挥,再加上指针语法,C语言能够灵活的操作内存,是追求极致效率的不二之选。

然而,灵活也意味着容易出错,C语言也是一门对使用者基本功要求特别高的编程语言,否则就很容易开发出漏洞百出,或者隐患重重的程序。从某个程度上来说,C语言程序员与程序员之间的水平差异非常大,借助于指针,高手们能够轻易写出占用资源少,但是运行效率却非常高的程序。

而有些C语言初学者却认为指针是不可捉摸的,甚至有些仇视指针,觉得指针是一种古老的、不安全的设计,应该被摒弃。更有甚者,直接就放弃学习C语言了。但是,世界上那么多C语言程序员,他们都是聪明绝顶的吗?显然不是,他们都是和我们一样的普通人,只不过“闻道有先后”而已。其实C语言只是一种基础工具,指针并没有那么难,花点时间肯定能掌握。

先来看这个题目热热身

这个题目是我国台湾省某著名杀毒软件公司的一道面试题,请看C语言代码如下:

#include <stdio.h>

int main()
{
     int max(x, y);
     int *p = &max;
     int a, b, c, d;

     printf("Please input three integer\n");
     scanf("%d%d%d", a, b, c);
     d = (*p)((*p)(a, b), c);
     printf("Among %d, %d, and %d, the max integer is %d\n",
                 a, b, c, d);
     return 0;
}
 int max(int x, int y)
{
     return x>y?x:y;
}

上面这段C语言程序是有问题的,那么哪几行有问题呢?以及为什么呢?

分析

显然,这道题考的是C语言的语法,那么编译器就能协助我们定位错误,输入编译命令:

# gcc t1.c

得到如下提示信息,应该注意每一个警告和错误:

最明显的错误是第 5 行 max() 函数的声明,以及第 10 行 scanf() 函数的传参,这两点相信即使是C语言初学者也能轻易看出,就不赘述了,我们修正它:

...
int main()
{
     int max(int, int);
...
     scanf("%d%d%d", &a, &b, &c);
...

再次编译,得到如下提示信息:

编译失败,提示我们 p 不是函数,也不是函数指针。这是当然的,p 是一个 int 型的指针,但是上面的C语言代码将 p 当成是函数指针,并让其指向 max() 函数了。修改之:

int *p = &max;
// 修改为 
int (*p)(int, int) = &max;

修改很简单,无非就是将 p 改为函数指针。再次编译修改后的C语言代码,并执行之,发现成功了:

# gcc t1.c
# ./a.out 
Please input three integer
1 2 3
Among 1, 2, and 3, the max integer is 3

不要把指针“特殊化”

初学者看到“函数指针”这样的字眼,可能又有些烦了:以前知道有 int 指针,float 指针,后来知道还有数组指针,指针数组,指针函数,现在又来一个函数指针,指针到底有几种?这样想其实就把指针“特殊化”了,反而不利于理解指针。其实,只要把指针像 int、float 一样看作是C语言中的一种数据类型,似乎一切就很简单了。

int 指针是 int 类型的指针,它指向 int 型的整数,这很好理解。那相应的,数组指针是“数组类型”的指针,它指向数组;函数指针是“函数类型”的指针,它指向函数,仅此而已。int 数组是存放一系列 int 整数的数组,那指针数组就是存放一系列指针的数组;int 函数是返回 int 整数的函数,那指针就是返回指针的函数。可以看出,指针实在没有什么特殊的。

作为练习,再来看看下面这个面试题

这是美国某著名计算机嵌入式公司的一道面试题,请看C语言代码如下:

float(**def)[10];
double*(*gh)[10];
double(*f[10])();
int*((*b)[10]);
long (*fun)(int);
int (*(*f)(int, int))(int);

问:上面这段C语言声明语句代表了什么?

首先来看第 5 个,我们上面才说过,显然 fun 是一个函数指针,它指向只有一个 int 型参数,返回值为 long 的函数。

类似的,第 3 个也就好理解了,[] 优先级比 * 优先级高,因此 f 首先是一个有 10 个元素的数组。数组里存放什么类型的元素呢?我们先把 f[10] 遮住,只看 double ( * )(); ,显然,它是函数指针类型。总体来看:

double (*f[10])();

f 是一个数组,它有 10 个元素,每个元素都是一个函数指针,指向没有参数,返回值为 double 的函数

再来看第 6 个,先只看 (* f)(int, int),显然这是一个指向两个int参数的函数指针,那么它的返回值是什么呢?同样的,先遮住 (* f)(int, int),得到 int(* )(int),很明显,这也是一个函数指针,它指向有一个int型参数,返回值为 int 的函数。结合起来看:

int (*(*f)(int, int))(int);

f 是一个函数指针,它指向的函数有两个 int 型的参数,返回值也是一个函数指针(指向有一个int型参数,返回值为 int 的函数)。

接着再来看第 1 个,显然 def 是一个二级指针,什么类型的指针呢?我们继续使用“遮挡分析法”,显然 def 是一个 float [10] 型的指针,即一个 float 型的数组(数组有 10 个元素)。结合起来看,def 就是一个二级指针,它指向的是一个一维数组的指针,数组的元素都是 float。

现在再来看第 2 个就简单了,它也是一个数组指针,也指向有 10 个元素的数组,只不过该数组里存放的都是 double * (double 型指针)。第 4 个就更简单了,它就相当于 int* (* b)[10],和第 2 个是非常类似的。

小结

你看,只要不对指针搞“特殊化”,像 int 、float 一样,把它当作是一种数据类型,一切就简单了。应明白,C语言只是一门基础工具,它并不是智商拔尖的精英专用工具,别人都能学好它,聪明的我们也一定能!

阅读更多:   C语言
添加新评论

icon_redface.gificon_idea.gificon_cool.gif2016kuk.gificon_mrgreen.gif2016shuai.gif2016tp.gif2016db.gif2016ch.gificon_razz.gif2016zj.gificon_sad.gificon_cry.gif2016zhh.gificon_question.gif2016jk.gif2016bs.gificon_lol.gif2016qiao.gificon_surprised.gif2016fendou.gif2016ll.gif