C语言 如何比较 C 条件预处理器指令中的字符串

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2335888/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-02 04:37:09  来源:igfitidea点击:

How to compare strings in C conditional preprocessor-directives

cconditionalc-preprocessor

提问by frx08

I have to do something like this in C. It works only if I use a char, but I need a string. How can I do this?

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

#define USER "Hyman" // Hyman or queen

#if USER == "Hyman"
#define USER_VS "queen"
#elif USER == "queen"
#define USER_VS "Hyman"
#endif

采纳答案by Brian R. Bondy

I don't think there is a way to do variable length string comparisons completely in preprocessor directives. You could perhaps do the following though:

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

#define USER_Hyman 1
#define USER_QUEEN 2

#define USER USER_Hyman 

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

Or you could refactor the code a little and use C code instead.

或者您可以稍微重构一下代码并改用 C 代码。

回答by Jesse Chisholm

[UPDATE: 2018.05.03]

[更新:2018.05.03]

CAVEAT: Not all compilers implement the C++11 specification in the same way. The below code works in the compiler I tested on, while many commenters used a different compiler.

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

Quoting from Shafik Yaghmour's answer at: Computing length of a C string at compile time. Is this really a constexpr?

引用 Shafik Yaghmour 的回答:Computing length of a C string at compile time。这真的是 constexpr 吗?

Constant expressions are not guaranteed to be evaluated at compile time, we only have a non-normative quote from draft C++ standard section 5.19 Constant expressions that says this though:

[...]>[ Note: Constant expressions can be evaluated during translation.—end note ]

不能保证在编译时计算常量表达式,我们只有 C++ 标准草案第 5.19 节常量表达式中的一个非规范引用,但它说明了这一点:

[...]>[ 注意:常量表达式可以在翻译过程中计算。-结束注释]

That word canmakes all the difference in the world.

这个词can让世界变得不同。

So, YMMV on this (or any) answer involving constexpr, depending on the compiler writer's interpretation of the spec.

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

[UPDATED 2016.01.31]

[更新 2016.01.31]

As some didn't like my earlier answer because it avoidedthe whole compile time string compareaspect of the OP by accomplishing the goal with no need for string compares, here is a more detailed answer.

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

You can't! Not in C98 or C99. Not even in C11. No amount of MACRO manipulation will change this.

你不能!不在 C98 或 C99 中。甚至在 C11 中也没有。再多的宏操作都不会改变这一点。

The definition of const-expressionused in the #ifdoes not allow strings.

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

It does allow characters, so if you limit yourself to characters you might use this:

它确实允许字符,所以如果你限制自己使用字符,你可以使用这个:

#define Hyman 'J'
#define QUEEN 'Q'

#define CHOICE Hyman     // or QUEEN, your choice

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

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

You can! In C++11. If you define a compile time helper function for the comparison.

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

