如何比较C条件预处理程序指令中的字符串

问题描述 投票:75回答:9

我必须在C中做这样的事情。它只有在我使用char时才有效,但我需要一个字符串。我怎样才能做到这一点?

#define USER "jack" // jack or queen

#if USER == "jack"
#define USER_VS "queen"
#elif USER == "queen"
#define USER_VS "jack"
#endif
c conditional c-preprocessor
9个回答
58
投票

我认为没有办法在预处理程序指令中完全进行可变长度字符串比较。您可以执行以下操作:

#define USER_JACK 1
#define USER_QUEEN 2

#define USER USER_JACK 

#if USER == USER_JACK
#define USER_VS USER_QUEEN
#elif USER == USER_QUEEN
#define USER_VS USER_JACK
#endif

或者您可以稍微重构代码并使用C代码。


20
投票

[更新:2018.05.03]

CAVEAT:并非所有编译器都以相同的方式实现C ++ 11规范。下面的代码在我测试的编译器中工作,而许多评论者使用不同的编译器。

引自Shafik Yaghmour的回答:Computing length of a C string at compile time. Is this really a constexpr?

在编译时不保证对常量表达式进行求值,我们只有草案C ++标准5.19节的非规范性引用,但这样说:

[...]> [注意:在翻译过程中可以评估常量表达式。-end note]

这个词can在世界上有所不同。

所以,YMMV对这个(或任何)涉及constexpr的答案,取决于编译器作者对规范的解释。

[更新2016.01.31]

有些人不喜欢我之前的回答,因为它通过完成目标而不需要字符串比较来避免OP的整个compile time string compare方面,这里有一个更详细的答案。

你不能!不在C98或C99中。甚至没有在C11。没有多少MACRO操作会改变这一点。

const-expression中使用的#if的定义不允许使用字符串。

它允许使用字符,因此如果您将自己限制为字符,则可以使用此字符:

#define JACK 'J'
#define QUEEN 'Q'

#define CHOICE JACK     // or QUEEN, your choice

#if 'J' == CHOICE
#define USER "jack"
#define USER_VS "queen"
#elif 'Q' == CHOICE
#define USER "queen"
#define USER_VS "jack"
#else
#define USER "anonymous1"
#define USER_VS "anonymous2"
#endif

#pragma message "USER    IS " USER
#pragma message "USER_VS IS " USER_VS

您可以!在C ++ 11中。如果为比较定义编译时辅助函数。

// compares two strings in compile time constant fashion
constexpr int c_strcmp( char const* lhs, char const* rhs )
{
    return (('\0' == lhs[0]) && ('\0' == rhs[0])) ? 0
        :  (lhs[0] != rhs[0]) ? (lhs[0] - rhs[0])
        : c_strcmp( lhs+1, rhs+1 );
}
// some compilers may require ((int)lhs[0] - (int)rhs[0])

#define JACK "jack"
#define QUEEN "queen"

#define USER JACK       // or QUEEN, your choice

#if 0 == c_strcmp( USER, JACK )
#define USER_VS QUEEN
#elif 0 == c_strcmp( USER, QUEEN )
#define USER_VS JACK
#else
#define USER_VS "unknown"
#endif

#pragma message "USER    IS " USER
#pragma message "USER_VS IS " USER_VS

因此,最终,您将不得不改变实现为USERUSER_VS选择最终字符串值的目标的方式。

您不能在C99中进行编译时字符串比较,但您可以选择字符串进行编译时。

如果您真的必须进行编译时间比较,那么您需要更改为允许该功能的C ++ 11或更新版本。

[原始回答]

尝试:

#define jack_VS queen
#define queen_VS jack

#define USER jack          // jack    or queen, your choice
#define USER_VS USER##_VS  // jack_VS or queen_VS

// stringify usage: S(USER) or S(USER_VS) when you need the string form.
#define S(U) S_(U)
#define S_(U) #U

更新:ANSI令牌粘贴有时不太明显。 ;-D

在宏之前放置一个#会导致它被更改为其值的字符串,而不是其裸值。

在两个令牌之间放置一个双##会使它们连接成一个令牌。

所以,宏USER_VS有扩展jack_VSqueen_VS,取决于你如何设置USER

stringify宏S(...)使用宏间接,因此命名宏的值将转换为字符串。而不是宏的名称。

因此,USER##_VS变成jack_VS(或queen_VS),取决于你如何设置USER

