GCC与Clang开启C99模式时是否意外违反序列点合规性?
背景
C99 6.5 §2规定:
在前一个和后一个序列点之间,对象的存储值通过表达式求值最多只能被修改一次。此外,读取先前的值只能是为了确定要存储的值。
其中“读取先前的值只能是为了确定要存储的值”等同于C11新增的正式术语value computation(值计算)。
C11 6.5 §2中的对应内容:
如果对标量对象的副作用相对于同一标量对象的另一副作用或使用同一标量对象值的value computation是未排序的,则行为未定义。如果表达式的子表达式有多种允许的求值顺序,只要其中任何一种顺序出现此类未排序的副作用,行为即未定义。
针对如下代码:
#define get_next() (0,array[i++]) ... get_next() + get_next()
C99与C11存在明确行为差异:
- 在C99中,这段代码并未在前一个和后一个序列点之间多次修改存储值,仅属于未指定行为,符合C99 6.5 §3的规定:
……子表达式的求值顺序和副作用发生的顺序均未指定。
- 在C11中,展开后的子表达式
(0,array[i++]) + (0,array[i++])里,两个i++彼此未排序——即便存在序列点,编译器仍可按如下顺序求值:
这种情况属于未定义行为。0 0 array[i++] array[i++] +
问题
GCC和Clang在支持C11后,是否完全忽略了C99(及更早版本)的合规性?以下是最小示例代码:
int i; int array[10]; #define get_next() (0,array[i++]) int main (void) { get_next() + get_next(); }
Clang编译诊断
使用Clang 18.1.0以-std=c99 -pedantic编译时,给出提示:
multiple unsequenced modifications to 'i' [-Wunsequenced]
“unsequenced”是C11才引入的术语,C99标准中根本不存在,为何符合C99标准的代码会被提示C11相关问题?
GCC编译诊断
使用GCC 14.1以-std=c99 -pedantic -Wall编译时,给出提示:
operation on 'i' may be undefined
依据C99 6.5 §2的规定,这段代码并非未定义行为,该诊断的判定依据是什么?
两款编译器的诊断信息似乎都不符合C99标准。目前尚不清楚编译器是否会据此将这段代码视为未定义行为并生成对应代码,若真是如此,问题会更严重。
内容的提问来源于stack exchange,提问作者Lundin