// compares two strings in compile time constant fashion
constexpr int c_strcmp( char const* lhs, char const* rhs )
{
    return (('
#define Hyman_VS queen
#define queen_VS Hyman

#define USER Hyman          // Hyman    or queen, your choice
#define USER_VS USER##_VS  // Hyman_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
' == lhs[0]) && ('
#define cat(a,...) cat_impl(a, __VA_ARGS__)
#define cat_impl(a,...) a ## __VA_ARGS__

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

#define USER Hyman // Hyman or queen

#if USER_VAL==xUSER_Hyman
  #error USER=Hyman
  #define USER_VS "queen"
#elif USER_VAL==xUSER_queen
  #error USER=queen
  #define USER_VS "Hyman"
#endif
' == 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 Hyman "Hyman" #define QUEEN "queen" #define USER Hyman // or QUEEN, your choice #if 0 == c_strcmp( USER, Hyman ) #define USER_VS QUEEN #elif 0 == c_strcmp( USER, QUEEN ) #define USER_VS Hyman #else #define USER_VS "unknown" #endif #pragma message "USER IS " USER #pragma message "USER_VS IS " USER_VS

So, ultimately, you will have to change the way you accomlish your goal of choosing final string values for USERand USER_VS.

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

You can't do compile time string compares in C99, but you can do compile time choosing of strings.

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

If you really must do compile time sting comparisons, then you need to change to C++11 or newer variants that allow that feature.

如果您确实必须进行编译时刺痛比较,那么您需要更改为 C++11 或允许该功能的更新变体。

[ORIGINAL ANSWER FOLLOWS]

[原答案如下]

Try:

尝试:

#define USER_Hyman strcmp(USER, "Hyman")
#define USER_QUEEN strcmp(USER, "queen")
#if $USER_Hyman == 0
#define USER_VS USER_QUEEN
#elif USER_QUEEN == 0
#define USER_VS USER_Hyman
#endif

UPDATE: ANSI token pasting is sometimes less than obvious. ;-D

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

Putting a single #before a macro causes it to be changed into a string of its value, instead of its bare value.

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

Putting a double ##between two tokens causes them to be concatenated into a single token.

##在两个标记之间放置一个 double会使它们连接成一个标记。

So, the macro USER_VShas the expansion Hyman_VSor queen_VS, depending on how you set USER.

因此,宏USER_VS具有扩展Hyman_VSqueen_VS,具体取决于您如何设置USER.

The stringifymacro S(...)uses macro indirection so the value of the named macro gets converted into a string. instead of the name of the macro.

字符串化S(...)使用宏间接这样命名宏的值被转换成字符串。而不是宏的名称。

Thus USER##_VSbecomes Hyman_VS(or queen_VS), depending on how you set USER.

因此USER##_VS变成Hyman_VS(或queen_VS),这取决于您如何设置USER

Later, when the stringifymacro is used as S(USER_VS)the value of USER_VS(Hyman_VSin this example) is passed to the indirection step S_(Hyman_VS)which converts its value (queen) into a string "queen".

稍后,当字符串化宏用作S(USER_VS)的值USER_VSHyman_VS在本例中)被传递到间接步骤S_(Hyman_VS)其值(转换queen)成一个字符串"queen"

If you set USERto queenthen the final result is the string "Hyman".

如果设置USER为,queen则最终结果是 string "Hyman"

For token concatenation, see: https://gcc.gnu.org/onlinedocs/cpp/Concatenation.html

对于令牌串联,请参阅:https: //gcc.gnu.org/onlinedocs/cpp/Concatenation.html

For token string conversion, see: https://gcc.gnu.org/onlinedocs/cpp/Stringification.html#Stringification

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

[UPDATED 2015.02.15 to correct a typo.]

[更新 2015.02.15 以更正错字。]

回答by Konstantin Bobrovskii

The following worked for me with clang. Allows what appears as symbolic macro value comparison. #error xxxis just to see what compiler really does. Replacing catdefinition with #define cat(a,b) a ## bbreaks things.

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

#define QUEEN 'Q'
#define Hyman 'J'

#define CHECK_QUEEN(s) (s==QUEEN)
#define CHECK_Hyman(s) (s==Hyman)

#define USER 'Q'

[... later on in code ...]

#if CHECK_QUEEN(USER)
  compile_queen_func();
#elif CHECK_Hyman(USER)
  compile_Hyman_func();
#elif
#error "unknown user"
#endif

回答by Patrick

Use numeric values instead of strings.

使用数值而不是字符串。

Finally to convert the constants Hyman or QUEEN to a string, use the stringize (and/or tokenize) operators.

最后,要将常量 Hyman 或 QUEEN 转换为字符串,请使用字符串化(和/或标记化)运算符。

回答by EpsilonVector

If your strings are compile time constants (as in your case) you can use the following trick:

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

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

#define Hyman_VS  queen
#define queen_VS Hyman

S (VS (Hyman))
S (Hyman)
S (VS (queen))
S (queen)

#define USER Hyman          // Hyman    or queen, your choice
#define USER_VS USER##_VS  // Hyman_VS or queen_VS
S (USER)
S (USER_VS)

The compiler can tell the result of the strcmp in advance and will replace the strcmp with its result, thus giving you a #define that can be compared with preprocessor directives. I don't know if there's any variance between compilers/dependance on compiler options, but it worked for me on GCC 4.7.2.

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

EDIT: upon further investigation, it look like this is a toolchain extension, not GCC extension, so take that into consideration...

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

回答by benni

The answere by Patrickand by Jesse Chisholmmade me do the following:

由的answere帕特里克杰西·奇泽姆让我做到以下几点:

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



"Hyman"
"USER_VS"

Instead of #define USER 'Q'#define USER QUEENshould also work but was not testedalso works and might be easier to handle.

代替 #define USER 'Q'#define USER QUEEN也应该工作,但没有经过测试也有效,可能更容易处理。

EDIT: According to the comment of @Jean-Fran?ois Fabre I adapted my answer.

编辑:根据@Jean-Fran?ois Fabre 的评论,我修改了我的答案。

回答by hermannk

As already stated above, the ISO-C11 preprocessor does notsupport string comparison. However, the problem of assigning a macro with the “opposite value” can be solved with “token pasting” and “table access”. Jesse's simple concatenate/stringify macro-solution fails with gcc 5.4.0 because the stringization is done beforethe evaluation of the concatenation (conforming to ISO C11). However, it can be fixed:

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

#define USER_IS(c0,c1,c2,c3,c4,c5,c6,c7,c8,c9)\
ch0==c0 && ch1==c1 && ch2==c2 && ch3==c3 && ch4==c4 && ch5==c5 && ch6==c6 && ch7==c7 ;

#define ch0 'j'
#define ch1 'a'
#define ch2 'c'
#define ch3 'k'

#if USER_IS('j','a','c','k',0,0,0,0)
#define USER_VS "queen"
#elif USER_IS('q','u','e','e','n',0,0,0)
#define USER_VS "Hyman"
#endif

The first line (macro P_()) adds one indirection to let the next line (macro VS()) finish the concatenation beforethe stringization (see Why do I need double layer of indirection for macros?). The stringization macros (S()and S_()) are from Jesse.

第一行 (macro P_()) 添加一个间接,让下一行 (macro VS())在字符串化之前完成连接(请参阅 为什么我需要为宏使用双层间接?)。字符串化宏(S()S_())来自 Jesse。

The table (macros Hyman_VSand queen_VS) which is much easier to maintain than the if-then-else construction of the OP is from Jesse.

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

Finally, the next four-line block invokes the function-style macros. The last four-line block is from Jesse's answer.

最后,下一个四行代码块调用函数风格的宏。最后一个四行块来自 Jesse 的回答。

Storing the code in foo.cand invoking the preprocessor gcc -nostdinc -E foo.cyields:

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

#define NAME Hyman    
#if NAME == queen 

The output is as expected. The last line shows that the USER_VSmacro is notexpanded before stringization.

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

回答by yan bellavance

##代码##

it basically a fixed length static char array initialized manually instead of a variable length static char array initialized automatically always ending with a terminating null char

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

回答by pabitra nayak

It's simple I think you can just say

很简单 我想你可以说

##代码##