C23引入了属性[[reproducible]][[unsequenced]].

  • 他们背后的动机是什么?
  • 它们是如何定义的,它们对函数有什么影响?
  • 我应该将它们应用于什么类型的函数?

推荐答案

激励性的问题是,当没有可用的函数定义时,编译器无法洞察函数.这会阻止几乎所有的编译器优化(除非使用LTO).

请考虑以下示例:

// note: this is redundant, because [[未排序]] implies [[可重现]]
int square(int x) [[可重现]] [[未排序]];

int arr[] = {
    square(2),
    square(2),
    square(3),
    square(3),
};

即使编译器没有square的定义,它也可以执行两个优化:

  • 由于函数为[[可重现]],因此连续两次调用square(2)会产生相同的结果,并且编译器可以决定只调用square(2)一次
  • 因为函数是[[未排序]],所以可以以任何顺序调用square,编译器甚至可以在程序启动时决定只计算square(2)一次.它还可以决定在square(2)之前计算square(3),如果这样做更有效率的话.

这样的优化也可以通过在头部中定义函数inline,并让编译器自己推断这些属性来实现.然而,对于复杂的函数,由于额外的编译速度减慢,将所有函数都设置为inline并不可行.

半形式化定义

有关更严格的说明,请参阅C23 standard working draft N3096§6.7.12.7函数类型的标准属性.

[[可重现]]

该属性断言函数是reproducible function,这意味着

  • 它是effectless,而且
  • idempotent美元

Effectless限制函数可以修改的状态.如果修改了任何非本地状态,则只能通过传递给它的指针进行修改.例如,如果一个void to_upper_case(char *str)函数只修改局部变量和str的内容,那么它就是effectless.(直觉上,该功能没有可观察到的副作用.)

Idempotent意味着多次调用该函数与调用一个函数具有相同的效果.例如,我们可以呼叫to_upper_case(s); to_upper_case(s);,这与只呼叫一次具有相同的效果.

[[未排序]]

该属性断言函数是unsequenced function,这意味着

  • 它是effectlessidempotent(也就是reproducible)
  • stateless美元
  • independent美元

Stateless表示staticthread_local个局部变量不能为非const,也不能为volatile.

Independent意味着函数的所有调用都将看到相同的全局变量值,不会更改全局状态,也不会通过指针参数更改任何状态.to_upper_case不是独立的,但像strlen这样的函数可以是独立的.

直观地说,unsequenced函数可以被任意排序,甚至可以在其观察到的状态改变之间并行地排序:(另见标准中的脚注196)

char *str = /* ... */;  // A
  strlen(str);
  global = 123;
  strlen(str);
strcpy(str, /* ... */); // B

在本例中,在点AB之间可以有一个、两个或无限多个对strlen的呼叫.这些可以顺序进行,也可以并行进行.无论如何,对于unsequenced函数,结果必须是相同的.global的Mutations 不允许改变strlen的结果.

关于GCC定语的注记

GCC属性pureconst是这些标准属性的灵感来源,它们的行为类似.有关比较,请参见N2956 5.8 Some differences with GCC const and pure.简而言之:

  • pure[[independent]]轻松多了
  • const[[未排序]]更严格

何时使用这些属性

These attributes are meant for advanced users who want to take advantage of compiler optimizations.

一般而言,您必须非常小心地应用它们.该程序格式错误,如果将它们应用于没有断言属性的函数,则不需要进行诊断.我们鼓励编译器检测这些属性的误用,但这不是必需的.

常见的例子(和惊喜)

  • 显然,printf既不是
  • strlenmemcmp可以是[[未排序]]
  • memcpy可以是[[可重现]]
  • memmove也不可能,因为对于重叠的内存区域来说,它不是idempotent
  • fabs可以是[[未排序]]
  • sqrt也不能,因为它修改了浮点环境并可能设置errno

看见

C++相关问答推荐

VS代码C/C++扩展intellisense无法检测环境特定函数'

DPDK-DumpCap不捕获端口上的传入数据包

带有sigLongjMP中断I/O的异常处理程序

Rust FFI--如何用给出返回引用的迭代器包装C风格的迭代器?

如何在不使用其他数组或字符串的情况下交换字符串中的两个单词?

预先分配虚拟地址空间的区域

在循环中复制与删除相同条件代码的性能

C中的FREE函数正在触发断点

==284==错误:AddressSaniizer:堆栈缓冲区下溢

如何只获取字符串的第一个单词,然后将其与c中的另一个单词进行比较?

添加函数会 destruct 嵌入式C代码(无IDE)

在C中创建任意类型的只读指针参数

如何使用libgpio(d)为Raspberry Pi编译C程序?

-Wnonnull-Compare警告不是具有误导性吗?

C23标准是否向后兼容?

为什么一个在线编译器拒绝这个VLA代码,而本地的Apple clang却不拒绝;t?

在NASM中链接Linux共享库时出错-';将R_ X86_64_;foo';

GCC认为这是一个VLA是对的吗?

在列表中查找素数

设置具有非零终止字符串的大整数