【问题标题】:Macro shadowing both function and its address宏遮蔽函数及其地址
【发布时间】:2020-06-17 11:53:13
【问题描述】:

我正在使用 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),&amp;print_numbers 不会被声明。有没有办法编写一个宏来声明用法和函数的地址?仅使用函数名称编写第二个定义会导致 print_numbers previously defined here 错误。

我想避免“特殊的空函数”,因为这个库中有 15 个其他函数需要在发布时删除。该库适用于嵌入式设备,我希望内存占用尽可能小。

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

【问题讨论】:

  • 我不明白你为什么要尝试获取和打印类似函数的宏的地址而不是定义的函数?宏只是在编译之前扩展的宏。它在内存中没有地址。任何这样做的尝试都是违反 C 语法的,编译器应该提供一个错误。
  • 不想获取类函数宏的地址。我希望这个函数的地址有一些值(最好是空),因为这个函数的地址在图书馆的很多地方都使用过,我试图尽可能少地修改。

标签: c macros variadic-macros


【解决方案1】:

如果print_numbers 不可用/ENABLE_PRINT_NUMBERS0,您可以将其初始化为NULL 指针,而不是类似函数的宏:

#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*)&amp;print_numbers); 打印函数地址是错误的。 x 转换说明符需要 unsigned int 类型的参数,而 C 标准不允许您将函数指针强制转换为 void 指针。

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

【讨论】:

    【解决方案2】:

    你的方法不对。

    应用程序代码应该检查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;
    }
    

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

    【讨论】:

      【解决方案3】:

      宏在编译之前进行处理(称为预处理)。因此,当条件为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

      宏在预处理期间被替换为文本(这是一种简化,但对于这个问题的范围来说已经足够了)。

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

      【讨论】:

      • 是的,我同意,但是如何处理这个函数指针呢?我希望它为空,但如何使用宏来实现这一点似乎并不明显。
      • 名称为print_numbers 的对象不存在。那么你想如何获得它的地址呢?预处理后代码中某处看到print_numbers函数的定义了吗?
      • 我在我的问题中添加了一个澄清。感谢您的回答:)
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-07-01
      • 1970-01-01
      • 2015-09-12
      • 1970-01-01
      • 2019-05-19
      • 1970-01-01
      相关资源
      最近更新 更多