C 标准对 extern const 有何规定

Dav*_*aux 4 c standards language-lawyer

考虑以下程序

\n
extern const int foo;\nextern void blah(void);\n\nint toto(void) {\n  int x = foo;\n  blah();\n  int y = foo;\n  return x + y;\n}\n
Run Code Online (Sandbox Code Playgroud)\n

arm-linux-gnueabihf-gcc -std=c99 -O2 -fno-pic -S extern_const2.c将其编译为

\n
toto:\n    @ args = 0, pretend = 0, frame = 0\n    @ frame_needed = 0, uses_anonymous_args = 0\n    movw    r3, #:lower16:foo\n    movt    r3, #:upper16:foo\n    push    {r4, lr}\n    ldr r4, [r3]\n    bl  blah\n    lsls    r0, r4, #1\n    pop {r4, pc}\n
Run Code Online (Sandbox Code Playgroud)\n

请注意,foo被读取一次,然后左移 1,这意味着假设gcc其值在函数调用期间保持不变。观察到类似的行为clang

\n

我没有看到 ISO/IEC 9899:2017 标准的哪一部分明确讨论了这个假设。\xc2\xa76.7.3-12 解释了对extern volatile const变量的假设(其值可以由硬件更改,但不能由程序分配),但这不适用于extern const.

\n

\xc2\xa76.7.3-6 指出程序不能分配给const。我不太清楚这意味着外部调用不能改变这个变量。

\n

另外,\xc2\xa76.7.3-6的含义我不太清楚。它说

\n
\n

如果尝试通过使用非 const 限定类型的左值来修改使用 const 限定类型定义的对象,则行为未定义

\n
\n

但是,在我的示例中,我将 声明为,而不是定义fooconst。声明和定义之间的区别在\xc2\xa76.7-5中给出:

\n
\n

声明指定一组标识符的解释和属性。标识符的定义是该标识符的声明: \xe2\x80\x94 对于对象,导致为该对象保留存储;

\n
\n

Eri*_*hil 6

\n

但是,在我的示例中,我将 声明为,而不是定义fooconst。声明和定义的区别在\xc2\xa76.7-5:\xe2\x80\xa6中给出

\n
\n

根据 C 2018 6.2.7 2,编译器可能会假设声明为 的对象const也定义为:const

\n
\n

引用同一对象或函数的所有声明应具有兼容的类型;否则,行为是未定义的。

\n
\n

和 6.7.3 11:

\n
\n

为了使两个合格类型兼容,两者都应具有兼容类型的相同合格版本;说明符或限定符列表中类型限定符的顺序不会影响指定的类型。

\n
\n

(请注意,该句子没有说明非限定类型与限定类型兼容的要求是什么,除非我们包含 \xe2\x80\x9cno qualifiers\xe2\x80\x9d 作为限定类型。但是,在整个 C 标准中,兼容性都以肯定的方式进行了说明:仅当有规则声明它们兼容时,两种类型才是兼容的;没有断言它们兼容的声明的任何类型都是不兼容的。)

\n
\n

\xc2\xa76.7.3-6 指出程序不能分配给变量const。我不太清楚这意味着外部调用不能改变这个变量。

\n
\n

根据上述内容,编译器可能会假设整个程序foo都是如此const,并且程序中没有任何内容会改变它。如果程序外部的某些东西可能会改变它,则应该声明它volatile

\n