C/C++ 警告或禁止文字字符串连接

C/C++ warn or prohibit literal string concatenation

提问人:gozag 提问时间:5/19/2023 最后编辑:InSyncgozag 更新时间:5/24/2023 访问量:3349

问:

有没有办法警告或禁止文字字符串连接,例如:

const char *a = "foo" " bar";

我花了几个小时在一个大型静态数组中发现了一个错误,该错误具有

const char * a[] = {"foo" "bar"};

而不是

const char * a[] = {"foo", "bar"};
C++ C 字符串 警告

评论

24赞 Steve Summit 5/19/2023
我同情你的痛苦。我们可能都去过那里。但是,您不希望完全禁止字符串连接,因为许多代码都刻意依赖于它。
2赞 Marek R 5/19/2023
当我有非常日志字符串时,我会使用它,所以这种警告对我来说会很烦人。由于此功能的使用是有效的,我认为没有简单的方法可以自动检测此拼写错误。您应该确保代码经过良好测试。
3赞 Tom Karzes 5/19/2023
@MarekR 是的,它不是万无一失的。但事实并非如此。更重要的是不要错过任何一个。在实践中,你的例子可能非常罕见。要检查的另一件事是以 结尾的行,可能尾随空格。如果需要,可以抓住这些。"grep '"\s*$'
3赞 Richard Critten 5/19/2023
文字字符串连接是编译过程中的逻辑阶段 6,发生在标记化之前。您可能对这个问题无能为力。
3赞 Peter Mortensen 5/21/2023
一些候选者(和/或起点):是否有 GCC 标志来检测字符串文字连接? (2015.“我最近修复了一个错误......有人在 string3“) 之后忘记了 以及 为什么允许串联字符串文字?(2010 年。“我最近被一个微妙的虫子咬了......我忘记了两个之后)

答:

18赞 nielsen 5/19/2023 #1

没有。字符串文字连接是 C/C++ 语法中不可或缺的一部分,并且有许多用例。因此,需要付出某种努力,这可能会破坏抓住失误的目标。

但是,字符串连接非常严格地适用于两个字符串文本,它们之间只有空格,因此中断空格将导致错误。例如,在这种情况下,你可以写成:

const *char[] = {("foo") ("bar")};  // Error

这将导致错误,而预期的语句不会:

const *char[] = {("foo"), ("bar")};  // OK

因此,简而言之,您无法通过某种方式明确地告诉编译器可以连接两个字符串文本,并在所有其他情况下使其失败,因此您必须明确告诉编译器何时字符串文本可能不连接。

评论

12赞 Richard Critten 5/19/2023
“......明确地告诉编译器字符串文字何时不能连接......“ 在它们之间放一个怎么样?我觉得我们已经走了整整一圈。,
4赞 nielsen 5/19/2023
@RichardCritten 是的,主要的一点是,我认为 OP 正在寻找的解决方案在 C/C++ 编译器服务中不存在。
2赞 Barmar 5/21/2023
我认为大多数“不可或缺”的用途都涉及将字符串文字与宏连接起来,例如 printf 格式字符串中使用的宏。更罕见的是,您真的需要只连接字符串文字。
6赞 Cody Gray - on strike 5/21/2023
我一直在连接裸字符串文字,而不涉及任何宏,仅仅是因为我喜欢包装代码行。将每个单独的字符串/元素括在括号中的建议解决方案很有趣。尽管在编写和阅读 C 和 C++ 代码方面有相当丰富的经验,但我不会立即知道该语法是否有效。这当然是有道理的,但它也让我感到可疑。我倾向于在代码审查中标记这一点。虽然有趣并且是上述问题的可能解决方法,但正如理查德所建议的那样,它确实为我们带来了完整的循环。
3赞 Peter Cordes 5/22/2023
@CodyGray:我发现 parens 最初很令人惊讶,但没过多久我就思考了为什么它有效:字符串文字是类型的对象(或者可能在 C 中,我忘记了)。Parens 可以出现在表达式中,并计算到包装的子表达式中。initializer-list 需要表达式列表。我确实不得不考虑几秒钟,但如果代码库到处使用它,我就会习惯它。因此,更大的问题是,这种用于读取代码的额外语法噪声是否比可能的问题更糟糕。const char*char*const char*
50赞 cigien 5/19/2023 #2

Clang 有一个警告 -Wstring-concatenation,它被明确设计用于捕获此类错误:

warning: suspicious concatenation of string literals in an array initialization; did you mean to separate the elements with a comma? [-Wstring-concatenation]
char const *a[]  = { "ok", "foo" "bar", "ok"};
                                 ^
                                ,

这并不完全适用于您展示的玩具示例,因为您需要有多个初始值设定项,并且只在几个地方遗漏逗号,即:

// no warning
char const *b[]  = {"foo" "bar"};
// no warning
char const *c[]  = {"ok", "foo" "bar"};
// no warning
char const *d[]  = {"foo" "bar", "ok"};

但是,当你在一个数组中有大量的初始值设定项,并且只在几个地方犯了一个错别字时,这似乎是理想的。

这是一个演示

GCC 似乎没有等效的警告,但有人请求添加它。

请注意,这仅适用于数组初始化。你的例子

const char *x = "foo" " bar";

不会被此警告(或我知道的任何其他警告)检测到。

另请注意,启用此警告可能会产生大量误报,但在尝试捕获 bug 时可以谨慎使用它。

评论

4赞 gozag 5/19/2023
还找到了一个叮叮当当的!clang.llvm.org/extra/clang-tidy/checks/bugprone/......
0赞 Marek R 5/19/2023
我想知道为什么它在这里不起作用:godbolt.org/z/W4cevbenj
6赞 nielsen 5/19/2023
GCC 通常会警告字符串连接(仅限 C),但可能不建议启用该选项。-Wtraditional
2赞 Juan 5/22/2023 #3

下面的任何一个宏都不可能意外地连接两个字符串。

CPP(C 预处理器)宏通常很棒。在元素列表的末尾使用尾随逗号也是合法的。

你可以做这样的事情:

#define STRINGCOMMA(a) a,

const char *x[] = {
    STRINGCOMMA("foo")
    STRINGCOMMA("bar")
};

甚至:

#define QUOTESTRINGCOMMA(a) #a,

const char *x[] = {
    QUOTESTRINGCOMMA(foo)
    QUOTESTRINGCOMMA(bar)};

逗号是为您添加的,您自己不小心这样做是违法的。

如果您有兴趣,也可以进一步利用这个概念,以允许创建具有相同参数但处理方式不同的并行列表:

X 宏

#define VARLIST \
  DO(foo) \
  DO(bar)

#define DO(a) #a,
  const char *x[] = {
VARLIST
};
#undef DO

如果要从同一名称列表创建枚举列表和字符串列表,这将非常有用。

-2赞 KevinZ 5/24/2023 #4

我花了几个小时在一个大的静态数组中发现了一个错误......

好吧,你可以这样做:

char const * a [] = 
    { "foo"
    , "bar"
    , "baz"
    , "asdf"
    , "ghjk"
    };

评论

0赞 phuclv 6/12/2023
这里有一个类似的问题:clang 格式化 C/C++ 函数是否可以在逗号之前断开参数列表?