33

替代标题(帮助搜索)

  • 将预处理器标记转换为字符串
  • 如何从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. 这甚至是个好主意吗?
4

7 回答 7

40

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

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

s1并且s2是相同的字符串。

因此,对于您的问题,答案(不粘贴令牌)是

#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
于 2009-04-28T14:30:22.230 回答
32

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

  1. 如果您只处理字符串文字,您可以简单地使用简单的使用字符串 - 一个接一个地放置一个字符串文字会导致编译器连接它们。

  2. 如果可能涉及字符串文字以外的其他内容(即,您正在从宏创建新标识符),请使用 ' ##" 预处理器标记粘贴运算符。您可能还需要使用 ' #' 'stringizing 运算符来制作宏成文字字符串。

#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 <stdio.h>

#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定义和上面的实用宏,您可以这样做以获得您想要的:

// Sub-Domain
#define IV_SECURE "secure." STRINGIFY( IV_DOMAIN)   //secure.domain.org etc
#define IV_MOBILE "m." STRINGIFY( IV_DOMAIN)
于 2009-04-28T14:38:27.453 回答
8

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

#define DOMAIN "example.com"
#define SUBDOMAIN "test." DOMAIN
const char *asCString = SUBDOMAIN;
NSString *asNSString = @SUBDOMAIN;
于 2009-04-28T14:25:41.367 回答
8

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

于 2009-04-28T14:40:04.033 回答
6

尝试使用 ## 运算符

#define IV_SECURE secure.##IV_DOMAIN
于 2009-04-28T14:23:26.803 回答
5

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

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

我想到的可能性是

#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 预处理器是多么容易混淆。

于 2009-04-28T14:29:36.630 回答
3

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

__TESTING

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

于 2009-04-28T14:30:26.007 回答