宏隐藏函数及其地址

Macro shadowing both function and its address

我正在使用 C 编写一个库。我有一个函数应该可供库的用户使用。当某个值定义为 1 时应该编译执行,但是当该值定义为 0 时根本不应该编译(用法应替换为 (void)0 将被优化掉)。

我想通了,我可以为此使用宏,如下所示:

#include <stdio.h>
#include <stdarg.h>

#define ENABLE_PRINT_NUMBERS 0

#if ENABLE_PRINT_NUMBERS
void print_numbers(int number, ...);
#endif

#if !ENABLE_PRINT_NUMBERS
#define print_numbers(number, ...) (void)0
#endif

#if ENABLE_PRINT_NUMBERS
void print_numbers(int number, ...) {
    va_list va;
    va_start(va, number);
    const int second_number = va_arg(va, int);
    va_end(va);

    printf("numbers: %d, %d\n", number, second_number);
}
#endif

int main() {
    printf("## Shadowing by macro - test ##\n\n");

    print_numbers(1, 2);

    printf("function address: 0x%x\n", (void*)&print_numbers);

    return 0;
}

然而,当这样一个函数没有被编译时(#define ENABLE_PRINT_NUMBERS 1),&print_numbers没有被声明。有没有一种方法可以编写一个宏来声明用法和函数的地址?仅使用函数名称编写第二个定义会导致 print_numbers previously defined here 错误。

我想避免使用 "special empty functions",因为这个库中还有大约 15 个其他函数需要在发布时删除。该库适用于嵌入式设备,我希望内存占用尽可能小。

要明确:
我不想获取类函数宏的地址。我希望这个函数的地址有一些值(最好是 null),因为这个函数的地址在库中的许多地方使用,我试图尽可能少地修改库。使用这种地址的地方会检查指针是否为空,所以不会导致未定义的行为。

宏在编译前进行处理(称为预处理)。因此,当条件为 false 时,位于 #if 块中的代码部分在编译开始时不存在。

传递给实际 C 编译器的生成的 C 文件是:

int main() {
    printf("## Shadowing by macro - test ##\n\n");

    (void)0;

    printf("function address: 0x%x\n", (void*)&print_numbers);

    return 0;
}

https://godbolt.org/z/TBM3hp

宏在预处理过程中以文本形式被替换(这是一种简化,但对于这个问题的范围来说已经足够好了)。

没有函数定义,因此没有它的地址。

你的做法是错误的。

应用程序代码应该检查 ENABLE_PRINT_NUMBERS 以查看是否调用有问题的函数。

int main() {
    printf("## Shadowing by macro - test ##\n\n");

#ifdef ENABLE_PRINT_NUMBERS
    print_numbers(1, 2);

    printf("function address: 0x%x\n", (void*)&print_numbers);
#else
    printf("print_numbers not defined, do something else instead\n");
#endif

    return 0;
}

这是应用程序代码检查库的不同版本中是否存在特定功能的普遍接受的方法。

您需要的是函数指针而不是类函数宏,如果 print_numbers 不可用/ENABLE_PRINT_NUMBERS 是 [=,您可以将其初始化为 NULL 指针14=]:

#include <stdio.h>
#include <stdarg.h>

#define ENABLE_PRINT_NUMBERS 1

#if ENABLE_PRINT_NUMBERS
void print_numbers(int number, ...);
#endif

int main (void) {

    void (*a)(int, ...);

    #if ENABLE_PRINT_NUMBERS
    a = &print_numbers; 
    #else
    a = 0;
    #endif

    printf("## Shadowing by macro - test ##\n\n");

    #if ENABLE_PRINT_NUMBERS

    print_numbers(1, 2);

    printf("function address: ");

    unsigned char *p = (unsigned char *)&a;
    size_t i;

    for (i = 0; i < sizeof a; i++)
    {
        printf("%02x ", p[i]);
    }

    putchar('\n');

    #endif

    return 0;
}

#if ENABLE_PRINT_NUMBERS
void print_numbers(int number, ...) {
    va_list va;
    va_start(va, number);
    const int second_number = va_arg(va, int);
    va_end(va);

    printf("numbers: %d, %d\n", number, second_number);
}
#endif

旁注:

  • 类似函数的宏在内存中没有地址。宏在编译前由预处理器展开。任何获取甚至打印其地址的尝试都是 C 语法违规。

  • printf("function address: 0x%x\n", (void*)&print_numbers);打印函数地址错误。 x 转换说明符需要类型为 unsigned int 的参数,而 C 标准不允许您将函数指针转换为 void 指针。

    我实际上发现几乎不可能打印函数地址而不是一种方法,我发现 here,使用从函数指针到 unsigned char 的转换指针和打印时的一些技巧。我用了那个方法。