函数名之标号地址

函数名之标号地址

 

       前几天,无聊之间在网上看到这样的一道题,有下面的几个函数,各函数的声明在相应的.h文件中进行声明,下面是这几个函数的实现。

int add(int a,int b)

{

       return a + b;

}

 

int sub(int a,int b)

{

       return a - b;

}

 

int mul(int a,int b)

{

       return a * b;

}

 

int div(int a,int b)

{

       return a / b;

}

 

int mod(int a,int b)

{

       return a % b;

}

要求写一个主函数,输入三个值来完成相应的功能,但是每次只能选择一种功能,各函数的编号从上到下从1开始。最后一个为5(可以不考虑容错问题)

输入值的格式为:第一操作数 第二操作数 功能选择

比如输入4 5 1

输出为:9 4 + 5

 

刚开始一看,这道题真的很简单,但是,从后面的跟贴一看,才知道是一个公司的笔试题,而且最后只有一个人的答案是他们想要的,我想也许是公司没有遇到所谓的高手去笔题吧。

       很多人,包括我第一想到的就是switch语句,如果个别基础较差的可能还会想到一串的if,else语句,这么一想,就不是公司希望出现的结果了,因为,这道题我想他们的考查范围不是这一点,而是指针及对函数名的理解上,这样一想,就可以想到第一个版本,函数指针数组。这样得到第一个版本的答案,相比switch语句的好多了,同时也考查对指针的了解程度。这个版本的完整代码实现如下:

#ifndef _TEST_C_

#define _TEST_C_

 

#include <stdio.h>

 

int _my_add(int,int);

int _my_sub(int,int);

int _my_mul(int,int);

int _my_div(int,int);

int _my_mod(int,int);

 

int main(void)

{

       int number_one;

       int number_two;

       int number_control;

 

       int (*function_buffer[])(int,int) = {

              _my_add,_my_sub,

              _my_mul,_my_div,

              _my_mod

       };

 

    while(1)

       {

              printf("这是一个简单的计算器,主要有下面的功能!/n");

              printf("    1:/n     2:/n     3:/n     4:/n     5:求模/n");

              printf("输入格式为:num1 num2 操作/n");

 

              scanf("%d %d %d",&number_one,&number_two,&number_control);

 

    if(((number_control == 4) &&(!(number_two | 0x0)))/

                     || (number_control > 5)){

                     printf("input is err/n");

                     continue;

              }

 

 

              printf("Result is:%d/n",/

                       function_buffer[number_control - 1](number_one,number_two));

       }

 

       return 0;

}

 

int _my_add(int number_one,int number_two)

{

       return number_one + number_two;

}

 

int _my_sub(int number_one,int number_two)

{

       return number_one - number_two;

}

 

int _my_mul(int number_one, int number_two)

{

       return number_one * number_two;

}

 

int _my_div(int number_one, int number_two)

{

       return number_one / number_two;

}

 

int _my_mod(int number_one,int number_two)

{

       return number_one % number_two;

}

 

#endif

 

当然,如果能写出这个版本的人的C语言应该很不错了,说明他对指针的应用也差不多了,同时也知道一些高级指针的用法,当然,写这样一个程序不一定也说明他的C语言功底,也许他在网上看过类似的题目呢,不过,看过能应用出来也是一种技巧。

 

       再一个版本是应用指针数组来实现,这种方法可以体现其对函数名的一点认识,也就是一个地址,所以,应用指针就可以进行访问,这种方法需要应用一个技巧就是显示类型转化,将指向函数地址的指针强制转化为函数指针,这样才能正确的代入参数,这个版本的实现代码如下:

#ifndef _TEST_C_

#define _TEST_C_

 

#include <stdio.h>

 

int _my_add(int,int);

int _my_sub(int,int);

int _my_mul(int,int);

int _my_div(int,int);

int _my_mod(int,int);

 

int main(void)

{

       int number_one;

       int number_two;

       int number_control;

       int result;

 

       void *function_buffer[][2] = {

              (void *)_my_add,"+",

              (void *)_my_sub,"-",

              (void *)_my_mul,"*",

              (void *)_my_div,"/",

              (void *)_my_mod,"%",

       };

    while(1)

       {

              printf("这是一个简单的计算器,主要有下面的功能!/n");

              printf("    1:/n     2:/n     3:/n     4:/n     5:求模/n");

              printf("请输入第一操作数,第二操作数,相关应算/n");

 

              scanf("%d %d %d",&number_one,&number_two,&number_control);

       

              if(((number_control == 4) &&(!(number_two | 0x0)))/

                     || (number_control > 5)){

                     printf("input is err/n");

                     continue;

              }

 

              result = ( ( int (*) (int,int) )/

                      (function_buffer[number_control - 1][0]) )/

                      (number_one,number_two);

       

              printf("/n%d %s %d = %d/n/n",number_one,/

                    function_buffer[number_control - 1][1],number_two,result);

       }

 

       return 0;

}

 

int _my_add(int number_one,int number_two)

{

       return number_one + number_two;

}

 

int _my_sub(int number_one,int number_two)

{

       return number_one - number_two;

}

 

int _my_mul(int number_one,int number_two)

