小心编译器的隐式声明

引子:
  在一个ecos项目中出现一个奇怪的问题,内核标准线程休眠函数cyg_thread_delay()在有的地方可以正常执行休眠,有的地方则一休眠不再醒过来。

问题跟踪

在cli线程里加入调试命令,测试该函数,代码如下:

static void sleep(int argc, char *argv)
{printf("-------------\n");cyg_thread_delay(100);  //休眠1秒printf("-------------\n");
}
CLI_NODE("sleep", sleep, "sleep test");

发现一执行命令,只能打印第一句printf,然后cli线程卡住无响应,线程状态为sleep状态。在内核对cyg_thead_delay的实现部位加了打印入参语句,发现入参是很大一个值。这就奇怪了,我不传的100进去吗?

后面偶然发现加入声明这个函数的头文件<cyg/kernel/kapi.h>就一切正常了,这是为什么呢,之前没声明不也编过了吗?

根因

这都是编译器隐式声明的陷阱,首先函数没声明的时候编译器会隐式声明并报警告(得开-Wall)

  • 隐式声明的规则为:隐式声明的函数返回值都为int,参数类型则会根据你的入参来决定。

在上面的例子中,我们传入的参数为100, 100的字面类型应该为int(或者一个更小的类型),即编译器在编译时加入了这么一句话来声明这个函数:

但这个函数的实际声明是这样的:

这两个声明返回值类型和入参类型都不一致,风险很大

cyg_tick_count_t定义为64位的unsigned long long型,即当声明参数为32位的int时,调用这个函数时就只使用一个寄存器写入100,传过去,但到函数体哪儿是64位的,会读两个寄存器,那么就会有一个寄存器的值不确定,组合起来往往为很大的一个数,所以一直休眠……

如果把代码改成这样,就能顺畅运行,也佐证了上面的论述

其他注意点: 上面是一个由于函数声明的入参类型不匹配导致的一个奇怪bug,还容易出现的一个bug为返回值类型不匹配。在一个大工程里面这些BUG发生后会使DEBUG人员感到十分困惑,不容易调查。

如何避免

四个字:编程规范,使用函数时记得要先声明。

知易行难,自己写一个文件时,包含各种头文件,函数声明我们一般不会少。但在改代码时就往往很难做到了,我们在修改一行代码时会先检查自己要调用的函数有声明吗?(其实检查也不是那么容易,因为在大工程里头文件包含层级关系比较复杂,当然我们也可以不检查,重复include也没关系,但有时我们甚至都不知道调用这个函数要include哪个头文件)所以一般就是改完,发现能编译过就皆大欢喜了。

还有其他的办法吗?
在引子里提到的问题,需要查找所有调用cyg_thread_delay函数的文件,在文件中加include语句吗?这样是可行的,但修改点可能较多。我是通过修改makefile来实现的,在顶层makefile的CFLAGS里加了-include选项CFLAGS += -include cyg/kernel/kapi.h,这句话的作用就相当于为每一个需要编译的c文件都加了#include <cyg/kernel/kapi.h>语句。

使用GCC的-include属性可以避免一些头文件包含的问题,缺点在于改了-include的这个头文件所有C文件都需要重新编译,耗时长

