|
|
|
|
移动端

由一道面试题所引出的C语言static变量特性

当时觉得蛮简单的,这不就是类似字符串逆转嘛,纸上得来终觉浅,绝知此事要躬行,自己尝试做了一下,发现还是有一些地方值得注意。今天在此整理一下常见的坑,巩固下基础东西。

作者:上善若水来源:码农有道|2018-02-01 16:26

技术沙龙 | 邀您于8月25日与国美/AWS/转转三位专家共同探讨小程序电商实战

最近部门在准备春招笔试题时,有这样一道题目:用C/C++语言实现一个函数,给定一个int类型的整数,函数输出逆序的整数对应的字符串,例如输入1234,则输出字符串"4321",,输入-1234,则输出字符串"-4321"。题目要求,不使用标准库,以及不能分配动态内存。当时觉得蛮简单的,这不就是类似字符串逆转嘛,纸上得来终觉浅,绝知此事要躬行,自己尝试做了一下,发现还是有一些地方值得注意。今天在此整理一下常见的坑,巩固下基础东西。

版本一

算法思路其实很简单:使用对10取余和除法操作依次获取每一位的数字,然后根据ASSIC码转换为字符,将结果存放在一个char型数组中,最后返回字符串数组结果,如下所示:

  1. #include<stdio.h> 
  2.  
  3. //版本一 
  4. const char * reverseInt(int n) 
  5.     char str[16] = {0}; 
  6.     int temp = n; 
  7.     int i = 0; 
  8.      
  9.     if (n < 0) 
  10.     { 
  11.         temp = -n; 
  12.         str[i++] = '-'
  13.     } 
  14.  
  15.     //当temp除到是一位数的时候退出 
  16.     while (0 != temp / 10)  
  17.     { 
  18.         char ch = temp % 10 + 48; 
  19.         temp = temp / 10; 
  20.         str[i++] = ch; 
  21.     } 
  22.  
  23.     //处理原始数据的最高位 
  24.     str[i++] = temp % 10 + 48; 
  25.  
  26.     return str; 
  27.  
  28. int main(int argc, char **agrv) 
  29. {     
  30.     int test_data1 = 12345;     
  31.     int test_data2 = 789; 
  32.  
  33.     printf("[test_data1] %d--->%s\n",  
  34.     test_data1, reverseInt(test_data1)); 
  35.  
  36.     printf("[test_data2] %d--->%s\n",  
  37.     test_data2, reverseInt(test_data2));     
  38.     return 0; 

发现编译出现了警告,如下:

  1. [root@epc.baidu.com ctest]# gcc -g -o test test.c  
  2. test.c: In function 'reverseInt'
  3. test.c:24:2: warning: function returns address of local variable [-Wreturn-local-addr]   
  4.   return str;  ^ 

从编译器给出的信息很清楚的说明了问题:返回了一个局部变量的地址,但是我们知道,函数的局部变量是存在stack中的,当这个函数调用过程结束时,这个局部变量都是要释放掉的,自然就不可再使用了,所以就会产生这样的warning,这个是和变量的生命周期相关的。

版本二

对于版本一存在的问题,很自然的会想到有两种解决方案,第一:使用malloc分配动态内存存放结果,但是题目中明确说明不能不能分配动态内存。因此自然排除掉。第二种方案就是将char result[16]改为static型:static char result[16];对,就是这么一点改动。

  1. #include<stdio.h> 
  2.  
  3. //版本二 
  4. const char * reverseInt(int n) 
  5.     static char str[16] = {0}; 
  6.     int temp = n; 
  7.     int i = 0; 
  8.      
  9.     if (n < 0) 
  10.     { 
  11.         temp = -n; 
  12.         str[i++] = '-'
  13.     } 
  14.  
  15.     //当temp除到是一位数的时候退出 
  16.     while (0 != temp / 10)  
  17.     { 
  18.         char ch = temp % 10 + 48; 
  19.         temp = temp / 10; 
  20.         str[i++] = ch; 
  21.     } 
  22.  
  23.     //处理原始数据的最高位 
  24.     str[i++] = temp % 10 + 48; 
  25.  
  26.     return str; 
  27.  
  28. int main(int argc, char **agrv) 
  29. {     
  30.     int test_data1 = 12345;     
  31.     int test_data2 = 789; 
  32.  
  33.     printf("[test_data1] %d--->%s\n",  
  34.     test_data1, reverseInt(test_data1)); 
  35.  
  36.     printf("[test_data2] %d--->%s\n",  
  37.     test_data2, reverseInt(test_data2));     
  38.     return 0; 

运行结果如下:

  1. [root@epc.baidu.com ctest]# ./test                 
  2. [test_data1] 12345--->54321 
  3. [test_data2] 789--->98721 

从运行结果上看,第一个测试数据其结果是正确的,但是第二个输出结果确实错误的。这是什么原因?先来回一下用static修饰所修饰的局部变量(也称静态局部变量)特点,如下:

1:静态局部变量定义时未赋初值,则默认初始化为0;

2:静态局部变量其作用域为函数或代码块,其生命周期为整个程序的运行期间;注意这两个概念不要混淆;

3:在一个进程的运行期间,静态局部变量只会初始化一次,就是第一次调用该静态局部变量所在函数的时候初始化,此后再调用不会初始化。

好了,到这里,其实问题的原因已经很明显了:在上面程序中,static char str[16] = {0}只会初始化一次,既在执行reverseInt(test_data1)时初始化,执行完该语句,将结果存放到str中,此时str中的内容为54321,既str[16] = {'5','4','3','2','1','\0'};当再次对第二个测试数进行转换调用reverseInt(test_data2)时,str仍然是上次的结果{'5','4','3','2','1','\0'},因此在转换后为98721。

版本三

那么如何解决版本二的问题了,一个很简单的办法就是在reverseInt函数中对static变量str每次使用for循环进行初始化,如下,鉴于篇幅,就不将main函数也贴出来了:

  1. const char * reverseInt(int n) 
  2.     static char str[16] = {0}; 
  3.     int temp = n; 
  4.     int i = 0; 
  5.     int j = 0; 
  6.  
  7.     for (; j < 16; j++)  
  8.     { 
  9.         str[j] = '\0'
  10.     } 
  11.  
  12.     if (n < 0) 
  13.     { 
  14.         temp = -n; 
  15.         str[i++] = '-'
  16.     } 
  17.  
  18.     //当temp除到是一位数的时候退出 
  19.     while (0 != temp / 10)  
  20.     { 
  21.         char ch = temp % 10 + 48; 
  22.         temp = temp / 10; 
  23.         str[i++] = ch; 
  24.     } 
  25.  
  26.     //处理原始数据的最高位 
  27.     str[i++] = temp % 10 + 48; 
  28.  
  29.     return str; 

运行,能得到我们期望的结果了:

  1. [root@epc.baidu.com ctest]# ./test                 
  2. [test_data1] 12345--->54321 
  3. [test_data2] 789--->987 

其实,版本三还有很多细节需要考虑的,比如:当输入的整数超过int的范围如何处理等等,虽然是小细节,但却十分重要,大家有兴趣可以思考下练练手。

【编辑推荐】

  1. 图例详解那道setTimeout与循环闭包的经典面试题
  2. 探讨Java中最常见的十道面试题(超经典)
  3. 妙解谷歌压箱底面试题:如何正确的从楼上抛鸡蛋
  4. 年薪20万阿里巴巴Python工程师面试题曝光
  5. 百度大牛总结十条Python面试题检验你的真实水平
【责任编辑:武晓燕 TEL:(010)68476606】

点赞 0
分享:
大家都在看
猜你喜欢

读 书 +更多

网管员必读-网络管理(第2版)

本书是对《网管员必读—网络管理》第1版的全面重写,因为书中所涉及的两个主要网络操作系统都已升级。本书介绍的是Windows Server 2003 R2...

订阅51CTO邮刊

点击这里查看样刊

订阅51CTO邮刊