免责声明:以下是纯粹的学术问题;我将此代码与任何生产系统保持至少100米的距离。这里提出的问题是在任何“现实生活”案件中都无法衡量的。
考虑以下代码(godbolt link):
#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);
}
编译器没有合理的方法知道unused_ptr_arg
是不需要的(因此归零是浪费时间),但我确实如此,所以我想通知编译器unused_ptr_arg
可能有任何值,例如寄存器中的任何内容,用于将其传递给func
。
有办法做到这一点吗?我知道我不在标准范围之内,所以我可以使用特定于编译器的扩展(特别是对于gcc和amp; clang)。
答案 0 :(得分:5)
在GCC和Clang以及其他支持GCC扩展汇编语法的编译器中,您可以这样做:
int *unused_ptr_arg;
__asm__("" : "=x" (unused_ptr_arg));
return (*func)(unused_ptr_arg);
那个__asm__
构造says“这里是一些汇编代码,此时要插入到程序中。它会在unused_ptr_arg
的任何位置将结果写入x
。“(""
约束意味着编译器可以选择内存,处理器寄存器或机器支持的任何其他内容。)但实际装配代码为空(unused_ptr_arg
)。因此,不会生成汇编代码,但编译器认为jmp
已初始化。在用于x86-64的Clang 6.0.0和GCC 7.3(当前位于Compiler Explorer中的最新版本)中,这将生成xor
而没有int *unused_ptr_arg;
(void) &unused_ptr_arg;
return (*func)(unused_ptr_arg);
。
考虑一下:
(void) &unused_ptr_arg;
unused_ptr_arg
的目的是取register
的地址,即使地址未被使用。这会禁用C 2011 [N1570] 6.3.2.1中的规则,如果程序使用可能已使用register
声明的自动存储持续时间的未初始化对象的值,则表示行为未定义。由于它的地址已被采用,因此无法使用jmp
声明它,因此根据此规则使用该值不再是未定义的行为。
因此,该对象具有不确定的值。然后存在指针是否可能具有陷阱表示的问题。如果指针在正在使用的C实现中没有陷阱表示,则由于仅仅引用该值而不会发生陷阱,就像将其作为参数传递一样。
result with Clang 6.0.0 at Compiler Explorer是-Wall -Werror
指令,没有设置参数寄存器,即使(void)
被添加到编译器选项中也是如此。相反,如果删除//The submitted reference code
$ref = $_POST['ref'];
//The array added to the meta query
$ref_query = array(
'meta_key' => 'reference_code',
'meta_value' => array($ref),
'meta_compare' => 'LIKE'
);
行,则会产生编译器错误。
答案 1 :(得分:2)
int *unused_ptr_arg = NULL;
这就是你应该做的。你不需要支付任何费用。归零int
是无操作。从技术上讲,它不是,但实际上它是。您永远不会在程序中看到此操作的时间。而且我并不是说它太小而你也不会注意到它。我的意思是它太小了,所以许多其他因素和操作的数量级要长一些都会“吞噬”它。
答案 2 :(得分:2)
出于一个很好的理由,这在所有架构中实际上都不可能。
对函数的调用可能需要将其参数溢出到堆栈中,而在IA64中,将未初始化的寄存器溢出到堆栈可能会崩溃,因为寄存器的先前内容是加载的推测性负载一个未映射的地址。
答案 3 :(得分:1)
为了防止每次unused_ptr_arg
次出现归零的可能性,只需制作static
int foo() {
func_t func = doesnt_use_the_ptr;
static int *unused_ptr_arg;
return (*func)(unused_ptr_arg);
}
。
mysqli_connect("localhost","root","password","dbname");