我需要将标准库函数tolower
设为静态而不是“公共”范围。
我正在使用IAR Embedded Workbench编译器编译MISRA C:2004。编译器将tolower
声明为内联:
inline
int tolower(int _C)
{
return isupper(_C) ? (_C + ('A' - 'a')) : _C;
}
我从编译器收到以下错误:
Error[Li013]: the symbol "tolower" in RS232_Server.o is public but
is only needed by code in the same module - all declarations at
file scope should be static where possible (MISRA C 2004 Rule 8.10)
以下是我建议的解决方案:
tolower
在另一个模块中,以便它
需要多个模块。tolower
的情况下实施功能。这是一个嵌入式系统。static
头文件之前定义为ctype.h
。我正在寻找MISRA链接器错误的解决方案。我更希望仅为RS232_Server转换单元使tolower
函数为静态(如果我在标准头文件中使tolower
静态,它可能会影响其他未来的项目。)
编译器是用于ARM处理器的IAR Embedded Workbench 6.30
我在32位模式下使用ARM7TDMI处理器(不是Thumb模式)
tolower
函数与调试端口一起使用。
我也收到_LocaleC_isupper
和_LocaleC_tolower
答案 0 :(得分:2)
我建议您禁用此特定的MISRA检查(您应该只为RS232_Server
翻译单元执行此操作),而不是使用您建议的其中一种解决方法。在我看来,规则8.10的实用性非常小,并且在建议的解决方法中跳过各种箍似乎更有可能引入比仅禁用规则更高的风险。请记住,MISRA的目的是让C代码不太可能出现错误。
请注意,MISRA认识到“在某些情况下可能有必要偏离规则”,并且有一个记录在案的“偏差程序”(MISRA-C 2004第4.3.2节)。
如果你出于某种原因不能或不能禁用该规则,我认为你应该在自己的功能中重新实现tolower()
的功能,特别是如果你不需要处理语言环境支持。与IAR开展支持事件也许是值得的。您可以使用规则3.6来说明“生产代码中使用的所有库都应符合[MISRA-C]”。
答案 1 :(得分:1)
谁销售MISRA链接器?它似乎有一个疯狂的错误。
您可以通过在文件范围内获取地址int (*foo)(int) = tolower;
来解决这个问题吗?
编辑:我的理由是:
一个。这是我十年来见过的最愚蠢的事情,所以它可能是一个错误,但是
湾将它推向边缘情况(通过全局导出其名称的符号)可能会将其关闭。
为了使其成为正确的行为,即不是错误,一旦包含任何库函数,就必须是MISRA错误,例如initialise_system_timer,initialise_watchdog_timer,......,这只会伤到我的头脑。
编辑:另一个想法。再次基于这是一个边缘情况错误的假设。理论:也许编译器同时执行内联,和生成函数的实现。然后该函数(当然)没有被调用。因此链接器规则正在看到未调用的函数。
GNU有防止内嵌的选项。 你可以为tolower的使用做同样的事吗?这会改变错误吗?您可以通过
进行测试#define inline /* nothing */
在包含ctype.h之前,然后取消宏。
另一个测试是定义:
#define inline static inline
之前包括ctype.h,这是我期望的内联版本。
EDIT2: 我认为应该报告一个错误。我想IAR有一个解决方法。我接受他们的建议。
经过一夜的睡眠,我强烈怀疑问题是inline int tolower()
而不是static inline int tolower
或int tolower
,因为它最有意义。拥有一个未被调用的公共函数似乎确实是程序中的错误的症状。
即使有文档,所有编码方法都有缺点。
我强烈支持OP,我不会更改标准头文件。我认为有几个原因。例如,工具链的未来升级(带有一组新的标题)会破坏旧的应用程序(如果它得到维护)。或者简单地在不同的机器上构建应用程序会产生错误,合并两个看似正确的应用程序可能会出错,.......这可能没什么好处的。 -100
使用#define ...
使错误消失。我不喜欢使用宏来更改标准库。这似乎是一个长期坏主意的种子。如果将来程序的另一部分使用另一个具有类似问题的功能,则应用“修复”功能。变差。一些缺乏经验的开发人员可能会负责维护代码,并且可以学习'在#define
周围包裹#include
诡计的奇怪内容是正常的&#39;实践。它成为公司的实践&#39;将#include <ctype.h>
包装在奇怪的宏解决方法中,这些解决方法在修复之后仍然存在多年。 -20
使用命令行编译器选项关闭内联。如果这工作,并且语义是正确的,即包括头和在两个源文件中使用函数不会导致多重定义的函数,那么没关系。我希望它会导致错误,但值得确认是错误报告的一部分。它给将来出现的其他人带来了令人沮丧的陷阱。如果使用了不同的inline
标准库函数,并且由于某种原因,一个人必须查看生成的代码,它也不会被包括在内。他们可能会有点疯狂,想知道为什么内联不被尊重。我猜想他们看待生成代码的原因是因为性能很关键。根据我的经验,人们会花很多时间查看代码,在查看构建脚本之前会感到困惑。如果使用抑制内联作为修复,我觉得最好到处处理它而不是一个文件。至少语义是一致的,高级别的评论或文档可能会被注意到。任何人使用构建脚本作为快速检查&#39;将得到一致的行为,这可能导致他们查看文档。 -1(无内插无处)-3(一个文件无内联)
在第二个源文件中以伪造的方式使用tolower。这具有很小的好处,即构建系统不会被感染&#39;有额外的编译器选项。它也是一个小文件,可以解释正在解决的错误。我不喜欢这么多,但我喜欢它,而不是摆弄标准标题。我目前担心的是它可能不起作用。它可能包括链接器无法解析的两个副本。我喜欢它比奇怪的更好地获取它的地址并查看链接器是否关闭(我认为这是一种测试边缘情况的有趣方法)。 -2
编码自己的权力。我不了解应用程序的更广泛背景。我的反应是不代码替换库函数,因为我担心测试(以及引入更多代码的单元测试)和长期维护。我对字符I / O更加紧张,因为应用程序可能需要能够处理更宽泛的字符集,例如UTF-8编码,并且用户定义的tolower将趋于不同步。它听起来像一个非常具体的应用程序(调试到端口),所以我可以应付。我不喜欢编写额外的代码来处理看起来像bug的东西,特别是如果它是商业软件。 -5
您是否可以在不丢失所有其他检查的情况下将错误转换为警告?我仍然觉得这是工具链中的一个错误,所以我更喜欢它是一个警告,而不是沉默,以便事件和一些文档有一个钩子,而且另一个错误蔓延的可能性较小否则请关闭错误。 +1(警告)0(错误)
关闭错误似乎失去了企业的意识&#39; (恕我直言)IAR欠你一个解释和长期修复,但它似乎比搞乱构建系统,用标准库编写宏观肮脏,或编写自己的代码来增加你的成本要好得多。
可能只是我,但我不喜欢编写代码来解决商业产品的缺陷。感觉这是供应商应该很高兴有机会证明其许可证成本的地方。我记得微软向我们收取了一些事件,但如果问题证明是他们的,那么事件和修复都是免费的。正确的做法似乎是让他们有机会赚钱。产品会有bug,所以默默地解决它而不给他们修复它的机会似乎也没那么有用。
答案 2 :(得分:1)
首先, MISRA-C:2004不允许内联也不允许C99 。即将推出的MISRA 2012将允许它。如果您尝试通过MISRA-C:2004静态分析器运行C99或非标准代码,则所有投注均已关闭。 MISRA检查程序应该为内联关键字提供错误。
我相信符合MISRA-C标准的代码版本如下:
static uint8_t tolower(uint8_t ch)
{
return (uint8_t)(isupper(ch) ? (uint8_t)((uint8_t)(ch + 'A') - 'a') :
ch);
}
对此有一些评论:MISRA鼓励char
类型用于字符文字,但同时警告不要使用普通字符类型,因为它具有实现定义的签名。因此我使用uint8_t代替。我认为假设存在具有负索引的ASCII表是非常愚蠢的。
(_C + ('A' - 'a'))
肯定不符合MISRA标准,正如MISRA所认为的那样,它包含两种隐式类型的促销。 MISRA将字符文字视为char,而不是int,就像C标准一样。
此外,您必须在每个表达式之后对基础类型进行类型转换。并且因为?:运算符包含隐式类型促销,所以您必须将其结果类型转换为基础类型。
由于事实证明这是一个非常难以理解的混乱,最好的想法是忘记一切?:完全重写功能。与此同时,我们可以摆脱对签名计算的不必要的依赖。
static uint8_t tolower (uint8_t ch)
{
if(isupper(ch))
{
ch = (uint8_t)(ch - 'A');
ch = (uint8_t)(ch + 'a');
}
return ch;
}