如何在给定数值宏值的情况下让C找到宏标识符?

时间:2015-01-22 20:25:26

标签: c c-preprocessor

说我有一些C代码,如:

#define FOO 2
#define BAR 3
#define BAZ 500

void get_value(int val) {
  printf("The value is %s\n", some_function_or_macro_or_something(val));
}

int main(int argc, char** argv) {
  get_value(BAZ);
  get_value(FOO);
  return 0;
}

我希望它打印出来 "价值是BAZ" 和 "值为FOO"。但是有成千上万的#defines,所以我想以编程方式进行。

4 个答案:

答案 0 :(得分:3)

C绝对能够使用#字符串化运算符执行此操作,但您需要直接使用标识符调用宏:

#include <stdio.h>

#define FOO 2
#define BAR 3
#define BAZ 500

#define get_value(val)     \
  printf("The value is %s\n", #val);

int main(void)
{
  get_value(BAZ);
  get_value(FOO);

  return 0;
}

在传递例如值之后的示例中BAZget_value函数,标记BAZ不再被识别,因为参数是按值传递的。换句话说,宏标识符已经消失(当然你仍然可以对其替换进行字符串化,请参阅下面的评论)。

我还能做些什么?

这是一个小服从这个&#34;顶级&#34;规则。假设您有少量具有唯一值的类似对象的宏(常量),那么您可以执行以下操作:

#include <stdio.h>

#define FOO 2
#define BAR 3
#define BAZ 500

#define STRINGIFY(val) #val

void get_value(int val) {
  switch (val) {
    case FOO :
      printf("The value is %s\n", STRINGIFY(FOO)); break;
    case BAR :
      printf("The value is %s\n", STRINGIFY(BAR)); break;
    case BAZ :
      printf("The value is %s\n", STRINGIFY(BAZ)); break;
  }
}

int main(int argc, char* argv) {
  get_value(BAZ);
  get_value(FOO);
  return 0;
}

结果:

The value is BAZ
The value is FOO

答案 1 :(得分:0)

使用正确的工具完成工作。哪个工具知道宏的全部内容?编译器。 GNU gcc和clang都允许转储已定义的宏列表:

$ cc -E -dM -x c /usr/include/stdio.h
#define BUFSIZ 1024
#define EOF (-1)
#define FILENAME_MAX 1024
#define FOPEN_MAX 20
#define L_ctermid 1024
#define L_cuserid 17
#define L_tmpnam 1024
#define NULL ((void *)0)
#define P_tmpdir "/tmp/"
#define SEEK_CUR 1
#define SEEK_END 2
#define SEEK_SET 0
/* etc */

将列出由<stdio.h>定义的整个shebang及其包含的所有内容。 如果需要在C程序中以编程方式处理此数据,

#include <stdio.h>

#define BIG_ENOUGH   1024

int main (void)
{
    char buffer[BIG_ENOUGH];
    FILE *fp = popen ("cc -E -dM -x c /usr/include/stdio.h", "r");
    while (fgets(buffer, sizeof buffer, fp) != NULL) {
       char identifier[BIG_ENOUGH];
       int value;
       if (sscanf (buffer, "#define %s %d", identifier, &value) == 2) {
           printf ("value %d is from macro %s\n", value, identifier);
       }
    }
    fclose(fp);
    return 0;
}

这里打印stdio.h

value 1024 is from macro BUFSIZ
value 1024 is from macro FILENAME_MAX
value 20 is from macro FOPEN_MAX
value 1024 is from macro L_ctermid
value 17 is from macro L_cuserid
value 1024 is from macro L_tmpnam
value 1 is from macro SEEK_CUR
value 2 is from macro SEEK_END
value 0 is from macro SEEK_SET
value 308915776 is from macro TMP_MAX
[...]

而不是stdio.h,您只需使用file.c作为输入。

答案 2 :(得分:0)

经典的,如果有点重量级的方法是表格文件:

enum.tab:

#ifndef DECL_CONST
#    define DECL_CONST(name, value)
#endif

DECL_CONST(FOO, 2)
DECL_CONST(BAR, 3)
DECL_CONST(BAZ, 500)
#undef DECL_CONST

enum.h:

enum My_Enum
{
#define DECL_CONST(name, value) name = value,
#include "enum.tab"
};

char const* get_name(enum My_Enum value);

enum.c:

#include "enum.h"

char const* get_name(enum My_Enum value)
{
    switch (value)
    {
    #define STR(name) #name
    #define DECL_CONST(name, value) case value: return STR(name);
    #include "enum.tab"
    default:
        return 0;
    }
}

main.c中:

#include "enum.h"

void process_value(int v)
{
    char const* s = get_name((enum My_Enum) v);
    if (s)
        printf("The name of value %d is %s\n", v, s);
    else
        printf("There is no name for value %d\n", v);
}

int main()
{
    process_value(1);
    process_value(2);
    process_value(3);
    process_value(500);
    process_value(501);
    return 0;
}

当然,您可以扩展此基本方案以包含任意数量的属性,并支持它们之间的任意数量的依赖关系。请注意,如果您为同一个值定义了多个名称,则switch语句的编译将失败。

答案 3 :(得分:-1)

不确定是否可行。海湾合作委员会在their manual中说明:

  

无法将宏参数转换为字符常量。