1. 是否sizeof((int[]){1,2,3})在编译时进行评估?并且由标准保证。
  2. 在这种情况下,sizeof 操作数是否会产生副作用?

我之所以问这个问题,是因为使用 Tasking 编译器时,这行代码会导致警告“忽略 ‘sizeof’ 操作数的副作用”。

我想知道这是虚假警告还是另有隐情。

我正在尝试遵守 MISRA 规则 13.6,该规则规定“sizeof() 运算符的操作数不应包含任何具有潜在副作用的表达式”。

9

  • 1
    作为一种可能的解决方法:它是否抱怨sizeof(int[3])


    – 

  • 复合文字((int[]){1,2,3})是在 C99 中引入的。也许您的编译器将该构造解释为不寻常的东西?:-)


    – 

  • @TedLyngmo 它没有抱怨。但是我不能用它作为解决方法,因为我不知道数组的大小,所以我要使用 sizeof。


    – 

  • @pmg 我不认为这是个问题。编译器符合 C99 标准。


    – 

  • 1
    @user1806687 回复:“我不知道数组的大小” ——哦,它是使用宏__VA_ARGS__还是您用其他什么方法生成的{1,2,3}


    – 


最佳答案
4

  1. 是否sizeof((int[]){1,2,3})在编译时进行评估?并且由标准保证。

规范没有说明,但可能如此。无论如何,这与 MISRA 13.6 没有直接关系

  1. sizeof在这种情况下操作数会产生副作用吗?

不,就规范而言不是。表达式(int[]){1,2,3}在求值时会产生一个值,但语言规范没有描述任何副作用(产生一个值本身不是副作用)。与 相比i++,它既会产生一个值,又会产生修改 存储值的副作用i

请注意,操作数根本没有被求值,其他答案也观察到了这一点,但这就是 MISRA 规则的要点。它希望您避免使用如果求值sizeof()产生副作用的操作数,因为操作数实际上没有被求值,因此不会发生副作用,这可能会令人惊讶。

我问这个问题是因为使用 Tasking 编译器时这行会导致警告“忽略 ‘sizeof’ 操作数的副作用”。

我想知道这是否是一个虚假警告或者是否有道理。

如果将 MISRA 解读为谈论语言规范定义的副作用,那么这个警告肯定是虚假的。

如果 MISRA 被解读为谈论特定 C 实现产生的副作用,包括语言规范未定义的任何副作用,并且如果在您的特定实现中,对复合文字进行求值确实产生了副作用,那么从技术上讲,警告并不是虚假的。但这似乎是可疑解读和不太可能的行为的结合。此外,即使在这种情况下,我认为警告也可能被忽略而不会违反规则的精神,因为没有人会对他们没有预料到的副作用没有发生感到困惑。

1

  • MISRA C 规范肯定是指 C 语言对副作用的正式定义。


    – 

这里没有副作用。

只有在类型为可变长度数组时才会对运算符的操作数sizeof进行求值。您给出的复合文字是固定大小的数组,因此不会在运行时对其进行求值。

第 6.5.3.4p2 节中有关sizeof运算符的内容对此进行了记录:

如果操作数的类型是可变长度数组类型,则对操作数进行求值;否则,不对操作数进行求值,结果为整型常量

1

  • 1
    是的,但我不确定这是否能回答这个问题,即OP 特定表达式的操作数sizeof是否具有“潜在”副作用。MISRA 规则的表述方式,我认为它是关于表达式本身作为语句表达式进行评估时是否会产生副作用。 也是这么认为的。


    – 


1.sizeof((int[]){1,2,3})在编译时进行评估吗?并且由标准保证。

运算sizeof符指定为(C11 6.5.3.4):

如果操作数的类型是可变长度数组类型,则对操作数进行求值;否则,不对操作数进行求值,并且结果为整型常量。

复合文字明确不是可变长度数组(VLA):

6.5.2.5 复合文字
约束

类型名称应指定完整的对象类型或未知大小的数组,但不能指定可变长度数组类型。

因此是的,sizeof((int[]){1,2,3})保证在编译时进行评估,尽管的操作数sizeof没有评估副作用。

类似这样的操作是行不通的:sizeof((int[]){i++})i++不会被执行,而且编译器只需要知道的类型i就可以知道大小。


2.sizeof这种情况下的操作数是否会产生副作用?

不。编译器和 MISRA C 规则都引用了 C 语言对副作用的正式定义,而不是一般的英语含义。该术语的正式定义可以在 C11 5.1.2.3/2 中找到:

访问易失性对象、修改对象、修改文件或调用执行上述任何操作的函数都是副作用

您的代码不会执行任何这些操作,因此没有副作用。这意味着 Tasking 警告非常直白,如果它声称是 C 编译器,它应该已经知道您的代码中没有副作用。

  1. 是的

这有两种副作用sizeof

显而易见的是 – 填充。您的数组int不存在该问题,因为int默认情况下大小与寻址内存的大小匹配。因此三个数组int恰好等于 int 的大小乘以三。

不太为人所知的副作用是如果你尝试做sizeof(variable length array). 那么将sizeof(有时) 推迟到运行时:

#include <stdio.h>
int main() {
    int foo = 100;
    double (*bar)[foo];
    printf("%ld\n", sizeof(*bar));
    return 0;
}

5

  • 1
    我不明白使用时填充可能产生的副作用sizeof。如果我不是唯一一个不理解这一点的人,也许你可以稍微澄清一下。


    – 


  • 本身不是副作用,更像是意外结果。对我来说,即使使用了 30 多年的 C 语言,有时sizeof()返回的值仍然令人惊讶。最常见的情况是,如果我struct在 a 中有一个struct,其中一些是打包的,一些不是 – 人们只是会忘记哪一个是为网络打包的,哪一个是为内存访问解包的……结果sizeof()返回了一些意想不到的东西……


    – 


  • 好的,那么实际上只有一个副作用,就像标准(和另一个答案)提到的那样,那就是sizeof如果操作数是 VLA,则会在运行时对其进行评估?


    – 

  • 2
    无关:通过撤消我的编辑,您再次使程序具有未定义的行为。%ld不是正确的转换说明符。%zu是。sizeof(type)和都sizeof expression导致size_t和转换说明符size_tzu


    – 


  • 3
    我认为您忽略了这一点(在编译器消息和 MISRA C 规则中),副作用是指副作用的正式 C 语言定义,而不是一般的英语含义。您的 VLA 示例不包含副作用,但它是在运行时进行评估的。副作用可能是更新sizeof(count++, *bar));位置之类的东西count。另一种形式的副作用是访问volatile变量。


    –