c - 如何让 gcc 在编译时优化带有常量参数的函数调用?

标签 c gcc

我正在尝试解析用户输入并根据用户给出的命令执行一些任务。因为在 C 中,switch 不适用于字符串,所以我决定使用 switch 字符串的哈希值来比较要执行的命令。

现在,像这样维护所有可用命令的所有哈希列表

#define EXIT 6385204799
...

真的是个乏味的任务,我在想是否有办法说服 gcc 在编译时使用常量参数评估散列函数,这样我就可以使用这样的东西

switch(hash(command)){
    case hash("exit"): exit();
    // I know, case labels must be compile time constants
    // but that should be fulfilled in my case
}

我知道我可以使用例如元编程,但我对使用 gcc 的解决方案更感兴趣。

有可能吗?

#include <stdio.h>


unsigned long hash(const char *str)
{
        unsigned long hash = 5381;
        int c;

        while ((c = *str++))
                hash = ((hash << 5) + hash) + c;
        return hash;
}

int main( int argc, char **argv ) {
        char *command=NULL;
        size_t size=0;
        printf("Enter string:");
        getline(&command, &size, stdin);
        printf("%ld",hash("exit")); // I want this to evaluate in compile time
        printf("%ld",hash(command)); // and this not
        return 0;
}

最佳答案

Is it even possible?

GCC 不能(对于 C - 它可以用于 C++,见下文),但 Clang/LLVM(版本 3.9.1)可以。使用 -O2 开关启用 2 级优化(或更高)。

Proof .查看反汇编——没有调用哈希函数,没有循环;编译器在编译时计算了哈希值。您的测试用例的简化形式:

#include <stdio.h>

static unsigned long hash(const char *str)
{
        unsigned long hash = 5381;
        int c;

        while ((c = *str++))
                hash = ((hash << 5) + hash) + c;
        return hash;
}

int main( int argc, char **argv ) {
        size_t size=0;
        printf("%ld",hash("exit")); // I want this to evaluate in compile time
        return 0;
}

编译为:

main:                                   # @main
# BB#0:
        push    rax
        #DEBUG_VALUE: main:argc <- %EDI
        #DEBUG_VALUE: main:argv <- %RSI
        #DEBUG_VALUE: main:size <- 0
        movabs  rsi, 6385204799
        mov     edi, .L.str
        xor     eax, eax
        call    printf
        xor     eax, eax
        pop     rcx
        ret

.L.str:
        .asciz  "%ld"

movabs rsi, 6385204799 行直接将预先计算的哈希值加载到rsi 寄存器中。

但是,为了在 switch 语句中的 case 标签中使用,该值不会被视为编译时常量。您需要使用 if ... else 进行比较,而不是使用 switch

如果您有兴趣,使用现代 C++,您可以使用 GCC 和 Clang/LLVM 实现这种类型的优化,您甚至可以使用 switch 语句:

#include <cstdio>

static constexpr unsigned long hash(const char *str)
{
        unsigned long hash = 5381;
        int c = *str;

        while ((c = *str++))
                hash = ((hash << 5) + hash) + c;
        return hash;
}

int main( int argc, char **argv ) {
        size_t size=0;
        printf("%ld",hash("exit")); // I want this to evaluate in compile time

        switch((unsigned long)5 /* i.e. some number */) {
        case hash("exit"):
            // etc
            ;
        }

        return 0;
}

这是 C++14 代码,您需要使用 -std=c++14 来编译它(或使用默认的 GCC 6+)。 (当然,代码不是惯用的 C++ - 它旨在尽可能接近前面的示例)。

关于c - 如何让 gcc 在编译时优化带有常量参数的函数调用?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41624772/

相关文章:

c - C 中的函数指针,带或不带 *?

c - Windows 可执行文件返回失败

c - 如何在支持gnu99的gcc中处理 "warning: inline function ` *stat6 4` declared but never defined"

编译.C 文件 : Undefined symbols for architecture x86_64

c++ - 尝试使用 tcc 针对 gcc 生成的 .o 文件编译源代码时出现奇怪的行为

c++ - 不明确的重载 - 带有参数包的部分函数模板排序

c - 在 C 中组队并对抗随机化器

c - 为什么这个定义指令似乎不起作用?

c - Web 服务器未收到所有请求

c - 为什么 C 语言中没有声明 nullptr?