【问题标题】:Expected an expression error预期表达式错误
【发布时间】:2011-12-25 23:10:24
【问题描述】:

我有以下 C++ 代码并在代码上运行 PC lint。

问题一:

#if !WIN32
#define ULONG_MAX 0xffffffff
#endif

上面的代码抛出如下的 lint 错误

错误 26:需要一个表达式,发现 'WIN32'
错误 30:应为整数常量

如何解决上述错误?

问题 2:

const char CompanyName[] = "mycompany"; 

错误:注释 960:违反 MISRA 要求的规则 8.5,头文件中没有对象/函数定义

如何解决上述错误?

问题 3:

unsigned long m_ClientThreadId; 
m_ClientThreadId        = 0;

注释 960:违反 MISRA 要求的规则 10.1,隐式转换更改签名

如何解决上述错误?

【问题讨论】:

  • 您需要提出更深入的问题,而不仅仅是“如何解决上述错误?” “如何解决上述错误?” “如何解决上述错误?”您还应该向我们展示您尝试过的东西,或者如果您还没有尝试过任何东西,请阅读并尝试一些东西。

标签: c++ lint


【解决方案1】:

第一:

您需要这样做:

#ifndef WIN32
#define ULONG_MAX 0xffffffff
#endif

第二:

不能在头文件中定义,否则同一个符号会出现在多个编译单元中。

您需要做的只是在标题中声明:

extern const char CompanyName[];

然后在其中一个模块中定义一次:

const char CompanyName[] = "mycompany"; 

第三:

unsigned long m_ClientThreadId; 
m_ClientThreadId        = 0;

这很不寻常,但似乎0 是一个有符号常量。并将其分配给 unsigned long 具有隐式类型转换。大多数编译器实际上并没有对此发出警告。

【讨论】:

  • +1,但请注意,虽然为 2) 提议的更改会使静态分析器静音,但我不会这样做。原因是 C++ 中的常量具有内部链接,因此在包含该标头的所有翻译单元 (TU) 中重新定义常量不会导致错误。通过执行建议的更改,除了一个 TU 之外,变量将具有不完整的类型(数组大小未知),这将限制使用(不多,但您无法静态获取大小),替代方法是手动提供标题中的大小,但这会增加维护成本:手动更新。
  • 我想另一种选择是将定义保留在标题中,但将其声明为静态。这将防止在编译时重复符号,但会导致它的多个副本。另一种选择是使用宏:#define CompanyName "mycompany"
  • extern const 没有内部链接,所以这很好。数组的大小对于模板技巧和 sizeof 来说很方便,但如果你不依赖它们,那没关系,定义绝对应该(哈哈)移出标题。
  • @Mysticial:还有一个选择是保持原样并忽略lint 警告......这是我会做的。
【解决方案2】:

对于第二个问题,该行是否在头文件中?一般情况下,您不应该在标头中定义变量,特别是如果该标头包含在多个文件中,因为这会创建同一变量的两个副本并导致链接错误。

【讨论】:

  • 这在 C++ 中并不完全正确。常量具有内部链接(除非之前声明为具有外部链接),因此不会触发编译错误,并且在大多数情况下链接器会删除重复的字符串。
【解决方案3】:

对于第一个问题,我猜你应该使用

#ifndef WIN32

而不是

#if !WIN32

由于 WIN32 宏并不总是存在,您需要检查它的存在而不是它的“假”。

