我必须在C中做这样的事情。它只在我使用char时有效,但我需要一个字符串。我怎么能这样做?
#define USER "jack" // jack or queen
#if USER == "jack"
#define USER_VS "queen"
#elif USER == "queen"
#define USER_VS "jack"
#endif
答案 0 :(得分:55)
我认为没有办法在预处理程序指令中完全进行可变长度字符串比较。您可以执行以下操作:
#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代码。
答案 1 :(得分:19)
[更新: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节这样说的常量表达式:
[...]> [注意:可以在期间评估常量表达式 翻译 - 结束说明]
这个词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
因此,最终,您必须改变实现为USER
和USER_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_VS
或queen_VS
,具体取决于您设置USER
的方式。
stringify 宏S(...)
使用宏间接,因此命名宏的值将转换为字符串。而不是宏的名称。
因此USER##_VS
变为jack_VS
(或queen_VS
),具体取决于您设置USER
的方式。
稍后,当 stringify 宏用作S(USER_VS)
时,USER_VS
(此示例中为jack_VS
)的值将传递到间接步骤{{ 1}}将其值(S_(jack_VS)
)转换为字符串queen
。
如果您将"queen"
设置为USER
,则最终结果为字符串queen
。
对于标记连接,请参阅:https://gcc.gnu.org/onlinedocs/cpp/Concatenation.html
有关令牌字符串转换,请参阅:https://gcc.gnu.org/onlinedocs/cpp/Stringification.html#Stringification
[更新2015.02.15纠正错字。]
答案 2 :(得分:7)
使用数值代替字符串。
最后要将常量JACK或QUEEN转换为字符串,请使用stringize(和/或tokenize)运算符。
答案 3 :(得分:3)
以下为clang工作了。允许显示为符号宏值比较。 #error xxx 只是为了看看编译器到底做了什么。用 #define cat(a,b)a ## b 替换 cat 定义会破坏事物。
#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
答案 4 :(得分:1)
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
而不是#define USER 'Q'
#define USER QUEEN
也应该可以工作但未经过测试也可以工作,可能更容易处理。
答案 5 :(得分: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_VS
和queen_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
宏不已展开。
答案 6 :(得分:0)
如果您的字符串是编译时常量(如您的情况),您可以使用以下技巧:
#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
编译器可以提前告诉strcmp的结果,并将strcmp替换为其结果,从而为您提供一个可以与预处理器指令进行比较的#define。我不知道编译器/依赖编译器选项之间是否存在任何差异,但它对GCC 4.7.2有用。
编辑:经过进一步调查,看起来这是一个工具链扩展,而不是GCC扩展,所以考虑到这一点...
答案 7 :(得分:0)
start = time.time()
基本上是固定长度的静态char数组,手动初始化 而不是自动初始化的可变长度静态char数组始终以终止的空char结尾
答案 8 :(得分:0)
如果将USER定义为带引号的字符串,则不能这样做。
但是如果USER只是JACK或QUEEN或Joker或其他任何东西,您可以这样做。
有两个技巧:
#define JACK
进行某些操作所以让我们开始:
#define JACK_QUEEN_OTHER(u) EXPANSION1(ReSeRvEd_, u, 1, 2, 3)
现在,如果我写JACK_QUEEN_OTHER(USER)
,并且USER是JACK,则预处理器
变成EXPANSION1(ReSeRvEd_, JACK, 1, 2, 3)
第二步是串联:
#define EXPANSION1(a, b, c, d, e) EXPANSION2(a##b, c, d, e)
现在JACK_QUEEN_OTHER(USER)
变成EXPANSION2(ReSeRvEd_JACK, 1, 2, 3)
这使您有机会根据字符串是否匹配来添加多个逗号:
#define ReSeRvEd_JACK x,x,x
#define ReSeRvEd_QUEEN x,x
如果USER为JACK,则JACK_QUEEN_OTHER(USER)
变为EXPANSION2(x,x,x, 1, 2, 3)
如果USER是QUEEN,则JACK_QUEEN_OTHER(USER)
变为EXPANSION2(x,x, 1, 2, 3)
如果USER是其他用户,则JACK_QUEEN_OTHER(USER)
变为EXPANSION2(ReSeRvEd_other, 1, 2, 3)
在这一点上,发生了一些关键的事情:EXPANSION2宏的第四个参数是1、2或3,具体取决于传递的原始参数是jack,queen还是其他。因此,我们要做的就是挑选出来。出于复杂的原因,最后一步需要两个宏;它们将是EXPANSION2和EXPANSION3,即使其中的一个似乎不必要。
将它们放在一起,我们有以下6个宏:
#define JACK_QUEEN_OTHER(u) EXPANSION1(ReSeRvEd_, u, 1, 2, 3)
#define EXPANSION1(a, b, c, d, e) EXPANSION2(a##b, c, d, e)
#define EXPANSION2(a, b, c, d, ...) EXPANSION3(a, b, c, d)
#define EXPANSION3(a, b, c, d, ...) d
#define ReSeRvEd_JACK x,x,x
#define ReSeRvEd_QUEEN x,x
您可能会这样使用它们:
int main() {
#if JACK_QUEEN_OTHER(USER) == 1
printf("Hello, Jack!\n");
#endif
#if JACK_QUEEN_OTHER(USER) == 2
printf("Hello, Queen!\n");
#endif
#if JACK_QUEEN_OTHER(USER) == 3
printf("Hello, who are you?\n");
#endif
}
强制性的锚定链接:https://godbolt.org/z/8WGa19
答案 9 :(得分:0)
虽然预处理器对字符串的处理非常有限,但大多数编译器在编译时也对字符串了解很多。例如,这可以在编译时成功比较 __BASE_FILE__
和 __FILE__
:
const int zero_div_warning __attribute__((unused)) =
42 / !strcmp(__FILE__ , __BASE_FILE__);
在 .c 文件中找到时使用 gcc -Wno-div-by-zero -Werr
编译成功,在 .h 文件中找到时失败(静态内联函数)
-Wno-div-by-zero 是 -Wall 的一部分
虽然这可能无法解决您的特定用例,但它确实为在编译时比较常量字符串提供了许多可能性。
答案 10 :(得分:-3)
这很简单我认为你可以说
#define NAME JACK
#if NAME == queen