是否有一种安全的方法来指定对象的值可能是未初始化的,因为它从未被使用过?

123*_*eee 4 c gcc clang micro-optimization

免责声明:以下是纯粹的学术问题; 我将此代码与任何生产系统保持至少100米的距离.这里提出的问题是任何"现实生活"案件都无法衡量的问题.

考虑以下代码(godbolt链接):

#include <stdlib.h>

typedef int (*func_t)(int *ptr); // functions must conform to this interface

extern int uses_the_ptr(int *ptr);
extern int doesnt_use_the_ptr(int *ptr);

int foo() {
    // actual selection is complex, there are multiple functions,
    // but I know `func` will point to a function that doesn't use the argument
    func_t func = doesnt_use_the_ptr;

    int *unused_ptr_arg = NULL; // I pay a zeroing (e.g. `xor reg reg`) in every compiler
    int *unused_ptr_arg; // UB, gcc zeroes (thanks for saving me from myself, gcc), clang doesn't
    int *unused_ptr_arg __attribute__((__unused__)); // Neither zeroing, nor UB, this is what I want

    return (*func)(unused_ptr_arg);
}
Run Code Online (Sandbox Code Playgroud)

编译器没有合理的方法知道unused_ptr_arg不需要(因此归零是浪费时间),但我这样做,所以我想通知编译器unused_ptr_arg可能有任何值,例如寄存器中发生的任何事情都是用于传递给func.

有没有办法做到这一点?我知道我已经超出了标准,所以我可以使用特定于编译器的扩展(特别是对于gcc和clang).

Eri*_*hil 5

使用GCC/Clang`asm`构造

在GCC和Clang以及其他支持GCC扩展汇编语法的编译器中,您可以这样做:

int *unused_ptr_arg;
__asm__("" : "=x" (unused_ptr_arg));

return (*func)(unused_ptr_arg);
Run Code Online (Sandbox Code Playgroud)

__asm__结构 "这里有一些汇编代码插入到程序中.它将结果写入unused_ptr_arg您为其选择的任何位置."(x约束意味着编译器可以选择内存,处理器寄存器或机器支持的任何其他内容.)但实际的汇编代码为空("").因此,不会生成汇编代码,但编译器认为unused_ptr_arg已初始化.在用于x86-64的Clang 6.0.0和GCC 7.3(目前在Compiler Explorer中的最新版本)中,这将生成一个jmpno xor.

使用标准C.

考虑一下:

int *unused_ptr_arg;
(void) &unused_ptr_arg;

return (*func)(unused_ptr_arg);
Run Code Online (Sandbox Code Playgroud)

即使没有使用(void) &unused_ptr_arg;地址,目的也是取地址unused_ptr_arg.这会禁用C 2011 [N1570] 6.3.2.1中的规则,该规则表示如果程序使用可能已声明的自动存储持续时间的未初始化对象的值,则行为未定义register.因为它的地址被采用,所以它不能被声明register,因此根据此规则使用该值不再是未定义的行为.

因此,该对象具有不确定的值.然后存在指针是否可能具有陷阱表示的问题.如果指针在正在使用的C实现中没有陷阱表示,则由于仅仅引用该值而不会发生陷阱,如将其作为参数传递时.

在Compiler Explorer中使用Clang 6.0.0结果是一条jmp没有设置参数寄存器的指令,即使-Wall -Werror添加到编译器选项中也是如此.相反,如果(void)删除该行,则会产生编译器错误.

  • @ PeterJ_01:正如问题所述,这是一般情况的一个特例,其中所有函数必须符合类型`int(*func_t)(int*ptr)`.因此,`func_t`用于在不同时间保存各种指针.某些函数确实使用参数,因此需要传递有效值.某些函数不使用该参数,并且不需要传递特定值.为了提高性能,OP希望在知道不使用参数时避免不必要的初始化. (4认同)
  • @ PeterJ_01:问题的前提是该函数不会取消引用指针. (3认同)
  • @ PeterJ_01:行为未定义.它由GCC和Clang定义.你可以在牙医处理牙菌斑. (2认同)
  • @ PeterJ_01我不知道为什么你还在讨论这个并浪费Eric的时间.我阅读了所有的评论,并且他正确地解释了我的问题,然后给出了一个很好的答案,即使在实际代码中使用也是如此(不,我不会留在那里,但我想测试它). (2认同)