在C中创建字符串的宏

dgsult0t  于 2023-05-22  发布在  其他
关注(0)|答案(7)|浏览(120)

替代标题(以帮助搜索)

  • 将预处理器标记转换为字符串
  • 如何从C宏的值生成字符串?

原始问题

我想使用C#define在编译时构建文字字符串。
字符串是为调试、发布等而改变的域。
我想做这样的事情:

#ifdef __TESTING
    #define IV_DOMAIN example.org            // In house testing
#elif __LIVE_TESTING
    #define IV_DOMAIN test.example.com       // Live testing servers
#else
    #define IV_DOMAIN example.com            // Production
#endif

// Subdomain
#define IV_SECURE "secure.IV_DOMAIN"         // secure.example.org, etc.
#define IV_MOBILE "m.IV_DOMAIN"

但预处理器不计算“”中的任何内容。
1.有办法解决吗?
1.这是个好主意吗

yyyllmsg

yyyllmsg1#

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

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

s1s2是同一个字符串。
因此,对于您的问题,答案(不粘贴标记)是

#ifdef __TESTING
    #define IV_DOMAIN "example.org"
#elif __LIVE_TESTING
    #define IV_DOMAIN "test.example.com"
#else
    #define IV_DOMAIN "example.com"
#endif

#define IV_SECURE "secure." IV_DOMAIN
#define IV_MOBILE "m." IV_DOMAIN
gdx19jrr

gdx19jrr2#

有几种方法可以做到这一点:
1.如果你只处理字符串文字,你可以简单地使用simply use strings-把一个字符串文字放在另一个字符串文字之后,编译器会把它们连接起来。
1.如果可能涉及字符串文字以外的其他内容(即,您正在从宏创建新的标识符),请使用“##”预处理器标记粘贴操作符。您可能还需要使用'#' '字符串化操作符将宏转换为文字字符串。
举个例子#1:

#ifdef __TESTING
    #define IV_DOMAIN "example.org"            // In house testing
#elif __LIVE_TESTING
    #define IV_DOMAIN "test.example.com"       // Live testing servers
#else
    #define IV_DOMAIN "example.com"            // Production
#endif

// Subdomain
#define IV_SECURE "secure." IV_DOMAIN          // secure.example.org, etc.
#define IV_MOBILE "m." IV_DOMAIN

至于标记粘贴操作符,我不认为大多数建议使用标记粘贴预处理器操作符的答案实际上已经尝试过了-它使用起来可能很棘手。
当你尝试使用IV_SECURE宏时,使用通常建议的答案会导致编译器错误,因为:

#define IV_SECURE "secure."##IV_DOMAIN

扩展到:

"secure"example.org

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

#define IV_SECURE "secure." #IV_DOMAIN

但这不起作用,因为它只对宏参数起作用,而不是对任何旧的宏。
使用token-paste('##')或stringizing('#')预处理操作符时需要注意的一件事是,必须使用额外的间接层,以便它们在所有情况下都能正常工作。
如果你不这样做,并且传递给标记粘贴操作符的项本身就是宏,你将得到可能不是你想要的结果:

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

// Subdomain
#define IV_SECURE "secure." STRINGIFY( IV_DOMAIN)   //secure.domain.org etc
#define IV_MOBILE "m." STRINGIFY( IV_DOMAIN)
wmvff8tz

wmvff8tz3#

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

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

lrl1mhuk4#

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

uujelgoq

uujelgoq5#

尝试使用##操作符

#define IV_SECURE secure.##IV_DOMAIN
nwwlzxa7

nwwlzxa76#

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

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

8wtpewkr

8wtpewkr7#

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

__TESTING

在C语言中(不知道Objective C)是为实现而保留的-您不允许在自己的代码中使用它们。保留名称是任何包含双下划线的名称,以及任何以下划线和大写字母开头的名称。

相关问题