小心编译器的隐式声明相关推荐

  1. C语言一定要有函数声明吗,1 什么是C语言的隐式函数声明在C语言中,函数在调用前不一定非要声明。如果没有声明,那么编译器会自动按照一种隐式声明的规则,为调用函数的C代码产生汇编代码。下...

    1 什么是C语言的隐式函数声明 在C语言中,函数在调用前不一定非要声明.如果没有声明,那么编译器会自动按照一种隐式声明的规则,为调用函数的C代码产生汇编代码.下面是一个例子: int main(int ...

  2. C语言中的隐式声明是什么,有什么危害?warning: implicit declaration of function ‘xxx’

    今天编译一个代码,提示CUSTOM_LAYER里面有一个C文件函数gettimeofday()隐式声明,用man手册查了一下,需要包含头文件#include <sys/time.h> 那么 ...

  3. c语言 隐式声明,关于C#:隐式函数声明和链接

    最近,我了解了C语言中的隐式函数声明.主要思想很明确,但在这种情况下,我对理解链接过程有些麻烦. 考虑以下代码(文件a.c): #include int main() { double someVal ...

  4. 函隐式声明函数是什么意思

    由于编译器在处理函数调用代码时没有找到函数原型,只好根据函数调用代码做隐式声明. 为什么编译器在处理函数调用代码时需要有函数原型?因为必须知道参数的类型和个数以及返回值的类型才知道生成什么样的指令.为 ...

  5. 警告:隐式声明与内建函数‘exit‘不兼容解决方案

    警告:隐式声明与内建函数'exit'不兼容解决方案 参考文章: (1)警告:隐式声明与内建函数'exit'不兼容解决方案 (2)https://www.cnblogs.com/davytitan/p/ ...

  6. 解决E1776:无法引用 函数 “A::A(const A)“ (已隐式声明) -- 它是已删除的函数

    出错原因 先介绍下博主遇到这个错误的背景情况. 有一个类A,没有定义默认构造函数A(): class A{int m_val=0;int m_type=0;A(int val):m_val(val){ ...

  7. #C语言#警告:隐式声明函数‘xxx’ [-Wimplicit-function-declaration]

    推荐阅读:C语言实现2048小游戏-粤嵌GE6818嵌入式系统实训 C语言编译时报错: 警告:隐式声明函数'xxx' [-Wimplicit-function-declaration] 加下对应函数的 ...

  8. 函数的隐式声明 及 rsp,rbp,被调用者和调用者保存的寄存器standerd manuel(Caller/Callee - saved registers)

    函数的隐式声明 https://blog.csdn.net/liangbo930522/article/details/73733415 mpx-linux64-abi.pdf https://sof ...

  9. C++“(已隐式声明)--它是已删除的函数 ” “尝试引用已删除的函数”知识点MARK

    今天写一个简单的文件处理工具类时遇到了一个新bug,是一个之前忽略掉的知识点,特此mark一下. 错误如下: c++类在初始化的时候有两种方式: 1. M_CLASS m_class(...); 2. ...

最新文章

  1. 给Anaconda安装国内镜像,加快下载速度
  2. Leet Code OJ 58. Length of Last Word [Difficulty: Easy]
  3. c# 按位与,按位或
  4. android studio生成签名导打包的方法
  5. MindSpore实践:对篮球运动员目标的检测
  6. 我是如何将系统QPS从300提升到6000的
  7. AJAX ControlToolkit学习日志-AnimationExtender控件(3)
  8. [转载] Python 递归函数
  9. CSDN发布博文时出错的截图
  10. android 屏幕分辨率 更改
  11. Ajax之搭建一个基本的Ajax框架(技术分析篇)
  12. 历史上有哪些最凶计算机病毒?
  13. 八猴渲染器4.0基本使用教程及渲染教程
  14. idea社区版创建springboot_IDEA社区版创建spring boot项目的安装插件的图文教程
  15. 现代几何学在计算机科学中的应用,CNCC2017
  16. Fragment和Activity之间的通信
  17. D3D11 加载静态3D模型(.obj格式)
  18. 重磅!罗振宇跨年演讲:扎心5问
  19. Android6.0 打开自启动管理页面(华为、小米)
  20. Qt数据库应用18-横向纵向排版

热门文章

  1. html中text函数,text函数的使用方法
  2. PID控制器开发笔记(转)
  3. swift简介(东拼西凑,看看就的了)
  4. Spark的存储管理
  5. ireport5.6+jasperreport6.3开发(四)--以javabean为基准的报表开发(ireport)
  6. GO实例3 Slice append打印
  7. 用Supermemo背单词达到8000词条
  8. LeetCode(561)——数组拆分 I(JavaScript)
  9. 纠正网上Mac 上使用Hbuilder运行夜神游模拟器,Hbuilder找不到模拟器
  10. mac如何安装python_手把手教你安装Python开发环境(二)之Mac电脑安装Python解释器...