如何在GCC命令行中定义字符串字面量?

106
在GCC命令行上,我想定义一个字符串,例如-Dname=Mary。然后在源代码中,我希望printf("%s", name);打印Mary
我该怎么做?

11
我强烈建议在使用这种方式定义的标记时,使用全大写字母 (-DNAME=\"Mary\"),以便它们看起来像其他宏。 - JSBձոգչ
字符串中的宏问题:https://dev59.com/anVC5IYBdhLWcg3wnCaA - Ciro Santilli OurBigBook.com
9个回答

118

有两个选项。第一个是转义引号,以防shell将其截断:

gcc -Dname=\"Mary\"

或者,如果你真的想要-Dname=Mary,你可以将其字符串化,尽管这有点hacky。

#include <stdio.h>

#define STRINGIZE(x) #x
#define STRINGIZE_VALUE_OF(x) STRINGIZE(x)


int main(int argc, char *argv[])
{
    printf("%s", STRINGIZE_VALUE_OF(name));
}
注意,STRINGIZE_VALUE_OF 将欣然评估宏的最终定义。

非常感谢你,Arthur。你一定是C语言的专家。 进一步的问题: 我更倾向于第二个选项。当我使用STRINGIZE_VALUE_OF(name)时,它将其翻译为"1",在我有gcc -Dname=Mary -DMary的情况下。有没有办法让gcc停止解释Mary呢? - richard
Richard,经过仔细审查,我认为我无法想出在上述示例中有效的方法。不幸的是,您的选择只有不扩展(例如,给您“name”)和完全扩展(例如,如果将Mary定义为1,则为name->Mary->1)。根据您的确切用例,可能会有解决方法--例如,如果Mary可以成为const int而不是define。 - Arthur Shipkowski
有人能解释为什么需要使用嵌套的字符串化宏吗?虽然结果应该相同,但调用STRINGIZE_VALUE_OF()似乎会强制执行参数的宏扩展,而STRINGIZE()则不会。 - Ionoclast Brigham
1
@IonoclastBrigham,直到今天才看到这个。其中一部分原因是有时您想将裸字串转换为字符串 - 例如,在许多情况下,stringize 用于实现 assert(),以便它可以打印出您确切的表达式 - 在这种情况下,您希望宏未展开。一旦您意识到基本的 stringize 是以这种方式工作的,嵌套就会强制进行第二轮宏展开。 - Arthur Shipkowski
如果你无法将 [双引号] 作为参数传递,则第二个选项是最好的选择。在我的情况下,我使用GitLab CI调用PowerShell来调用从Eclipse(Java)编译的.exe文件。在调用gcc之前有太多的解释,因此最好在最后在C代码中处理它。 - ofaurax

34
为了避免 shell “吃掉”引号和其他字符,您可以尝试使用单引号,像这样:
gcc -o test test.cpp -DNAME='"Mary"'

这样你就可以完全控制定义的内容(引号、空格、特殊字符等等)。


10
到目前为止,我发现最便携的方法是使用\"Mary\"。它不仅适用于GCC,还适用于任何其他C编译器。例如,如果您尝试在Microsoft编译器中使用/Dname='"Mary"',它会停止并显示错误,但是/Dname=\"Mary\"将可以正常工作。

8

我在Ubuntu上使用了一个别名(alias),它定义了CFLAGS。CFLAGS包括一个定义字符串的宏,在Makefile中我使用了CFLAGS。我需要转义双引号和反斜杠。代码大致如下:

CFLAGS='" -DMYPATH=\\\"/home/root\\\" "'

2
有时候,单独转义和用单引号包裹都不起作用,但这个方法可以。其他时候则不行。我认为区别在于是否将标志整体放在引号中: 1) DEFINES=-DLOGPATH=\"./logfile\" CFLAGS = -v $(DEFINES)....2) DEFINES=-DLOGPATH=\\\"./logfile\\\" CFLAGS = "-v $(DEFINES)...."使用-v编译器选项可以查看预处理器的操作。 - Den-Jason

2
这是我对-DUSB_PRODUCT=\""Arduino Leonardo\""的解决方案:
我在一个Makefile中使用了它:
GNU Make 3.81 (来自GnuWin32)