{

       return number_one * number_two;

}

 

int _my_div(int number_one,int number_two)

{

       return number_one / number_two;

}

 

int _my_mod(int number_one,int number_two)

{

       return number_one % number_two;

}

 

#endif

 

很简洁的一段代码,这段代码可以看出答题者对指针函数、函数名、函数指针的理解,同时也可以看出其对void * 这种指针类型的理解,void * 不同于void类型。void *C中的重要之处是不可小看的,其代表不定类型指针,其实也可以看成为任意指针,他可以接收任何指针类型。代码中的”+” “- “ “*” “%” “/”这几个为字符串,他们传给数组的是第一个字每符的内存地址,当然也是一个指针。当然,写出这段代码的人一定可以写出第一版的代码,因为第一版的代码是基础性的,没有多少技术性,只要对指针有一定的理解,就可以写出第一版代码,但是,写出第一版代码的答题者不一定写出第二版。

 

       前面说明,函数名其实也就是一个标号,标志着一个地址,那么在计算机内部,内存地址一般应用unsigned long型来表示,所以,可以应用内存地址来实现函数的调用,如果学过汇编的同学知道,BL Lable(基于ARM处理器)来说也是实现函数的调用,如果在线调试过程序,查看PC的值,当进行函数的调用时,PC的值直接改变(当然还有其它的一些处理,比如现场保护等)。所以,说明函数名也就是一个内存地址。这样一来就可以出现第三个版本。应用一个unsigned int型组数来实现函数的调用,当然,也需要应用第二个版本中出现的强制类型转化。第三个版本的代码如下:

#ifndef _TEST_C_

#define _TEST_C_

 

#include <stdio.h>

 

int _my_add(int,int);

int _my_sub(int,int);

int _my_mul(int,int);

int _my_div(int,int);

int _my_mod(int,int);

 

int main(void)

{

       int number_one;

       int number_two;

       int number_control;

       int result;

 

       unsigned long function_buffer[][2] = {

              (unsigned long) _my_add,'+',

              (unsigned long) _my_sub,'-',

              (unsigned long) _my_mul,'*',

              (unsigned long) _my_div,'/',

              (unsigned long) _my_mod,'%',

       };

    while(1)

       {

              printf("这是一个简单的计算器,主要有下面的功能!/n");

              printf("    1:/n     2:/n     3:/n     4:/n     5:求模/n");

              printf("请输入第一操作数  第二操作数  相关运算/n");

 

              scanf("%d %d %d",&number_one,&number_two,&number_control);

             

    if(((number_control == 4) && (!(number_two | 0x0)))/

                     || (number_control > 5)){

                     printf("input is err/n");

                     continue;

              }

             

              result = ( ( int (*) (int,int) )/

                       (function_buffer[number_control - 1][0]) )/

                       (number_one,number_two);

       

              printf("/n%d %c %d = %d/n/n",/

                    number_one,/

*(*(function_buffer + number_control - 1) + 1),number_two,result);

       }

 

       return 0;

}

 

int _my_add(int number_one,int number_two)

{

       return number_one + number_two;

}

 

int _my_sub(int number_one,int number_two)

{

 

       return number_one - number_two;

}

 

int _my_mul(int number_one,int number_two)

{

       return number_one * number_two;

}

 

int _my_div(int number_one,int number_two)

{

       return number_one / number_two;

}

 

int _my_mod(int number_one,int number_two)

{

       return number_one % number_two;

}

 

#endif

 

这段代码中直接应用内存地址进行调用函数,这样更加直接地说明的函数名的实质,当然也看出答题者对汇编有一定的了解,要不然,其不会把内存地址看和这么重,因为也只有对汇编有一定理解的人,才有可能知道标号在内存中说明一个内存地址,其实,我们的声明的变量也是这样的,只是编译器帮我们做了很多事情,让我们不去想应用变量名与应用内存地址单元有什么区别。在二维数组中,有一个自动类型转化过程,也就是字符型自己提升为unsigned long型。

 

好了,我不知道还有没有其它的版本,但是本人现在的知识范围只能想得出这三种较好一点的版本来了,如果我是公司的人,如果做嵌入式领域的话,我会最先选择第三个版本的,因为可以看出答题者有一定的汇编基础,在嵌入式领域,不懂汇编对代码优化,写出高质量的代码有着一定的约束力。

如果就一般的C语言开发领域,或者以C或者C++为主的领域,我会优先选择第二版和第三版的答题者。最后才会选择第一版的答题者,应用switch的答题为待定者,if ,else答题者不在考虑范围内。一般的话,switch的答题者在这一题上没有什么优势,不过看其编码风格,也可以看出一些实战能力。

 

通过此题我个人认为,要做对一公司的笔试题也许很容易,其实,我想,笔试应该要做到别人一看就知道你的能力你是他们想要的人,别人能从你答题中看出你的能力,你的习惯,你的编码风格,你对某些领域的专注,基础,以及熟练程度才是真正应该展示的。这比任何语言所能表达在别人的印像还要深,因为公司从你的答题看出来你就是一个与其它笔试者不是一个档次的人。

 

 

 

 

 

 

 

                                                                                                                              舒稳

2010.8.3于长沙理工大学

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值