【问题标题】:C Macros to create strings用于创建字符串的 C 宏
【发布时间】:2010-10-22 08:15:35
【问题描述】:

替代标题(帮助搜索)

  • 将预处理器标记转换为字符串
  • 如何从 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 macros c-preprocessor stringification


【解决方案1】:

尝试使用 ## 运算符

#define IV_SECURE secure.##IV_DOMAIN

【讨论】:

  • 我认为 OP 是在询问字符串连接 (#),而不是令牌粘贴 (##)。不过,我没有投反对票。这个问题有点模棱两可。
  • 令牌粘贴 ('##') 或字符串化 ('#') 将无法在没有大量额外内容的情况下工作。
【解决方案2】:

C 编译器将下一个字符串组合在一起。

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

【讨论】:

    【解决方案3】:

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

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

    我想到的可能性是

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

    应该将 IV_SECURE 更改为

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

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

    另一个编辑:请阅读 cmets,它显示了我是如何设法弄糊涂的。请记住,我在 C 语言方面经验丰富,尽管可能有点生疏。我会删除这个答案,但我想我会把它作为一个例子,说明 C 预处理器是多么容易混淆。

    【讨论】:

    • ## 不粘贴字符串,它粘贴标记。微妙但重要的区别。
    • 很遗憾,在我的cygwin盒子里运行的cpp版本不理解你描述的#操作符。
    • # 运算符仅适用于宏参数。 IE。 #define IV_SECURE(DOMAIN) “安全”。 #DOMAIN 在这种情况下帮助不大。
    • 嗯,是的。我现在已经改变了。感谢您的更正。
    • 还有一个问题 - IV_SECURE(IV_DOMAIN) 产生“安全”。 “IV_DOMAIN”不是“安全的”。 “域名.org”
    【解决方案4】:

    在 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
    

    【讨论】:

      【解决方案5】:

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

      __TESTING
      

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

      【讨论】:

      • Objective C 应该是 C 的严格超集,所以这应该是一个有效的通知。
      【解决方案6】:

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

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

      2. 1234563 '#' '字符串化操作符,让你的宏变成文字字符串。

      #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)
      

      【讨论】:

        【解决方案7】:

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

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-07-28
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多