avr-g++ (AVR_8_bit_GNU_Toolchain_3.5.0_1662) 4.9.2
在g++中使用-E选项,结果是预编译文件:
const u8 STRING_PRODUCT[] __attribute__((__progmem__)) = "Arduino Leonardo";

1
这是一个简单的例子:

#include <stdio.h>
#define A B+20 
#define B 10
int main()
{
    #ifdef __DEBUG__
        printf("__DEBUG__ DEFINED\n");
        printf("%d\n",A);
    #else
        printf("__DEBUG__ not defined\n");
        printf("%d\n",B);
    #endif
    return 0;
}

If I compile:

$gcc test.c

输出:

__DEBUG__ not defined
10

If I compile:

$gcc -D __DEBUG__ test.c

输出:

__DEBUG__ defined
30

这是一个很好的定义示例,可以像布尔值一样使用。然而,OP询问了更棘手的字符串定义。 - Lassi

0

我刚发现我们的一个应用程序在Ubuntu上无法编译。由于Linux和Windows没有达成共识,我使用了这个方法:

NAME := "Mary"

ifeq ($(SystemRoot),)
    # building on another OS
    CFLAGS_ADD += -Dname=\"Mary\"
else
    # building on Windows
    CFLAGS_ADD += -Dname=\\\"Mary\\\"
endif

0

提示:显然,即使是同一系统上的同一工具链的不同版本,在这方面的表现也可能不同... (就像,似乎这是一个shell传递问题,但显然不仅限于shell)。

在这里,我们有xc32-gcc 4.8.3与(avr-)gcc 4.7.2(以及其他几个) 使用相同的makefile和main.c,唯一的区别是'make CC=xc32-gcc'等。

CFLAGS += -D'THING="$(THINGDIR)/thing.h"'已经在多个版本的gcc(和bash)上使用了数年。

为了使其与xc32-gcc兼容(并考虑到另一个评论声称“"比'更便携”),必须执行以下操作:

CFLAGS += -DTHING=\"$(THINGDIR)/thing.h\"

ifeq "$(CC)" "xc32-gcc"
CFLAGS := $(subst \",\\\",$(CFLAGS))
endif

为了让在发现这个问题时变得更加混乱:显然,带有//的未引用的-D会导致#定义以注释结尾...例如:

THINGDIR=/thingDir/ -> #define /thingDir//thing.h -> #define /thingDir

(顺便感谢这里的答案提供的帮助)。


0

考虑:

{
    "version": "2.0.0",
    "tasks": [
        {
            "type": "cppbuild",
            "label": "C/C++: g++.exe build active file",
            "command": "C:\\Program Files\\mingw-w64\\x86_64-8.1.0-posix-seh-rt_v6-rev0\\mingw64\\bin\\g++.exe",
            "args": [
                "-g",
                "-DSHERAJ",
                "${file}",
                "-o",
                "${fileDirname}\\${fileBasenameNoExtension}.exe"
            ],
            "options": {
                "cwd": "C:\\Program Files\\mingw-w64\\x86_64-8.1.0-posix-seh-rt_v6-rev0\\mingw64\\bin"
            },
            "problemMatcher": [
                "$gcc"
            ],
            "group": {
                "kind": "build",
                "isDefault": true
            },
            "detail": "compiler: \"C:\\Program Files\\mingw-w64\\x86_64-8.1.0-posix-seh-rt_v6-rev0\\mingw64\\bin\\g++.exe\""
        }
    ]
}

我在 Visual Studio Code 中完成了 #define SHERAJ。它非常适用于竞赛编程,因为:

int main() {
    ios_base::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    #ifdef SHERAJ
        freopen("input.txt", "r", stdin);
    #endif
    int T;
    cin>>T;
    for(int test_case = 1;test_case<=T;test_case++) {
        cout<<"Hello, World!"<<endl;
    }
}

对我来说,在Mac和Windows上,这对Visual Studio Code都有效。这里描述的其他方法,如"-Dname=\"SHERAJ\"""-Dname=\\\"SHERAJ\\\""对我无效。

所以答案是"-DSHERAJ"


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接