之后,当stringify宏用作S(USER_VS)时,USER_VS(本例中为jack_VS)的值被传递给间接步骤S_(jack_VS),后者将其值(queen)转换为字符串"queen"

如果你将USER设置为queen,那么最终结果就是字符串"jack"

有关标记连接,请参阅:https://gcc.gnu.org/onlinedocs/cpp/Concatenation.html

有关令牌字符串转换,请参阅:https://gcc.gnu.org/onlinedocs/cpp/Stringification.html#Stringification

[更新2015.02.15纠正错字。]


7
投票

使用数值而不是字符串。

最后,要将常量JACK或QUEEN转换为字符串,请使用stringize(和/或tokenize)运算符。


3
投票

以下是clang为我工作的。允许显示为符号宏值比较。 #error xxx只是为了看看编译器到底做了什么。用#define cat(a,b)替换cat定义a ## b会破坏事物。

#define cat(a,...) cat_impl(a, __VA_ARGS__)
#define cat_impl(a,...) a ## __VA_ARGS__

#define xUSER_jack 0
#define xUSER_queen 1
#define USER_VAL cat(xUSER_,USER)

#define USER jack // jack or queen

#if USER_VAL==xUSER_jack
  #error USER=jack
  #define USER_VS "queen"
#elif USER_VAL==xUSER_queen
  #error USER=queen
  #define USER_VS "jack"
#endif

1
投票

如上所述,ISO-C11预处理器不支持字符串比较。但是,分配具有“相反值”的宏的问题可以通过“令牌粘贴”和“表访问”来解决。 Jesse的简单连接/字符串化宏解决方案因gcc 5.4.0而失败,因为字符串化是在评估连接之前完成的(符合ISO C11)。但是,它可以修复:

#define P_(user) user ## _VS
#define VS(user) P_ (user)
#define S(U) S_(U)
#define S_(U) #U

#define jack_VS  queen
#define queen_VS jack

S (VS (jack))
S (jack)
S (VS (queen))
S (queen)

#define USER jack          // jack    or queen, your choice
#define USER_VS USER##_VS  // jack_VS or queen_VS
S (USER)
S (USER_VS)

第一行(宏P_())添加一个间接让下一行(宏VS())在字符串化之前完成连接(参见Why do I need double layer of indirection for macros?)。字符串化宏(S()S_())来自Jesse。

表格(宏jack_VSqueen_VS)比OP的if-then-else结构更容易维护,来自Jesse。

最后,下一个四行块调用函数式宏。最后四行是来自Jesse的回答。

foo.c中存储代码并调用预处理器gcc -nostdinc -E foo.c会产生:

# 1 "foo.c"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "foo.c"
# 9 "foo.c"
"queen"
"jack"
"jack"
"queen"



"jack"
"USER_VS"

输出符合预期。最后一行显示USER_VS宏在字符串化之前未展开。


1
投票

qazxsw poi和qazxsw poi的回答让我做了以下事情:

Patrick

而不是Jesse Chisholm #define QUEEN 'Q' #define JACK 'J' #define CHECK_QUEEN(s) (s==QUEEN) #define CHECK_JACK(s) (s==JACK) #define USER 'Q' [... later on in code ...] #if CHECK_QUEEN(USER) compile_queen_func(); #elif CHECK_JACK(USER) compile_jack_func(); #elif #error "unknown user" #endif 也应该工作但没有经过测试 也工作,可能更容易处理。

编辑:根据@ Jean-FrançoisFabre的评论我调整了我的答案。


0
投票

如果你的字符串是编译时常量(如你的情况),你可以使用以下技巧:

#define USER 'Q'

编译器可以提前告诉strcmp的结果,并将strcmp替换为其结果,从而为您提供一个可以与预处理器指令进行比较的#define。我不知道编译器/依赖编译器选项之间是否存在任何差异,但它对GCC 4.7.2有用。

编辑:经过进一步调查,看起来这是一个工具链扩展,而不是GCC扩展,所以考虑到这一点......


0
投票
#define USER QUEEN

它基本上是手动初始化的固定长度静态字符数组,而不是自动初始化的可变长度静态字符数组,总是以终止空字符结尾


-4
投票

这很简单,我想你可以说

#define USER_JACK strcmp(USER, "jack")
#define USER_QUEEN strcmp(USER, "queen")
#if $USER_JACK == 0
#define USER_VS USER_QUEEN
#elif USER_QUEEN == 0
#define USER_VS USER_JACK
#endif
© www.soinside.com 2019 - 2024. All rights reserved.