未定义的C/C++符号作为运算符

3

我注意到在C/C++中,字符/符号“`”和“@”并不作为运算符使用,

  1. 有人知道这个原因或历史上为什么会这样吗?
  2. 如果它们真的没有被使用,是否可以安全地使用#define将这些符号定义为另一个运算符/语句?

请注意,@ 在 Objective-C(C 的超集)中被广泛使用。 - kennytm
我严格要求使用C或C++,而不是其他衍生版本或超集,但还是谢谢。 - uray
3个回答

12
通常情况下,#define 只接受宏名称中的有效标识符 - 因此您无法执行以下操作:
#define @      at
#define @(x)   [x]

同样适用于反引号。你没有提到"$",有时允许在标识符中使用。
可能会有特定于编译器的扩展来允许这种映射,但我不会使用它。

关于这一历史原因,ISO 646字符集的某些部分被保留给国家的字符实现。这些保留部分包括引起问题的字符。标准C中的三字符和双字符特性(因此也包括标准C++)分别在1989年和1994年添加到ISO C中,以提供解决问题的方法。

三字符

三字符是在C89标准化过程中添加的,旨在防止人们在其C代码中看到使用字母字符(在斯堪的纳维亚语言中)的情况(从B Stroustrup的“C ++设计与演化”示例中进行了修改,使用丹麦终端):

#include <stdio.h>
int main(int argc, char **argvÆÅ)
æ
    if (argc < 1 øø *argvÆ1Å == 'Ø0') return 0;
    printf("Hello, %sØn", argvÆ1Å);
å

或者,在ISO 8859-1字符集(或任何ISO 8859-x字符集)中:

#include <stdio.h>
int main(int argc, char **argv[])
{
     if (argc < 1 || argv[1] == '\0') return 0;
     printf("Hello, %s\n", argv[1]);
}

三字符序列被引入以产生代码的中性格式:
??=include <stdio.h>
int main(int argc, char **argv??(??))
??<
    if (argc < 1 ??!??! *argv??(1??) == '??/0') return 0;
    printf("Hello, %s??/n", argv??(1??));
??>

这也不是很易读,但对于每个人来说都是一样的。

Trigraph      Equivalent to
??/           \      backslash
??<           {      open brace
??>           }      close brace
??(           [      open square bracket
??)           ]      close square bracket
??=           #      hash (pound in American, but a pound is £ in English)
??'           ^      caret
??!           |      pipe
??-           ~      tilde

标准规定“没有其他三字符组”。这就是为什么转义序列“\?”被识别为一个简单的问号——尽管这可能是“??/?”。请注意,GNU编译器集合(GCC)不会解释三字符组,除非你在命令行上指定“-trigraphs”。
双字符组是在1994年添加的,它们不像三字符组那样普遍或具有侵入性;它们只出现在字符串和字符串字面值之外。这些双字符组包括:
Digraph       Equivalent to
<:            [
:>            ]
<%            {
%>            }
%:            #
%:%:          ##

使用二元组(和三元组)的示例:

示例:

%:include <stdio.h>
%:include <iso646.h>
int main(int argc, char **argv<::>)
<%
    if (argc < 1 or *argv<:1:> == '??/0') return 0;
    printf("Hello, %s??/n", argv<:1:>);
%>

关于@符号和反引号?

如果您查看上面的维基百科URL,您会发现'@'和'`'有时会被国家字符替换,因此不是很好的标识符。不使用'@'的另一个原因是,在引入C语言时,默认的擦除字符是'#',终端的杀死(行擦除)字符是'@'。所以你必须记得对它们进行转义。由于'#'只出现在一行的开头,这并不是太大的问题('#'和'##'要晚得多 - 再次标准化),但'@'将抹掉该行前面的所有输入。而这是在“vi”之前的日子里,“ed是标准的Unix编辑器”。


关于$的问题,你说得对--例如,在大多数平台上,GCC允许在标识符中使用它。 - Stephen Canon

1

这样做可能是安全的,但几乎肯定是一个非常糟糕的想法。由于@不是标准运算符,任何阅读您代码的其他人都必须去追踪@的定义。我们命名函数并不仅使用符号,以便人类读取代码可以弄清楚它的作用。

顺便说一句,Objective-C使用@。不确定是否与您的项目相关,但如果有人尝试从ObjC使用您的C代码,则所有他们的代码都会因为您的#define而中断。


换句话说,我可以通过使用@符号来防止我的代码被链接使用Objective-C,对吗?... - uray
@uray:不需要。ObjC用户可以使用自己制作的头文件。 - kennytm
当然,你可以防止你的代码在Objective-C中使用,但它们也不会是有效的C。每个有效的C程序都是一个有效的Objective-C程序。 - Stephen Canon

1
关于C语言:
一个被 #define 宏定义的宏有一个C标识符名称(§6.10)。
标识符可以由 _a-zA-Z0-9 组成(§6.4.2.1)。其他任何字符都是实现定义的。如果您在宏名称中使用 @,它可能会在某些编译器上工作(尽管我会感到惊讶),但它不具备可移植性。
我不知道C++的情况如何。

基本上,在这个语境中,就标准标识符而言,C和C++是相同的。 - Jonathan Leffler

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