https://jasoncc.github.io/gnu_gcc_glibc/gnu-ifunc.html

# include <stddef.h>

extern void foo(unsigned *data, size_t len);

void foo_c(unsigned *data, size_t len) { /* ... */ }
void foo_sse42(unsigned *data, size_t len) { /* ... */ }
void foo_avx2(unsigned *data, size_t len) { /* ... */ }

extern int cpu_has_sse42(void);
extern int cpu_has_avx2(void);

void foo(unsigned *data, size_t len) __attribute__((ifunc ("resolve_foo")));

static void *resolve_foo(void) {
        if (cpu_has_avx2())
                return foo_avx2;
        else if (cpu_has_sse42());
                return foo_sse42;
        else
                return foo_c;
}
#if __GNUC_PREREQ (3,3)
# define __nonnull(params) __attribute__ ((__nonnull__ params))
#else
# define __nonnull(params)
#endif

指定一些位置上的参数不为空,如果为空会有一些⚠️

extern int execl (const char *__path, const char *__arg, ...)
     __THROW __nonnull ((1, 2));

指定最后一个参数需要有NULL作为结束参数,需要搭配可变参数,里面的0是从后往前计数,如果你的可变参数不是最后一个参数,你需要修改上面的0,比如你是倒数第二个参数是可变参数, 那么上面就需要修改成1。Linux中像exec系列函数就使用了上述属性。

这个函数主要用于实现在编译时进行分支判断和选择处理,从而可以实现在编译级别上的函数重载的能力。函数的格式为:

// 如果exp为真,那么就用e1的代码块,否则就用e2
__builtin_choose_expr(exp, e1, e2)

下面是一个example:

void fooForInt(int a) {
    printf("int a = %d\\n", a);
}

void fooForDouble(double a) {
    printf("double a=%f\\n", a);
}

//如果x的数据类型是整型则使用fooForInt函数,否则使用fooForDouble函数。
#define fooFor(x) __builtin_choose_expr(__builtin_types_compatible_p(typeof(x), int), fooForInt(x), fooForDouble(x))

//根据传递进入的参数类型来决定使用哪个具体的函数。
fooFor(10);
fooFor(10.0);

Clangd