C宏来创建字符串

替代标题(以帮助搜索)

  • 将预处理程序标记转换为字符串
  • 如何从C宏的值创建一个char字符串?

原始问题

我想使用C #define在编译时构建文字字符串。

该字符串是为调试,发布等而更改的域。

我想要一些这样的事情:

 #ifdef __TESTING #define IV_DOMAIN domain.org //in house testing #elif __LIVE_TESTING #define IV_DOMAIN test.domain.com //live testing servers #else #define IV_DOMAIN domain.com //production #endif // Sub-Domain #define IV_SECURE "secure.IV_DOMAIN" //secure.domain.org etc #define IV_MOBILE "m.IV_DOMAIN" 

但预处理器不评估“”内的任何内容

  1. 有没有解决的办法?
  2. 这是一个好主意吗?

在C中,字符串文字自动连接。 例如,

 const char * s1 = "foo" "bar"; const char * s2 = "foobar"; 

s1s2是相同的字符串。

所以,对于你的问题,答案(没有标记粘贴)是

 #ifdef __TESTING #define IV_DOMAIN "domain.org" #elif __LIVE_TESTING #define IV_DOMAIN "test.domain.com" #else #define IV_DOMAIN "domain.com" #endif #define IV_SECURE "secure." IV_DOMAIN #define IV_MOBILE "m." IV_DOMAIN 

有几种方法可以做到这一点:

  1. 如果你只处理字符串文字,你可以简单地使用简单地使用字符串 – 将一个字符串文字放在另一个之后会导致编译器连接它们。

  2. 如果可能存在除了字符串文字之外的其他内容(即,您正在从宏创建新标识符),请使用’ ## ‘预处理程序标记粘贴运算符。您可能还需要使用’ # ”字符串化运算符来使您的宏成为文字字符串。

#1的一个例子:

 #ifdef __TESTING #define IV_DOMAIN "domain.org" //in house testing #elif __LIVE_TESTING #define IV_DOMAIN "test.domain.com" //live testing servers #else #define IV_DOMAIN "domain.com" //production #endif // Sub-Domain #define IV_SECURE "secure." IV_DOMAIN //secure.domain.org etc #define IV_MOBILE "m." IV_DOMAIN 

就令牌粘贴操作符而言,我认为使用令牌粘贴预处理器操作符的大多数答案都没有实际尝试过 – 使用它可能很棘手。

当您尝试使用IV_SECURE宏时,使用经常建议的答案将导致编译器错误,因为:

 #define IV_SECURE "secure."##IV_DOMAIN 

扩展为:

 "secure"domain.org 

您可能想尝试使用' #`”stringizing’运算符:

 #define IV_SECURE "secure." #IV_DOMAIN 

但这不起作用,因为它只适用于宏参数 – 而不仅仅是任何旧的宏。

当你使用令牌粘贴(’##’)或字符串化(’#’)预处理操作符时,要注意的一件事是你必须使用额外的间接级别才能在所有情况下正常工作。

如果你不这样做并且传递给令牌粘贴操作符的项目本身就是宏,那么你将获得可能不是你想要的结果:

 #include  #define STRINGIFY2( x) #x #define STRINGIFY(x) STRINGIFY2(x) #define PASTE2( a, b) a##b #define PASTE( a, b) PASTE2( a, b) #define BAD_PASTE(x,y) x##y #define BAD_STRINGIFY(x) #x #define SOME_MACRO function_name int main() { printf( "buggy results:\n"); printf( "%s\n", STRINGIFY( BAD_PASTE( SOME_MACRO, __LINE__))); printf( "%s\n", BAD_STRINGIFY( BAD_PASTE( SOME_MACRO, __LINE__))); printf( "%s\n", BAD_STRINGIFY( PASTE( SOME_MACRO, __LINE__))); printf( "\n" "desired result:\n"); printf( "%s\n", STRINGIFY( PASTE( SOME_MACRO, __LINE__))); } 

输出:

 buggy results: SOME_MACRO__LINE__ BAD_PASTE( SOME_MACRO, __LINE__) PASTE( SOME_MACRO, __LINE__) desired result: function_name21 

因此,使用您原来的IV_DOMAIN定义和上面的utilty宏,您可以这样做以获得您想要的:

 // Sub-Domain #define IV_SECURE "secure." STRINGIFY( IV_DOMAIN) //secure.domain.org etc #define IV_MOBILE "m." STRINGIFY( IV_DOMAIN) 

接下来的字符串由C编译器组合在一起。

 #define DOMAIN "example.com" #define SUBDOMAIN "test." DOMAIN const char *asCString = SUBDOMAIN; NSString *asNSString = @SUBDOMAIN; 

我看到你的第一个问题有很多正确的答案,但是你的第二个问题没有,所以这就是:我认为这是一个糟糕的主意。 为什么要重建软件(特别是发布版本)只是为了更改服务器名称? 另外,您如何知道哪个版本的软件指向哪个服务器? 您必须构建一个在运行时检查的机制。 如果它在您的平台上完全可行,我建议您从配置文件加载域/ URL。 只有最小的嵌入式平台可能不是“实用”的目的:)

尝试使用##运算符

 #define IV_SECURE secure.##IV_DOMAIN 

您需要的是#和##运算符,以及自动字符串连接。

#preprocessing操作符将宏参数转换为字符串。 ##运算符将两个标记(例如宏参数)粘贴在一起。

我想到的可能性是

 #define IV_DOMAIN domain.org #define IV_SECURE(DOMAIN) "secure." #DOMAIN 

这应该改变IV_SECURE

 #define IV_SECURE "secure." "domain.org" 

它将自动连接到“secure.domain.org”(假设C中的翻译阶段与C ++相同)。

另一个编辑:请,请阅读评论,这些评论显示我是如何感到困惑的。 请记住,我在C中经验丰富,虽然可能是生锈的。 我会删除这个答案,但我想我会留下它作为一个例子,让C预处理器感到困惑是多么容易。

正如其他人所说,使用令牌粘贴。 您还应该知道宏名称如

 __TESTING 

在C中保留(不了解Objective C) – 您不能在自己的代码中使用它们。 保留名称是包含双下划线和任何以下划线和大写字母开头的内容。