【讨论】:

    【解决方案4】:

    还有#if !defined(WIN32),不过#ifndef WIN32更容易理解。

    【讨论】:

      【解决方案5】:

      这些报告的错误都不是 C++ 错误;它们是风格问题。

      第一:

      #if !WIN32
      #define ULONG_MAX 0xffffffff
      #endif
      

      这是合法的。在#if 指令中,任何未定义的标记都将替换为0。但正如其他人已经建议的那样,写#ifndef WIN32 可能更好。

      但实际上,整个事情可能是个坏主意。 ULONG_MAX 是在 C 标准头文件 <limits.h> 和 C++ 标准头文件 <climits> 中定义的宏。将以上 3 行替换为:

      #include <climits>
      

      第二:

      const char CompanyName[] = "mycompany";
      

      合法,但不是一个好主意。如果头文件是来自不同翻译单元的#included,您将拥有CompanyName 的多个定义。 (我不太确定 C++ 规则对此有何规定。)请参阅 Mysticial 的回答。

      第三:

      unsigned long m_ClientThreadId; 
      m_ClientThreadId        = 0;
      

      这里 PC-lint 过于挑剔了。是的,0(类型为int)到unsigned long 的隐式转换确实会改变签名,但在这种情况下它不会导致任何可能的问题。但是您可以通过使用 unsigned long 类型的文字来避免警告:

      unsigned long m_ClientThreadId; 
      m_ClientThreadId        = 0UL;
      

      【讨论】:

        【解决方案6】:

        有几点需要澄清。例如,该行:

        #if !WIN32
        

        实际上已经被标准很好地定义了,并且可以合理使用 如果您的编译器调用始终包含/DWIN32=1-DWIN32=0。 就此而言,标准规定未定义的符号是 宏扩展时替换为0,所以从来没有任何问题 除非有其他约定说明该符号 只会在 Windows 机器上定义,但它定义的值是 未指定;在这种情况下,您需要类似:

        #ifndef WIN32
        

        最后,这取决于您建立的约定 处理编译器依赖项。

        另一方面,应避免紧随其后的行, 因为它定义了一个在 C 中定义的符号 (ULONG_MAX) 和 C++ 标准。这里的三行顺序应该换成:

        #include <limits.h>
        

        关于第二个问题,我不确定错误是不是 对 MISRA 规则的错误解释。在 C++ 中,const 表示内部 默认情况下链接:在标题中定义这样的符号将导致 变量的多个实例化(在 每个翻译单元),但不会导致多个翻译单元出现问题 定义。替代方案也有其缺点。我的 这里的首选是将变量定义替换为 宏:

        #define CompanyName "mycompany"
        

        但是宏也有自己的问题。声明符号extern,并且 然后在一个(并且只有一个)源文件中定义它是另一个 替代方案,但这涉及两个不同文件中的两个语句, 在哪里(取决于变量所扮演的角色),一个可能是 更可取。 (从名字来看,我不认为这两种说法 将是一个问题,但在其他情况下更可取 保持文本在标题中可见。)保持原样 也是一个可行的选择,除非你的公司有严格的规定 反对它。

        关于最后一点,表达式0 的类型为int,即 已签署。你可以清楚地指定类型,0UL,但坦率地说,这个 不应该是必要的:00,无论类型如何,而 在某些情况下,您可能想要强制输入类型,以确保 算术以某种方式发生,这不是其中之一。至于 错误/警告,我怀疑这也是对 MISRA 规则;改变符号的隐式转换可以是 有问题,但不是在转换的内容非常小时 非负常数整数。所以如果你需要坚持写0UL 遵守公司规则,但要意识到这是把事情带到 point of siliness:一个基本健全的规则被应用在 不相关的情况。

        【讨论】:

        • +1 作为答案,我很想再给一个 +1,因为 这是把事情带到愚蠢的地步。静态分析器可以帮助检测潜在问题,但这并不意味着他们抱怨的每一行都必须更改,只需审查即可。
        • @DavidRodríguez-dribeas 没有什么可以禁止将一些常识编程到静态分析器中。通知可能导致值更改的隐式转换非常好。通知可能影响表达式评估方式的隐式转换。通知 0 的隐式转换只是噪音,会降低工具的实用性。
        • 问题中提到的 MISRA 规则是 MISRA C 2004 规则,而不是 MISRA C++ 规则。在 C 中,const 并不意味着内部链接,因此在 MISRA C 中没有考虑这一点,报告的 8.5 违规是有效的。规则 10.1 绝对禁止使用未修饰的 0 分配或初始化无符号变量,这已在 Exemplar Suite 和 MISRA 讨论论坛中明确说明。 PC-Lint 在这里并没有误解任何东西,它只是在做它的工作;不提供 MISRA 警告,除非它们被要求并且很容易禁用。
        • @RobertGamble 好的。但是禁止使用朴素的 0 进行初始化似乎是一个非常愚蠢的规则。 (从我迄今为止看到的情况来看,MISRA 规则并不愚蠢。可能是过度的,但是,它们被设计用于过度限制性规则是合理的。但并不愚蠢。我想知道理由是什么。)跨度>
        • @James 我的意思是 PC-Lint 只是遵守规则。规则 10.1 中 0 没有例外的理由是一致性。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2016-11-21
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多